From ef8d9eb7503d88a107616092916efed4aac2fc51 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Thu, 20 Nov 2025 23:37:38 +1100 Subject: [PATCH] Add CMD/Ctrl+Enter keyboard shortcuts to modals Added keyboard event handlers to all modals to enable quick save/submit with CMD/Ctrl+Enter. Each modal now properly registers and cleans up its keyboard handler on open/close. Also added kanban card container styles for scrolling and drag cursor states, and removed deprecated tsconfig ignoreDeprecations flag. --- src/modals/ICSNoteCreationModal.ts | 17 +++++++++++++++++ src/modals/PropertySelectorModal.ts | 17 +++++++++++++++++ src/modals/TaskEditModal.ts | 17 +++++++++++++++++ src/modals/TaskModal.ts | 21 +++++++++++++++++++++ src/modals/TimeEntryEditorModal.ts | 16 ++++++++++++++++ src/modals/TimeblockCreationModal.ts | 16 ++++++++++++++++ src/modals/TimeblockInfoModal.ts | 16 ++++++++++++++++ styles/kanban-view.css | 18 ++++++++++++++++++ tsconfig.json | 1 - 9 files changed, 138 insertions(+), 1 deletion(-) diff --git a/src/modals/ICSNoteCreationModal.ts b/src/modals/ICSNoteCreationModal.ts index 8fad3372..98305f6a 100644 --- a/src/modals/ICSNoteCreationModal.ts +++ b/src/modals/ICSNoteCreationModal.ts @@ -27,6 +27,7 @@ export class ICSNoteCreationModal extends Modal { private templateContainer: HTMLElement; private templateInput: HTMLInputElement; private previewContainer: HTMLElement; + private keyboardHandler: ((e: KeyboardEvent) => void) | null = null; constructor(app: App, plugin: TaskNotesPlugin, options: ICSNoteCreationOptions) { super(app); @@ -42,10 +43,26 @@ export class ICSNoteCreationModal extends Modal { onOpen() { this.containerEl.addClass("tasknotes-plugin", "ics-note-creation-modal"); + + // Add global keyboard shortcut handler for CMD/Ctrl+Enter + this.keyboardHandler = (e: KeyboardEvent) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.handleCreate(); + } + }; + this.containerEl.addEventListener("keydown", this.keyboardHandler); + this.createModalContent(); } onClose() { + // Clean up keyboard handler + if (this.keyboardHandler) { + this.containerEl.removeEventListener("keydown", this.keyboardHandler); + this.keyboardHandler = null; + } + this.contentEl.empty(); } diff --git a/src/modals/PropertySelectorModal.ts b/src/modals/PropertySelectorModal.ts index c07fa7dd..ca10dacb 100644 --- a/src/modals/PropertySelectorModal.ts +++ b/src/modals/PropertySelectorModal.ts @@ -11,6 +11,7 @@ export class PropertySelectorModal extends Modal { private tempSelection: string[]; private modalTitle: string; private modalDescription: string; + private keyboardHandler: ((e: KeyboardEvent) => void) | null = null; constructor( app: App, @@ -33,6 +34,16 @@ export class PropertySelectorModal extends Modal { const { contentEl } = this; contentEl.empty(); + // Add global keyboard shortcut handler for CMD/Ctrl+Enter + this.keyboardHandler = (e: KeyboardEvent) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.onSubmit(this.tempSelection); + this.close(); + } + }; + this.containerEl.addEventListener("keydown", this.keyboardHandler); + contentEl.createEl("h2", { text: this.modalTitle }); contentEl.createEl("p", { @@ -91,6 +102,12 @@ export class PropertySelectorModal extends Modal { } onClose() { + // Clean up keyboard handler + if (this.keyboardHandler) { + this.containerEl.removeEventListener("keydown", this.keyboardHandler); + this.keyboardHandler = null; + } + const { contentEl } = this; contentEl.empty(); } diff --git a/src/modals/TaskEditModal.ts b/src/modals/TaskEditModal.ts index 550f3d12..8cc563d4 100644 --- a/src/modals/TaskEditModal.ts +++ b/src/modals/TaskEditModal.ts @@ -37,6 +37,7 @@ export class TaskEditModal extends TaskModal { private task: TaskInfo; private options: TaskEditOptions; private metadataContainer: HTMLElement; + private editModalKeyboardHandler: ((e: KeyboardEvent) => void) | null = null; // Changed from Set to array for consistency with other state management private completedInstancesChanges: string[] = []; private calendarWrapper: HTMLElement | null = null; @@ -227,6 +228,16 @@ export class TaskEditModal extends TaskModal { // Set the modal title using the standard Obsidian approach (preserves close button) this.titleEl.setText(this.getModalTitle()); + // Add global keyboard shortcut handler for CMD/Ctrl+Enter + this.editModalKeyboardHandler = async (e: KeyboardEvent) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + await this.handleSave(); + this.close(); + } + }; + this.containerEl.addEventListener("keydown", this.editModalKeyboardHandler); + this.initializeFormData().then(() => { this.createModalContent(); // Render projects list after modal content is created @@ -371,6 +382,12 @@ export class TaskEditModal extends TaskModal { } onClose(): void { + // Clean up keyboard handler + if (this.editModalKeyboardHandler) { + this.containerEl.removeEventListener("keydown", this.editModalKeyboardHandler); + this.editModalKeyboardHandler = null; + } + // Clean up markdown editor if (this.markdownEditor) { this.markdownEditor.destroy(); diff --git a/src/modals/TaskModal.ts b/src/modals/TaskModal.ts index 7671df25..87bd5c2a 100644 --- a/src/modals/TaskModal.ts +++ b/src/modals/TaskModal.ts @@ -42,6 +42,7 @@ interface DependencyItem { export abstract class TaskModal extends Modal { plugin: TaskNotesPlugin; + private keyboardHandler: ((e: KeyboardEvent) => void) | null = null; // Dependency item definition protected createDependencyItemFromFile( @@ -486,6 +487,20 @@ export abstract class TaskModal extends Modal { // Set the modal title using the standard Obsidian approach (preserves close button) this.titleEl.setText(this.getModalTitle()); + // Add global keyboard shortcut handler for CMD/Ctrl+Enter + this.keyboardHandler = (e: KeyboardEvent) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + // Skip if event comes from a markdown editor (which has its own handler) + const target = e.target as HTMLElement; + if (target.closest('.cm-editor')) { + return; + } + e.preventDefault(); + this.handleSave(); + } + }; + this.containerEl.addEventListener("keydown", this.keyboardHandler); + this.initializeFormData().then(() => { this.createModalContent(); this.focusTitleInput(); @@ -1889,6 +1904,12 @@ export abstract class TaskModal extends Modal { } onClose(): void { + // Clean up keyboard handler + if (this.keyboardHandler) { + this.containerEl.removeEventListener("keydown", this.keyboardHandler); + this.keyboardHandler = null; + } + // Clean up markdown editor if it exists if (this.detailsMarkdownEditor) { this.detailsMarkdownEditor.destroy(); diff --git a/src/modals/TimeEntryEditorModal.ts b/src/modals/TimeEntryEditorModal.ts index 11615029..5352137f 100644 --- a/src/modals/TimeEntryEditorModal.ts +++ b/src/modals/TimeEntryEditorModal.ts @@ -11,6 +11,7 @@ export class TimeEntryEditorModal extends Modal { private onSave: (timeEntries: TimeEntry[]) => void; private translate: (key: TranslationKey, variables?: Record) => string; private entriesContainerEl: HTMLElement; + private keyboardHandler: ((e: KeyboardEvent) => void) | null = null; constructor( app: App, @@ -37,6 +38,15 @@ export class TimeEntryEditorModal extends Modal { this.translate("modals.timeEntryEditor.title", { taskTitle: this.task.title }) ); + // Add global keyboard shortcut handler for CMD/Ctrl+Enter + this.keyboardHandler = (e: KeyboardEvent) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.save(); + } + }; + this.containerEl.addEventListener("keydown", this.keyboardHandler); + // Create container for entries this.entriesContainerEl = contentEl.createDiv({ cls: "time-entry-editor-modal__entries" }); @@ -261,6 +271,12 @@ export class TimeEntryEditorModal extends Modal { } onClose() { + // Clean up keyboard handler + if (this.keyboardHandler) { + this.containerEl.removeEventListener("keydown", this.keyboardHandler); + this.keyboardHandler = null; + } + const { contentEl } = this; contentEl.empty(); } diff --git a/src/modals/TimeblockCreationModal.ts b/src/modals/TimeblockCreationModal.ts index 8cb0bdbe..74d19bf2 100644 --- a/src/modals/TimeblockCreationModal.ts +++ b/src/modals/TimeblockCreationModal.ts @@ -43,6 +43,7 @@ export class TimeblockCreationModal extends Modal { // Attachment management private selectedAttachments: TAbstractFile[] = []; private attachmentsList: HTMLElement; + private keyboardHandler: ((e: KeyboardEvent) => void) | null = null; constructor(app: App, plugin: TaskNotesPlugin, options: TimeblockCreationOptions) { super(app); @@ -56,6 +57,15 @@ export class TimeblockCreationModal extends Modal { contentEl.empty(); contentEl.addClass("timeblock-creation-modal"); + // Add global keyboard shortcut handler for CMD/Ctrl+Enter + this.keyboardHandler = (e: KeyboardEvent) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.handleSubmit(); + } + }; + this.containerEl.addEventListener("keydown", this.keyboardHandler); + new Setting(contentEl).setName(this.translate("modals.timeblockCreation.heading")).setHeading(); // Date display (read-only) @@ -386,6 +396,12 @@ export class TimeblockCreationModal extends Modal { } onClose() { + // Clean up keyboard handler + if (this.keyboardHandler) { + this.containerEl.removeEventListener("keydown", this.keyboardHandler); + this.keyboardHandler = null; + } + const { contentEl } = this; contentEl.empty(); } diff --git a/src/modals/TimeblockInfoModal.ts b/src/modals/TimeblockInfoModal.ts index 52dc7f15..f0fd09ce 100644 --- a/src/modals/TimeblockInfoModal.ts +++ b/src/modals/TimeblockInfoModal.ts @@ -48,6 +48,7 @@ export class TimeblockInfoModal extends Modal { // Attachment management private selectedAttachments: TAbstractFile[] = []; private attachmentsList: HTMLElement; + private keyboardHandler: ((e: KeyboardEvent) => void) | null = null; constructor( app: App, @@ -70,6 +71,15 @@ export class TimeblockInfoModal extends Modal { contentEl.empty(); contentEl.addClass("timeblock-info-modal"); + // Add global keyboard shortcut handler for CMD/Ctrl+Enter + this.keyboardHandler = (e: KeyboardEvent) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.handleSave(); + } + }; + this.containerEl.addEventListener("keydown", this.keyboardHandler); + new Setting(contentEl).setName(this.translate("modals.timeblockInfo.editHeading")).setHeading(); // Date and time display (read-only) @@ -505,6 +515,12 @@ export class TimeblockInfoModal extends Modal { } onClose() { + // Clean up keyboard handler + if (this.keyboardHandler) { + this.containerEl.removeEventListener("keydown", this.keyboardHandler); + this.keyboardHandler = null; + } + const { contentEl } = this; contentEl.empty(); } diff --git a/styles/kanban-view.css b/styles/kanban-view.css index 73324e52..aa00a6c4 100644 --- a/styles/kanban-view.css +++ b/styles/kanban-view.css @@ -376,6 +376,24 @@ KANBAN TASK CARDS ================================================ */ +/* Cards container (used for non-virtualized columns) */ +.tasknotes-plugin .kanban-view__cards { + display: flex; + flex-direction: column; + gap: var(--tn-spacing-xs); + overflow-y: auto; /* Allow vertical scrolling */ + flex: 1; /* Fill available space in column */ + min-height: 0; /* Allow container to shrink and enable scrolling */ +} + +/* Dragging state for card wrappers */ +.tasknotes-plugin .kanban-view__card-wrapper[draggable="true"]:hover { + cursor: grab; +} + +.tasknotes-plugin .kanban-view__card-wrapper[draggable="true"]:active { + cursor: grabbing; +} /* ================================================ RESPONSIVE DESIGN diff --git a/tsconfig.json b/tsconfig.json index c02400d5..c6d64e6a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,6 @@ "allowJs": true, "noImplicitAny": true, "moduleResolution": "node", - "ignoreDeprecations": "6.0", "importHelpers": true, "isolatedModules": true, "strictNullChecks": true,