refactor: migrate to signals for querries

This commit is contained in:
Johannes Millan 2024-12-29 17:17:31 +01:00
parent 8663819185
commit 150e582936
37 changed files with 293 additions and 266 deletions

View file

@ -4,8 +4,8 @@ import {
HostBinding,
HostListener,
OnDestroy,
ViewChild,
ViewContainerRef,
viewChild,
} from '@angular/core';
import { ChromeExtensionInterfaceService } from './core/chrome-extension-interface/chrome-extension-interface.service';
import { ShortcutService } from './core-ui/shortcut/shortcut.service';
@ -65,8 +65,8 @@ export class AppComponent implements OnDestroy {
@HostBinding('@.disabled') isDisableAnimations = false;
@ViewChild('notesElRef', { read: ViewContainerRef }) notesElRef?: ViewContainerRef;
@ViewChild('sideNavElRef', { read: ViewContainerRef }) sideNavElRef?: ViewContainerRef;
readonly notesElRef = viewChild('notesElRef', { read: ViewContainerRef });
readonly sideNavElRef = viewChild('sideNavElRef', { read: ViewContainerRef });
isRTL: boolean = false;

View file

@ -5,7 +5,7 @@ import {
OnDestroy,
OnInit,
Renderer2,
ViewChild,
viewChild,
} from '@angular/core';
import { ProjectService } from '../../features/project/project.service';
import { LayoutService } from '../layout/layout.service';
@ -44,7 +44,7 @@ export class MainHeaderComponent implements OnInit, OnDestroy {
circumference: number = this.progressCircleRadius * Math.PI * 2;
isShowSimpleCounterBtnsMobile: boolean = false;
@ViewChild('circleSvg', { static: true }) circleSvg?: ElementRef;
readonly circleSvg = viewChild<ElementRef>('circleSvg');
currentTaskContext$: Observable<Project | Tag | null> =
this.taskService.currentTaskParentOrCurrent$.pipe(
@ -104,17 +104,14 @@ export class MainHeaderComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.taskService.currentTaskProgress$.subscribe((progressIN) => {
if (this.circleSvg) {
const circleSvg = this.circleSvg();
if (circleSvg) {
let progress = progressIN || 1;
if (progress > 1) {
progress = 1;
}
const dashOffset = this.circumference * -1 * progress;
this._renderer.setStyle(
this.circleSvg.nativeElement,
'stroke-dashoffset',
dashOffset,
);
this._renderer.setStyle(circleSvg.nativeElement, 'stroke-dashoffset', dashOffset);
}
});
}

View file

@ -4,7 +4,7 @@ import {
ElementRef,
HostBinding,
input,
ViewChild,
viewChild,
} from '@angular/core';
import { RouterLink, RouterModule } from '@angular/router';
import { UiModule } from '../../../ui/ui.module';
@ -41,8 +41,7 @@ export class SideNavItemComponent {
contextMenuPosition: { x: string; y: string } = { x: '0px', y: '0px' };
@ViewChild('routeBtn', { static: true, read: ElementRef })
routeBtn!: ElementRef;
readonly routeBtn = viewChild.required('routeBtn', { read: ElementRef });
@HostBinding('class.hasTasks')
get workContextHasTasks(): boolean {
@ -60,6 +59,6 @@ export class SideNavItemComponent {
}
focus(): void {
this.routeBtn.nativeElement.focus();
this.routeBtn().nativeElement.focus();
}
}

View file

@ -5,9 +5,8 @@ import {
HostBinding,
HostListener,
OnDestroy,
QueryList,
ViewChild,
ViewChildren,
viewChildren,
viewChild,
} from '@angular/core';
import { ProjectService } from '../../features/project/project.service';
import { T } from '../../t.const';
@ -53,13 +52,13 @@ import { updateProject } from '../../features/project/store/project.actions';
standalone: false,
})
export class SideNavComponent implements OnDestroy {
@ViewChildren('menuEntry') navEntries?: QueryList<MatMenuItem>;
readonly navEntries = viewChildren<MatMenuItem>('menuEntry');
IS_MOUSE_PRIMARY = IS_MOUSE_PRIMARY;
IS_TOUCH_PRIMARY = IS_TOUCH_PRIMARY;
DRAG_DELAY_FOR_TOUCH_LONGER = DRAG_DELAY_FOR_TOUCH_LONGER;
keyboardFocusTimeout?: number;
@ViewChild('projectExpandBtn', { read: ElementRef }) projectExpandBtn?: ElementRef;
readonly projectExpandBtn = viewChild('projectExpandBtn', { read: ElementRef });
isProjectsExpanded: boolean = this.fetchProjectListState();
isProjectsExpanded$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(
this.isProjectsExpanded,
@ -77,7 +76,7 @@ export class SideNavComponent implements OnDestroy {
),
);
@ViewChild('tagExpandBtn', { read: ElementRef }) tagExpandBtn?: ElementRef;
readonly tagExpandBtn = viewChild('tagExpandBtn', { read: ElementRef });
isTagsExpanded: boolean = this.fetchTagListState();
isTagsExpanded$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(
this.isTagsExpanded,
@ -123,9 +122,10 @@ export class SideNavComponent implements OnDestroy {
this._subs.add(
this._layoutService.isShowSideNav$.subscribe((isShow) => {
if (this.navEntries && isShow) {
const navEntries = this.navEntries();
if (navEntries && isShow) {
this.keyManager = new FocusKeyManager<MatMenuItem>(
this.navEntries,
navEntries,
).withVerticalOrientation(true);
window.clearTimeout(this.keyboardFocusTimeout);
this.keyboardFocusTimeout = window.setTimeout(() => {
@ -190,9 +190,9 @@ export class SideNavComponent implements OnDestroy {
}
checkFocusProject(ev: KeyboardEvent): void {
if (ev.key === 'ArrowLeft' && this.projectExpandBtn?.nativeElement) {
const targetIndex = this.navEntries?.toArray().findIndex((value) => {
return value._getHostElement() === this.projectExpandBtn?.nativeElement;
if (ev.key === 'ArrowLeft' && this.projectExpandBtn()?.nativeElement) {
const targetIndex = this.navEntries().findIndex((value) => {
return value._getHostElement() === this.projectExpandBtn()?.nativeElement;
});
if (targetIndex) {
this.keyManager?.setActiveItem(targetIndex);
@ -217,9 +217,9 @@ export class SideNavComponent implements OnDestroy {
}
checkFocusTag(ev: KeyboardEvent): void {
if (ev.key === 'ArrowLeft' && this.tagExpandBtn?.nativeElement) {
const targetIndex = this.navEntries?.toArray().findIndex((value) => {
return value._getHostElement() === this.tagExpandBtn?.nativeElement;
if (ev.key === 'ArrowLeft' && this.tagExpandBtn()?.nativeElement) {
const targetIndex = this.navEntries().findIndex((value) => {
return value._getHostElement() === this.tagExpandBtn()?.nativeElement;
});
if (targetIndex) {
this.keyManager?.setActiveItem(targetIndex);

View file

@ -36,6 +36,8 @@ export class BookmarkBarComponent {
private readonly _matDialog: MatDialog,
) {}
// TODO: Skipped for migration because:
// Accessor queries cannot be migrated as they are too complex.
@ViewChild('bookmarkBar', { read: ElementRef }) set bookmarkBarEl(content: ElementRef) {
if (content && content.nativeElement) {
this.bookmarkBarHeight = content.nativeElement.offsetHeight;

View file

@ -9,8 +9,8 @@ import {
OnDestroy,
OnInit,
Output,
ViewChild,
ViewContainerRef,
viewChild,
} from '@angular/core';
import { expandAnimation } from '../../../ui/animations/expand.ani';
import {
@ -40,8 +40,7 @@ export class ConfigSectionComponent implements OnInit, OnDestroy {
sectionKey: GlobalConfigSectionKey | ProjectCfgFormKey | TagCfgFormKey;
config: any;
}> = new EventEmitter();
@ViewChild('customForm', { read: ViewContainerRef, static: true })
customFormRef?: ViewContainerRef;
readonly customFormRef = viewChild('customForm', { read: ViewContainerRef });
isExpanded: boolean = false;
private _subs: Subscription = new Subscription();
private _instance?: Component;
@ -84,13 +83,14 @@ export class ConfigSectionComponent implements OnInit, OnDestroy {
this._workContextService.onWorkContextChange$.subscribe(() => {
this._cd.markForCheck();
const customFormRef = this.customFormRef();
if (
this.section &&
this.section.customSection &&
this.customFormRef &&
customFormRef &&
this.section.customSection
) {
this.customFormRef.clear();
customFormRef.clear();
// dirty trick to make sure data is actually there
this._viewDestroyTimeout = window.setTimeout(() => {
this._loadCustomSection((this.section as any).customSection);
@ -126,7 +126,7 @@ export class ConfigSectionComponent implements OnInit, OnDestroy {
if (componentToRender) {
const factory: ComponentFactory<any> =
this._componentFactoryResolver.resolveComponentFactory(componentToRender as any);
const ref = exists<any>(this.customFormRef).createComponent(factory);
const ref = exists<any>(this.customFormRef()).createComponent(factory);
// NOTE: important that this is set only if we actually have a value
// otherwise the default fallback will be overwritten

View file

@ -5,7 +5,7 @@ import {
input,
OnInit,
signal,
ViewChild,
viewChild,
} from '@angular/core';
import { ErrorCardComponent } from '../../../ui/error-card/error-card.component';
import { IssuePreviewItemComponent } from '../issue-preview-item/issue-preview-item.component';
@ -39,7 +39,7 @@ export class IssuePanelCalendarAgendaComponent implements OnInit {
error = signal<string | undefined>(undefined);
isLoading = signal(false);
@ViewChild(CdkDropList) dropList?: CdkDropList;
readonly dropList = viewChild(CdkDropList);
agendaItems = signal<
{

View file

@ -8,7 +8,7 @@ import {
input,
OnDestroy,
signal,
ViewChild,
viewChild,
} from '@angular/core';
import { IssuePreviewItemComponent } from '../issue-preview-item/issue-preview-item.component';
import { MatIcon } from '@angular/material/icon';
@ -173,8 +173,8 @@ export class IssueProviderTabComponent implements OnDestroy, AfterViewInit {
);
issueItems = toSignal(this.issueItems$.pipe(map((v) => v.notAdded)));
@ViewChild(CdkDropList) dropList?: CdkDropList;
@ViewChild('searchTextEl') searchTextEl!: ElementRef;
readonly dropList = viewChild(CdkDropList);
readonly searchTextEl = viewChild.required<ElementRef>('searchTextEl');
private _focusTimeout?: number;
@ -182,7 +182,7 @@ export class IssueProviderTabComponent implements OnDestroy, AfterViewInit {
// this.dropListService.registerDropList(this.dropList!);
if (this.searchText().length <= 1 && IS_MOUSE_PRIMARY) {
this._focusTimeout = window.setTimeout(() => {
this.searchTextEl?.nativeElement.focus();
this.searchTextEl()?.nativeElement.focus();
}, 500);
}
this.searchText.set(this.issueProvider().pinnedSearch || '');

View file

@ -4,7 +4,7 @@ import {
Input,
OnChanges,
SimpleChanges,
ViewChild,
viewChild,
} from '@angular/core';
import { Note } from '../note.model';
import { NoteService } from '../note.service';
@ -37,7 +37,7 @@ export class NoteComponent implements OnChanges {
@Input() isFocus?: boolean;
@ViewChild('markdownEl') markdownEl?: HTMLElement;
readonly markdownEl = viewChild<HTMLElement>('markdownEl');
isLongNote?: boolean;
shortenedNote?: string;

View file

@ -2,7 +2,7 @@ import {
ChangeDetectionStrategy,
Component,
HostListener,
ViewChild,
viewChild,
} from '@angular/core';
import { NoteService } from '../note.service';
import { MatButton } from '@angular/material/button';
@ -30,7 +30,7 @@ export class NotesComponent {
isDragOver: boolean = false;
dragEnterTarget?: HTMLElement;
@ViewChild('buttonEl', { static: true }) buttonEl?: MatButton;
readonly buttonEl = viewChild<MatButton>('buttonEl');
constructor(
public noteService: NoteService,

View file

@ -4,7 +4,7 @@ import {
ChangeDetectorRef,
Component,
Inject,
ViewChild,
viewChild,
} from '@angular/core';
import { UiModule } from '../../../ui/ui.module';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
@ -52,7 +52,7 @@ import { WorkContextService } from '../../work-context/work-context.service';
export class DialogScheduleTaskComponent implements AfterViewInit {
T: typeof T = T;
minDate = new Date();
@ViewChild(MatCalendar, { static: true }) calendar!: MatCalendar<Date>;
readonly calendar = viewChild.required(MatCalendar);
remindAvailableOptions: TaskReminderOption[] = TASK_REMINDER_OPTIONS;
task: TaskCopy = this.data.task;
@ -124,7 +124,7 @@ export class DialogScheduleTaskComponent implements AfterViewInit {
this.selectedDate = dateStrToUtcDate(this.data.targetDay);
}
this.calendar.activeDate = new Date(this.selectedDate || new Date());
this.calendar().activeDate = new Date(this.selectedDate || new Date());
this._cd.detectChanges();
setTimeout(() => {
@ -168,7 +168,7 @@ export class DialogScheduleTaskComponent implements AfterViewInit {
if (
this.selectedDate &&
new Date(this.selectedDate).getTime() ===
new Date(this.calendar.activeDate).getTime()
new Date(this.calendar().activeDate).getTime()
) {
this.submit();
}
@ -211,7 +211,7 @@ export class DialogScheduleTaskComponent implements AfterViewInit {
// we do the timeout is there to make sure this happens after our click handler
setTimeout(() => {
this.selectedDate = new Date(newDate);
this.calendar.activeDate = this.selectedDate;
this.calendar().activeDate = this.selectedDate;
});
}

View file

@ -7,7 +7,7 @@ import {
Input,
OnDestroy,
OnInit,
ViewChild,
viewChild,
} from '@angular/core';
import { TaskCopy } from '../../tasks/task.model';
import { EMPTY, Observable } from 'rxjs';
@ -41,8 +41,9 @@ export class PlannerTaskComponent extends BaseComponent implements OnInit, OnDes
moveToProjectList$!: Observable<Project[]>;
@ViewChild('taskContextMenu', { static: true, read: TaskContextMenuComponent })
taskContextMenu?: TaskContextMenuComponent;
readonly taskContextMenu = viewChild('taskContextMenu', {
read: TaskContextMenuComponent,
});
@HostBinding('class.isDone')
get isDone(): boolean {
@ -114,7 +115,7 @@ export class PlannerTaskComponent extends BaseComponent implements OnInit, OnDes
}
openContextMenu(event: TouchEvent | MouseEvent): void {
this.taskContextMenu?.open(event);
this.taskContextMenu()?.open(event);
}
estimateTimeClick(ev: MouseEvent): void {

View file

@ -11,7 +11,7 @@ import {
output,
signal,
Signal,
ViewChild,
viewChild,
} from '@angular/core';
import { MatIcon } from '@angular/material/icon';
import { TaskService } from '../../tasks/task.service';
@ -46,8 +46,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy {
// time = computed(() => {})
editEnd = output<void>();
@ViewChild('textAreaElement', { static: true, read: ElementRef })
textAreaElement?: ElementRef<HTMLTextAreaElement>;
readonly textAreaElement = viewChild('textAreaElement', { read: ElementRef });
private _editEndTimeout: number | undefined;
@ -59,7 +58,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy {
@HostListener('click', ['$event'])
onClick(event: MouseEvent): void {
event.stopPropagation();
this.textAreaElement?.nativeElement.focus();
this.textAreaElement()?.nativeElement.focus();
// cancel blur
window.clearTimeout(this._editEndTimeout);
}
@ -70,7 +69,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy {
) {
effect(() => {
if (this.isEditMode()) {
this.textAreaElement?.nativeElement.focus();
this.textAreaElement()?.nativeElement.focus();
}
});
}
@ -87,15 +86,16 @@ export class CreateTaskPlaceholderComponent implements OnDestroy {
}
async onKeyDown(event: KeyboardEvent): Promise<void> {
const textAreaElement = this.textAreaElement();
if (
event.key === 'Enter' &&
typeof this.plannedAt() === 'number' &&
this.textAreaElement?.nativeElement.value
textAreaElement?.nativeElement.value
) {
this.editEnd.emit();
if (this.isForDayMode()) {
const id = this._taskService.add(
this.textAreaElement?.nativeElement.value || '',
textAreaElement?.nativeElement.value || '',
false,
{
timeEstimate: 30 * 60 * 1000,
@ -110,7 +110,7 @@ export class CreateTaskPlaceholderComponent implements OnDestroy {
);
} else {
this._taskService.addAndSchedule(
this.textAreaElement?.nativeElement.value || '',
textAreaElement?.nativeElement.value || '',
{
timeEstimate: 30 * 60 * 1000,
},

View file

@ -11,7 +11,7 @@ import {
Input,
LOCALE_ID,
OnInit,
ViewChild,
viewChild,
} from '@angular/core';
import { ScheduleEvent, ScheduleFromCalendarEvent } from '../schedule.model';
import { MatIcon } from '@angular/material/icon';
@ -77,8 +77,9 @@ export class ScheduleEventComponent implements OnInit {
contextMenuPosition: { x: string; y: string } = { x: '0px', y: '0px' };
@ViewChild('taskContextMenu', { static: false, read: TaskContextMenuComponent })
taskContextMenu?: TaskContextMenuComponent;
readonly taskContextMenu = viewChild('taskContextMenu', {
read: TaskContextMenuComponent,
});
protected readonly SVEType = SVEType;
destroyRef = inject(DestroyRef);
@ -264,7 +265,7 @@ export class ScheduleEventComponent implements OnInit {
}
openContextMenu(event: TouchEvent | MouseEvent): void {
this.taskContextMenu?.open(event);
this.taskContextMenu()?.open(event);
}
deleteTask(): void {

View file

@ -8,7 +8,7 @@ import {
Inject,
LOCALE_ID,
OnDestroy,
ViewChild,
viewChild,
} from '@angular/core';
import { UiModule } from '../../../ui/ui.module';
import { BehaviorSubject, combineLatest, fromEvent, Observable } from 'rxjs';
@ -258,7 +258,7 @@ export class ScheduleComponent implements AfterViewInit, OnDestroy {
dragCloneEl: HTMLElement | null = null;
destroyRef = inject(DestroyRef);
@ViewChild('gridContainer') gridContainer!: ElementRef;
readonly gridContainer = viewChild.required<ElementRef>('gridContainer');
private _currentAniTimeout: number | undefined;
@ -332,7 +332,7 @@ export class ScheduleComponent implements AfterViewInit, OnDestroy {
// console.log(ev);
if (ev.target instanceof HTMLElement && ev.target.classList.contains('col')) {
const gridContainer = this.gridContainer.nativeElement;
const gridContainer = this.gridContainer().nativeElement;
const gridStyles = window.getComputedStyle(gridContainer);
const rowSizes = gridStyles.gridTemplateRows

View file

@ -6,7 +6,7 @@ import {
EventEmitter,
OnDestroy,
Output,
ViewChild,
viewChild,
} from '@angular/core';
import { T } from '../../t.const';
import { UntypedFormControl } from '@angular/forms';
@ -46,9 +46,9 @@ const MAX_RESULTS = 100;
export class SearchBarComponent implements AfterViewInit, OnDestroy {
@Output() blurred: EventEmitter<any> = new EventEmitter();
@ViewChild('inputEl') inputEl!: ElementRef;
@ViewChild('searchForm') searchForm!: ElementRef;
@ViewChild(MatAutocompleteTrigger) autocomplete!: MatAutocompleteTrigger;
readonly inputEl = viewChild.required<ElementRef>('inputEl');
readonly searchForm = viewChild.required<ElementRef>('searchForm');
readonly autocomplete = viewChild.required(MatAutocompleteTrigger);
T: typeof T = T;
isLoading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
@ -143,7 +143,7 @@ export class SearchBarComponent implements AfterViewInit, OnDestroy {
);
this._attachKeyDownHandlerTimeout = window.setTimeout(() => {
this.inputEl.nativeElement.addEventListener('keydown', (ev: KeyboardEvent) => {
this.inputEl().nativeElement.addEventListener('keydown', (ev: KeyboardEvent) => {
if (ev.key === 'Escape') {
this.blurred.emit();
} else if (
@ -176,15 +176,15 @@ export class SearchBarComponent implements AfterViewInit, OnDestroy {
}
private _shakeSearchForm(): void {
this.searchForm.nativeElement.classList.toggle('shake-form');
this.searchForm.nativeElement.onanimationend = () => {
this.searchForm.nativeElement.classList.toggle('shake-form');
this.searchForm().nativeElement.classList.toggle('shake-form');
this.searchForm().nativeElement.onanimationend = () => {
this.searchForm().nativeElement.classList.toggle('shake-form');
};
}
onAnimationEvent(event: AnimationEvent): void {
if (event.fromState) {
this.inputEl.nativeElement.focus();
this.inputEl().nativeElement.focus();
}
}

View file

@ -6,7 +6,7 @@ import {
inject,
input,
output,
ViewChild,
viewChild,
} from '@angular/core';
import {
MatAutocomplete,
@ -70,8 +70,8 @@ export class TagEditComponent {
inputCtrl: UntypedFormControl = new UntypedFormControl();
separatorKeysCodes: number[] = DEFAULT_SEPARATOR_KEY_CODES;
@ViewChild('inputElRef', { static: true }) inputEl?: ElementRef<HTMLInputElement>;
@ViewChild('autoElRef', { static: true }) matAutocomplete?: MatAutocomplete;
readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputElRef');
readonly matAutocomplete = viewChild<MatAutocomplete>('autoElRef');
inputVal = toSignal<string>(this.inputCtrl.valueChanges);
tagSuggestions = toSignal(this._tagService.tagsNoMyDayAndNoList$, { initialValue: [] });
@ -99,11 +99,12 @@ export class TagEditComponent {
});
add(event: MatChipInputEvent): void {
if (!this.matAutocomplete) {
const matAutocomplete = this.matAutocomplete();
if (!matAutocomplete) {
throw new Error('Auto complete undefined');
}
if (!this.matAutocomplete.isOpen) {
if (!matAutocomplete.isOpen) {
const inp = event.input;
const value = event.value;
@ -125,8 +126,9 @@ export class TagEditComponent {
}
focusInput(): void {
if (this.inputEl) {
this.inputEl.nativeElement.focus();
const inputEl = this.inputEl();
if (inputEl) {
inputEl.nativeElement.focus();
}
}
@ -136,8 +138,9 @@ export class TagEditComponent {
selected(event: MatAutocompleteSelectedEvent): void {
this._add(event.option.value);
if (this.inputEl) {
this.inputEl.nativeElement.value = '';
const inputEl = this.inputEl();
if (inputEl) {
inputEl.nativeElement.value = '';
}
this.inputCtrl.setValue(null);
}

View file

@ -7,7 +7,7 @@ import {
OnDestroy,
output,
signal,
ViewChild,
viewChild,
} from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { TaskService } from '../task.service';
@ -56,7 +56,7 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
isSearchIssueProviders = signal(false);
isSearchIssueProviders$ = toObservable(this.isSearchIssueProviders);
@ViewChild('inputEl', { static: true }) inputEl?: ElementRef;
readonly inputEl = viewChild<ElementRef>('inputEl');
T: typeof T = T;
@ -108,7 +108,7 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
}
this._attachKeyDownHandlerTimeout = window.setTimeout(() => {
(this.inputEl as ElementRef).nativeElement.addEventListener(
(this.inputEl() as ElementRef).nativeElement.addEventListener(
'keydown',
(ev: KeyboardEvent) => {
if (ev.key === 'Escape') {
@ -133,8 +133,8 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
sessionStorage.setItem(SS.TODO_TMP, '');
this.taskSuggestionsCtrl.setValue(savedTodo);
this._saveTmpTodoTimeout = window.setTimeout(() => {
(this.inputEl as ElementRef).nativeElement.value = savedTodo;
(this.inputEl as ElementRef).nativeElement.select();
(this.inputEl() as ElementRef).nativeElement.value = savedTodo;
(this.inputEl() as ElementRef).nativeElement.select();
});
}
}
@ -175,15 +175,13 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
className.includes('shepherd-enabled');
}
const inputEl = this.inputEl();
if (!relatedTarget || (relatedTarget && !isUIelement)) {
sessionStorage.setItem(
SS.TODO_TMP,
(this.inputEl as ElementRef).nativeElement.value,
);
sessionStorage.setItem(SS.TODO_TMP, (inputEl as ElementRef).nativeElement.value);
}
if (relatedTarget && isUIelement) {
(this.inputEl as ElementRef).nativeElement.focus();
(inputEl as ElementRef).nativeElement.focus();
} else {
// we need to wait since otherwise addTask is not working
window.clearTimeout(this._delayBlurTimeout);
@ -270,14 +268,14 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
private _focusInput(): void {
if (IS_ANDROID_WEB_VIEW) {
document.body.focus();
(this.inputEl as ElementRef).nativeElement.focus();
(this.inputEl() as ElementRef).nativeElement.focus();
this._autofocusTimeout = window.setTimeout(() => {
document.body.focus();
(this.inputEl as ElementRef).nativeElement.focus();
(this.inputEl() as ElementRef).nativeElement.focus();
}, 1000);
} else {
this._autofocusTimeout = window.setTimeout(() => {
(this.inputEl as ElementRef).nativeElement.focus();
(this.inputEl() as ElementRef).nativeElement.focus();
});
}
}

View file

@ -6,8 +6,8 @@ import {
input,
Input,
output,
ViewChild,
ViewEncapsulation,
viewChild,
} from '@angular/core';
import { AsyncPipe, DatePipe } from '@angular/common';
import { IssueModule } from '../../../issue/issue.module';
@ -95,10 +95,11 @@ export class TaskContextMenuInnerComponent implements AfterViewInit {
contextMenuPosition: { x: string; y: string } = { x: '100px', y: '100px' };
@ViewChild('contextMenuTriggerEl', { static: true, read: MatMenuTrigger })
contextMenuTrigger?: MatMenuTrigger;
readonly contextMenuTrigger = viewChild('contextMenuTriggerEl', {
read: MatMenuTrigger,
});
@ViewChild('contextMenu', { static: true, read: MatMenu }) contextMenu?: MatMenu;
readonly contextMenu = viewChild('contextMenu', { read: MatMenu });
task!: TaskWithSubTasks | Task;
@ -186,7 +187,7 @@ export class TaskContextMenuInnerComponent implements AfterViewInit {
}
this._isOpenedFromKeyboard = isOpenedFromKeyBoard;
this.contextMenuTrigger?.openMenu();
this.contextMenuTrigger()?.openMenu();
}
focusRelatedTaskOrNext(): void {

View file

@ -3,7 +3,7 @@ import {
ChangeDetectorRef,
Component,
input,
ViewChild,
viewChild,
} from '@angular/core';
import { IssueModule } from '../../issue/issue.module';
import { TranslateModule } from '@ngx-translate/core';
@ -23,17 +23,15 @@ export class TaskContextMenuComponent {
isShowInner: boolean = false;
@ViewChild('taskContextMenuInner', {
static: false,
readonly taskContextMenuInner = viewChild('taskContextMenuInner', {
read: TaskContextMenuInnerComponent,
})
taskContextMenuInner?: TaskContextMenuInnerComponent;
});
constructor(private _cd: ChangeDetectorRef) {}
open(ev: MouseEvent | KeyboardEvent | TouchEvent, isOpenedFromKeyBoard = false): void {
this.isShowInner = true;
this._cd.detectChanges();
this.taskContextMenuInner?.open(ev, isOpenedFromKeyBoard);
this.taskContextMenuInner()?.open(ev, isOpenedFromKeyBoard);
}
}

View file

@ -8,9 +8,8 @@ import {
Input,
LOCALE_ID,
OnDestroy,
QueryList,
ViewChild,
ViewChildren,
viewChildren,
viewChild,
} from '@angular/core';
import { ShowSubTasksMode, TaskDetailTargetPanel, TaskWithSubTasks } from '../task.model';
import { IssueService } from '../../issue/issue.service';
@ -94,10 +93,9 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy {
@Input() isOver: boolean = false;
@Input() isDialogMode: boolean = false;
@ViewChildren(TaskDetailItemComponent)
itemEls?: QueryList<TaskDetailItemComponent>;
@ViewChild('attachmentPanelElRef')
attachmentPanelElRef?: TaskDetailItemComponent;
readonly itemEls = viewChildren(TaskDetailItemComponent);
readonly attachmentPanelElRef =
viewChild<TaskDetailItemComponent>('attachmentPanelElRef');
IS_TOUCH_PRIMARY = IS_TOUCH_PRIMARY;
_onDestroy$ = new Subject<void>();
@ -377,11 +375,12 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy {
)
.subscribe(([v]) => {
if (v === TaskDetailTargetPanel.Attachments) {
if (!this.attachmentPanelElRef) {
const attachmentPanelElRef = this.attachmentPanelElRef();
if (!attachmentPanelElRef) {
devError('this.attachmentPanelElRef not ready');
this._focusFirst();
} else {
this.focusItem(this.attachmentPanelElRef);
this.focusItem(attachmentPanelElRef);
}
} else {
this._focusFirst();
@ -458,30 +457,29 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy {
}
onItemKeyPress(ev: KeyboardEvent): void {
if (!this.itemEls) {
const itemEls = this.itemEls();
if (!itemEls) {
throw new Error();
}
if (ev.key === 'ArrowUp' && this.selectedItemIndex > 0) {
this.selectedItemIndex--;
this.itemEls.toArray()[this.selectedItemIndex].focusEl();
} else if (
ev.key === 'ArrowDown' &&
this.itemEls.toArray().length > this.selectedItemIndex + 1
) {
itemEls[this.selectedItemIndex].focusEl();
} else if (ev.key === 'ArrowDown' && itemEls.length > this.selectedItemIndex + 1) {
this.selectedItemIndex++;
this.itemEls.toArray()[this.selectedItemIndex].focusEl();
itemEls[this.selectedItemIndex].focusEl();
}
}
focusItem(cmpInstance: TaskDetailItemComponent, timeoutDuration: number = 150): void {
window.clearTimeout(this._focusTimeout);
this._focusTimeout = window.setTimeout(() => {
if (!this.itemEls) {
const itemEls = this.itemEls();
if (!itemEls) {
throw new Error();
}
const i = this.itemEls.toArray().findIndex((el) => el === cmpInstance);
const i = itemEls.findIndex((el) => el === cmpInstance);
if (i === -1) {
this.focusItem(cmpInstance);
} else {
@ -503,10 +501,11 @@ export class TaskDetailPanelComponent implements AfterViewInit, OnDestroy {
private _focusFirst(): void {
this._focusTimeout = window.setTimeout(() => {
if (!this.itemEls) {
const itemEls = this.itemEls();
if (!itemEls) {
throw new Error();
}
this.focusItem(this.itemEls.first, 0);
this.focusItem(itemEls.at(0)!, 0);
}, 150);
}

View file

@ -5,7 +5,7 @@ import {
computed,
input,
OnDestroy,
ViewChild,
viewChild,
} from '@angular/core';
import { DropListModelSource, Task, TaskCopy, TaskWithSubTasks } from '../task.model';
import { TaskService } from '../task.service';
@ -86,7 +86,7 @@ export class TaskListComponent implements OnDestroy, AfterViewInit {
});
allTasksLength = computed(() => this.tasks()?.length ?? 0);
@ViewChild(CdkDropList) dropList?: CdkDropList;
readonly dropList = viewChild(CdkDropList);
T: typeof T = T;
@ -99,11 +99,11 @@ export class TaskListComponent implements OnDestroy, AfterViewInit {
) {}
ngAfterViewInit(): void {
this.dropListService.registerDropList(this.dropList!, this.listId() === 'SUB');
this.dropListService.registerDropList(this.dropList()!, this.listId() === 'SUB');
}
ngOnDestroy(): void {
this.dropListService.unregisterDropList(this.dropList!);
this.dropListService.unregisterDropList(this.dropList()!);
}
trackByFn(i: number, task: Task): string {

View file

@ -8,7 +8,7 @@ import {
input,
OnDestroy,
Renderer2,
ViewChild,
viewChild,
} from '@angular/core';
import { TaskService } from '../task.service';
import { EMPTY, forkJoin, of } from 'rxjs';
@ -112,14 +112,16 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
ShowSubTasksMode: typeof ShowSubTasksMode = ShowSubTasksMode;
isFirstLineHover: boolean = false;
@ViewChild('taskTitleEditEl', { static: true }) taskTitleEditEl?: ElementRef;
@ViewChild('blockLeftEl') blockLeftElRef?: ElementRef;
@ViewChild('blockRightEl') blockRightElRef?: ElementRef;
@ViewChild('innerWrapperEl', { static: true }) innerWrapperElRef?: ElementRef;
@ViewChild('projectMenuTriggerEl', { static: false, read: MatMenuTrigger })
projectMenuTrigger?: MatMenuTrigger;
@ViewChild('taskContextMenu', { static: true, read: TaskContextMenuComponent })
taskContextMenu?: TaskContextMenuComponent;
readonly taskTitleEditEl = viewChild<ElementRef>('taskTitleEditEl');
readonly blockLeftElRef = viewChild<ElementRef>('blockLeftEl');
readonly blockRightElRef = viewChild<ElementRef>('blockRightEl');
readonly innerWrapperElRef = viewChild<ElementRef>('innerWrapperEl');
readonly projectMenuTrigger = viewChild('projectMenuTriggerEl', {
read: MatMenuTrigger,
});
readonly taskContextMenu = viewChild('taskContextMenu', {
read: TaskContextMenuComponent,
});
private _task$ = toObservable(this.task);
@ -468,17 +470,18 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
}
focusTitleForEdit(): void {
if (!this.taskTitleEditEl || !(this.taskTitleEditEl as any).textarea.nativeElement) {
console.log(this.taskTitleEditEl);
const taskTitleEditEl = this.taskTitleEditEl();
if (!taskTitleEditEl || !(taskTitleEditEl as any).textarea.nativeElement) {
console.log(taskTitleEditEl);
throw new Error('No el');
}
(this.taskTitleEditEl as any).textarea.nativeElement.focus();
(taskTitleEditEl as any).textarea.nativeElement.focus();
// (this.taskTitleEditEl as any).textarea.nativeElement.focus();
}
openContextMenu(event: TouchEvent | MouseEvent): void {
(this.taskTitleEditEl as any).textarea.nativeElement?.blur();
this.taskContextMenu?.open(event);
(this.taskTitleEditEl() as any).textarea.nativeElement?.blur();
this.taskContextMenu()?.open(event);
}
onTagsUpdated(tagIds: string[]): void {
@ -489,7 +492,8 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
if (!IS_TOUCH_PRIMARY) {
return;
}
if (!this.taskTitleEditEl) {
const taskTitleEditEl = this.taskTitleEditEl();
if (!taskTitleEditEl) {
throw new Error('No el');
}
@ -498,7 +502,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
if (
(targetEl.className.indexOf && targetEl.className.indexOf('drag-handle') > -1) ||
Math.abs(ev.deltaY) > Math.abs(ev.deltaX) ||
document.activeElement === (this.taskTitleEditEl as any).textarea.nativeElement ||
document.activeElement === (taskTitleEditEl as any).textarea.nativeElement ||
ev.isFinal
) {
return;
@ -514,13 +518,15 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
if (!IS_TOUCH_PRIMARY || (!this.isLockPanLeft && !this.isLockPanRight)) {
return;
}
if (!this.blockLeftElRef || !this.blockRightElRef) {
const blockLeftElRef = this.blockLeftElRef();
const blockRightElRef = this.blockRightElRef();
if (!blockLeftElRef || !blockRightElRef) {
throw new Error('No el');
}
this.isPreventPointerEventsWhilePanning = false;
this._renderer.removeStyle(this.blockLeftElRef.nativeElement, 'transition');
this._renderer.removeStyle(this.blockRightElRef.nativeElement, 'transition');
this._renderer.removeStyle(blockLeftElRef.nativeElement, 'transition');
this._renderer.removeStyle(blockRightElRef.nativeElement, 'transition');
if (this._currentPanTimeout) {
window.clearTimeout(this._currentPanTimeout);
@ -528,11 +534,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
if (this.isActionTriggered) {
if (this.isLockPanLeft) {
this._renderer.setStyle(
this.blockRightElRef.nativeElement,
'transform',
`scaleX(1)`,
);
this._renderer.setStyle(blockRightElRef.nativeElement, 'transform', `scaleX(1)`);
this._currentPanTimeout = window.setTimeout(() => {
if (this.workContextService.isToday) {
if (this.task().repeatCfgId) {
@ -554,11 +556,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
this._resetAfterPan();
}, 100);
} else if (this.isLockPanRight) {
this._renderer.setStyle(
this.blockLeftElRef.nativeElement,
'transform',
`scaleX(1)`,
);
this._renderer.setStyle(blockLeftElRef.nativeElement, 'transform', `scaleX(1)`);
this._currentPanTimeout = window.setTimeout(() => {
this.toggleTaskDone();
this._resetAfterPan();
@ -696,11 +694,14 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
) {
return;
}
if (!this.innerWrapperElRef) {
const innerWrapperElRef = this.innerWrapperElRef();
if (!innerWrapperElRef) {
throw new Error('No el');
}
const targetRef = this.isLockPanRight ? this.blockLeftElRef : this.blockRightElRef;
const targetRef = this.isLockPanRight
? this.blockLeftElRef()
: this.blockRightElRef();
const MAGIC_FACTOR = 2;
this.isPreventPointerEventsWhilePanning = true;
@ -720,7 +721,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
this._renderer.setStyle(targetRef.nativeElement, 'width', `${moveBy}px`);
this._renderer.setStyle(targetRef.nativeElement, 'transition', `none`);
this._renderer.setStyle(
this.innerWrapperElRef.nativeElement,
innerWrapperElRef.nativeElement,
'transform',
`translateX(${ev.deltaX}px`,
);
@ -728,11 +729,14 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
}
private _resetAfterPan(): void {
const blockLeftElRef = this.blockLeftElRef();
const blockRightElRef = this.blockRightElRef();
const innerWrapperElRef = this.innerWrapperElRef();
if (
!this.taskTitleEditEl ||
!this.blockLeftElRef ||
!this.blockRightElRef ||
!this.innerWrapperElRef
!this.taskTitleEditEl() ||
!blockLeftElRef ||
!blockRightElRef ||
!innerWrapperElRef
) {
throw new Error('No el');
}
@ -744,9 +748,9 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
// const scale = 0;
// this._renderer.setStyle(this.blockLeftEl.nativeElement, 'transform', `scaleX(${scale})`);
// this._renderer.setStyle(this.blockRightEl.nativeElement, 'transform', `scaleX(${scale})`);
this._renderer.removeClass(this.blockLeftElRef.nativeElement, 'isActive');
this._renderer.removeClass(this.blockRightElRef.nativeElement, 'isActive');
this._renderer.setStyle(this.innerWrapperElRef.nativeElement, 'transform', ``);
this._renderer.removeClass(blockLeftElRef.nativeElement, 'isActive');
this._renderer.removeClass(blockRightElRef.nativeElement, 'isActive');
this._renderer.setStyle(innerWrapperElRef.nativeElement, 'transform', ``);
}
get kb(): KeyboardConfig {
@ -793,16 +797,18 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
this.addAttachment();
}
if (!t.parentId && checkKeyCombo(ev, keys.taskMoveToProject)) {
if (!this.projectMenuTrigger) {
const projectMenuTrigger = this.projectMenuTrigger();
if (!projectMenuTrigger) {
throw new Error('No el');
}
this.projectMenuTrigger.openMenu();
projectMenuTrigger.openMenu();
}
if (checkKeyCombo(ev, keys.taskOpenContextMenu)) {
if (!this.taskContextMenu) {
const taskContextMenu = this.taskContextMenu();
if (!taskContextMenu) {
throw new Error('No el');
}
this.taskContextMenu.open(ev, true);
taskContextMenu.open(ev, true);
}
if (checkKeyCombo(ev, keys.togglePlay)) {

View file

@ -7,7 +7,7 @@ import {
Input,
Output,
Renderer2,
ViewChild,
viewChild,
} from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@ -32,7 +32,7 @@ export class SplitComponent implements AfterViewInit {
pos: number = 100;
eventSubs?: Subscription;
@ViewChild('buttonEl', { static: true }) buttonEl?: ElementRef;
readonly buttonEl = viewChild<ElementRef>('buttonEl');
private _isDrag: boolean = false;
private _isViewInitialized: boolean = false;

View file

@ -105,6 +105,8 @@ export class WorkViewComponent implements OnInit, OnDestroy, AfterContentInit {
private _addTasksForTomorrowService: AddTasksForTomorrowService,
) {}
// TODO: Skipped for migration because:
// Accessor queries cannot be migrated as they are too complex.
@ViewChild('splitTopEl', { read: ElementRef }) set splitTopElRef(ref: ElementRef) {
if (ref) {
this.splitTopEl$.next(ref.nativeElement);

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, ElementRef, ViewChild } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, viewChild } from '@angular/core';
import { DataImportService } from '../sync/data-import.service';
import { SnackService } from '../../core/snack/snack.service';
import { AppDataComplete } from '../sync/sync.model';
@ -16,7 +16,7 @@ import { privacyExport } from './privacy-export';
standalone: false,
})
export class FileImexComponent {
@ViewChild('fileInput', { static: true }) fileInputRef?: ElementRef;
readonly fileInputRef = viewChild<ElementRef>('fileInput');
T: typeof T = T;
constructor(
@ -50,14 +50,15 @@ export class FileImexComponent {
await this._dataImportService.importCompleteSyncData(data as AppDataComplete);
}
if (!this.fileInputRef) {
const fileInputRef = this.fileInputRef();
if (!fileInputRef) {
throw new Error('No file input Ref element');
}
// clear input
this.fileInputRef.nativeElement.value = '';
this.fileInputRef.nativeElement.type = 'text';
this.fileInputRef.nativeElement.type = 'file';
fileInputRef.nativeElement.value = '';
fileInputRef.nativeElement.type = 'text';
fileInputRef.nativeElement.type = 'file';
};
reader.readAsText(file);
}

View file

@ -71,6 +71,8 @@ export class BetterDrawerContainerComponent
return this._isOver;
}
// TODO: Skipped for migration because:
// Accessor queries cannot be migrated as they are too complex.
@ViewChild('contentElRef', { read: ElementRef }) set setContentElRef(ref: ElementRef) {
this.contentEl$.next(ref.nativeElement);
}

View file

@ -35,6 +35,8 @@ export class BetterSimpleDrawerComponent implements OnInit, OnDestroy {
return this._isOpen;
}
// TODO: Skipped for migration because:
// Accessor queries cannot be migrated as they are too complex.
@ViewChild('contentElRef', { read: ElementRef }) set setContentElRef(ref: ElementRef) {
this.contentEl$.next(ref.nativeElement);
}

View file

@ -7,7 +7,7 @@ import {
Input,
OnDestroy,
Output,
ViewChild,
viewChild,
} from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { Observable } from 'rxjs';
@ -56,8 +56,8 @@ export class ChipListInputComponent implements OnDestroy {
inputCtrl: UntypedFormControl = new UntypedFormControl();
separatorKeysCodes: number[] = DEFAULT_SEPARATOR_KEY_CODES;
isAutoFocus = false;
@ViewChild('inputElRef', { static: true }) inputEl?: ElementRef<HTMLInputElement>;
@ViewChild('autoElRef', { static: true }) matAutocomplete?: MatAutocomplete;
readonly inputEl = viewChild<ElementRef<HTMLInputElement>>('inputElRef');
readonly matAutocomplete = viewChild<MatAutocomplete>('autoElRef');
private _modelIds: string[] = [];
filteredSuggestions: Observable<Suggestion[]> = this.inputCtrl.valueChanges.pipe(
@ -77,7 +77,7 @@ export class ChipListInputComponent implements OnDestroy {
if (typeof autoFocus === 'string') {
this.isAutoFocus = true;
this._autoFocusTimeout = window.setTimeout(() => {
this.inputEl?.nativeElement.focus();
this.inputEl()?.nativeElement.focus();
// NOTE: we need to wait a little for the tag dialog to be there
}, 300);
}
@ -100,11 +100,12 @@ export class ChipListInputComponent implements OnDestroy {
}
add(event: MatChipInputEvent): void {
if (!this.matAutocomplete) {
const matAutocomplete = this.matAutocomplete();
if (!matAutocomplete) {
throw new Error('Auto complete undefined');
}
if (!this.matAutocomplete.isOpen) {
if (!matAutocomplete.isOpen) {
const input = event.input;
const value = event.value;
@ -125,8 +126,9 @@ export class ChipListInputComponent implements OnDestroy {
selected(event: MatAutocompleteSelectedEvent): void {
this._add(event.option.value);
if (this.inputEl) {
this.inputEl.nativeElement.value = '';
const inputEl = this.inputEl();
if (inputEl) {
inputEl.nativeElement.value = '';
}
this.inputCtrl.setValue(null);
}

View file

@ -4,7 +4,7 @@ import {
input,
OnInit,
TemplateRef,
ViewChild,
viewChild,
} from '@angular/core';
import {
MatMenu,
@ -27,8 +27,9 @@ export class ContextMenuComponent implements OnInit {
rightClickTriggerEl = input.required<HTMLElement | MatMenuItem | MatIconButton>();
contextMenu = input.required<TemplateRef<any>>();
@ViewChild('contextMenuTriggerEl', { static: true, read: MatMenuTrigger })
contextMenuTriggerEl!: MatMenuTrigger;
readonly contextMenuTriggerEl = viewChild.required('contextMenuTriggerEl', {
read: MatMenuTrigger,
});
contextMenuPosition: { x: string; y: string } = { x: '0px', y: '0px' };
ngOnInit(): void {
@ -61,10 +62,11 @@ export class ContextMenuComponent implements OnInit {
('touches' in event ? event.touches[0].clientX : event.clientX) + 'px';
this.contextMenuPosition.y =
('touches' in event ? event.touches[0].clientY : event.clientY) + 'px';
this.contextMenuTriggerEl.menuData = {
const contextMenuTriggerEl = this.contextMenuTriggerEl();
contextMenuTriggerEl.menuData = {
x: this.contextMenuPosition.x,
y: this.contextMenuPosition.y,
};
this.contextMenuTriggerEl.openMenu();
contextMenuTriggerEl.openMenu();
}
}

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, ElementRef, ViewChild } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, viewChild } from '@angular/core';
import { FieldType } from '@ngx-formly/material';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { stringToMs } from '../string-to-ms.pipe';
@ -11,7 +11,7 @@ import { stringToMs } from '../string-to-ms.pipe';
standalone: false,
})
export class InputDurationFormlyComponent extends FieldType<FormlyFieldConfig> {
@ViewChild('inputEl', { static: true, read: ElementRef }) input!: ElementRef;
readonly input = viewChild.required('inputEl', { read: ElementRef });
// @ViewChild(MatInput, {static: true}) formFieldControl?: MatInput;
onInputValueChange(ev: Event): void {
@ -30,10 +30,11 @@ export class InputDurationFormlyComponent extends FieldType<FormlyFieldConfig> {
private _updateValue(val: string): void {
this.formControl.setValue(val ? stringToMs(val) : null);
this.input.nativeElement.value = val;
this.input().nativeElement.value = val;
setTimeout(() => {
if (this.input.nativeElement.value !== val) {
this.input.nativeElement.value = val;
const input = this.input();
if (input.nativeElement.value !== val) {
input.nativeElement.value = val;
}
});
}

View file

@ -8,7 +8,7 @@ import {
OnDestroy,
OnInit,
Output,
ViewChild,
viewChild,
} from '@angular/core';
import { nanoid } from 'nanoid';
import moment from 'moment';
@ -34,7 +34,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
endHandler?: () => void;
moveHandler?: (ev: any) => void;
@ViewChild('circleEl', { static: true }) circleEl?: ElementRef;
readonly circleEl = viewChild<ElementRef>('circleEl');
@Input() label: string = '';
@Output() modelChange: EventEmitter<number> = new EventEmitter();
@ -57,7 +57,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.startHandler = (ev) => {
if (!this.endHandler || !this.moveHandler || !this.circleEl) {
if (!this.endHandler || !this.moveHandler || !this.circleEl()) {
throw new Error();
}
@ -83,7 +83,8 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
) {
return;
}
if (!this.endHandler || !this.moveHandler || !this.circleEl) {
const circleEl = this.circleEl();
if (!this.endHandler || !this.moveHandler || !circleEl) {
throw new Error();
}
@ -92,8 +93,8 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
const convertThetaToCssDegrees = (thetaIN: number): number => 90 - thetaIN;
const centerX = this.circleEl.nativeElement.offsetWidth / 2;
const centerY = this.circleEl.nativeElement.offsetHeight / 2;
const centerX = circleEl.nativeElement.offsetWidth / 2;
const centerY = circleEl.nativeElement.offsetHeight / 2;
let offsetX;
@ -117,7 +118,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
};
this.endHandler = () => {
if (!this.endHandler || !this.moveHandler || !this.circleEl) {
if (!this.endHandler || !this.moveHandler || !this.circleEl()) {
throw new Error();
}
@ -138,7 +139,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
}
ngOnDestroy(): void {
if (!this.endHandler || !this.moveHandler || !this.startHandler || !this.circleEl) {
if (!this.endHandler || !this.moveHandler || !this.startHandler || !this.circleEl()) {
throw new Error();
}
@ -154,10 +155,11 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
}
setCircleRotation(cssDegrees: number): void {
if (!this.circleEl) {
const circleEl = this.circleEl();
if (!circleEl) {
throw new Error();
}
this.circleEl.nativeElement.style.transform = 'rotate(' + cssDegrees + 'deg)';
circleEl.nativeElement.style.transform = 'rotate(' + cssDegrees + 'deg)';
}
setDots(hours: number = 0): void {

View file

@ -4,7 +4,7 @@ import {
ElementRef,
OnDestroy,
OnInit,
ViewChild,
viewChild,
} from '@angular/core';
import { FieldType } from '@ngx-formly/core';
import { Subscription } from 'rxjs';
@ -21,7 +21,7 @@ export class FormlyTranslatedTemplateComponent
extends FieldType
implements OnInit, OnDestroy
{
@ViewChild('tplWrapper', { static: true }) tplWrapper?: ElementRef;
readonly tplWrapper = viewChild<ElementRef>('tplWrapper');
private _el?: HTMLElement;
private _subs: Subscription = new Subscription();
@ -54,11 +54,12 @@ export class FormlyTranslatedTemplateComponent
}
private _createTag(): void {
if (!this.field.templateOptions || !this.tplWrapper) {
const tplWrapper = this.tplWrapper();
if (!this.field.templateOptions || !tplWrapper) {
throw new Error();
}
const tag = this.field.templateOptions.tag || 'div';
const tplWrapperEl = this.tplWrapper.nativeElement;
const tplWrapperEl = tplWrapper.nativeElement;
if (tplWrapperEl) {
this._el = document.createElement(tag);
@ -67,7 +68,7 @@ export class FormlyTranslatedTemplateComponent
(this._el as HTMLElement).classList.add(this.field.templateOptions.class);
}
this.tplWrapper.nativeElement.append(this._el);
tplWrapper.nativeElement.append(this._el);
}
}
}

View file

@ -7,7 +7,7 @@ import {
HostBinding,
Input,
Output,
ViewChild,
viewChild,
} from '@angular/core';
@Component({
@ -24,8 +24,8 @@ export class InlineInputComponent implements AfterViewInit {
@Input() newValue?: string | number;
@Output() changed: EventEmitter<string | number> = new EventEmitter();
@ViewChild('inputEl') inputEl?: ElementRef;
@ViewChild('inputElDuration') inputElDuration?: ElementRef;
readonly inputEl = viewChild<ElementRef>('inputEl');
readonly inputElDuration = viewChild<ElementRef>('inputElDuration');
@HostBinding('class.isFocused') isFocused: boolean = false;
@ -36,15 +36,15 @@ export class InlineInputComponent implements AfterViewInit {
ngAfterViewInit(): void {
this.activeInputEl =
this.type === 'duration'
? (this.inputElDuration as ElementRef).nativeElement
: (this.inputEl as ElementRef).nativeElement;
? (this.inputElDuration() as ElementRef).nativeElement
: (this.inputEl() as ElementRef).nativeElement;
}
focusInput(): void {
this.activeInputEl =
this.type === 'duration'
? (this.inputElDuration as ElementRef).nativeElement
: (this.inputEl as ElementRef).nativeElement;
? (this.inputElDuration() as ElementRef).nativeElement
: (this.inputEl() as ElementRef).nativeElement;
this.isFocused = true;
(this.activeInputEl as HTMLElement).focus();

View file

@ -9,7 +9,7 @@ import {
OnDestroy,
OnInit,
Output,
ViewChild,
viewChild,
} from '@angular/core';
import { fadeAnimation } from '../animations/fade.ani';
import { MarkdownComponent } from 'ngx-markdown';
@ -40,9 +40,9 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
@Output() focused: EventEmitter<Event> = new EventEmitter();
@Output() blurred: EventEmitter<Event> = new EventEmitter();
@Output() keyboardUnToggle: EventEmitter<Event> = new EventEmitter();
@ViewChild('wrapperEl', { static: true }) wrapperEl: ElementRef | undefined;
@ViewChild('textareaEl') textareaEl: ElementRef | undefined;
@ViewChild('previewEl') previewEl: MarkdownComponent | undefined;
readonly wrapperEl = viewChild<ElementRef>('wrapperEl');
readonly textareaEl = viewChild<ElementRef>('textareaEl');
readonly previewEl = viewChild<MarkdownComponent>('previewEl');
isHideOverflow: boolean = false;
isChecklistMode: boolean = false;
@ -144,10 +144,11 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
this.resizeParsedToFit();
this.isShowEdit = false;
}
if (!this.textareaEl) {
const textareaEl = this.textareaEl();
if (!textareaEl) {
throw new Error('Textarea not visible');
}
this.modelCopy = this.textareaEl.nativeElement.value;
this.modelCopy = textareaEl.nativeElement.value;
if (this.modelCopy !== this.model) {
this.model = this.modelCopy;
@ -157,17 +158,17 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
resizeTextareaToFit(): void {
this._hideOverflow();
if (!this.textareaEl) {
const textareaEl = this.textareaEl();
if (!textareaEl) {
throw new Error('Textarea not visible');
}
if (!this.wrapperEl) {
const wrapperEl = this.wrapperEl();
if (!wrapperEl) {
throw new Error('Wrapper el not visible');
}
this.textareaEl.nativeElement.style.height = 'auto';
this.textareaEl.nativeElement.style.height =
this.textareaEl.nativeElement.scrollHeight + 'px';
this.wrapperEl.nativeElement.style.height =
this.textareaEl.nativeElement.offsetHeight + 'px';
textareaEl.nativeElement.style.height = 'auto';
textareaEl.nativeElement.style.height = textareaEl.nativeElement.scrollHeight + 'px';
wrapperEl.nativeElement.style.height = textareaEl.nativeElement.offsetHeight + 'px';
}
openFullScreen(): void {
@ -193,20 +194,22 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
this._hideOverflow();
setTimeout(() => {
if (!this.previewEl) {
if (this.textareaEl) {
const previewEl = this.previewEl();
if (!previewEl) {
if (this.textareaEl()) {
this.resizeTextareaToFit();
}
return;
}
if (!this.wrapperEl) {
const wrapperEl = this.wrapperEl();
if (!wrapperEl) {
throw new Error('Wrapper el not visible');
}
this.previewEl.element.nativeElement.style.height = 'auto';
previewEl.element.nativeElement.style.height = 'auto';
// NOTE: somehow this pixel seem to help
this.wrapperEl.nativeElement.style.height =
this.previewEl.element.nativeElement.offsetHeight + 'px';
this.previewEl.element.nativeElement.style.height = '';
wrapperEl.nativeElement.style.height =
previewEl.element.nativeElement.offsetHeight + 'px';
previewEl.element.nativeElement.style.height = '';
});
}
@ -234,11 +237,12 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
this.isShowEdit = true;
this.modelCopy = this.model || '';
setTimeout(() => {
if (!this.textareaEl) {
const textareaEl = this.textareaEl();
if (!textareaEl) {
throw new Error('Textarea not visible');
}
this.textareaEl.nativeElement.value = this.modelCopy;
this.textareaEl.nativeElement.focus();
textareaEl.nativeElement.value = this.modelCopy;
textareaEl.nativeElement.focus();
this.resizeTextareaToFit();
});
}
@ -256,10 +260,11 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
}
private _makeLinksWorkForElectron(): void {
if (!this.wrapperEl) {
const wrapperEl = this.wrapperEl();
if (!wrapperEl) {
throw new Error('Wrapper el not visible');
}
this.wrapperEl.nativeElement.addEventListener('click', (ev: MouseEvent) => {
wrapperEl.nativeElement.addEventListener('click', (ev: MouseEvent) => {
const target = ev.target as HTMLElement;
if (target.tagName && target.tagName.toLowerCase() === 'a') {
const href = target.getAttribute('href');
@ -273,7 +278,7 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
private _handleCheckboxClick(targetEl: HTMLElement): void {
const allCheckboxes =
this.previewEl?.element.nativeElement.querySelectorAll('.checkbox-wrapper');
this.previewEl()?.element.nativeElement.querySelectorAll('.checkbox-wrapper');
const checkIndex = Array.from(allCheckboxes || []).findIndex((el) => el === targetEl);
if (checkIndex !== -1 && this._model) {

View file

@ -6,7 +6,7 @@ import {
HostBinding,
Input,
Output,
ViewChild,
viewChild,
} from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { T } from 'src/app/t.const';
@ -47,7 +47,8 @@ export class InlineMultilineInputComponent {
lastExternalValue?: string;
tmpValue?: string;
@ViewChild('textAreaElement') textarea!: ElementRef<HTMLTextAreaElement>;
readonly textarea =
viewChild.required<ElementRef<HTMLTextAreaElement>>('textAreaElement');
@Output() valueEdited = new EventEmitter<{
newVal: string;
@ -78,7 +79,7 @@ export class InlineMultilineInputComponent {
this._setTxtHeight();
try {
window.setTimeout(() => {
const el = this.textarea.nativeElement;
const el = this.textarea().nativeElement;
el.setSelectionRange(el.value.length, el.value.length);
el.selectionStart = el.selectionEnd = el.value.length;
});
@ -124,7 +125,7 @@ export class InlineMultilineInputComponent {
}
private _forceBlur(): void {
this.textarea.nativeElement.blur();
this.textarea().nativeElement.blur();
}
private _submit(): void {
@ -153,15 +154,15 @@ export class InlineMultilineInputComponent {
private _setTxtHeight(): void {
try {
// reset height
this.textarea.nativeElement.style.height = 'auto';
this.textarea.nativeElement.style.height =
this.textarea.nativeElement.scrollHeight + 'px';
const textarea = this.textarea();
textarea.nativeElement.style.height = 'auto';
textarea.nativeElement.style.height = textarea.nativeElement.scrollHeight + 'px';
} catch (e) {
setTimeout(() => {
// reset height
this.textarea.nativeElement.style.height = 'auto';
this.textarea.nativeElement.style.height =
this.textarea.nativeElement.scrollHeight + 'px';
const textarea = this.textarea();
textarea.nativeElement.style.height = 'auto';
textarea.nativeElement.style.height = textarea.nativeElement.scrollHeight + 'px';
});
}
}

View file

@ -4,7 +4,7 @@ import {
ElementRef,
Input,
Renderer2,
ViewChild,
viewChild,
} from '@angular/core';
@Component({
@ -16,21 +16,22 @@ import {
})
export class ProgressCircleComponent {
@Input() set progress(progressIN: number) {
if (this.progressCircle) {
const progressCircle = this.progressCircle();
if (progressCircle) {
let progress = progressIN || 0;
if (progress > 100) {
progress = 100;
}
this._renderer.setStyle(
this.progressCircle.nativeElement,
progressCircle.nativeElement,
'stroke-dasharray',
`${progress} ,100`,
);
}
}
@ViewChild('progressCircle', { static: true }) progressCircle?: ElementRef;
readonly progressCircle = viewChild<ElementRef>('progressCircle');
constructor(private readonly _renderer: Renderer2) {}
}