From 2fc2a2dad521b8db9d60bdf72dd63fb2aed119a8 Mon Sep 17 00:00:00 2001 From: splincode Date: Tue, 17 Sep 2024 13:20:27 +0300 Subject: [PATCH] feat(cdk): support provide custom query selector for auto focus directive --- .../auto-focus/autofocus.options.ts | 45 +++++++++---------- .../auto-focus/handlers/abstract.handler.ts | 11 +++-- .../auto-focus/handlers/default.handler.ts | 6 ++- .../auto-focus/handlers/ios.handler.ts | 4 +- .../test/auto-focus.directive.spec.ts | 13 +++++- projects/cdk/utils/di/create-options.ts | 5 +-- .../cdk/utils/miscellaneous/create-token.ts | 6 +-- 7 files changed, 51 insertions(+), 39 deletions(-) diff --git a/projects/cdk/directives/auto-focus/autofocus.options.ts b/projects/cdk/directives/auto-focus/autofocus.options.ts index 3631ae9ad292..05d5f4753259 100644 --- a/projects/cdk/directives/auto-focus/autofocus.options.ts +++ b/projects/cdk/directives/auto-focus/autofocus.options.ts @@ -1,8 +1,7 @@ -import type {Provider} from '@angular/core'; -import {ElementRef, InjectionToken, NgZone, Renderer2} from '@angular/core'; +import {ElementRef, NgZone, Renderer2} from '@angular/core'; import {WA_ANIMATION_FRAME, WA_WINDOW} from '@ng-web-apis/common'; import {TUI_IS_IOS} from '@taiga-ui/cdk/tokens'; -import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous'; +import {tuiCreateOptions, tuiCreateToken} from '@taiga-ui/cdk/utils'; import type {Observable} from 'rxjs'; import {TuiDefaultAutofocusHandler} from './handlers/default.handler'; @@ -14,31 +13,29 @@ export interface TuiAutofocusHandler { export interface TuiAutofocusOptions { readonly delay: number; + readonly query: string; } -export const TUI_AUTOFOCUS_DEFAULT_OPTIONS: TuiAutofocusOptions = { - delay: NaN, // NaN = no delay/sync -}; +export const [TUI_AUTOFOCUS_OPTIONS, tuiAutoFocusOptionsProvider] = + tuiCreateOptions({ + delay: NaN, // NaN = no delay/sync + query: 'input, textarea, select, [contenteditable]', + }); -export const TUI_AUTOFOCUS_OPTIONS = tuiCreateToken(TUI_AUTOFOCUS_DEFAULT_OPTIONS); - -export function tuiAutoFocusOptionsProvider( - options: Partial, -): Provider { - return tuiProvideOptions( - TUI_AUTOFOCUS_OPTIONS, - options, - TUI_AUTOFOCUS_DEFAULT_OPTIONS, - ); -} - -export const TUI_AUTOFOCUS_HANDLER = new InjectionToken( - '[TUI_AUTOFOCUS_HANDLER]', -); +export const TUI_AUTOFOCUS_HANDLER = tuiCreateToken(); export const TUI_AUTOFOCUS_PROVIDERS = [ { provide: TUI_AUTOFOCUS_HANDLER, + deps: [ + ElementRef, + WA_ANIMATION_FRAME, + Renderer2, + NgZone, + WA_WINDOW, + TUI_IS_IOS, + TUI_AUTOFOCUS_OPTIONS, + ], useFactory: ( el: ElementRef, animationFrame$: Observable, @@ -46,10 +43,10 @@ export const TUI_AUTOFOCUS_PROVIDERS = [ zone: NgZone, win: Window, isIos: boolean, + options: TuiAutofocusOptions, ) => isIos - ? new TuiIosAutofocusHandler(el, renderer, zone, win) - : new TuiDefaultAutofocusHandler(el, animationFrame$, zone), - deps: [ElementRef, WA_ANIMATION_FRAME, Renderer2, NgZone, WA_WINDOW, TUI_IS_IOS], + ? new TuiIosAutofocusHandler(el, renderer, zone, win, options) + : new TuiDefaultAutofocusHandler(el, animationFrame$, zone, options), }, ]; diff --git a/projects/cdk/directives/auto-focus/handlers/abstract.handler.ts b/projects/cdk/directives/auto-focus/handlers/abstract.handler.ts index f6ff0e75c84f..fa6bf4631438 100644 --- a/projects/cdk/directives/auto-focus/handlers/abstract.handler.ts +++ b/projects/cdk/directives/auto-focus/handlers/abstract.handler.ts @@ -1,22 +1,25 @@ import type {ElementRef} from '@angular/core'; -import type {TuiAutofocusHandler} from '../autofocus.options'; +import type {TuiAutofocusHandler, TuiAutofocusOptions} from '../autofocus.options'; export abstract class AbstractTuiAutofocusHandler implements TuiAutofocusHandler { - constructor(protected readonly el: ElementRef) {} + constructor( + protected readonly el: ElementRef, + protected readonly options: TuiAutofocusOptions, + ) {} public abstract setFocus(): void; protected get element(): HTMLElement { // TODO: Remove when legacy controls are dropped const el = this.el.nativeElement.tagName.includes('-') - ? this.el.nativeElement.querySelector('input,textarea') + ? this.el.nativeElement.querySelector(this.options.query) : this.el.nativeElement; return el || this.el.nativeElement; } protected get isTextFieldElement(): boolean { - return this.element.matches('input, textarea, [contenteditable]'); + return this.element.matches(this.options.query); } } diff --git a/projects/cdk/directives/auto-focus/handlers/default.handler.ts b/projects/cdk/directives/auto-focus/handlers/default.handler.ts index 505a4825a829..81ee57b71c98 100644 --- a/projects/cdk/directives/auto-focus/handlers/default.handler.ts +++ b/projects/cdk/directives/auto-focus/handlers/default.handler.ts @@ -3,6 +3,7 @@ import {tuiZonefreeScheduler} from '@taiga-ui/cdk/observables'; import type {Observable} from 'rxjs'; import {map, race, skipWhile, take, throttleTime, timer} from 'rxjs'; +import type {TuiAutofocusOptions} from '../autofocus.options'; import {AbstractTuiAutofocusHandler} from './abstract.handler'; const TIMEOUT = 1000; @@ -13,14 +14,15 @@ export class TuiDefaultAutofocusHandler extends AbstractTuiAutofocusHandler { el: ElementRef, private readonly animationFrame$: Observable, private readonly zone: NgZone, + options: TuiAutofocusOptions, ) { - super(el); + super(el, options); } public setFocus(): void { if (this.isTextFieldElement) { race( - timer(TIMEOUT), + timer(this.options.delay || TIMEOUT), this.animationFrame$.pipe( throttleTime(100, tuiZonefreeScheduler(this.zone)), map(() => this.element.closest(NG_ANIMATION_SELECTOR)), diff --git a/projects/cdk/directives/auto-focus/handlers/ios.handler.ts b/projects/cdk/directives/auto-focus/handlers/ios.handler.ts index b53523be68b4..6c4e0f80dd43 100644 --- a/projects/cdk/directives/auto-focus/handlers/ios.handler.ts +++ b/projects/cdk/directives/auto-focus/handlers/ios.handler.ts @@ -1,6 +1,7 @@ import type {ElementRef, NgZone, Renderer2} from '@angular/core'; import {tuiIsPresent, tuiPx} from '@taiga-ui/cdk/utils'; +import type {TuiAutofocusOptions} from '../autofocus.options'; import {AbstractTuiAutofocusHandler} from './abstract.handler'; const TEXTFIELD_ATTRS = [ @@ -22,8 +23,9 @@ export class TuiIosAutofocusHandler extends AbstractTuiAutofocusHandler { private readonly renderer: Renderer2, private readonly zone: NgZone, private readonly win: Window, + options: TuiAutofocusOptions, ) { - super(el); + super(el, options); this.patchCssStyles(); } diff --git a/projects/cdk/directives/auto-focus/test/auto-focus.directive.spec.ts b/projects/cdk/directives/auto-focus/test/auto-focus.directive.spec.ts index 36e3044a971a..d47ec6f7043e 100644 --- a/projects/cdk/directives/auto-focus/test/auto-focus.directive.spec.ts +++ b/projects/cdk/directives/auto-focus/test/auto-focus.directive.spec.ts @@ -9,8 +9,10 @@ import { import type {ComponentFixture} from '@angular/core/testing'; import {fakeAsync, TestBed, tick} from '@angular/core/testing'; import {WA_WINDOW} from '@ng-web-apis/common'; +import type {TuiAutofocusOptions} from '@taiga-ui/cdk'; import { TUI_AUTOFOCUS_HANDLER, + TUI_AUTOFOCUS_OPTIONS, TuiAutoFocus, TuiIosAutofocusHandler, tuiIsNativeFocused, @@ -81,13 +83,20 @@ describe('TuiAutoFocus directive', () => { { provide: TUI_AUTOFOCUS_HANDLER, useClass: TuiIosAutofocusHandler, + deps: [ + ElementRef, + Renderer2, + NgZone, + WA_WINDOW, + TUI_AUTOFOCUS_OPTIONS, + ], useFactory: ( el: ElementRef, renderer: Renderer2, zone: NgZone, win: Window, - ) => new TuiIosAutofocusHandler(el, renderer, zone, win), - deps: [ElementRef, Renderer2, NgZone, WA_WINDOW], + options: TuiAutofocusOptions, + ) => new TuiIosAutofocusHandler(el, renderer, zone, win, options), }, ], }); diff --git a/projects/cdk/utils/di/create-options.ts b/projects/cdk/utils/di/create-options.ts index 70e3863527b5..d2f84baa356f 100644 --- a/projects/cdk/utils/di/create-options.ts +++ b/projects/cdk/utils/di/create-options.ts @@ -1,10 +1,9 @@ -import type {InjectionToken, Provider} from '@angular/core'; -import type {TuiHandler} from '@taiga-ui/cdk/types'; +import type {FactoryProvider, InjectionToken} from '@angular/core'; import {tuiCreateToken, tuiProvideOptions} from '@taiga-ui/cdk/utils/miscellaneous'; export function tuiCreateOptions( defaults: T, -): [token: InjectionToken, provider: TuiHandler, Provider>] { +): [token: InjectionToken, provider: (item: Partial) => FactoryProvider] { const token = tuiCreateToken(defaults); return [token, (options) => tuiProvideOptions(token, options, defaults)]; diff --git a/projects/cdk/utils/miscellaneous/create-token.ts b/projects/cdk/utils/miscellaneous/create-token.ts index 1046ea41410e..0948841228c4 100644 --- a/projects/cdk/utils/miscellaneous/create-token.ts +++ b/projects/cdk/utils/miscellaneous/create-token.ts @@ -1,9 +1,9 @@ import {InjectionToken} from '@angular/core'; -export function tuiCreateToken(defaults: T): InjectionToken { +export function tuiCreateToken(defaults?: T): InjectionToken { return tuiCreateTokenFromFactory(() => defaults); } -export function tuiCreateTokenFromFactory(factory: () => T): InjectionToken { - return new InjectionToken('', {factory}); +export function tuiCreateTokenFromFactory(factory?: () => T): InjectionToken { + return factory ? new InjectionToken('', {factory}) : new InjectionToken(''); }