@taiga-ui/cdk
Version:
Base library for creating Angular components and applications using Taiga UI principles regarding of actual visual appearance
97 lines (89 loc) • 5.07 kB
JavaScript
import { DOCUMENT } from '@angular/common';
import { InjectionToken, inject, RendererFactory2, NgZone } from '@angular/core';
import { WA_WINDOW } from '@ng-web-apis/common';
import { tuiTypedFromEvent, tuiZonefreeScheduler } from '@taiga-ui/cdk/observables';
import { tuiGetDocumentOrShadowRoot, tuiGetActualTarget, tuiWindowSize } from '@taiga-ui/cdk/utils/dom';
import { tuiIsFocusable } from '@taiga-ui/cdk/utils/focus';
import { Subject, startWith, switchMap, timer, map, share, merge, filter, takeUntil, repeat, withLatestFrom, of, take, distinctUntilChanged } from 'rxjs';
import { WA_IS_IOS, WA_IS_ANDROID } from '@ng-web-apis/platform';
import { toObservable } from '@angular/core/rxjs-interop';
// TODO: Remove when fixed: https://issues.chromium.org/issues/41484175
const TUI_REMOVED_ELEMENT = new InjectionToken(ngDevMode ? 'TUI_REMOVED_ELEMENT' : '', {
factory: () => {
const element$ = new Subject();
const renderer = inject(RendererFactory2).createRenderer(null, null);
const proto = Object.getPrototypeOf(renderer.delegate ?? renderer);
const { removeChild } = proto;
proto.removeChild = function (...args) {
element$.next(args[1]);
removeChild.apply(this, args);
};
return element$.pipe(startWith(null), switchMap((element) => timer(0).pipe(map(() => null), startWith(element))), share());
},
});
// Checks if focusout event should be considered leaving active zone
function isValidFocusout(target, removedElement = null) {
return (
// Not due to switching tabs/going to DevTools
tuiGetDocumentOrShadowRoot(target).activeElement !== target &&
// Not due to button/input becoming disabled or under disabled fieldset
!target.matches(':disabled') &&
// Not due to element being removed from DOM
!removedElement?.contains(target) &&
// Not due to scrollable element became non-scrollable
(target.getAttribute('tabIndex') === '-1' || tuiIsFocusable(target)));
}
function shadowRootActiveElement(root) {
return merge(tuiTypedFromEvent(root, 'focusin').pipe(map(({ target }) => target)), tuiTypedFromEvent(root, 'focusout').pipe(filter(({ target, relatedTarget }) => !!relatedTarget && isValidFocusout(target)), map(({ relatedTarget }) => relatedTarget)));
}
/**
* Active element on the document for ActiveZone
*/
const TUI_ACTIVE_ELEMENT = new InjectionToken(ngDevMode ? 'TUI_ACTIVE_ELEMENT' : '', {
factory: () => {
const removedElement$ = inject(TUI_REMOVED_ELEMENT);
const win = inject(WA_WINDOW);
const doc = inject(DOCUMENT);
const zone = inject(NgZone);
const focusout$ = tuiTypedFromEvent(win, 'focusout', { capture: true });
const focusin$ = tuiTypedFromEvent(win, 'focusin', { capture: true });
const blur$ = tuiTypedFromEvent(win, 'blur');
const mousedown$ = tuiTypedFromEvent(win, 'mousedown');
const mouseup$ = tuiTypedFromEvent(win, 'mouseup');
const pointerdown$ = tuiTypedFromEvent(win, 'pointerdown');
const pointercancel$ = tuiTypedFromEvent(win, 'pointercancel');
return merge(focusout$.pipe(takeUntil(pointerdown$.pipe(filter((e) => !e.defaultPrevented))), repeat({ delay: () => merge(mouseup$, pointercancel$) }), withLatestFrom(removedElement$), filter(([event, removedElement]) => isValidFocusout(tuiGetActualTarget(event), removedElement)), map(([{ relatedTarget }]) => relatedTarget)), blur$.pipe(map(() => doc.activeElement), filter((element) => !!element?.matches('iframe'))), focusin$.pipe(switchMap((event) => {
const target = tuiGetActualTarget(event);
const root = tuiGetDocumentOrShadowRoot(target) || doc;
return root === doc
? of(target)
: shadowRootActiveElement(root).pipe(startWith(target));
})), mousedown$.pipe(map(tuiGetActualTarget), switchMap((target) => !doc.activeElement || doc.activeElement === doc.body
? of(target)
: focusout$.pipe(take(1), map(() => target), takeUntil(timer(0, tuiZonefreeScheduler(zone))))))).pipe(distinctUntilChanged(), share());
},
});
const TUI_FALLBACK_VALUE = new InjectionToken(ngDevMode ? 'TUI_FALLBACK_VALUE' : '', { factory: () => null });
function tuiFallbackValueProvider(useValue) {
return {
provide: TUI_FALLBACK_VALUE,
useValue,
};
}
const TUI_PLATFORM = new InjectionToken(ngDevMode ? 'TUI_PLATFORM' : '', {
factory: () => {
if (inject(WA_IS_IOS)) {
return 'ios';
}
return inject(WA_IS_ANDROID) ? 'android' : 'web';
},
});
/**
* @deprecated: use {@link tuiWindowSize}
*/
const TUI_WINDOW_SIZE = new InjectionToken(ngDevMode ? 'TUI_WINDOW_SIZE' : '', { factory: () => toObservable(tuiWindowSize(inject(WA_WINDOW))) });
/**
* Generated bundle index. Do not edit.
*/
export { TUI_ACTIVE_ELEMENT, TUI_FALLBACK_VALUE, TUI_PLATFORM, TUI_REMOVED_ELEMENT, TUI_WINDOW_SIZE, tuiFallbackValueProvider };
//# sourceMappingURL=taiga-ui-cdk-tokens.mjs.map