diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c468ac479..406d6a0a4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -81,7 +81,7 @@ import { ProjectService } from './features/project/project.service'; import { TagService } from './features/tag/tag.service'; import { ContextMenuComponent } from './ui/context-menu/context-menu.component'; import { WorkContextThemeCfg } from './features/work-context/work-context.model'; -import { IsInputElement } from './util/dom-element'; +import { isInputElement } from './util/dom-element'; import { MobileBottomNavComponent } from './core-ui/mobile-bottom-nav/mobile-bottom-nav.component'; interface BeforeInstallPromptEvent extends Event { @@ -338,7 +338,7 @@ export class AppComponent implements OnDestroy, AfterViewInit { @HostListener('document:paste', ['$event']) onPaste(ev: ClipboardEvent): void { // Skip handling inside input elements const target = ev.target as HTMLElement; - if (IsInputElement(target)) return; + if (isInputElement(target)) return; const clipboardData = ev.clipboardData; if (!clipboardData) return; diff --git a/src/app/core-ui/shortcut/shortcut.service.ts b/src/app/core-ui/shortcut/shortcut.service.ts index b045a1b2c..5111bca30 100644 --- a/src/app/core-ui/shortcut/shortcut.service.ts +++ b/src/app/core-ui/shortcut/shortcut.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable } from '@angular/core'; import { IS_ELECTRON } from '../../app.constants'; import { checkKeyCombo } from '../../util/check-key-combo'; -import { IsInputElement } from '../../util/dom-element'; +import { isInputElement } from '../../util/dom-element'; import { GlobalConfigService } from '../../features/config/global-config.service'; import { ActivatedRoute, Router } from '@angular/router'; import { LayoutService } from '../layout/layout.service'; @@ -83,7 +83,7 @@ export class ShortcutService { const el = ev.target as HTMLElement; // Skip handling if no special keys are used and inside input elements - if (!ev.metaKey && IsInputElement(el)) return; + if (!ev.metaKey && isInputElement(el)) return; if ( checkKeyCombo(ev, keys.toggleBacklog) && diff --git a/src/app/features/note/note.service.ts b/src/app/features/note/note.service.ts index 67b4c8eb0..d77d678ef 100644 --- a/src/app/features/note/note.service.ts +++ b/src/app/features/note/note.service.ts @@ -22,7 +22,7 @@ import { DropPasteInput } from '../../core/drop-paste-input/drop-paste.model'; import { WorkContextService } from '../work-context/work-context.service'; import { WorkContextType } from '../work-context/work-context.model'; import { PfapiService } from '../../pfapi/pfapi.service'; -import { IsInputElement } from '../../util/dom-element'; +import { isInputElement } from '../../util/dom-element'; @Injectable({ providedIn: 'root', @@ -116,7 +116,7 @@ export class NoteService { // Skip handling inside input elements const target = ev.target as HTMLElement; - if (IsInputElement(target)) return; + if (isInputElement(target)) return; const note: Partial = { content: drop.path, diff --git a/src/app/features/right-panel/right-panel.component.ts b/src/app/features/right-panel/right-panel.component.ts index 390b8c795..8a80c5956 100644 --- a/src/app/features/right-panel/right-panel.component.ts +++ b/src/app/features/right-panel/right-panel.component.ts @@ -33,6 +33,7 @@ import { INITIAL_LAYOUT_STATE, selectLayoutFeatureState, } from '../../core-ui/layout/store/layout.reducer'; +import { isInputElement } from '../../util/dom-element'; // Right panel resize constants const RIGHT_PANEL_CONFIG = { @@ -257,11 +258,10 @@ export class RightPanelComponent implements AfterViewInit, OnDestroy { close(): void { // FORCE blur because otherwise task notes won't save if (IS_TOUCH_PRIMARY) { - document.querySelectorAll('input,textarea').forEach((element) => { - if (element === document.activeElement) { - return (element as HTMLElement).blur(); - } - }); + const activeElement = document.activeElement as HTMLElement; + if (activeElement && isInputElement(activeElement)) { + activeElement.blur(); + } } // Delegate to task service and layout service to close the panel @@ -399,7 +399,7 @@ export class RightPanelComponent implements AfterViewInit, OnDestroy { document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; if (isTouch) { - (document.body.style as any).touchAction = 'none'; + document.body.style.setProperty('touch-action', 'none'); } } @@ -408,7 +408,7 @@ export class RightPanelComponent implements AfterViewInit, OnDestroy { document.removeEventListener('pointerup', this._boundOnPointerUp); document.body.style.cursor = ''; document.body.style.userSelect = ''; - (document.body.style as any).touchAction = ''; + document.body.style.setProperty('touch-action', ''); } onCloseButtonPointerUp(event: PointerEvent): void { diff --git a/src/app/features/tasks/task-attachment/task-attachment.service.ts b/src/app/features/tasks/task-attachment/task-attachment.service.ts index f003c8f14..0941addc3 100644 --- a/src/app/features/tasks/task-attachment/task-attachment.service.ts +++ b/src/app/features/tasks/task-attachment/task-attachment.service.ts @@ -13,7 +13,7 @@ import { import { TaskState } from '../task.model'; import { createFromDrop } from 'src/app/core/drop-paste-input/drop-paste-input'; import { TaskLog } from '../../../core/log'; -import { IsInputElement } from '../../../util/dom-element'; +import { isInputElement } from '../../../util/dom-element'; @Injectable({ providedIn: 'root', @@ -77,7 +77,7 @@ export class TaskAttachmentService { // don't intervene with text inputs const targetEl = ev.target as HTMLElement; - if (!isSkipTextareaCheck && IsInputElement(targetEl)) return; + if (!isSkipTextareaCheck && isInputElement(targetEl)) return; ev.preventDefault(); ev.stopPropagation(); diff --git a/src/app/features/tasks/task-detail-panel/task-additional-info-item/task-detail-item.component.ts b/src/app/features/tasks/task-detail-panel/task-additional-info-item/task-detail-item.component.ts index 085760c18..2390f97d2 100644 --- a/src/app/features/tasks/task-detail-panel/task-additional-info-item/task-detail-item.component.ts +++ b/src/app/features/tasks/task-detail-panel/task-additional-info-item/task-detail-item.component.ts @@ -16,7 +16,7 @@ import { MatExpansionPanelHeader, MatExpansionPanelTitle, } from '@angular/material/expansion'; -import { IsInputElement } from '../../../../util/dom-element'; +import { isInputElement } from '../../../../util/dom-element'; @Component({ selector: 'task-detail-item', @@ -56,7 +56,7 @@ export class TaskDetailItemComponent { @HostListener('keydown', ['$event']) onKeyDown(ev: KeyboardEvent): void { // Skip handling inside input elements const targetEl = ev.target as HTMLElement; - if (IsInputElement(targetEl)) return; + if (isInputElement(targetEl)) return; this.keyPress.emit(ev); if (ev.code === 'Escape') { diff --git a/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts b/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts index b4ee67671..c7e60713a 100644 --- a/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts +++ b/src/app/features/tasks/task-detail-panel/task-detail-panel.component.ts @@ -75,7 +75,7 @@ import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-i import { getDbDateStr } from '../../../util/get-db-date-str'; import { isMarkdownChecklist } from '../../markdown-checklist/is-markdown-checklist'; import { Log } from '../../../core/log'; -import { IsInputElement } from '../../../util/dom-element'; +import { isInputElement } from '../../../util/dom-element'; @Component({ selector: 'task-detail-panel', @@ -148,7 +148,7 @@ export class TaskDetailPanelComponent implements OnInit, AfterViewInit, OnDestro onKeydown(ev: KeyboardEvent): void { // Skip handling inside input elements const target = ev.target as HTMLElement; - if (IsInputElement(target)) return; + if (isInputElement(target)) return; const cfg = this._globalConfigService.cfg(); if (!cfg) throw new Error('No config service available'); diff --git a/src/app/util/dom-element.ts b/src/app/util/dom-element.ts index a687ba1d0..52efb29c2 100644 --- a/src/app/util/dom-element.ts +++ b/src/app/util/dom-element.ts @@ -1,7 +1,7 @@ // HERE YOU CAN PUT HELPFUL UTIL FUNCTIONS RELATED TO DOM ELEMENTS /** Checks if the element is an input (input, textarea, or contenteditable) */ -export const IsInputElement = (el: HTMLElement): boolean => { +export const isInputElement = (el: HTMLElement): boolean => { return !!( el.tagName.toUpperCase() === 'INPUT' || el.tagName.toUpperCase() === 'TEXTAREA' || diff --git a/tools/add-missing-i18n-variables.js b/tools/add-missing-i18n-variables.js index ed539f82d..11d991e8c 100644 --- a/tools/add-missing-i18n-variables.js +++ b/tools/add-missing-i18n-variables.js @@ -71,7 +71,12 @@ for (const file of i18nFiles) { console.log(`✓ Updated ${file}`); updatedFiles++; } catch (error) { - console.error(`✗ Error processing ${file}:`, error.message); + console.error(`✗ Error processing ${file}:`); + console.error(` Path: ${langPath}`); + console.error(` Error: ${error.message}`); + if (error.stack) { + console.error(` Stack: ${error.stack.split('\n')[1]?.trim()}`); + } errors++; } }