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.
This commit is contained in:
callumalpass 2025-11-20 23:37:38 +11:00
parent b8c3747e72
commit ef8d9eb750
9 changed files with 138 additions and 1 deletions

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();

View file

@ -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();

View file

@ -11,6 +11,7 @@ export class TimeEntryEditorModal extends Modal {
private onSave: (timeEntries: TimeEntry[]) => void;
private translate: (key: TranslationKey, variables?: Record<string, any>) => 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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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

View file

@ -8,7 +8,6 @@
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"ignoreDeprecations": "6.0",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,