@ethlete/core 5.0.0-next.60 → 5.0.0-next.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/fesm2022/ethlete-core.mjs +972 -597
- package/fesm2022/ethlete-core.mjs.map +1 -1
- package/generators/generators.json +10 -0
- package/generators/migrate-create-destroy/create-destroy.js +317 -0
- package/generators/migrate-create-destroy/migration.js +46 -0
- package/generators/migrate-create-destroy/schema.json +26 -0
- package/generators/migrate-legacy-color-themes/legacy-color-themes.js +123 -0
- package/generators/migrate-legacy-color-themes/migration.js +93 -0
- package/generators/migrate-legacy-color-themes/schema.json +26 -0
- package/generators/migrate-to-v5/apply-replacements.js +14 -0
- package/generators/migrate-to-v5/color-naming.js +11 -57
- package/generators/migrate-to-v5/create-provider.js +56 -44
- package/generators/migrate-to-v5/migration-files.js +13 -0
- package/generators/migrate-to-v5/migration.js +31 -27
- package/generators/migrate-to-v5/removed-exports.js +101 -33
- package/generators/migrate-to-v5/router-state-service.js +118 -350
- package/generators/migrate-to-v5/schema.json +12 -0
- package/generators/migrate-to-v5/service-classes.js +108 -0
- package/generators/migrate-to-v5/viewport-service.js +195 -443
- package/generators/tailwind-4-color-theme/generator.js +71 -85
- package/migrations/legacy-color-themes.md +38 -0
- package/migrations/seo-directive-removed.md +2 -1
- package/migrations.json +15 -0
- package/package.json +11 -11
- package/types/ethlete-core.d.ts +300 -202
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { assertInInjectionContext, inject, DestroyRef, ElementRef, TemplateRef, InjectionToken, QueryList, RendererFactory2, RendererStyleFlags2, isSignal, DOCUMENT, signal, linkedSignal, isDevMode, computed, afterNextRender, effect, untracked, ApplicationRef, runInInjectionContext, NgZone, Injector,
|
|
2
|
+
import { assertInInjectionContext, inject, DestroyRef, ElementRef, TemplateRef, InjectionToken, booleanAttribute, QueryList, RendererFactory2, RendererStyleFlags2, isSignal, DOCUMENT, signal, linkedSignal, isDevMode, computed, afterNextRender, effect, untracked, ApplicationRef, runInInjectionContext, NgZone, Injector, numberAttribute, PLATFORM_ID, afterRenderEffect, EnvironmentInjector, createComponent, ViewEncapsulation, Component, Directive, model, ViewContainerRef, input, CSP_NONCE, provideEnvironmentInitializer, output, createEnvironmentInjector, Pipe, afterEveryRender } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed, toSignal, toObservable, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
4
|
-
import { Subject, fromEvent, switchMap, startWith, of, defer, from, map, catchError, timer, takeUntil, pairwise, debounceTime, merge, distinctUntilChanged, EMPTY, expand, skip,
|
|
4
|
+
import { Subject, fromEvent, switchMap, startWith, of, defer, from, map, catchError, timer, takeUntil, tap, pairwise, debounceTime, merge, distinctUntilChanged, EMPTY, expand, skip, take, filter, BehaviorSubject, combineLatest, Observable, isObservable, firstValueFrom, defaultIfEmpty, concatMap, concat, finalize, exhaustMap, share, shareReplay, ReplaySubject } from 'rxjs';
|
|
5
5
|
import { FormGroup, FormArray, FormControl } from '@angular/forms';
|
|
6
6
|
import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';
|
|
7
|
-
import { isPlatformBrowser, DOCUMENT as DOCUMENT$1 } from '@angular/common';
|
|
7
|
+
import { isPlatformBrowser, Location, DOCUMENT as DOCUMENT$1 } from '@angular/common';
|
|
8
8
|
import { Router, NavigationEnd, NavigationSkipped, NavigationStart, NavigationError, RouterLink } from '@angular/router';
|
|
9
9
|
import { Title, Meta, DomSanitizer } from '@angular/platform-browser';
|
|
10
|
-
import { offset, flip, shift, limitShift, computePosition, autoUpdate, hide, arrow, size } from '@floating-ui/dom';
|
|
10
|
+
import { platform, offset, flip, shift, limitShift, computePosition, autoUpdate, hide, arrow, size } from '@floating-ui/dom';
|
|
11
11
|
|
|
12
12
|
const ANIMATION_DEBUG_STORAGE_KEY = 'et-overlay-debug';
|
|
13
13
|
let cachedEnabled = null;
|
|
@@ -63,6 +63,9 @@ const randomId = () => {
|
|
|
63
63
|
return `et-id-${(randomIdFallbackCounter++).toString(36)}-${Date.now().toString(36)}`;
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @deprecated Use `takeUntilDestroyed()` from `@angular/core/rxjs-interop`. `yarn nx g @ethlete/core:migrate-create-destroy` rewrites the common pattern.
|
|
68
|
+
*/
|
|
66
69
|
const createDestroy = () => {
|
|
67
70
|
assertInInjectionContext(createDestroy);
|
|
68
71
|
const destroy$ = new Subject();
|
|
@@ -91,11 +94,13 @@ const getObjectProperty = (obj, prop) => {
|
|
|
91
94
|
if (!isObject(value))
|
|
92
95
|
return undefined;
|
|
93
96
|
if (prop.includes('[')) {
|
|
94
|
-
const [key,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
const [key, ...indexes] = prop.split('[').map((part) => part.replace(']', ''));
|
|
98
|
+
value = value[key];
|
|
99
|
+
for (const index of indexes) {
|
|
100
|
+
if (!Array.isArray(value))
|
|
101
|
+
return undefined;
|
|
102
|
+
value = value[+index];
|
|
103
|
+
}
|
|
99
104
|
}
|
|
100
105
|
else {
|
|
101
106
|
value = value[prop];
|
|
@@ -187,7 +192,8 @@ const createValueProviders = (token, defaultValue, valueOverride, options) => [
|
|
|
187
192
|
];
|
|
188
193
|
const maybeMergeValues = (defaultValue, valueOverride) => {
|
|
189
194
|
if (isObject(valueOverride) && isObject(defaultValue)) {
|
|
190
|
-
|
|
195
|
+
const definedOverride = Object.fromEntries(Object.entries(valueOverride).filter(([, value]) => value !== undefined));
|
|
196
|
+
return { ...defaultValue, ...definedOverride };
|
|
191
197
|
}
|
|
192
198
|
return valueOverride ?? defaultValue;
|
|
193
199
|
};
|
|
@@ -248,6 +254,12 @@ const applyHostListeners = (listeners, options) => {
|
|
|
248
254
|
}
|
|
249
255
|
};
|
|
250
256
|
|
|
257
|
+
function optionalBooleanAttribute(value) {
|
|
258
|
+
if (value === null || value === undefined)
|
|
259
|
+
return value;
|
|
260
|
+
return booleanAttribute(value);
|
|
261
|
+
}
|
|
262
|
+
|
|
251
263
|
class TypedQueryList extends QueryList {
|
|
252
264
|
get changes() {
|
|
253
265
|
return super.changes;
|
|
@@ -540,7 +552,7 @@ const equal = (foo, bar) => {
|
|
|
540
552
|
if (!tmp)
|
|
541
553
|
return false;
|
|
542
554
|
}
|
|
543
|
-
if (!equal(len[1], bar.get(tmp))) {
|
|
555
|
+
if (!bar.has(tmp) || !equal(len[1], bar.get(tmp))) {
|
|
544
556
|
return false;
|
|
545
557
|
}
|
|
546
558
|
}
|
|
@@ -580,6 +592,9 @@ const equal = (foo, bar) => {
|
|
|
580
592
|
return foo !== foo && bar !== bar;
|
|
581
593
|
};
|
|
582
594
|
|
|
595
|
+
// Only what a cookie value cannot hold is escaped, so base64 and JWT values stay byte-identical for
|
|
596
|
+
// servers and older readers that do not decode them.
|
|
597
|
+
const encodeCookieValue = (value) => encodeURIComponent(value).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g, decodeURIComponent);
|
|
583
598
|
const hasCookie = (name) => {
|
|
584
599
|
if (typeof document === 'undefined') {
|
|
585
600
|
return false;
|
|
@@ -593,38 +608,63 @@ const getCookie = (name) => {
|
|
|
593
608
|
return null;
|
|
594
609
|
}
|
|
595
610
|
const cookie = document.cookie.split(';').find((entry) => entry.trim().startsWith(`${name}=`));
|
|
596
|
-
|
|
611
|
+
const raw = cookie?.trim().slice(name.length + 1);
|
|
612
|
+
if (raw === undefined)
|
|
613
|
+
return null;
|
|
614
|
+
try {
|
|
615
|
+
return decodeURIComponent(raw);
|
|
616
|
+
}
|
|
617
|
+
catch {
|
|
618
|
+
return raw;
|
|
619
|
+
}
|
|
597
620
|
};
|
|
598
|
-
const setCookie = (name, data, expiresInDays, domain
|
|
621
|
+
const setCookie = (name, data, expiresInDays, domain, path = '/', sameSite = 'lax') => {
|
|
599
622
|
if (typeof document === 'undefined') {
|
|
600
623
|
return;
|
|
601
624
|
}
|
|
625
|
+
const derivedDomain = domain === undefined;
|
|
626
|
+
const resolvedDomain = derivedDomain ? getDomain() : domain;
|
|
602
627
|
const sameSiteUpper = sameSite.toUpperCase();
|
|
603
628
|
const expires = expiresInDays ?? 30;
|
|
604
|
-
let cookieString = `${name}=${data}; path=${path}`;
|
|
629
|
+
let cookieString = `${name}=${encodeCookieValue(data)}; path=${path}`;
|
|
605
630
|
if (expiresInDays !== null) {
|
|
606
631
|
const date = new Date();
|
|
607
632
|
date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
|
|
608
633
|
cookieString += `; expires=${date.toUTCString()}`;
|
|
609
634
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
635
|
+
const attributes = (withDomain) => {
|
|
636
|
+
let result = cookieString;
|
|
637
|
+
if (withDomain && resolvedDomain) {
|
|
638
|
+
result += `; domain=${resolvedDomain}`;
|
|
639
|
+
}
|
|
640
|
+
return result;
|
|
641
|
+
};
|
|
642
|
+
let suffix = `; SameSite=${sameSiteUpper}`;
|
|
614
643
|
if (sameSite === 'none' || (typeof window !== 'undefined' && window.location.protocol === 'https:')) {
|
|
615
|
-
|
|
644
|
+
suffix += '; Secure';
|
|
645
|
+
}
|
|
646
|
+
suffix += ';';
|
|
647
|
+
document.cookie = attributes(true) + suffix;
|
|
648
|
+
if (derivedDomain && resolvedDomain && getCookie(name) !== data) {
|
|
649
|
+
document.cookie = attributes(false) + suffix;
|
|
616
650
|
}
|
|
617
|
-
cookieString += ';';
|
|
618
|
-
document.cookie = cookieString;
|
|
619
651
|
};
|
|
620
|
-
const deleteCookie = (name, path = '/', domain
|
|
621
|
-
if (hasCookie(name))
|
|
652
|
+
const deleteCookie = (name, path = '/', domain) => {
|
|
653
|
+
if (!hasCookie(name))
|
|
654
|
+
return;
|
|
655
|
+
const derivedDomain = domain === undefined;
|
|
656
|
+
const resolvedDomain = derivedDomain ? getDomain() : domain;
|
|
657
|
+
const expire = (withDomain) => {
|
|
622
658
|
document.cookie =
|
|
623
659
|
name +
|
|
624
660
|
'=' +
|
|
625
661
|
(path ? ';path=' + path : '') +
|
|
626
|
-
(
|
|
662
|
+
(withDomain && resolvedDomain ? ';domain=' + resolvedDomain : '') +
|
|
627
663
|
';expires=Thu, 01 Jan 1970 00:00:01 GMT';
|
|
664
|
+
};
|
|
665
|
+
expire(true);
|
|
666
|
+
if (derivedDomain && hasCookie(name)) {
|
|
667
|
+
expire(false);
|
|
628
668
|
}
|
|
629
669
|
};
|
|
630
670
|
const getDomain = () => {
|
|
@@ -646,6 +686,7 @@ const getDomain = () => {
|
|
|
646
686
|
return hostname;
|
|
647
687
|
};
|
|
648
688
|
|
|
689
|
+
/** @deprecated Keyed on the deprecated `KeyboardEvent.keyCode`. Compare `KeyboardEvent.key` instead. */
|
|
649
690
|
class KeyPressManager {
|
|
650
691
|
constructor(key) {
|
|
651
692
|
this.key = key;
|
|
@@ -660,8 +701,8 @@ class KeyPressManager {
|
|
|
660
701
|
this.keyPressCount++;
|
|
661
702
|
this._stopTimeout$.next();
|
|
662
703
|
timer(100)
|
|
663
|
-
.pipe(takeUntil(this._stopTimeout$))
|
|
664
|
-
.subscribe(
|
|
704
|
+
.pipe(takeUntil(this._stopTimeout$), tap(() => this.clear()))
|
|
705
|
+
.subscribe();
|
|
665
706
|
}
|
|
666
707
|
else {
|
|
667
708
|
this.clear();
|
|
@@ -1493,7 +1534,7 @@ const signalAnimatedNumber = (source, options = {}) => {
|
|
|
1493
1534
|
if (destroyed)
|
|
1494
1535
|
return;
|
|
1495
1536
|
const elapsed = timestamp - startTime;
|
|
1496
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
1537
|
+
const progress = duration > 0 ? Math.min(Math.max(elapsed / duration, 0), 1) : 1;
|
|
1497
1538
|
const easedProgress = easing(progress);
|
|
1498
1539
|
const value = startValue + delta * easedProgress;
|
|
1499
1540
|
currentAnimatedValue = value;
|
|
@@ -1693,24 +1734,36 @@ const resolveFromMap = (map, bp, defaultValue) => {
|
|
|
1693
1734
|
}
|
|
1694
1735
|
return defaultValue;
|
|
1695
1736
|
};
|
|
1737
|
+
/** Builds an input transform that coerces a value or a per-breakpoint map to `T` for the current breakpoint. */
|
|
1696
1738
|
const breakpointTransformBase = (coerce, defaultValue) => {
|
|
1697
1739
|
const currentBp = injectCurrentBreakpoint();
|
|
1698
1740
|
const injector = inject(Injector);
|
|
1699
1741
|
const raw = signal(undefined, /* @ts-ignore */
|
|
1700
1742
|
...(ngDevMode ? [{ debugName: "raw" }] : /* istanbul ignore next */ []));
|
|
1743
|
+
let warnedMissingInstance = false;
|
|
1701
1744
|
let cachedSig = null;
|
|
1702
1745
|
const transformFn = (value) => {
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1746
|
+
if (isBreakpointMap(value)) {
|
|
1747
|
+
const map = value;
|
|
1748
|
+
raw.set(map);
|
|
1749
|
+
return resolveFromMap(map, currentBp(), defaultValue);
|
|
1750
|
+
}
|
|
1751
|
+
raw.set(undefined);
|
|
1752
|
+
return coerce(value);
|
|
1706
1753
|
};
|
|
1707
1754
|
effect(() => {
|
|
1708
1755
|
const bp = currentBp();
|
|
1709
1756
|
const r = raw();
|
|
1710
1757
|
if (!cachedSig) {
|
|
1711
1758
|
const instance = injector.get(BREAKPOINT_INSTANCE_TOKEN, null);
|
|
1712
|
-
if (!instance)
|
|
1759
|
+
if (!instance) {
|
|
1760
|
+
if (ngDevMode && r !== undefined && !warnedMissingInstance) {
|
|
1761
|
+
warnedMissingInstance = true;
|
|
1762
|
+
console.warn('[ethlete] A breakpoint map was bound to an input, but its component does not call ' +
|
|
1763
|
+
'provideBreakpointInstance(), so the value will not follow the breakpoint after the first render.');
|
|
1764
|
+
}
|
|
1713
1765
|
return;
|
|
1766
|
+
}
|
|
1714
1767
|
for (const key of Object.keys(instance)) {
|
|
1715
1768
|
const val = instance[key];
|
|
1716
1769
|
if (val && typeof val === 'function' && val[SIGNAL]?.transformFn === transformFn) {
|
|
@@ -1719,7 +1772,7 @@ const breakpointTransformBase = (coerce, defaultValue) => {
|
|
|
1719
1772
|
}
|
|
1720
1773
|
}
|
|
1721
1774
|
}
|
|
1722
|
-
if (!cachedSig || r === undefined
|
|
1775
|
+
if (!cachedSig || r === undefined)
|
|
1723
1776
|
return;
|
|
1724
1777
|
const resolved = resolveFromMap(r, bp, defaultValue);
|
|
1725
1778
|
untracked(() => setInputSignal(cachedSig, resolved));
|
|
@@ -1852,8 +1905,8 @@ const signalCountdown = (deadline) => {
|
|
|
1852
1905
|
const remainingMs = endsAt - Date.now();
|
|
1853
1906
|
return remainingMs <= 0 ? EMPTY : timer(remainingMs % SECOND_MS || SECOND_MS);
|
|
1854
1907
|
}), skip(1));
|
|
1855
|
-
}), takeUntilDestroyed())
|
|
1856
|
-
.subscribe(
|
|
1908
|
+
}), tap(() => tick.update((count) => count + 1)), takeUntilDestroyed())
|
|
1909
|
+
.subscribe();
|
|
1857
1910
|
}
|
|
1858
1911
|
return computed(() => {
|
|
1859
1912
|
tick();
|
|
@@ -1897,13 +1950,13 @@ const signalDeferredLoading = (source, options) => {
|
|
|
1897
1950
|
}
|
|
1898
1951
|
const remaining = minDuration - (performance.now() - shownAt);
|
|
1899
1952
|
return remaining <= 0 ? of(false) : timer(remaining).pipe(map(() => false));
|
|
1900
|
-
}),
|
|
1901
|
-
.subscribe((isVisible) => {
|
|
1953
|
+
}), tap((isVisible) => {
|
|
1902
1954
|
if (isVisible) {
|
|
1903
1955
|
shownAt = performance.now();
|
|
1904
1956
|
}
|
|
1905
1957
|
visible.set(isVisible);
|
|
1906
|
-
})
|
|
1958
|
+
}), takeUntilDestroyed())
|
|
1959
|
+
.subscribe();
|
|
1907
1960
|
return visible.asReadonly();
|
|
1908
1961
|
};
|
|
1909
1962
|
|
|
@@ -1948,8 +2001,6 @@ const signalElementChildren = (el, options) => {
|
|
|
1948
2001
|
if (!isRendered())
|
|
1949
2002
|
return [];
|
|
1950
2003
|
const els = firstEl();
|
|
1951
|
-
// We are not interested what the mutation is, just that there is one.
|
|
1952
|
-
// Changes to the DOM may affect the children of the element.
|
|
1953
2004
|
elementMutations();
|
|
1954
2005
|
if (!els.currentElement)
|
|
1955
2006
|
return [];
|
|
@@ -1967,14 +2018,13 @@ const signalElementChildren = (el, options) => {
|
|
|
1967
2018
|
const buildSignalEffects = (el, config) => {
|
|
1968
2019
|
const elements = buildElementSignal(el);
|
|
1969
2020
|
const injector = inject(Injector);
|
|
2021
|
+
const liveTokens = new Map(Object.entries(config.tokenMap));
|
|
1970
2022
|
effect(() => {
|
|
1971
2023
|
const { currentElements, previousElements } = elements();
|
|
1972
2024
|
for (const previousEl of previousElements) {
|
|
1973
2025
|
if (currentElements.includes(previousEl))
|
|
1974
2026
|
continue;
|
|
1975
|
-
const tokens =
|
|
1976
|
-
.map((key) => key.split(' '))
|
|
1977
|
-
.flat();
|
|
2027
|
+
const tokens = [...liveTokens.keys()].flatMap((key) => key.split(' '));
|
|
1978
2028
|
if (!tokens.length)
|
|
1979
2029
|
continue;
|
|
1980
2030
|
config.cleanupFn(previousEl, tokens);
|
|
@@ -1982,7 +2032,7 @@ const buildSignalEffects = (el, config) => {
|
|
|
1982
2032
|
for (const currentEl of currentElements) {
|
|
1983
2033
|
if (previousElements.includes(currentEl))
|
|
1984
2034
|
continue;
|
|
1985
|
-
for (const [tokens, condition] of
|
|
2035
|
+
for (const [tokens, condition] of liveTokens) {
|
|
1986
2036
|
untracked(() => {
|
|
1987
2037
|
const tokenArray = tokens.split(' ');
|
|
1988
2038
|
if (!tokenArray.length)
|
|
@@ -1997,6 +2047,7 @@ const buildSignalEffects = (el, config) => {
|
|
|
1997
2047
|
const push = (tokens, signal) => {
|
|
1998
2048
|
if (has(tokens))
|
|
1999
2049
|
return;
|
|
2050
|
+
liveTokens.set(tokens, signal);
|
|
2000
2051
|
runInInjectionContext(injector, () => {
|
|
2001
2052
|
effects[tokens] = effect(() => {
|
|
2002
2053
|
const { currentElements } = untracked(() => elements());
|
|
@@ -2018,6 +2069,7 @@ const buildSignalEffects = (el, config) => {
|
|
|
2018
2069
|
const remove = (tokens) => {
|
|
2019
2070
|
effects[tokens]?.destroy();
|
|
2020
2071
|
delete effects[tokens];
|
|
2072
|
+
liveTokens.delete(tokens);
|
|
2021
2073
|
for (const el of elements().currentElements) {
|
|
2022
2074
|
const tokenArray = tokens.split(' ');
|
|
2023
2075
|
if (!tokenArray.length)
|
|
@@ -2099,8 +2151,6 @@ const signalStyles = (el, styleMap) => {
|
|
|
2099
2151
|
const signalHostStyles = (styleMap) => signalStyles(inject(ElementRef), styleMap);
|
|
2100
2152
|
|
|
2101
2153
|
const elementCanScroll = (element, direction) => {
|
|
2102
|
-
// Omitting the element means "the document scroller". Resolved lazily and guarded so a call on the
|
|
2103
|
-
// server answers "cannot scroll" instead of throwing on a missing `document`.
|
|
2104
2154
|
const el = element || (typeof document === 'undefined' ? null : document.documentElement);
|
|
2105
2155
|
if (!el) {
|
|
2106
2156
|
return false;
|
|
@@ -2115,9 +2165,6 @@ const elementCanScroll = (element, direction) => {
|
|
|
2115
2165
|
return scrollHeight > clientHeight || scrollWidth > clientWidth;
|
|
2116
2166
|
};
|
|
2117
2167
|
const createViewportRect = () => {
|
|
2118
|
-
// Collapses to a zero rect where there is no window (server). `isElementVisible` never reaches the
|
|
2119
|
-
// intersection math there anyway - `elementCanScroll` already reports the document as unscrollable
|
|
2120
|
-
// - so the rect only has to be a valid shape.
|
|
2121
2168
|
const hasWindow = typeof window !== 'undefined';
|
|
2122
2169
|
const width = hasWindow ? window.innerWidth : 0;
|
|
2123
2170
|
const height = hasWindow ? window.innerHeight : 0;
|
|
@@ -2169,9 +2216,22 @@ const isElementVisible = (options) => {
|
|
|
2169
2216
|
intersectionRatio: Math.round(Math.min(inlineIntersectionPercentage, blockIntersectionPercentage) * 100) / 100,
|
|
2170
2217
|
};
|
|
2171
2218
|
};
|
|
2219
|
+
const nearestAlignment = (elStart, elEnd, conStart, conEnd) => {
|
|
2220
|
+
const startOutside = elStart < conStart;
|
|
2221
|
+
const endOutside = elEnd > conEnd;
|
|
2222
|
+
const fits = elEnd - elStart <= conEnd - conStart;
|
|
2223
|
+
if (startOutside === endOutside)
|
|
2224
|
+
return null;
|
|
2225
|
+
if (startOutside === fits)
|
|
2226
|
+
return 'start';
|
|
2227
|
+
return 'end';
|
|
2228
|
+
};
|
|
2172
2229
|
const getElementScrollCoordinates = (options) => {
|
|
2173
2230
|
const { container, element, direction, behavior = 'smooth', origin = 'nearest', scrollBlockMargin = 0, scrollInlineMargin = 0, } = options;
|
|
2174
|
-
if (!element ||
|
|
2231
|
+
if (!element ||
|
|
2232
|
+
!container ||
|
|
2233
|
+
(container.isConnected && !container.contains(element)) ||
|
|
2234
|
+
!elementCanScroll(container)) {
|
|
2175
2235
|
return {
|
|
2176
2236
|
behavior,
|
|
2177
2237
|
left: undefined,
|
|
@@ -2199,33 +2259,33 @@ const getElementScrollCoordinates = (options) => {
|
|
|
2199
2259
|
let scrollTopTo = scrollTop;
|
|
2200
2260
|
const relativeTop = elTop - conTop;
|
|
2201
2261
|
const relativeLeft = elLeft - conLeft;
|
|
2262
|
+
const calculateInlineStart = () => (scrollLeftTo = scrollLeft + relativeLeft - scrollInlineMargin);
|
|
2263
|
+
const calculateBlockStart = () => (scrollTopTo = scrollTop + relativeTop - scrollBlockMargin);
|
|
2264
|
+
const calculateInlineEnd = () => (scrollLeftTo = scrollLeft + relativeLeft - conWidth + elWidth + scrollInlineMargin);
|
|
2265
|
+
const calculateBlockEnd = () => (scrollTopTo = scrollTop + relativeTop - conHeight + elHeight + scrollBlockMargin);
|
|
2202
2266
|
const calculateScrollToStart = () => {
|
|
2203
|
-
|
|
2204
|
-
|
|
2267
|
+
calculateInlineStart();
|
|
2268
|
+
calculateBlockStart();
|
|
2205
2269
|
};
|
|
2206
2270
|
const calculateScrollToEnd = () => {
|
|
2207
|
-
|
|
2208
|
-
|
|
2271
|
+
calculateInlineEnd();
|
|
2272
|
+
calculateBlockEnd();
|
|
2209
2273
|
};
|
|
2210
2274
|
const calculateScrollToCenter = () => {
|
|
2211
2275
|
scrollLeftTo = scrollLeft + relativeLeft - conWidth / 2 + elWidth / 2;
|
|
2212
2276
|
scrollTopTo = scrollTop + relativeTop - conHeight / 2 + elHeight / 2;
|
|
2213
2277
|
};
|
|
2214
2278
|
const calculateScrollToNearest = () => {
|
|
2215
|
-
const
|
|
2216
|
-
const
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
if (
|
|
2224
|
-
|
|
2225
|
-
}
|
|
2226
|
-
else if (isBelow || isPartialBelow || isRight || isPartialRight) {
|
|
2227
|
-
calculateScrollToEnd();
|
|
2228
|
-
}
|
|
2279
|
+
const inline = nearestAlignment(elLeft, elRight, conLeft, conRight);
|
|
2280
|
+
const block = nearestAlignment(elTop, elBottom, conTop, conBottom);
|
|
2281
|
+
if (inline === 'start')
|
|
2282
|
+
calculateInlineStart();
|
|
2283
|
+
else if (inline === 'end')
|
|
2284
|
+
calculateInlineEnd();
|
|
2285
|
+
if (block === 'start')
|
|
2286
|
+
calculateBlockStart();
|
|
2287
|
+
else if (block === 'end')
|
|
2288
|
+
calculateBlockEnd();
|
|
2229
2289
|
};
|
|
2230
2290
|
switch (origin) {
|
|
2231
2291
|
case 'start':
|
|
@@ -2252,6 +2312,7 @@ const scrollToElement = (options) => {
|
|
|
2252
2312
|
};
|
|
2253
2313
|
|
|
2254
2314
|
const MIN_SNAP_DELTA_PX = 1;
|
|
2315
|
+
const FULLY_VISIBLE_RATIO = 0.99;
|
|
2255
2316
|
const getScrollSnapTarget = (items, container, direction, origin, margin = 0) => {
|
|
2256
2317
|
if (!items.length)
|
|
2257
2318
|
return null;
|
|
@@ -2371,7 +2432,7 @@ const getScrollItemTarget = (entries, container, direction, scrollOrigin, axisDi
|
|
|
2371
2432
|
}
|
|
2372
2433
|
const entry = direction === 'start' ? firstVisible : lastVisible;
|
|
2373
2434
|
const entryIndex = direction === 'start' ? firstIndex : lastIndex;
|
|
2374
|
-
if (
|
|
2435
|
+
if (entry.intersectionRatio >= FULLY_VISIBLE_RATIO) {
|
|
2375
2436
|
if (direction === 'start' && entryIndex === 0)
|
|
2376
2437
|
return null;
|
|
2377
2438
|
if (direction === 'end' && entryIndex === entries.length - 1)
|
|
@@ -2408,8 +2469,10 @@ const createPositionObject = (entry) => {
|
|
|
2408
2469
|
isVisible,
|
|
2409
2470
|
};
|
|
2410
2471
|
};
|
|
2472
|
+
const unwrap = (value) => (isSignal(value) ? value() : value);
|
|
2473
|
+
const isZeroMargin = (margin) => !margin || /^(0(px|%)?\s*)+$/.test(margin.trim());
|
|
2411
2474
|
const signalElementIntersection = (el, options) => {
|
|
2412
|
-
const { root: rootBinding, enabled, ...observerOptions } = options ?? {};
|
|
2475
|
+
const { root: rootBinding, enabled, rootMargin, threshold, ...observerOptions } = options ?? {};
|
|
2413
2476
|
const destroyRef = inject(DestroyRef);
|
|
2414
2477
|
const elements = buildElementSignal(el);
|
|
2415
2478
|
const root = firstElementSignal(rootBinding ? buildElementSignal(rootBinding) : createEmptyElementSignal());
|
|
@@ -2421,6 +2484,7 @@ const signalElementIntersection = (el, options) => {
|
|
|
2421
2484
|
const observer = signal(null, /* @ts-ignore */
|
|
2422
2485
|
...(ngDevMode ? [{ debugName: "observer" }] : /* istanbul ignore next */ []));
|
|
2423
2486
|
const currentlyObservedElements = new Set();
|
|
2487
|
+
let observedRootMargin;
|
|
2424
2488
|
const updateIntersections = (entries) => {
|
|
2425
2489
|
let currentValues = [...elementIntersectionSignal()];
|
|
2426
2490
|
for (const entry of entries) {
|
|
@@ -2450,7 +2514,7 @@ const signalElementIntersection = (el, options) => {
|
|
|
2450
2514
|
}
|
|
2451
2515
|
zone.run(() => elementIntersectionSignal.set(currentValues));
|
|
2452
2516
|
};
|
|
2453
|
-
const updateIntersectionObserver = (rendered, enabled, rootEl) => {
|
|
2517
|
+
const updateIntersectionObserver = (rendered, enabled, rootEl, margin, thresholdValue) => {
|
|
2454
2518
|
observer()?.disconnect();
|
|
2455
2519
|
currentlyObservedElements.clear();
|
|
2456
2520
|
if (!rendered || !enabled) {
|
|
@@ -2460,8 +2524,11 @@ const signalElementIntersection = (el, options) => {
|
|
|
2460
2524
|
}
|
|
2461
2525
|
const newObserver = new IntersectionObserver((entries) => updateIntersections(entries), {
|
|
2462
2526
|
...observerOptions,
|
|
2527
|
+
...(margin !== undefined && { rootMargin: margin }),
|
|
2528
|
+
...(thresholdValue !== undefined && { threshold: thresholdValue }),
|
|
2463
2529
|
root: rootEl,
|
|
2464
2530
|
});
|
|
2531
|
+
observedRootMargin = margin;
|
|
2465
2532
|
observer.set(newObserver);
|
|
2466
2533
|
};
|
|
2467
2534
|
const updateObservedElements = (observer, elements) => {
|
|
@@ -2474,15 +2541,16 @@ const signalElementIntersection = (el, options) => {
|
|
|
2474
2541
|
const newIntersectionValue = [];
|
|
2475
2542
|
for (const el of elements.currentElements) {
|
|
2476
2543
|
if (currentlyObservedElements.has(el)) {
|
|
2477
|
-
const
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
console.warn('Could not find existing entry for element. The intersection observer might be broken now.', el);
|
|
2481
|
-
continue;
|
|
2482
|
-
}
|
|
2483
|
-
newIntersectionValue.push(existingEntry);
|
|
2544
|
+
const existingEntry = currIntersectionValue.find((v) => v.target === el);
|
|
2545
|
+
if (existingEntry)
|
|
2546
|
+
newIntersectionValue.push(existingEntry);
|
|
2484
2547
|
continue;
|
|
2485
2548
|
}
|
|
2549
|
+
currentlyObservedElements.add(el);
|
|
2550
|
+
observer.observe(el);
|
|
2551
|
+
// isElementVisible ignores rootMargin, so with one set the observer's own first entry is the only correct one.
|
|
2552
|
+
if (!isZeroMargin(observedRootMargin))
|
|
2553
|
+
continue;
|
|
2486
2554
|
const initialElementVisibility = isElementVisible({
|
|
2487
2555
|
container: rootEl,
|
|
2488
2556
|
element: el,
|
|
@@ -2502,8 +2570,6 @@ const signalElementIntersection = (el, options) => {
|
|
|
2502
2570
|
...intersectionEntry,
|
|
2503
2571
|
...createPositionObject(intersectionEntry),
|
|
2504
2572
|
});
|
|
2505
|
-
currentlyObservedElements.add(el);
|
|
2506
|
-
observer.observe(el);
|
|
2507
2573
|
}
|
|
2508
2574
|
for (const el of elements.previousElements) {
|
|
2509
2575
|
if (elements.currentElements.includes(el))
|
|
@@ -2517,9 +2583,13 @@ const signalElementIntersection = (el, options) => {
|
|
|
2517
2583
|
const rootEl = root().currentElement;
|
|
2518
2584
|
const rendered = isRendered();
|
|
2519
2585
|
const enabled = isEnabled();
|
|
2520
|
-
|
|
2586
|
+
const margin = rootMargin === undefined ? undefined : unwrap(rootMargin);
|
|
2587
|
+
const thresholdValue = threshold === undefined ? undefined : unwrap(threshold);
|
|
2588
|
+
untracked(() => updateIntersectionObserver(rendered, enabled, rootEl, margin, thresholdValue));
|
|
2521
2589
|
});
|
|
2522
|
-
effect
|
|
2590
|
+
// The initial entries are measured here, not in an effect: an effect runs during change detection,
|
|
2591
|
+
// before elements rendered in the same pass have their layout.
|
|
2592
|
+
afterRenderEffect(() => {
|
|
2523
2593
|
const els = elements();
|
|
2524
2594
|
const obs = observer();
|
|
2525
2595
|
untracked(() => updateObservedElements(obs, els));
|
|
@@ -2545,6 +2615,8 @@ const signalElementLastScrollDirection = (el) => {
|
|
|
2545
2615
|
lastScrollLeft = 0;
|
|
2546
2616
|
return of(null);
|
|
2547
2617
|
}
|
|
2618
|
+
lastScrollTop = currentElement.scrollTop;
|
|
2619
|
+
lastScrollLeft = currentElement.scrollLeft;
|
|
2548
2620
|
return fromEvent(currentElement, 'scroll').pipe(tap(() => {
|
|
2549
2621
|
const { scrollTop, scrollLeft } = currentElement;
|
|
2550
2622
|
const time = Date.now();
|
|
@@ -2623,8 +2695,6 @@ const signalElementScrollState = (el, options) => {
|
|
|
2623
2695
|
return computed(() => {
|
|
2624
2696
|
const element = observedEl().currentElement;
|
|
2625
2697
|
const dimensions = elementDimensions();
|
|
2626
|
-
// We are not interested what the mutation is, just that there is one.
|
|
2627
|
-
// Changes to the DOM can affect the scroll state of the element.
|
|
2628
2698
|
elementMutations();
|
|
2629
2699
|
if (!element || !isRendered())
|
|
2630
2700
|
return notScrollable(dimensions);
|
|
@@ -2651,11 +2721,12 @@ const writeScrollbarSizeToCssVariables = () => {
|
|
|
2651
2721
|
if (!isPlatformBrowser(inject(PLATFORM_ID))) {
|
|
2652
2722
|
return;
|
|
2653
2723
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2724
|
+
// The app's injector, not the caller's: a writer started from a component must outlive it.
|
|
2725
|
+
const appInjector = inject(ApplicationRef).injector;
|
|
2726
|
+
if (scrollbarSizeWriters.has(appInjector)) {
|
|
2656
2727
|
return;
|
|
2657
2728
|
}
|
|
2658
|
-
scrollbarSizeWriters.add(
|
|
2729
|
+
scrollbarSizeWriters.add(appInjector);
|
|
2659
2730
|
const document = inject(DOCUMENT);
|
|
2660
2731
|
const renderer = injectRenderer();
|
|
2661
2732
|
const scrollbarDimensions = injectScrollbarDimensions();
|
|
@@ -2667,7 +2738,7 @@ const writeScrollbarSizeToCssVariables = () => {
|
|
|
2667
2738
|
'--et-sw': `${dimensions.width}px`,
|
|
2668
2739
|
'--et-sh': `${dimensions.height}px`,
|
|
2669
2740
|
});
|
|
2670
|
-
});
|
|
2741
|
+
}, { injector: appInjector });
|
|
2671
2742
|
};
|
|
2672
2743
|
const viewportSizeWriters = /* @__PURE__ */ new WeakSet();
|
|
2673
2744
|
/**
|
|
@@ -2679,11 +2750,11 @@ const writeViewportSizeToCssVariables = () => {
|
|
|
2679
2750
|
if (!isPlatformBrowser(inject(PLATFORM_ID))) {
|
|
2680
2751
|
return;
|
|
2681
2752
|
}
|
|
2682
|
-
const
|
|
2683
|
-
if (viewportSizeWriters.has(
|
|
2753
|
+
const appInjector = inject(ApplicationRef).injector;
|
|
2754
|
+
if (viewportSizeWriters.has(appInjector)) {
|
|
2684
2755
|
return;
|
|
2685
2756
|
}
|
|
2686
|
-
viewportSizeWriters.add(
|
|
2757
|
+
viewportSizeWriters.add(appInjector);
|
|
2687
2758
|
const document = inject(DOCUMENT);
|
|
2688
2759
|
const renderer = injectRenderer();
|
|
2689
2760
|
const htmlElementDimensions = injectViewportDimensions();
|
|
@@ -2695,7 +2766,7 @@ const writeViewportSizeToCssVariables = () => {
|
|
|
2695
2766
|
'--et-vw': `${dimensions.width}px`,
|
|
2696
2767
|
'--et-vh': `${dimensions.height}px`,
|
|
2697
2768
|
});
|
|
2698
|
-
});
|
|
2769
|
+
}, { injector: appInjector });
|
|
2699
2770
|
};
|
|
2700
2771
|
|
|
2701
2772
|
/** The deadzone in pixels after which the cursor drag scroll will take effect. */
|
|
@@ -2745,14 +2816,12 @@ const useCursorDragScroll = (el, options) => {
|
|
|
2745
2816
|
// A host destroyed mid-drag never re-runs the styling effect, so the grabbing cursor it wrote on
|
|
2746
2817
|
// `<html>` would outlive the page it was dragged on.
|
|
2747
2818
|
destroyRef.onDestroy(() => renderer.removeStyle(document.documentElement, 'cursor'));
|
|
2748
|
-
// Cleanup if the element the cursor drag scroll is bound to gets changed
|
|
2749
2819
|
effect(() => {
|
|
2750
2820
|
const { previousElement } = element();
|
|
2751
2821
|
if (previousElement) {
|
|
2752
2822
|
renderer.removeStyle(previousElement, 'cursor');
|
|
2753
2823
|
}
|
|
2754
2824
|
});
|
|
2755
|
-
// Conditionally apply styles/classes to the element and the document
|
|
2756
2825
|
effect(() => {
|
|
2757
2826
|
const currCanScroll = canScroll();
|
|
2758
2827
|
const isEnabled = enabled();
|
|
@@ -2792,7 +2861,6 @@ const useCursorDragScroll = (el, options) => {
|
|
|
2792
2861
|
}
|
|
2793
2862
|
});
|
|
2794
2863
|
});
|
|
2795
|
-
// Update the element's scroll position when the user drags
|
|
2796
2864
|
effect(() => {
|
|
2797
2865
|
const currDragAmount = dragAmount();
|
|
2798
2866
|
untracked(() => {
|
|
@@ -2886,7 +2954,8 @@ const createInitialRoute = () => {
|
|
|
2886
2954
|
if (!isBrowser) {
|
|
2887
2955
|
return router.url;
|
|
2888
2956
|
}
|
|
2889
|
-
|
|
2957
|
+
const path = inject(Location).path(true);
|
|
2958
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
2890
2959
|
};
|
|
2891
2960
|
/** Inject the current router event */
|
|
2892
2961
|
const injectRouterEvent = /* @__PURE__ */ memoizeSignal(() => {
|
|
@@ -2953,6 +3022,7 @@ const injectRoute = /* @__PURE__ */ memoizeSignal(() => {
|
|
|
2953
3022
|
return withoutFragment;
|
|
2954
3023
|
});
|
|
2955
3024
|
});
|
|
3025
|
+
const withInjector = (config, fn) => config?.injector ? runInInjectionContext(config.injector, fn) : fn();
|
|
2956
3026
|
const createRouterState = (router) => {
|
|
2957
3027
|
let route = router.routerState.snapshot.root;
|
|
2958
3028
|
while (route.firstChild) {
|
|
@@ -2992,37 +3062,35 @@ const injectRouterState = /* @__PURE__ */ memoizeSignal(() => {
|
|
|
2992
3062
|
});
|
|
2993
3063
|
/** Inject a signal containing the current route fragment (the part after the # inside the url if present) */
|
|
2994
3064
|
const injectFragment = (config) => {
|
|
2995
|
-
const routerState = config
|
|
2996
|
-
? runInInjectionContext(config.injector, injectRouterState)
|
|
2997
|
-
: injectRouterState();
|
|
3065
|
+
const routerState = withInjector(config, injectRouterState);
|
|
2998
3066
|
const fragment = computed(() => routerState().fragment, /* @ts-ignore */
|
|
2999
3067
|
...(ngDevMode ? [{ debugName: "fragment" }] : /* istanbul ignore next */ []));
|
|
3000
3068
|
return transformOrReturn(fragment, config);
|
|
3001
3069
|
};
|
|
3002
3070
|
/** Inject all currently available query parameters as a signal */
|
|
3003
|
-
const injectQueryParams = () => {
|
|
3004
|
-
const routerState = injectRouterState
|
|
3071
|
+
const injectQueryParams = (config) => {
|
|
3072
|
+
const routerState = withInjector(config, injectRouterState);
|
|
3005
3073
|
const queryParams = computed(() => routerState().queryParams, /* @ts-ignore */
|
|
3006
3074
|
...(ngDevMode ? [{ debugName: "queryParams" }] : /* istanbul ignore next */ []));
|
|
3007
3075
|
return queryParams;
|
|
3008
3076
|
};
|
|
3009
3077
|
/** Inject all currently available route data as a signal */
|
|
3010
|
-
const injectRouteData = () => {
|
|
3011
|
-
const routerState = injectRouterState
|
|
3078
|
+
const injectRouteData = (config) => {
|
|
3079
|
+
const routerState = withInjector(config, injectRouterState);
|
|
3012
3080
|
const data = computed(() => routerState().data, /* @ts-ignore */
|
|
3013
3081
|
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
3014
3082
|
return data;
|
|
3015
3083
|
};
|
|
3016
3084
|
/** Inject the current route title as a signal */
|
|
3017
3085
|
const injectRouteTitle = (config) => {
|
|
3018
|
-
const routerState = injectRouterState
|
|
3086
|
+
const routerState = withInjector(config, injectRouterState);
|
|
3019
3087
|
const title = computed(() => routerState().title, /* @ts-ignore */
|
|
3020
3088
|
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
3021
3089
|
return transformOrReturn(title, config);
|
|
3022
3090
|
};
|
|
3023
3091
|
/** Inject all currently available path parameters as a signal */
|
|
3024
|
-
const injectPathParams = () => {
|
|
3025
|
-
const routerState = injectRouterState
|
|
3092
|
+
const injectPathParams = (config) => {
|
|
3093
|
+
const routerState = withInjector(config, injectRouterState);
|
|
3026
3094
|
const pathParams = computed(() => routerState().pathParams, /* @ts-ignore */
|
|
3027
3095
|
...(ngDevMode ? [{ debugName: "pathParams" }] : /* istanbul ignore next */ []));
|
|
3028
3096
|
return pathParams;
|
|
@@ -3033,21 +3101,48 @@ const getQueryParamFromUrl = (key) => {
|
|
|
3033
3101
|
const urlParams = new URLSearchParams(window.location.search);
|
|
3034
3102
|
return urlParams.get(key);
|
|
3035
3103
|
};
|
|
3036
|
-
|
|
3104
|
+
const getAllQueryParamsFromUrl = (key) => {
|
|
3105
|
+
if (typeof window === 'undefined')
|
|
3106
|
+
return [];
|
|
3107
|
+
return new URLSearchParams(window.location.search).getAll(key);
|
|
3108
|
+
};
|
|
3109
|
+
const toValues = (value) => {
|
|
3110
|
+
if (value === undefined || value === null)
|
|
3111
|
+
return [];
|
|
3112
|
+
return Array.isArray(value) ? value : [value];
|
|
3113
|
+
};
|
|
3114
|
+
/**
|
|
3115
|
+
* Inject a specific query parameter as a signal. A repeated parameter (`?tag=a&tag=b`) resolves to its first value;
|
|
3116
|
+
* use `injectQueryParamAll` to read every value.
|
|
3117
|
+
*/
|
|
3037
3118
|
const injectQueryParam = (key, config) => {
|
|
3038
|
-
const queryParams = injectQueryParams();
|
|
3039
|
-
const src = computed(() =>
|
|
3119
|
+
const queryParams = injectQueryParams(config);
|
|
3120
|
+
const src = computed(() => {
|
|
3121
|
+
const first = toValues(queryParams()[key])[0];
|
|
3122
|
+
return first ?? (config?.requireSync ? getQueryParamFromUrl(key) : null);
|
|
3123
|
+
}, /* @ts-ignore */
|
|
3124
|
+
...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
3125
|
+
return transformOrReturn(src, config);
|
|
3126
|
+
};
|
|
3127
|
+
/** Inject every value of a specific query parameter as a signal. Resolves to an empty array when it is absent. */
|
|
3128
|
+
const injectQueryParamAll = (key, config) => {
|
|
3129
|
+
const queryParams = injectQueryParams(config);
|
|
3130
|
+
const src = computed(() => {
|
|
3131
|
+
const values = toValues(queryParams()[key]);
|
|
3132
|
+
return values.length || !config?.requireSync ? values : getAllQueryParamsFromUrl(key);
|
|
3133
|
+
}, /* @ts-ignore */
|
|
3134
|
+
...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
3040
3135
|
return transformOrReturn(src, config);
|
|
3041
3136
|
};
|
|
3042
3137
|
/** Inject a specific route data item as a signal */
|
|
3043
3138
|
const injectRouteDataItem = (key, config) => {
|
|
3044
|
-
const data = injectRouteData();
|
|
3139
|
+
const data = injectRouteData(config);
|
|
3045
3140
|
const src = computed(() => data()[key] ?? null);
|
|
3046
3141
|
return transformOrReturn(src, config);
|
|
3047
3142
|
};
|
|
3048
3143
|
/** Inject a specific path parameter as a signal */
|
|
3049
3144
|
const injectPathParam = (key, config) => {
|
|
3050
|
-
const pathParams = injectPathParams();
|
|
3145
|
+
const pathParams = injectPathParams(config);
|
|
3051
3146
|
const src = computed(() => pathParams()[key] ?? null);
|
|
3052
3147
|
return transformOrReturn(src, config);
|
|
3053
3148
|
};
|
|
@@ -3120,9 +3215,9 @@ const injectPathParamChanges = /* @__PURE__ */ memoizeSignal(() => {
|
|
|
3120
3215
|
* protected cameFromCart = this.navState?.fromCart ?? false;
|
|
3121
3216
|
* }
|
|
3122
3217
|
*/
|
|
3123
|
-
const injectRouterNavigationState = () => {
|
|
3124
|
-
const router = inject(Router);
|
|
3125
|
-
return router.
|
|
3218
|
+
const injectRouterNavigationState = (config) => {
|
|
3219
|
+
const router = withInjector(config, () => inject(Router));
|
|
3220
|
+
return router.currentNavigation()?.extras.state ?? null;
|
|
3126
3221
|
};
|
|
3127
3222
|
|
|
3128
3223
|
const ET_DISABLE_SCROLL_TOP = /* @__PURE__ */ Symbol('ET_DISABLE_SCROLL_TOP');
|
|
@@ -3351,7 +3446,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3351
3446
|
// Read off the navigation rather than `history.state`: a symbol key does not survive being
|
|
3352
3447
|
// structured-cloned into the history entry, which is what keeps the mark from outliving this
|
|
3353
3448
|
// navigation and re-firing when the user later pops back to the entry it created.
|
|
3354
|
-
const navigationState = router.
|
|
3449
|
+
const navigationState = router.currentNavigation()?.extras.state;
|
|
3355
3450
|
wantsRestore = !isPopstate && navigationState?.[ET_RESTORE_SCROLL] === true;
|
|
3356
3451
|
};
|
|
3357
3452
|
const onNavigationEnd = (event) => {
|
|
@@ -3425,14 +3520,14 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3425
3520
|
}
|
|
3426
3521
|
};
|
|
3427
3522
|
router.events
|
|
3428
|
-
.pipe(filter((e) => e instanceof NavigationStart || e instanceof NavigationEnd || e instanceof NavigationSkipped),
|
|
3429
|
-
.subscribe((event) => {
|
|
3523
|
+
.pipe(filter((e) => e instanceof NavigationStart || e instanceof NavigationEnd || e instanceof NavigationSkipped), tap((event) => {
|
|
3430
3524
|
if (event instanceof NavigationStart) {
|
|
3431
3525
|
onNavigationStart(event);
|
|
3432
3526
|
return;
|
|
3433
3527
|
}
|
|
3434
3528
|
onNavigationEnd(event);
|
|
3435
|
-
})
|
|
3529
|
+
}), takeUntilDestroyed())
|
|
3530
|
+
.subscribe();
|
|
3436
3531
|
destroyRef.onDestroy(() => {
|
|
3437
3532
|
cancelPendingRestore();
|
|
3438
3533
|
if (restoreEnabled && previousScrollRestoration !== null) {
|
|
@@ -3444,7 +3539,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3444
3539
|
const DISABLE_LOGGER_PARAM = 'et-logger-quiet';
|
|
3445
3540
|
const createLogger = (config) => {
|
|
3446
3541
|
const { scope, feature } = config;
|
|
3447
|
-
const disableLogging = injectQueryParam(DISABLE_LOGGER_PARAM);
|
|
3542
|
+
const disableLogging = injectQueryParam(DISABLE_LOGGER_PARAM, { transform: (value) => value !== null });
|
|
3448
3543
|
return {
|
|
3449
3544
|
log: (...args) => {
|
|
3450
3545
|
if (!disableLogging())
|
|
@@ -3470,15 +3565,31 @@ const escapeHtmlPreservingEntities = (str) => str
|
|
|
3470
3565
|
.replace(/</g, '<')
|
|
3471
3566
|
.replace(/>/g, '>')
|
|
3472
3567
|
.replace(/"/g, '"');
|
|
3568
|
+
/** Attribute that makes `htmlToMarkdown` write an element's text verbatim, without Markdown escaping:
|
|
3569
|
+
* `<span data-markdown-verbatim>{{user:_x}}</span>` becomes `{{user:_x}}`. The element must hold
|
|
3570
|
+
* text only. */
|
|
3571
|
+
const MARKDOWN_VERBATIM_ATTR = 'data-markdown-verbatim';
|
|
3572
|
+
const VERBATIM_RE = /* @__PURE__ */ new RegExp(`<span\\b[^>]*\\b${MARKDOWN_VERBATIM_ATTR}\\b[^>]*>([^<]*)<\\/span>`, 'gi');
|
|
3573
|
+
const codePointToString = (codePoint) => (codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : '\ufffd');
|
|
3473
3574
|
const decodeUrlCharacterReferences = (url) => url
|
|
3474
|
-
.replace(/&#(\d+);?/g, (_, value) =>
|
|
3475
|
-
.replace(/&#x([0-9a-f]+);?/gi, (_, value) =>
|
|
3575
|
+
.replace(/&#(\d+);?/g, (_, value) => codePointToString(Number(value)))
|
|
3576
|
+
.replace(/&#x([0-9a-f]+);?/gi, (_, value) => codePointToString(Number.parseInt(value, 16)))
|
|
3476
3577
|
.replace(/&(colon|tab|newline);/gi, (_, entity) => entity.toLowerCase() === 'colon' ? ':' : entity.toLowerCase() === 'tab' ? '\t' : '\n');
|
|
3578
|
+
/** Whether `url` may be embedded as an image `src`: everything except `javascript:` and `vbscript:`
|
|
3579
|
+
* URLs and non-image `data:` URLs, however they are cased, spaced or entity-encoded. */
|
|
3477
3580
|
const isSafeUrl = (url) => {
|
|
3478
3581
|
// eslint-disable-next-line no-control-regex -- URL parsers ignore these characters inside schemes.
|
|
3479
3582
|
const normalized = decodeUrlCharacterReferences(url).replace(/[\s\u0000-\u001f]/g, '');
|
|
3480
3583
|
return (!/^(javascript|vbscript):/i.test(normalized) && !/^data:(?!image\/(?:avif|gif|jpeg|png|webp);)/i.test(normalized));
|
|
3481
3584
|
};
|
|
3585
|
+
/** Whether `url` may become a link: `http:`, `https:`, `mailto:` and `tel:` URLs, relative paths and
|
|
3586
|
+
* fragments pass; any other scheme (`javascript:`, `data:`, `vbscript:`, ...) is refused, however
|
|
3587
|
+
* it is cased, spaced or entity-encoded. */
|
|
3588
|
+
const isSafeLinkUrl = (url) => {
|
|
3589
|
+
// eslint-disable-next-line no-control-regex -- URL parsers ignore these characters inside schemes.
|
|
3590
|
+
const normalized = decodeUrlCharacterReferences(url).replace(/[\s\u0000-\u001f]/g, '');
|
|
3591
|
+
return !/^[a-z][a-z0-9+.-]*:/i.test(normalized) || /^(?:https?|mailto|tel):/i.test(normalized);
|
|
3592
|
+
};
|
|
3482
3593
|
const unescapeHtml = (str) => str
|
|
3483
3594
|
.replace(/&/g, '&')
|
|
3484
3595
|
.replace(/</g, '<')
|
|
@@ -3527,7 +3638,7 @@ const sanitizeInlineHtml = (html) => {
|
|
|
3527
3638
|
const name = tag.toLowerCase();
|
|
3528
3639
|
if (name === 'a') {
|
|
3529
3640
|
const href = closing ? null : /\bhref\s*=\s*"([^"]*)"/i.exec(full)?.[1];
|
|
3530
|
-
const anchor = closing ? '</a>' : href &&
|
|
3641
|
+
const anchor = closing ? '</a>' : href && isSafeLinkUrl(href) ? `<a href="${href}">` : '<a>';
|
|
3531
3642
|
return makePlaceholder('TAG', kept.push(anchor) - 1);
|
|
3532
3643
|
}
|
|
3533
3644
|
if (!SAFE_INLINE_TAGS.has(name))
|
|
@@ -3538,10 +3649,10 @@ const sanitizeInlineHtml = (html) => {
|
|
|
3538
3649
|
};
|
|
3539
3650
|
const processInline = (text) => {
|
|
3540
3651
|
const inlineCodes = [];
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3652
|
+
const escapes = [];
|
|
3653
|
+
text = text.replace(/\\([!-/:-@[-`{-~])|`([^`]+)`/g, (_, escaped, code) => escaped !== undefined
|
|
3654
|
+
? makePlaceholder('ESC', escapes.push(escapeHtml(escaped)) - 1)
|
|
3655
|
+
: makePlaceholder('IC', inlineCodes.push(`<code>${escapeHtml(code ?? '')}</code>`) - 1));
|
|
3545
3656
|
// "Open in new tab" links round-trip as raw HTML because Markdown has no `target` syntax. Extract
|
|
3546
3657
|
// them before the raw-HTML escape below, keeping only a safe href, `target="_blank"` and a forced
|
|
3547
3658
|
// `rel="noopener noreferrer"` (any other attribute - including event handlers - is dropped); their
|
|
@@ -3550,9 +3661,11 @@ const processInline = (text) => {
|
|
|
3550
3661
|
text = text.replace(/<a\b([^>]*)>([\s\S]*?)<\/a>/gi, (full, attrs, inner) => {
|
|
3551
3662
|
const href = /\bhref\s*=\s*"([^"]*)"/i.exec(attrs)?.[1];
|
|
3552
3663
|
const newTab = /\btarget\s*=\s*"_blank"/i.test(attrs);
|
|
3553
|
-
if (!newTab || !href
|
|
3664
|
+
if (!newTab || !href)
|
|
3554
3665
|
return full;
|
|
3555
|
-
const anchor =
|
|
3666
|
+
const anchor = isSafeLinkUrl(href)
|
|
3667
|
+
? `<a href="${href}" target="_blank" rel="noopener noreferrer">${processInline(inner)}</a>`
|
|
3668
|
+
: processInline(inner);
|
|
3556
3669
|
return makePlaceholder('NTLINK', newTabLinks.push(anchor) - 1);
|
|
3557
3670
|
});
|
|
3558
3671
|
// Raw HTML in Markdown text is escaped, not rendered - the editor writes this HTML straight
|
|
@@ -3570,10 +3683,16 @@ const processInline = (text) => {
|
|
|
3570
3683
|
text = text.replace(/~~([^\s~](?:.*?[^\s~])?)~~/g, '<del>$1</del>');
|
|
3571
3684
|
// Images before links (order matters); URLs with script-running schemes stay literal text
|
|
3572
3685
|
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => isSafeUrl(src) ? `<img src="${src}" alt="${alt}">` : match);
|
|
3573
|
-
text = text.replace(
|
|
3686
|
+
text = text.replace(/(?<!!)\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => isSafeLinkUrl(href) ? `<a href="${href}">${label}</a>` : label);
|
|
3687
|
+
// New-tab links first: their inner markup still holds this call's code and escape placeholders.
|
|
3574
3688
|
return text
|
|
3575
|
-
.replace(placeholderRe('
|
|
3576
|
-
.replace(placeholderRe('
|
|
3689
|
+
.replace(placeholderRe('NTLINK'), (_, i) => newTabLinks[+i] ?? '')
|
|
3690
|
+
.replace(placeholderRe('IC'), (match, i) => inlineCodes[+i] ?? match)
|
|
3691
|
+
.replace(placeholderRe('ESC'), (match, i) => escapes[+i] ?? match);
|
|
3692
|
+
};
|
|
3693
|
+
const parseStartNumber = (openTag) => {
|
|
3694
|
+
const value = /\bstart\s*=\s*["']?(-?\d+)/i.exec(openTag)?.[1];
|
|
3695
|
+
return value === undefined ? 1 : Number(value);
|
|
3577
3696
|
};
|
|
3578
3697
|
/** The first top-level `<ul>`/`<ol>` in `html`, matched with balanced nesting. */
|
|
3579
3698
|
const findList = (html) => {
|
|
@@ -3593,6 +3712,7 @@ const findList = (html) => {
|
|
|
3593
3712
|
start: open.index,
|
|
3594
3713
|
end: tag.lastIndex,
|
|
3595
3714
|
ordered: openTagName.toLowerCase() === 'ol',
|
|
3715
|
+
startNumber: parseStartNumber(open[0]),
|
|
3596
3716
|
inner: html.slice(innerStart, match.index),
|
|
3597
3717
|
};
|
|
3598
3718
|
}
|
|
@@ -3692,15 +3812,15 @@ const buildBlockquoteHtml = (lines) => {
|
|
|
3692
3812
|
return `<blockquote>${html || '<br>'}</blockquote>`;
|
|
3693
3813
|
};
|
|
3694
3814
|
/** Serializes a list's inner HTML to Markdown, indenting nested lists two spaces per level. */
|
|
3695
|
-
const listToMarkdown = (inner, ordered, depth) => {
|
|
3815
|
+
const listToMarkdown = (inner, ordered, startNumber, depth) => {
|
|
3696
3816
|
const indent = ' '.repeat(depth);
|
|
3697
|
-
let n =
|
|
3817
|
+
let n = startNumber;
|
|
3698
3818
|
return findListItems(inner)
|
|
3699
3819
|
.map((itemInner) => {
|
|
3700
3820
|
let content = itemInner;
|
|
3701
3821
|
let nestedMarkdown = '';
|
|
3702
3822
|
for (let nested = findList(content); nested; nested = findList(content)) {
|
|
3703
|
-
nestedMarkdown += `\n${listToMarkdown(nested.inner, nested.ordered, depth + 1)}`;
|
|
3823
|
+
nestedMarkdown += `\n${listToMarkdown(nested.inner, nested.ordered, nested.startNumber, depth + 1)}`;
|
|
3704
3824
|
content = content.slice(0, nested.start) + content.slice(nested.end);
|
|
3705
3825
|
}
|
|
3706
3826
|
const marker = ordered ? `${n++}. ` : '- ';
|
|
@@ -3714,7 +3834,12 @@ const parseListLines = (lines) => lines
|
|
|
3714
3834
|
.map((line) => {
|
|
3715
3835
|
const match = /^(\s*)([-*+]|\d+\.)\s+(.*)$/.exec(line);
|
|
3716
3836
|
return match
|
|
3717
|
-
? {
|
|
3837
|
+
? {
|
|
3838
|
+
indent: Math.floor((match[1] ?? '').length / 2),
|
|
3839
|
+
ordered: /\d/.test(match[2] ?? ''),
|
|
3840
|
+
number: Number.parseInt(match[2] ?? '', 10),
|
|
3841
|
+
text: match[3] ?? '',
|
|
3842
|
+
}
|
|
3718
3843
|
: null;
|
|
3719
3844
|
})
|
|
3720
3845
|
.filter((line) => line !== null);
|
|
@@ -3735,17 +3860,53 @@ const buildListHtml = (lines, start, baseIndent) => {
|
|
|
3735
3860
|
items += `<li>${item}</li>`;
|
|
3736
3861
|
}
|
|
3737
3862
|
const tag = ordered ? 'ol' : 'ul';
|
|
3738
|
-
|
|
3863
|
+
const startNumber = ordered ? (lines[start]?.number ?? 1) : 1;
|
|
3864
|
+
const startAttr = startNumber === 1 ? '' : ` start="${startNumber}"`;
|
|
3865
|
+
return { html: `<${tag}${startAttr}>${items}</${tag}>`, next: i };
|
|
3866
|
+
};
|
|
3867
|
+
const escapeMarkdownText = (text) => text
|
|
3868
|
+
.replace(/[\\`[\]]/g, '\\$&')
|
|
3869
|
+
.replace(/(?<!\s)\*|\*(?!\s)/g, '\\*')
|
|
3870
|
+
.replace(/_+/g, (run, offset, whole) => run.length === 1 && /[a-z0-9]/i.test(whole[offset - 1] ?? '') && /[a-z0-9]/i.test(whole[offset + 1] ?? '')
|
|
3871
|
+
? run
|
|
3872
|
+
: run.replace(/_/g, '\\_'))
|
|
3873
|
+
.replace(/~{2,}/g, (run) => run.replace(/~/g, '\\~'))
|
|
3874
|
+
.replace(/<(?=\/?(?:u|a|p|h[1-6]|div)\b)/gi, '\\<')
|
|
3875
|
+
.replace(/(^|\n)([ \t]*)(?=[-+*#|:]|>|\d+\.)/g, '$1$2\u{E002}');
|
|
3876
|
+
const escapeMarkdownAlt = (alt) => alt.replace(/[\\[\]]/g, '\\$&');
|
|
3877
|
+
const escapeTextNodes = (html) => {
|
|
3878
|
+
let codeDepth = 0;
|
|
3879
|
+
return html.replace(/(<[^>]*>)|([^<]+)/g, (_, tag, text) => {
|
|
3880
|
+
if (tag) {
|
|
3881
|
+
const code = /^<(\/?)(?:pre|code)\b/i.exec(tag);
|
|
3882
|
+
if (code)
|
|
3883
|
+
codeDepth = Math.max(0, codeDepth + (code[1] ? -1 : 1));
|
|
3884
|
+
return tag;
|
|
3885
|
+
}
|
|
3886
|
+
return codeDepth > 0 ? (text ?? '') : escapeMarkdownText(text ?? '');
|
|
3887
|
+
});
|
|
3739
3888
|
};
|
|
3889
|
+
const escapeLineStarts = (md) => md
|
|
3890
|
+
.replace(/^([ \t]*(?:>[ \t]?)*)\u{E002}(?:([-+*])(?=[ \t]|$|--[ \t]*$)|(#{1,6})(?=[ \t]|$)|([>|])|(\d+)\.(?=[ \t]|$)|(?=[-:| \t]*\|[-:| \t]*$)(?=.*-))/gmu, (_, prefix, bullet, hashes, marker, digits) => digits !== undefined ? `${prefix}${digits}\\.` : `${prefix}\\${bullet ?? hashes ?? marker ?? ''}`)
|
|
3891
|
+
.replace(/\u{E002}/gu, '');
|
|
3740
3892
|
/**
|
|
3741
3893
|
* Converts a markdown string to HTML.
|
|
3742
3894
|
* Covers headings, bold, italic, strikethrough, inline code, fenced code blocks,
|
|
3743
3895
|
* links, images, block quotes, unordered/ordered (and nested) lists, tables, horizontal rules, and paragraphs.
|
|
3896
|
+
* A backslash before a punctuation character keeps that character literal.
|
|
3897
|
+
* Text matching `options.verbatim` is emitted as plain text, untouched by any Markdown syntax:
|
|
3898
|
+
* `markdownToHtml('{{field:__x__}}', { verbatim: /\{\{[^}]+\}\}/ })` returns `<p>{{field:__x__}}</p>`.
|
|
3744
3899
|
*/
|
|
3745
|
-
const markdownToHtml = (markdown) => {
|
|
3900
|
+
const markdownToHtml = (markdown, options = {}) => {
|
|
3746
3901
|
if (!markdown)
|
|
3747
3902
|
return '';
|
|
3748
3903
|
let text = markdown.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
3904
|
+
const verbatims = [];
|
|
3905
|
+
const { verbatim } = options;
|
|
3906
|
+
if (verbatim) {
|
|
3907
|
+
const pattern = new RegExp(verbatim.source, verbatim.flags.includes('g') ? verbatim.flags : `${verbatim.flags}g`);
|
|
3908
|
+
text = text.replace(pattern, (match) => match ? makePlaceholder('VERB', verbatims.push(escapeHtmlPreservingEntities(match)) - 1) : match);
|
|
3909
|
+
}
|
|
3749
3910
|
// Extract fenced code blocks before any other processing
|
|
3750
3911
|
const codeBlocks = [];
|
|
3751
3912
|
text = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => {
|
|
@@ -3822,17 +3983,23 @@ const markdownToHtml = (markdown) => {
|
|
|
3822
3983
|
})
|
|
3823
3984
|
.filter(Boolean)
|
|
3824
3985
|
.join('\n');
|
|
3825
|
-
return html
|
|
3986
|
+
return html
|
|
3987
|
+
.replace(placeholderRe('CODE'), (_, i) => codeBlocks[+i] ?? '')
|
|
3988
|
+
.replace(placeholderRe('VERB'), (match, i) => verbatims[+i] ?? match);
|
|
3826
3989
|
};
|
|
3827
3990
|
/**
|
|
3828
3991
|
* Converts an HTML string to markdown.
|
|
3829
3992
|
* Covers headings, bold, italic, strikethrough, inline code, fenced code blocks,
|
|
3830
3993
|
* links, images, block quotes, unordered/ordered lists, tables, horizontal rules, and paragraphs.
|
|
3994
|
+
* Text that would read as Markdown is backslash-escaped (code and {@link MARKDOWN_VERBATIM_ATTR}
|
|
3995
|
+
* elements are left as is), and a link whose URL
|
|
3996
|
+
* fails {@link isSafeLinkUrl} keeps only its text.
|
|
3831
3997
|
*/
|
|
3832
3998
|
const htmlToMarkdown = (html) => {
|
|
3833
3999
|
if (!html)
|
|
3834
4000
|
return '';
|
|
3835
|
-
|
|
4001
|
+
const verbatims = [];
|
|
4002
|
+
let md = html.replace(VERBATIM_RE, (_, text) => makePlaceholder('VERB', verbatims.push(unescapeHtml(text)) - 1));
|
|
3836
4003
|
// Aligned blocks: text-align has no Markdown form, so preserve them as HTML (their inner markup
|
|
3837
4004
|
// stays HTML) and round-trip via a placeholder - extracted before the block passes below rewrite
|
|
3838
4005
|
// them, restored after the final tag-strip.
|
|
@@ -3841,6 +4008,7 @@ const htmlToMarkdown = (html) => {
|
|
|
3841
4008
|
const name = tag.toLowerCase();
|
|
3842
4009
|
return makePlaceholder('ALIGN', alignedBlocks.push(`<${name}${alignClass(alignOf(attrs))}>${inner}</${name}>`) - 1);
|
|
3843
4010
|
});
|
|
4011
|
+
md = escapeTextNodes(md);
|
|
3844
4012
|
// Code blocks - process before inline code
|
|
3845
4013
|
md = md.replace(/<pre[^>]*><code[^>]*class="language-([^"]+)"[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (_, lang, code) => `\n\`\`\`${lang}\n${unescapeHtml(code.trim())}\n\`\`\`\n`);
|
|
3846
4014
|
md = md.replace(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (_, code) => `\n\`\`\`\n${unescapeHtml(code.trim())}\n\`\`\`\n`);
|
|
@@ -3865,14 +4033,16 @@ const htmlToMarkdown = (html) => {
|
|
|
3865
4033
|
const newTabLinks = [];
|
|
3866
4034
|
md = md.replace(/<a\b([^>]*)>([\s\S]*?)<\/a>/gi, (_, attrs, inner) => {
|
|
3867
4035
|
const href = /\bhref\s*=\s*"([^"]*)"/i.exec(attrs)?.[1] ?? '';
|
|
4036
|
+
if (!isSafeLinkUrl(href))
|
|
4037
|
+
return inner;
|
|
3868
4038
|
if (/\btarget\s*=\s*"_blank"/i.test(attrs) && href) {
|
|
3869
4039
|
const anchor = `<a href="${href}" target="_blank" rel="noopener noreferrer">${inner}</a>`;
|
|
3870
4040
|
return makePlaceholder('NTLINK', newTabLinks.push(anchor) - 1);
|
|
3871
4041
|
}
|
|
3872
4042
|
return href ? `[${inner}](${href})` : inner;
|
|
3873
4043
|
});
|
|
3874
|
-
md = md.replace(/<img[^>]+src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi,
|
|
3875
|
-
md = md.replace(/<img[^>]+alt="([^"]*)"[^>]*src="([^"]*)"[^>]*\/?>/gi,
|
|
4044
|
+
md = md.replace(/<img[^>]+src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi, (_, src, alt) => ``);
|
|
4045
|
+
md = md.replace(/<img[^>]+alt="([^"]*)"[^>]*src="([^"]*)"[^>]*\/?>/gi, (_, alt, src) => ``);
|
|
3876
4046
|
md = md.replace(/<img[^>]+src="([^"]*)"[^>]*\/?>/gi, '');
|
|
3877
4047
|
// Underline has no Markdown form - preserve it as raw <u> so it round-trips. Extract it now (its
|
|
3878
4048
|
// inner markup is already converted above) so the block passes below don't strip the tag; the
|
|
@@ -3886,7 +4056,7 @@ const htmlToMarkdown = (html) => {
|
|
|
3886
4056
|
}
|
|
3887
4057
|
// Lists - replace each top-level list with its recursively-serialized Markdown (handles nesting).
|
|
3888
4058
|
for (let list = findList(md); list; list = findList(md)) {
|
|
3889
|
-
md = `${md.slice(0, list.start)}\n${listToMarkdown(list.inner, list.ordered, 0)}\n${md.slice(list.end)}`;
|
|
4059
|
+
md = `${md.slice(0, list.start)}\n${listToMarkdown(list.inner, list.ordered, list.startNumber, 0)}\n${md.slice(list.end)}`;
|
|
3890
4060
|
}
|
|
3891
4061
|
// Tables
|
|
3892
4062
|
md = md.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, tableContent) => {
|
|
@@ -3926,7 +4096,7 @@ const htmlToMarkdown = (html) => {
|
|
|
3926
4096
|
// aligned blocks are block-level, so pad with blank lines before collapsing so they survive as
|
|
3927
4097
|
// their own Markdown block
|
|
3928
4098
|
md = md.replace(placeholderRe('ALIGN'), (_, i) => `\n\n${alignedBlocks[+i] ?? ''}\n\n`);
|
|
3929
|
-
return md.replace(/\n{3,}/g, '\n\n').trim();
|
|
4099
|
+
return escapeLineStarts(md.replace(/\n{3,}/g, '\n\n').trim()).replace(placeholderRe('VERB'), (_, i) => verbatims[+i] ?? '');
|
|
3930
4100
|
};
|
|
3931
4101
|
|
|
3932
4102
|
const clamp = (value, min = 0, max = 100) => {
|
|
@@ -4197,8 +4367,15 @@ const suppressTextSelection = (doc) => {
|
|
|
4197
4367
|
existing.count++;
|
|
4198
4368
|
}
|
|
4199
4369
|
else {
|
|
4200
|
-
|
|
4201
|
-
doc
|
|
4370
|
+
const style = doc.documentElement.style;
|
|
4371
|
+
suppressions.set(doc, {
|
|
4372
|
+
count: 1,
|
|
4373
|
+
previousUserSelect: style.userSelect,
|
|
4374
|
+
previousWebkitUserSelect: style.webkitUserSelect,
|
|
4375
|
+
});
|
|
4376
|
+
style.userSelect = 'none';
|
|
4377
|
+
// Safari and WebKitGTK ignore the unprefixed `user-select`.
|
|
4378
|
+
style.webkitUserSelect = 'none';
|
|
4202
4379
|
}
|
|
4203
4380
|
let released = false;
|
|
4204
4381
|
return () => {
|
|
@@ -4210,7 +4387,8 @@ const suppressTextSelection = (doc) => {
|
|
|
4210
4387
|
if (state.count > 0)
|
|
4211
4388
|
return;
|
|
4212
4389
|
suppressions.delete(doc);
|
|
4213
|
-
doc.documentElement.style.userSelect = state.
|
|
4390
|
+
doc.documentElement.style.userSelect = state.previousUserSelect;
|
|
4391
|
+
doc.documentElement.style.webkitUserSelect = state.previousWebkitUserSelect;
|
|
4214
4392
|
};
|
|
4215
4393
|
};
|
|
4216
4394
|
|
|
@@ -4476,6 +4654,22 @@ const createUserConsentProvider = (options) => ({
|
|
|
4476
4654
|
}),
|
|
4477
4655
|
});
|
|
4478
4656
|
|
|
4657
|
+
/**
|
|
4658
|
+
* Declares the `et-visually-hidden` class as a styles-only component: content that stays in the
|
|
4659
|
+
* accessibility tree but is clipped out of the layout. Mount it from any component whose template
|
|
4660
|
+
* uses the class.
|
|
4661
|
+
*/
|
|
4662
|
+
class VisuallyHiddenStylesComponent {
|
|
4663
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: VisuallyHiddenStylesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4664
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.6", type: VisuallyHiddenStylesComponent, isStandalone: true, selector: "et-visually-hidden-styles", ngImport: i0, template: '', isInline: true, styles: ["@layer components{.et-visually-hidden{position:absolute;inline-size:1px;block-size:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}}\n"], encapsulation: i0.ViewEncapsulation.None }); }
|
|
4665
|
+
}
|
|
4666
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: VisuallyHiddenStylesComponent, decorators: [{
|
|
4667
|
+
type: Component,
|
|
4668
|
+
args: [{ selector: 'et-visually-hidden-styles', template: '', encapsulation: ViewEncapsulation.None, styles: ["@layer components{.et-visually-hidden{position:absolute;inline-size:1px;block-size:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}}\n"] }]
|
|
4669
|
+
}] });
|
|
4670
|
+
/** Mounts {@link VisuallyHiddenStylesComponent}. Call from an injection context. */
|
|
4671
|
+
const mountVisuallyHidden = () => injectStyleManager().mount(VisuallyHiddenStylesComponent);
|
|
4672
|
+
|
|
4479
4673
|
/**
|
|
4480
4674
|
* Declares the `--ease-*` custom properties that every animated stylesheet in the SDK
|
|
4481
4675
|
* transitions with, as a styles-only component. Mount it from any component or directive whose
|
|
@@ -4610,25 +4804,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
4610
4804
|
}]
|
|
4611
4805
|
}], ctorParameters: () => [] });
|
|
4612
4806
|
|
|
4807
|
+
// codes 9000-9099
|
|
4808
|
+
const ANIMATION_ERROR_CODES = {
|
|
4809
|
+
/** `*etAnimatedIf` was used without an `[etAnimatedLifecycle]` element around it. */
|
|
4810
|
+
MISSING_ANIMATED_LIFECYCLE: 9000,
|
|
4811
|
+
};
|
|
4812
|
+
|
|
4813
|
+
/** Runs `cb` two frames from now. Returns a function that cancels it if it has not run yet. */
|
|
4613
4814
|
const nextFrame = (cb) => {
|
|
4614
4815
|
if (typeof requestAnimationFrame === 'undefined') {
|
|
4615
|
-
return;
|
|
4816
|
+
return () => undefined;
|
|
4616
4817
|
}
|
|
4617
|
-
requestAnimationFrame(() => {
|
|
4618
|
-
requestAnimationFrame(cb);
|
|
4818
|
+
let id = requestAnimationFrame(() => {
|
|
4819
|
+
id = requestAnimationFrame(cb);
|
|
4619
4820
|
});
|
|
4821
|
+
return () => cancelAnimationFrame(id);
|
|
4620
4822
|
};
|
|
4621
4823
|
const fromNextFrame = () => {
|
|
4622
|
-
return new Observable((observer) => {
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
});
|
|
4627
|
-
});
|
|
4824
|
+
return new Observable((observer) => nextFrame(() => {
|
|
4825
|
+
observer.next();
|
|
4826
|
+
observer.complete();
|
|
4827
|
+
}));
|
|
4628
4828
|
};
|
|
4629
4829
|
const forceReflow = (element) => {
|
|
4630
|
-
// The `document.body` fallback is resolved lazily and guarded: there is nothing to reflow on the
|
|
4631
|
-
// server, so this becomes a no-op instead of throwing on a missing `document`.
|
|
4632
4830
|
const el = element ?? (typeof document === 'undefined' ? null : document.body);
|
|
4633
4831
|
return el?.offsetHeight ?? 0;
|
|
4634
4832
|
};
|
|
@@ -4671,7 +4869,12 @@ class AnimatedLifecycleDirective {
|
|
|
4671
4869
|
this.transitionIdCounter = 0;
|
|
4672
4870
|
this.forcedThisFrame = false;
|
|
4673
4871
|
this.forcedFrameResetId = null;
|
|
4674
|
-
this.
|
|
4872
|
+
this.stateSubject$ = new BehaviorSubject('init');
|
|
4873
|
+
this._state = signal('init', /* @ts-ignore */
|
|
4874
|
+
...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
|
|
4875
|
+
this.state = this._state.asReadonly();
|
|
4876
|
+
/** Emits the current lifecycle state synchronously on subscribe, then every change. */
|
|
4877
|
+
this.state$ = this.stateSubject$.asObservable();
|
|
4675
4878
|
this.stateChange = outputFromObservable(this.state$);
|
|
4676
4879
|
this.skipNextEnter = model(false, /* @ts-ignore */
|
|
4677
4880
|
...(ngDevMode ? [{ debugName: "skipNextEnter" }] : /* istanbul ignore next */ []));
|
|
@@ -4694,7 +4897,7 @@ class AnimatedLifecycleDirective {
|
|
|
4694
4897
|
this.isConstructed = true;
|
|
4695
4898
|
}
|
|
4696
4899
|
enter() {
|
|
4697
|
-
const currentState = this.state
|
|
4900
|
+
const currentState = this.state();
|
|
4698
4901
|
if (currentState === 'entering')
|
|
4699
4902
|
return;
|
|
4700
4903
|
if ((currentState === 'init' && !this.isConstructed) || this.skipNextEnter()) {
|
|
@@ -4764,7 +4967,7 @@ class AnimatedLifecycleDirective {
|
|
|
4764
4967
|
previousCancel$.complete();
|
|
4765
4968
|
}
|
|
4766
4969
|
leave() {
|
|
4767
|
-
const currentState = this.state
|
|
4970
|
+
const currentState = this.state();
|
|
4768
4971
|
if (currentState === 'leaving')
|
|
4769
4972
|
return;
|
|
4770
4973
|
if (currentState === 'init') {
|
|
@@ -4865,11 +5068,11 @@ class AnimatedLifecycleDirective {
|
|
|
4865
5068
|
this.addClass(activeClass);
|
|
4866
5069
|
fromNextFrame()
|
|
4867
5070
|
.pipe(tap(() => {
|
|
4868
|
-
if (this.state
|
|
5071
|
+
if (this.state() === expectedState) {
|
|
4869
5072
|
this.removeClass(fromClass);
|
|
4870
5073
|
this.addClass(toClass);
|
|
4871
5074
|
}
|
|
4872
|
-
}), switchMap(() => this.whenAnimationsSettled()), tap(() => this.debugLog(`${transitionId}: settled`)), filter(() => this.state
|
|
5075
|
+
}), switchMap(() => this.whenAnimationsSettled()), tap(() => this.debugLog(`${transitionId}: settled`)), filter(() => this.state() === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
|
|
4873
5076
|
.subscribe();
|
|
4874
5077
|
}
|
|
4875
5078
|
handleInterruptedTransition(config) {
|
|
@@ -4877,7 +5080,7 @@ class AnimatedLifecycleDirective {
|
|
|
4877
5080
|
this.removeClasses(...removeClasses);
|
|
4878
5081
|
addClasses.forEach((cls) => this.addClass(cls));
|
|
4879
5082
|
this.whenAnimationsSettled()
|
|
4880
|
-
.pipe(tap(() => this.debugLog(`${transitionId}: interrupt settled`)), filter(() => this.state
|
|
5083
|
+
.pipe(tap(() => this.debugLog(`${transitionId}: interrupt settled`)), filter(() => this.state() === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
|
|
4881
5084
|
.subscribe();
|
|
4882
5085
|
}
|
|
4883
5086
|
/**
|
|
@@ -4912,8 +5115,9 @@ class AnimatedLifecycleDirective {
|
|
|
4912
5115
|
this.renderer.removeClass(this.element, ...classNames);
|
|
4913
5116
|
}
|
|
4914
5117
|
updateState(newState) {
|
|
4915
|
-
this.debugLog(`state: ${this.state
|
|
4916
|
-
this.
|
|
5118
|
+
this.debugLog(`state: ${this.state()} → ${newState}`);
|
|
5119
|
+
this._state.set(newState);
|
|
5120
|
+
this.stateSubject$.next(newState);
|
|
4917
5121
|
}
|
|
4918
5122
|
debugLog(message, data) {
|
|
4919
5123
|
const el = this.element;
|
|
@@ -4953,27 +5157,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
4953
5157
|
|
|
4954
5158
|
const ANIMATED_IF_TOKEN = new InjectionToken('ANIMATED_IF_TOKEN');
|
|
4955
5159
|
class AnimatedIfDirective {
|
|
5160
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
5161
|
+
return true;
|
|
5162
|
+
}
|
|
4956
5163
|
constructor() {
|
|
4957
5164
|
this.templateRef = inject(TemplateRef);
|
|
4958
5165
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
4959
|
-
this.animatedLifecycle = inject(ANIMATED_LIFECYCLE_TOKEN);
|
|
5166
|
+
this.animatedLifecycle = inject(ANIMATED_LIFECYCLE_TOKEN, { optional: true });
|
|
4960
5167
|
this.hasView = false;
|
|
5168
|
+
this.context = { $implicit: null, etAnimatedIf: null };
|
|
4961
5169
|
this.ifValue = input(null, { ...(ngDevMode ? { debugName: "ifValue" } : /* istanbul ignore next */ {}), alias: 'etAnimatedIf' });
|
|
5170
|
+
const animatedLifecycle = this.animatedLifecycle;
|
|
5171
|
+
if (!animatedLifecycle) {
|
|
5172
|
+
throw new RuntimeError(ANIMATION_ERROR_CODES.MISSING_ANIMATED_LIFECYCLE, '*etAnimatedIf needs an element with [etAnimatedLifecycle] around it to animate against.');
|
|
5173
|
+
}
|
|
4962
5174
|
toObservable(this.ifValue)
|
|
4963
5175
|
.pipe(switchMap((value) => {
|
|
4964
5176
|
if (value) {
|
|
5177
|
+
this.context.$implicit = value;
|
|
5178
|
+
this.context.etAnimatedIf = value;
|
|
4965
5179
|
if (!this.hasView) {
|
|
4966
|
-
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
|
5180
|
+
this.viewContainerRef.createEmbeddedView(this.templateRef, this.context);
|
|
4967
5181
|
this.hasView = true;
|
|
4968
5182
|
}
|
|
4969
|
-
|
|
5183
|
+
if (animatedLifecycle.state() !== 'entered')
|
|
5184
|
+
animatedLifecycle.enter();
|
|
4970
5185
|
return of(null);
|
|
4971
5186
|
}
|
|
4972
5187
|
if (!this.hasView) {
|
|
4973
5188
|
return of(null);
|
|
4974
5189
|
}
|
|
4975
|
-
|
|
4976
|
-
return
|
|
5190
|
+
animatedLifecycle.leave();
|
|
5191
|
+
return animatedLifecycle.state$.pipe(filter((state) => state === 'left'), take(1), tap(() => {
|
|
4977
5192
|
this.viewContainerRef.clear();
|
|
4978
5193
|
this.hasView = false;
|
|
4979
5194
|
}));
|
|
@@ -5030,14 +5245,16 @@ const createFlipAnimationGroup = (config) => {
|
|
|
5030
5245
|
}
|
|
5031
5246
|
};
|
|
5032
5247
|
flips.forEach((animation) => {
|
|
5033
|
-
animation.onStart
|
|
5248
|
+
animation.onStart$
|
|
5249
|
+
.pipe(tap(() => {
|
|
5034
5250
|
startedCount++;
|
|
5035
5251
|
if (startedCount === flips.length) {
|
|
5036
5252
|
onStart$.next();
|
|
5037
5253
|
}
|
|
5038
|
-
})
|
|
5039
|
-
|
|
5040
|
-
animation.
|
|
5254
|
+
}))
|
|
5255
|
+
.subscribe();
|
|
5256
|
+
animation.onFinish$.pipe(tap(() => settle(false))).subscribe();
|
|
5257
|
+
animation.onCancel$.pipe(tap(() => settle(true))).subscribe();
|
|
5041
5258
|
});
|
|
5042
5259
|
const updateInit = () => {
|
|
5043
5260
|
flips.forEach((animation) => animation.updateInit());
|
|
@@ -5147,6 +5364,23 @@ const createFlipAnimation = (config) => {
|
|
|
5147
5364
|
};
|
|
5148
5365
|
};
|
|
5149
5366
|
|
|
5367
|
+
/** The confirm an unsaved-changes tracker or guard runs when its own config has none. */
|
|
5368
|
+
const UNSAVED_CHANGES_CONFIRM = /* @__PURE__ */ new InjectionToken('UNSAVED_CHANGES_CONFIRM');
|
|
5369
|
+
/**
|
|
5370
|
+
* Sets the confirm every unsaved-changes tracker and guard below this injector runs when its config has no
|
|
5371
|
+
* `confirm`. `factory` runs once, in an injection context, and returns the confirm.
|
|
5372
|
+
*
|
|
5373
|
+
* @example
|
|
5374
|
+
* provideUnsavedChangesConfirm(() => {
|
|
5375
|
+
* const dialogs = inject(MyDialogs);
|
|
5376
|
+
* return () => dialogs.confirmDiscard();
|
|
5377
|
+
* });
|
|
5378
|
+
*/
|
|
5379
|
+
const provideUnsavedChangesConfirm = (factory) => ({
|
|
5380
|
+
provide: UNSAVED_CHANGES_CONFIRM,
|
|
5381
|
+
useFactory: factory,
|
|
5382
|
+
});
|
|
5383
|
+
|
|
5150
5384
|
const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
5151
5385
|
const _registrations = signal([], /* @ts-ignore */
|
|
5152
5386
|
...(ngDevMode ? [{ debugName: "_registrations" }] : /* istanbul ignore next */ []));
|
|
@@ -5155,7 +5389,7 @@ const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() =>
|
|
|
5155
5389
|
const hasUnsavedChanges = computed(() => _registrations().some((registration) => registration.hasChanges()), /* @ts-ignore */
|
|
5156
5390
|
...(ngDevMode ? [{ debugName: "hasUnsavedChanges" }] : /* istanbul ignore next */ []));
|
|
5157
5391
|
let pending = null;
|
|
5158
|
-
/**
|
|
5392
|
+
/** Runs a tracker's confirm under the single-flight + abandon regime. */
|
|
5159
5393
|
const runCheck = (runner) => {
|
|
5160
5394
|
// A confirm is already on screen - adopt its answer rather than opening a second dialog.
|
|
5161
5395
|
if (pending) {
|
|
@@ -5183,7 +5417,6 @@ const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() =>
|
|
|
5183
5417
|
_isCheckPending.set(true);
|
|
5184
5418
|
return promise;
|
|
5185
5419
|
};
|
|
5186
|
-
/** @internal */
|
|
5187
5420
|
const register = (registration) => {
|
|
5188
5421
|
_registrations.update((current) => [...current, registration]);
|
|
5189
5422
|
return () => _registrations.update((current) => current.filter((entry) => entry !== registration));
|
|
@@ -5231,6 +5464,11 @@ const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() =>
|
|
|
5231
5464
|
const provideUnsavedChangesCoordinator = /* @__PURE__ */ toProvideFn(UNSAVED_CHANGES_COORDINATOR_DEF);
|
|
5232
5465
|
const injectUnsavedChangesCoordinator = /* @__PURE__ */ toInjectFn(UNSAVED_CHANGES_COORDINATOR_DEF);
|
|
5233
5466
|
|
|
5467
|
+
const UNSAVED_CHANGES_ERROR_CODES = {
|
|
5468
|
+
/** An unsaved-changes tracker or guard has no `confirm`, and no `provideUnsavedChangesConfirm()` is in scope. */
|
|
5469
|
+
MISSING_CONFIRM: 9100,
|
|
5470
|
+
};
|
|
5471
|
+
|
|
5234
5472
|
const isAbstractControl = (value) => !!value &&
|
|
5235
5473
|
typeof value === 'object' &&
|
|
5236
5474
|
typeof value.getRawValue === 'function' &&
|
|
@@ -5254,22 +5492,25 @@ const normalizeUnsavedChangesSource = (source) => {
|
|
|
5254
5492
|
const peek = untracked(source);
|
|
5255
5493
|
// A signal that currently holds a FieldTree - or nothing yet (the late/async form case).
|
|
5256
5494
|
if (peek === null || peek === undefined || isFieldTree(peek)) {
|
|
5257
|
-
const
|
|
5495
|
+
const lateSource = source;
|
|
5258
5496
|
return {
|
|
5259
5497
|
value: computed(() => {
|
|
5260
|
-
const
|
|
5261
|
-
|
|
5498
|
+
const current = lateSource();
|
|
5499
|
+
if (isFieldTree(current))
|
|
5500
|
+
return current().value();
|
|
5501
|
+
return (current ?? null);
|
|
5262
5502
|
}),
|
|
5263
5503
|
setValue: (next) => {
|
|
5264
|
-
const
|
|
5265
|
-
if (isFieldTree(
|
|
5266
|
-
|
|
5504
|
+
const current = untracked(lateSource);
|
|
5505
|
+
if (isFieldTree(current)) {
|
|
5506
|
+
current().value.set(next);
|
|
5507
|
+
}
|
|
5508
|
+
else if (current !== null && current !== undefined && 'set' in source) {
|
|
5509
|
+
source.set(next);
|
|
5267
5510
|
}
|
|
5268
5511
|
},
|
|
5269
5512
|
};
|
|
5270
5513
|
}
|
|
5271
|
-
// A plain writable value signal (escape hatch). Initialize it with a non-null value so it isn't
|
|
5272
|
-
// mistaken for a late FieldTree source above.
|
|
5273
5514
|
const writable = source;
|
|
5274
5515
|
return { value: writable, setValue: (next) => writable.set(next) };
|
|
5275
5516
|
}
|
|
@@ -5438,6 +5679,7 @@ const FAVICON_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
5438
5679
|
}
|
|
5439
5680
|
return new Promise((resolve) => {
|
|
5440
5681
|
const image = new Image();
|
|
5682
|
+
image.crossOrigin = 'anonymous';
|
|
5441
5683
|
image.onload = () => {
|
|
5442
5684
|
baseImage = image;
|
|
5443
5685
|
resolve(image);
|
|
@@ -5670,6 +5912,9 @@ const LINK_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
5670
5912
|
}, { name: 'Link Store' });
|
|
5671
5913
|
const provideLinkStore = /* @__PURE__ */ toProvideFn(LINK_STORE_DEF);
|
|
5672
5914
|
const injectLinkStore = /* @__PURE__ */ toInjectFn(LINK_STORE_DEF);
|
|
5915
|
+
/**
|
|
5916
|
+
* Binds a link tag. Only removes the elements it created; pre-existing links are left in place, unlike meta bindings.
|
|
5917
|
+
*/
|
|
5673
5918
|
const applyLinkBinding = (binding) => {
|
|
5674
5919
|
const linkStore = injectLinkStore();
|
|
5675
5920
|
const linkId = Symbol('link-tag');
|
|
@@ -5814,7 +6059,6 @@ const applyHeadTitleMarker = (binding) => {
|
|
|
5814
6059
|
};
|
|
5815
6060
|
|
|
5816
6061
|
const DEFAULT_MULTI_INSTANCE_TAGS = /* @__PURE__ */ new Set([
|
|
5817
|
-
// Open Graph - Media
|
|
5818
6062
|
'property="og:image"',
|
|
5819
6063
|
'property="og:image:url"',
|
|
5820
6064
|
'property="og:image:secure_url"',
|
|
@@ -5824,32 +6068,23 @@ const DEFAULT_MULTI_INSTANCE_TAGS = /* @__PURE__ */ new Set([
|
|
|
5824
6068
|
'property="og:audio"',
|
|
5825
6069
|
'property="og:audio:url"',
|
|
5826
6070
|
'property="og:audio:secure_url"',
|
|
5827
|
-
// Open Graph - Locale alternatives
|
|
5828
6071
|
'property="og:locale:alternate"',
|
|
5829
|
-
// Twitter Card
|
|
5830
6072
|
'name="twitter:image"',
|
|
5831
6073
|
'name="twitter:player"',
|
|
5832
|
-
// Article tags (arrays)
|
|
5833
6074
|
'property="article:author"',
|
|
5834
6075
|
'property="article:tag"',
|
|
5835
|
-
// Music tags (arrays)
|
|
5836
6076
|
'property="music:song"',
|
|
5837
6077
|
'property="music:musician"',
|
|
5838
6078
|
'property="music:album"',
|
|
5839
|
-
// Video tags (arrays)
|
|
5840
6079
|
'property="video:actor"',
|
|
5841
6080
|
'property="video:director"',
|
|
5842
6081
|
'property="video:writer"',
|
|
5843
6082
|
'property="video:tag"',
|
|
5844
|
-
// Book tags (arrays)
|
|
5845
6083
|
'property="book:author"',
|
|
5846
6084
|
'property="book:tag"',
|
|
5847
|
-
// Profile (arrays)
|
|
5848
6085
|
'property="profile:username"',
|
|
5849
|
-
// Product tags (arrays)
|
|
5850
6086
|
'property="product:category"',
|
|
5851
6087
|
'property="product:retailer_item_id"',
|
|
5852
|
-
// App Links (multiple platforms)
|
|
5853
6088
|
'property="al:ios:url"',
|
|
5854
6089
|
'property="al:ios:app_store_id"',
|
|
5855
6090
|
'property="al:ios:app_name"',
|
|
@@ -5866,16 +6101,12 @@ const DEFAULT_MULTI_INSTANCE_TAGS = /* @__PURE__ */ new Set([
|
|
|
5866
6101
|
'property="al:windows_universal:url"',
|
|
5867
6102
|
'property="al:windows_universal:app_id"',
|
|
5868
6103
|
'property="al:windows_universal:app_name"',
|
|
5869
|
-
// Dublin Core (can have multiple)
|
|
5870
6104
|
'name="DC.creator"',
|
|
5871
6105
|
'name="DC.contributor"',
|
|
5872
6106
|
'name="DC.subject"',
|
|
5873
|
-
// Schema.org (via meta tags - though JSON-LD is preferred)
|
|
5874
6107
|
'itemprop="image"',
|
|
5875
6108
|
'itemprop="author"',
|
|
5876
|
-
// Apple iOS
|
|
5877
6109
|
'name="apple-itunes-app"', // Can have multiple app arguments
|
|
5878
|
-
// Verification tags (multiple services)
|
|
5879
6110
|
'name="google-site-verification"',
|
|
5880
6111
|
'name="msvalidate.01"',
|
|
5881
6112
|
'name="yandex-verification"',
|
|
@@ -6004,6 +6235,9 @@ const META_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
6004
6235
|
}, { name: 'Meta Store' });
|
|
6005
6236
|
const provideMetaStore = /* @__PURE__ */ toProvideFn(META_STORE_DEF);
|
|
6006
6237
|
const injectMetaStore = /* @__PURE__ */ toInjectFn(META_STORE_DEF);
|
|
6238
|
+
/**
|
|
6239
|
+
* Binds a meta tag. Removes any pre-existing tags matching the same selector, unlike link bindings, which leave pre-existing links alone.
|
|
6240
|
+
*/
|
|
6007
6241
|
const applyMetaBinding = (binding) => {
|
|
6008
6242
|
const metaStore = injectMetaStore();
|
|
6009
6243
|
const tagId = Symbol('meta-tag');
|
|
@@ -6199,7 +6433,7 @@ const STRUCTURED_DATA_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
6199
6433
|
renderer.setAttributes(script, { type: 'application/ld+json' });
|
|
6200
6434
|
if (nonce)
|
|
6201
6435
|
renderer.setAttribute(script, 'nonce', nonce);
|
|
6202
|
-
renderer.setTextContent(script, JSON.stringify(data));
|
|
6436
|
+
renderer.setTextContent(script, JSON.stringify(data).replace(/</g, '\\u003C'));
|
|
6203
6437
|
const target = getTargetElement();
|
|
6204
6438
|
renderer.appendChild(target, script);
|
|
6205
6439
|
newScripts.set(id, script);
|
|
@@ -6437,12 +6671,12 @@ const createUnsavedChangesTabLock = (config) => {
|
|
|
6437
6671
|
return { destroy };
|
|
6438
6672
|
};
|
|
6439
6673
|
|
|
6440
|
-
const toBooleanPromise = (result) => {
|
|
6674
|
+
const toBooleanPromise = (result, signal) => {
|
|
6441
6675
|
if (result instanceof Promise) {
|
|
6442
6676
|
return result.then(Boolean);
|
|
6443
6677
|
}
|
|
6444
6678
|
if (isObservable(result)) {
|
|
6445
|
-
return firstValueFrom(result.pipe(take(1), map(Boolean), defaultIfEmpty(false), catchError(() => of(false))));
|
|
6679
|
+
return firstValueFrom(result.pipe(take(1), map(Boolean), takeUntil(fromEvent(signal, 'abort')), defaultIfEmpty(false), catchError(() => of(false))));
|
|
6446
6680
|
}
|
|
6447
6681
|
return Promise.resolve(Boolean(result));
|
|
6448
6682
|
};
|
|
@@ -6463,7 +6697,11 @@ const toBooleanPromise = (result) => {
|
|
|
6463
6697
|
*/
|
|
6464
6698
|
const createUnsavedChangesTracker = (config) => {
|
|
6465
6699
|
assertInInjectionContext(createUnsavedChangesTracker);
|
|
6466
|
-
const { compareFn
|
|
6700
|
+
const { compareFn } = config;
|
|
6701
|
+
const confirm = config.confirm ?? inject(UNSAVED_CHANGES_CONFIRM, { optional: true });
|
|
6702
|
+
if (!confirm && isDevMode()) {
|
|
6703
|
+
throw new RuntimeError(UNSAVED_CHANGES_ERROR_CODES.MISSING_CONFIRM, '[createUnsavedChangesTracker] No confirm: pass `confirm` or call `provideUnsavedChangesConfirm()` in a parent injector.');
|
|
6704
|
+
}
|
|
6467
6705
|
const normalized = normalizeUnsavedChangesSource(config.source);
|
|
6468
6706
|
const hasExplicitDefault = config.defaultValue !== undefined;
|
|
6469
6707
|
const initialDefault = hasExplicitDefault
|
|
@@ -6514,7 +6752,10 @@ const createUnsavedChangesTracker = (config) => {
|
|
|
6514
6752
|
if (untracked(_isAbandoned) || !untracked(hasChanges)) {
|
|
6515
6753
|
return Promise.resolve(true);
|
|
6516
6754
|
}
|
|
6517
|
-
|
|
6755
|
+
if (!confirm) {
|
|
6756
|
+
return Promise.resolve(true);
|
|
6757
|
+
}
|
|
6758
|
+
return coordinator.runCheck((context) => toBooleanPromise(confirm(untracked(normalized.value), context), context.signal));
|
|
6518
6759
|
};
|
|
6519
6760
|
const refreshDefaultValue = () => _defaultValue.set(untracked(normalized.value));
|
|
6520
6761
|
const restoreDefaultValue = () => {
|
|
@@ -6550,6 +6791,26 @@ const createUnsavedChangesGuard = (config) => {
|
|
|
6550
6791
|
};
|
|
6551
6792
|
|
|
6552
6793
|
const isCrossOrigin = (source) => /^(https?:)?\/\//.test(source);
|
|
6794
|
+
const HTML_COMMENT = /<!--[\s\S]*?-->/g;
|
|
6795
|
+
const SCRIPT_SRC = /<script\b[^>]*?\ssrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/gi;
|
|
6796
|
+
const CHARACTER_REFERENCE = /&(?:#(\d+)|#[xX]([\da-fA-F]+)|(amp|quot|apos|lt|gt));/g;
|
|
6797
|
+
const NAMED_REFERENCES = { amp: '&', quot: '"', apos: "'", lt: '<', gt: '>' };
|
|
6798
|
+
const decodeCharacterReferences = (value) => value.replace(CHARACTER_REFERENCE, (_match, decimal, hex, name) => decimal
|
|
6799
|
+
? String.fromCodePoint(Number(decimal))
|
|
6800
|
+
: hex
|
|
6801
|
+
? String.fromCodePoint(parseInt(hex, 16))
|
|
6802
|
+
: (NAMED_REFERENCES[name ?? ''] ?? ''));
|
|
6803
|
+
const toFingerprint = (sources) => sources
|
|
6804
|
+
.filter((source) => !!source && !isCrossOrigin(source))
|
|
6805
|
+
.sort()
|
|
6806
|
+
.join('|');
|
|
6807
|
+
/**
|
|
6808
|
+
* Must decode what `getAttribute('src')` decodes, or every check reports an update.
|
|
6809
|
+
*
|
|
6810
|
+
* Not `DOMParser`: its document inherits the page's CSP, and under a nonce `style-src` each `<style>`
|
|
6811
|
+
* in the fetched document, with its fresh nonce, logs a violation.
|
|
6812
|
+
*/
|
|
6813
|
+
const readBuildFingerprintFromHtml = (html) => toFingerprint(Array.from(html.replace(HTML_COMMENT, '').matchAll(SCRIPT_SRC), ([, doubleQuoted, singleQuoted, unquoted]) => decodeCharacterReferences(doubleQuoted ?? singleQuoted ?? unquoted ?? '')));
|
|
6553
6814
|
/**
|
|
6554
6815
|
* Identifies the build a document boots from, by its entry script filenames. Those carry a content
|
|
6555
6816
|
* hash in any production build, so two documents share a fingerprint exactly when reloading one would
|
|
@@ -6559,32 +6820,17 @@ const isCrossOrigin = (source) => /^(https?:)?\/\//.test(source);
|
|
|
6559
6820
|
* Cross-origin scripts are left out: an analytics tag with a cache-busting query would otherwise read
|
|
6560
6821
|
* as a new deploy on every check.
|
|
6561
6822
|
*/
|
|
6562
|
-
const readBuildFingerprint = (document) => Array.from(document.querySelectorAll('script[src]'))
|
|
6563
|
-
.map((script) => script.getAttribute('src') ?? '')
|
|
6564
|
-
.filter((source) => !!source && !isCrossOrigin(source))
|
|
6565
|
-
.sort()
|
|
6566
|
-
.join('|');
|
|
6823
|
+
const readBuildFingerprint = (document) => toFingerprint(Array.from(document.querySelectorAll('script[src]'), (script) => script.getAttribute('src') ?? ''));
|
|
6567
6824
|
/**
|
|
6568
|
-
* Reads the currently deployed build's fingerprint from the app's entry document.
|
|
6825
|
+
* Reads the currently deployed build's fingerprint from the app's entry document. Cold: nothing is fetched
|
|
6826
|
+
* until it is subscribed.
|
|
6569
6827
|
*
|
|
6570
6828
|
* `null` for anything that is not a usable answer - a network failure, a non-2xx response, or a body
|
|
6571
6829
|
* with no entry scripts in it. Callers must treat that as "no information" rather than as a change:
|
|
6572
6830
|
* an error page parses perfectly well as HTML, and acting on its empty fingerprint would reload the
|
|
6573
6831
|
* app in a loop.
|
|
6574
6832
|
*/
|
|
6575
|
-
const fetchDeployedBuildFingerprint =
|
|
6576
|
-
try {
|
|
6577
|
-
const response = await fetch(url, { cache: 'no-store', headers: { accept: 'text/html' } });
|
|
6578
|
-
if (!response.ok) {
|
|
6579
|
-
return null;
|
|
6580
|
-
}
|
|
6581
|
-
const parsed = new DOMParser().parseFromString(await response.text(), 'text/html');
|
|
6582
|
-
return readBuildFingerprint(parsed) || null;
|
|
6583
|
-
}
|
|
6584
|
-
catch {
|
|
6585
|
-
return null;
|
|
6586
|
-
}
|
|
6587
|
-
};
|
|
6833
|
+
const fetchDeployedBuildFingerprint$ = (url) => defer(() => fetch(url, { cache: 'no-store', headers: { accept: 'text/html' } })).pipe(switchMap((response) => (response.ok ? response.text() : of(null))), map((html) => (html === null ? null : readBuildFingerprintFromHtml(html) || null)), catchError(() => of(null)));
|
|
6588
6834
|
|
|
6589
6835
|
/**
|
|
6590
6836
|
* How each engine words a lazy chunk that could not be loaded. There is no error code and no shared
|
|
@@ -6668,19 +6914,15 @@ const APP_UPDATES_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
6668
6914
|
}, /* @ts-ignore */
|
|
6669
6915
|
...(ngDevMode ? [{ debugName: "isAvailable" }] : /* istanbul ignore next */ []));
|
|
6670
6916
|
let lastCheckAt = 0;
|
|
6671
|
-
const check =
|
|
6917
|
+
const check$ = () => defer(() => {
|
|
6672
6918
|
lastCheckAt = Date.now();
|
|
6673
|
-
|
|
6919
|
+
return fetchDeployedBuildFingerprint$(config.entryUrl);
|
|
6920
|
+
}).pipe(tap((deployed) => {
|
|
6674
6921
|
if (deployed) {
|
|
6675
6922
|
_deployedBuild.set(deployed);
|
|
6676
6923
|
}
|
|
6677
|
-
};
|
|
6678
|
-
const checkIfDue =
|
|
6679
|
-
if (Date.now() - lastCheckAt < config.minCheckInterval) {
|
|
6680
|
-
return;
|
|
6681
|
-
}
|
|
6682
|
-
await check();
|
|
6683
|
-
};
|
|
6924
|
+
}), map(() => undefined));
|
|
6925
|
+
const checkIfDue$ = () => defer(() => (Date.now() - lastCheckAt < config.minCheckInterval ? EMPTY : check$()));
|
|
6684
6926
|
const reload = () => {
|
|
6685
6927
|
// The reload is deliberate and the user has already answered for it, so release every
|
|
6686
6928
|
// beforeunload lock rather than have the browser ask the same question again.
|
|
@@ -6728,7 +6970,7 @@ const APP_UPDATES_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
6728
6970
|
if (config.pollInterval > 0) {
|
|
6729
6971
|
const isDocumentVisible = injectIsDocumentVisible();
|
|
6730
6972
|
toObservable(isDocumentVisible)
|
|
6731
|
-
.pipe(switchMap((isVisible) => (isVisible ? timer(0, config.pollInterval) : EMPTY)), switchMap(() =>
|
|
6973
|
+
.pipe(switchMap((isVisible) => (isVisible ? timer(0, config.pollInterval) : EMPTY)), switchMap(() => checkIfDue$()), takeUntilDestroyed(destroyRef))
|
|
6732
6974
|
.subscribe();
|
|
6733
6975
|
}
|
|
6734
6976
|
}
|
|
@@ -6736,7 +6978,7 @@ const APP_UPDATES_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
6736
6978
|
isAvailable,
|
|
6737
6979
|
isRequired: _isRequired.asReadonly(),
|
|
6738
6980
|
wouldDiscardChanges: coordinator.hasUnsavedChanges,
|
|
6739
|
-
check
|
|
6981
|
+
check$,
|
|
6740
6982
|
reload,
|
|
6741
6983
|
};
|
|
6742
6984
|
}, { name: 'App Updates' });
|
|
@@ -6825,12 +7067,19 @@ class ClickOutsideDirective {
|
|
|
6825
7067
|
this.elementRef = inject(ElementRef);
|
|
6826
7068
|
this.document = inject(DOCUMENT);
|
|
6827
7069
|
this.didClickOutside = output({ alias: 'etClickOutside' });
|
|
7070
|
+
this.pressStartedInside = false;
|
|
7071
|
+
fromEvent(this.document.documentElement, 'pointerdown', { capture: true })
|
|
7072
|
+
.pipe(tap((event) => {
|
|
7073
|
+
this.pressStartedInside = event.composedPath().includes(this.elementRef.nativeElement);
|
|
7074
|
+
}), takeUntilDestroyed())
|
|
7075
|
+
.subscribe();
|
|
6828
7076
|
fromEvent(this.document.documentElement, 'click')
|
|
6829
7077
|
.pipe(tap((event) => {
|
|
6830
7078
|
const hostElement = this.elementRef.nativeElement;
|
|
6831
7079
|
const activeElement = event.target;
|
|
6832
|
-
const
|
|
6833
|
-
|
|
7080
|
+
const startedInside = this.pressStartedInside;
|
|
7081
|
+
this.pressStartedInside = false;
|
|
7082
|
+
if (startedInside || event.composedPath().includes(hostElement))
|
|
6834
7083
|
return;
|
|
6835
7084
|
if (isOnHigherOverlayLayer(activeElement, resolveOverlayLayer(hostElement)))
|
|
6836
7085
|
return;
|
|
@@ -6854,7 +7103,7 @@ class RepeatDirective {
|
|
|
6854
7103
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
6855
7104
|
this.repeatCount = input(2, { ...(ngDevMode ? { debugName: "repeatCount" } : /* istanbul ignore next */ {}), alias: 'etRepeat', transform: numberAttribute });
|
|
6856
7105
|
effect(() => {
|
|
6857
|
-
const count = Math.max(0, this.repeatCount());
|
|
7106
|
+
const count = Math.max(0, this.repeatCount()) || 0;
|
|
6858
7107
|
while (this.viewContainerRef.length < count) {
|
|
6859
7108
|
this.viewContainerRef.createEmbeddedView(this.mainTemplateRef);
|
|
6860
7109
|
}
|
|
@@ -6931,12 +7180,10 @@ class ScrollObserverDirective {
|
|
|
6931
7180
|
this.isAtEnd = computed(() => this._endIntersection()[0]?.isIntersecting ?? false, /* @ts-ignore */
|
|
6932
7181
|
...(ngDevMode ? [{ debugName: "isAtEnd" }] : /* istanbul ignore next */ []));
|
|
6933
7182
|
}
|
|
6934
|
-
/** @internal */
|
|
6935
7183
|
registerStart(el) {
|
|
6936
7184
|
this._startEl.set(el);
|
|
6937
7185
|
return () => this._startEl.update((current) => (current === el ? null : current));
|
|
6938
7186
|
}
|
|
6939
|
-
/** @internal */
|
|
6940
7187
|
registerEnd(el) {
|
|
6941
7188
|
this._endEl.set(el);
|
|
6942
7189
|
return () => this._endEl.update((current) => (current === el ? null : current));
|
|
@@ -6986,7 +7233,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
6986
7233
|
|
|
6987
7234
|
// jsdom and other non-browser DOMs ship no pointer capture; the document listeners track the
|
|
6988
7235
|
// gesture either way, so a failure here only costs events over a cross-origin frame.
|
|
6989
|
-
const capturePointer = (el, pointerId) => {
|
|
7236
|
+
const capturePointer$1 = (el, pointerId) => {
|
|
6990
7237
|
try {
|
|
6991
7238
|
el.setPointerCapture(pointerId);
|
|
6992
7239
|
}
|
|
@@ -7016,7 +7263,7 @@ const setupDragObservable = (startEvent, el, commitThreshold) => {
|
|
|
7016
7263
|
return EMPTY;
|
|
7017
7264
|
}
|
|
7018
7265
|
committed = true;
|
|
7019
|
-
capturePointer(el, pointerId);
|
|
7266
|
+
capturePointer$1(el, pointerId);
|
|
7020
7267
|
lastX = e.clientX;
|
|
7021
7268
|
lastY = e.clientY;
|
|
7022
7269
|
// Emit the catch-up move together with start so consumers snap to the pointer
|
|
@@ -7229,6 +7476,7 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
7229
7476
|
}).pipe(tap((state) => permission.set(state)));
|
|
7230
7477
|
let nextTagId = 0;
|
|
7231
7478
|
const clickHandlers = new Map();
|
|
7479
|
+
const confirmedTags = new Set();
|
|
7232
7480
|
const openCloseFns = new Set();
|
|
7233
7481
|
let isClickRelayAttached = false;
|
|
7234
7482
|
const attachClickRelay = () => {
|
|
@@ -7250,6 +7498,18 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
7250
7498
|
? // Firefox rejects getNotifications() when the worker is not the one that showed them.
|
|
7251
7499
|
from(registration.getNotifications(tag ? { tag } : undefined)).pipe(catchError(() => of([])))
|
|
7252
7500
|
: of([])), tap((notifications) => notifications.forEach((notification) => notification.close())));
|
|
7501
|
+
const pruneDismissedHandlers = (registration) => from(registration.getNotifications()).pipe(catchError(() => of(null)), map((shown) => {
|
|
7502
|
+
if (!shown) {
|
|
7503
|
+
return;
|
|
7504
|
+
}
|
|
7505
|
+
const shownTags = new Set(shown.map((notification) => notification.tag));
|
|
7506
|
+
for (const tag of confirmedTags) {
|
|
7507
|
+
if (!shownTags.has(tag)) {
|
|
7508
|
+
confirmedTags.delete(tag);
|
|
7509
|
+
clickHandlers.delete(tag);
|
|
7510
|
+
}
|
|
7511
|
+
}
|
|
7512
|
+
}));
|
|
7253
7513
|
const show = (config) => {
|
|
7254
7514
|
const tag = config.tag ?? `et-notification-${++nextTagId}`;
|
|
7255
7515
|
const options = toNotificationOptions(config, tag);
|
|
@@ -7268,6 +7528,7 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
7268
7528
|
closed$.next();
|
|
7269
7529
|
closed$.complete();
|
|
7270
7530
|
clickHandlers.delete(tag);
|
|
7531
|
+
confirmedTags.delete(tag);
|
|
7271
7532
|
openCloseFns.delete(close);
|
|
7272
7533
|
pageNotification?.close();
|
|
7273
7534
|
// A no-op unless this notification took the persistent path.
|
|
@@ -7301,16 +7562,27 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
7301
7562
|
if (!registration || isClosed) {
|
|
7302
7563
|
return of('unavailable');
|
|
7303
7564
|
}
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7565
|
+
return pruneDismissedHandlers(registration).pipe(switchMap(() => {
|
|
7566
|
+
if (isClosed) {
|
|
7567
|
+
return of('unavailable');
|
|
7568
|
+
}
|
|
7569
|
+
if (config.onClick) {
|
|
7570
|
+
clickHandlers.set(tag, config.onClick);
|
|
7571
|
+
attachClickRelay();
|
|
7572
|
+
}
|
|
7573
|
+
return from(registration.showNotification(config.title, options)).pipe(switchMap(() => {
|
|
7574
|
+
if (isClosed) {
|
|
7575
|
+
// Closed while the worker was still putting it up - take back what just appeared.
|
|
7576
|
+
return closePersistentNotifications(tag).pipe(map(() => 'unavailable'));
|
|
7577
|
+
}
|
|
7578
|
+
if (config.onClick) {
|
|
7579
|
+
confirmedTags.add(tag);
|
|
7580
|
+
}
|
|
7581
|
+
return of('service-worker');
|
|
7582
|
+
}), catchError(() => {
|
|
7583
|
+
clickHandlers.delete(tag);
|
|
7584
|
+
return of('unavailable');
|
|
7585
|
+
}));
|
|
7314
7586
|
}));
|
|
7315
7587
|
}));
|
|
7316
7588
|
const shown$ = defer(() => {
|
|
@@ -7556,8 +7828,6 @@ const overlayViewportInsetsFor = (element) => overlayViewportInsets(resolveOverl
|
|
|
7556
7828
|
/**
|
|
7557
7829
|
* Runs `listener` whenever a reservation is made or released, so an already positioned overlay can be
|
|
7558
7830
|
* laid out again. Returns the unsubscribe callback.
|
|
7559
|
-
*
|
|
7560
|
-
* @internal
|
|
7561
7831
|
*/
|
|
7562
7832
|
const onOverlayViewportInsetsChange = (listener) => {
|
|
7563
7833
|
listeners.add(listener);
|
|
@@ -7566,10 +7836,6 @@ const onOverlayViewportInsetsChange = (listener) => {
|
|
|
7566
7836
|
};
|
|
7567
7837
|
};
|
|
7568
7838
|
|
|
7569
|
-
/**
|
|
7570
|
-
* Shrinks the host box to the part of the viewport nothing above this overlay has reserved, so a
|
|
7571
|
-
* centered or globally placed pane is laid out inside it instead of under a docked panel.
|
|
7572
|
-
*/
|
|
7573
7839
|
const applyViewportInsets = (hostElement, renderer) => {
|
|
7574
7840
|
const insets = overlayViewportInsetsFor(hostElement);
|
|
7575
7841
|
renderer.setStyle(hostElement, {
|
|
@@ -7667,8 +7933,6 @@ let anchoredPositionSetup = null;
|
|
|
7667
7933
|
* Installs the anchored positioning implementation. Called by `anchoredOverlayPosition()`, which is
|
|
7668
7934
|
* the only thing that pulls `@floating-ui/dom` into the bundle - apps that never anchor an overlay
|
|
7669
7935
|
* do not ship it.
|
|
7670
|
-
*
|
|
7671
|
-
* @internal
|
|
7672
7936
|
*/
|
|
7673
7937
|
const registerAnchoredPositionSetup = (setup) => {
|
|
7674
7938
|
anchoredPositionSetup = setup;
|
|
@@ -7700,8 +7964,6 @@ let middlewareExtras = null;
|
|
|
7700
7964
|
/**
|
|
7701
7965
|
* Installs the `size` / `arrow` / `hide` middleware. Called by
|
|
7702
7966
|
* `enableAnchoredOverlayPositionExtras()`.
|
|
7703
|
-
*
|
|
7704
|
-
* @internal
|
|
7705
7967
|
*/
|
|
7706
7968
|
const registerAnchoredPositionMiddlewareExtras = (extras) => {
|
|
7707
7969
|
middlewareExtras = extras;
|
|
@@ -7773,10 +8035,6 @@ const toSideObject = (padding) => typeof padding === 'number'
|
|
|
7773
8035
|
bottom: padding.bottom ?? 0,
|
|
7774
8036
|
left: padding.left ?? 0,
|
|
7775
8037
|
};
|
|
7776
|
-
/**
|
|
7777
|
-
* The strategy's own viewport padding plus the space reserved on each edge. Every middleware that
|
|
7778
|
-
* measures overflow gets it, so an edge a docked panel covers is as unavailable as the viewport edge.
|
|
7779
|
-
*/
|
|
7780
8038
|
const paddingWithInsets = (padding, insets) => {
|
|
7781
8039
|
const own = toSideObject(padding);
|
|
7782
8040
|
return {
|
|
@@ -7786,6 +8044,43 @@ const paddingWithInsets = (padding, insets) => {
|
|
|
7786
8044
|
left: own.left + insets.left,
|
|
7787
8045
|
};
|
|
7788
8046
|
};
|
|
8047
|
+
/**
|
|
8048
|
+
* A platform whose first measurement of the pane, in each `computePosition` run, ignores the cap
|
|
8049
|
+
* `size` set in the previous run. `flip` reads that first measurement: against a capped pane, the side
|
|
8050
|
+
* the pane was squeezed onto always fits, so a pane whose content grows after it opened never flips
|
|
8051
|
+
* to the roomier side. Later measurements in the same run see the cap, and `size` resets with them,
|
|
8052
|
+
* so the final position is computed from the pane as it renders.
|
|
8053
|
+
*/
|
|
8054
|
+
const platformMeasuringUncappedPaneFirst = (paneElement, renderer) => {
|
|
8055
|
+
let measuredUncapped = false;
|
|
8056
|
+
return {
|
|
8057
|
+
...platform,
|
|
8058
|
+
getDimensions: (element) => {
|
|
8059
|
+
if (element !== paneElement || measuredUncapped)
|
|
8060
|
+
return platform.getDimensions(element);
|
|
8061
|
+
measuredUncapped = true;
|
|
8062
|
+
const maxWidth = paneElement.style.getPropertyValue('--et-overlay-max-width');
|
|
8063
|
+
const maxHeight = paneElement.style.getPropertyValue('--et-overlay-max-height');
|
|
8064
|
+
if (!maxWidth && !maxHeight)
|
|
8065
|
+
return platform.getDimensions(element);
|
|
8066
|
+
// The uncapped layout clamps the scroll offset of every scroller the cap made scrollable.
|
|
8067
|
+
const scrolled = [...paneElement.querySelectorAll('*')]
|
|
8068
|
+
.filter((el) => el.scrollTop || el.scrollLeft)
|
|
8069
|
+
.map((el) => ({ el, top: el.scrollTop, left: el.scrollLeft }));
|
|
8070
|
+
renderer.setCssProperties(paneElement, { '--et-overlay-max-width': null, '--et-overlay-max-height': null });
|
|
8071
|
+
const dimensions = platform.getDimensions(element);
|
|
8072
|
+
renderer.setCssProperties(paneElement, {
|
|
8073
|
+
'--et-overlay-max-width': maxWidth || null,
|
|
8074
|
+
'--et-overlay-max-height': maxHeight || null,
|
|
8075
|
+
});
|
|
8076
|
+
for (const { el, top, left } of scrolled) {
|
|
8077
|
+
el.scrollTop = top;
|
|
8078
|
+
el.scrollLeft = left;
|
|
8079
|
+
}
|
|
8080
|
+
return dimensions;
|
|
8081
|
+
},
|
|
8082
|
+
};
|
|
8083
|
+
};
|
|
7789
8084
|
const createAnchoredPositionCleanup = (strategy, paneElement, overlayRef, renderer) => {
|
|
7790
8085
|
// mirrorWidth needs a real element to measure; virtual references fall back to max-content
|
|
7791
8086
|
const mirrorWidthElement = strategy.mirrorWidth && isHTMLElement(strategy.referenceElement) ? strategy.referenceElement : null;
|
|
@@ -7898,6 +8193,9 @@ const createAnchoredPositionCleanup = (strategy, paneElement, overlayRef, render
|
|
|
7898
8193
|
placement: strategy.placement ?? 'bottom',
|
|
7899
8194
|
strategy: 'absolute',
|
|
7900
8195
|
middleware,
|
|
8196
|
+
platform: strategy.autoResize && strategy.minAvailableSpace === undefined
|
|
8197
|
+
? platformMeasuringUncappedPaneFirst(paneElement, renderer)
|
|
8198
|
+
: platform,
|
|
7901
8199
|
}).then(({ x, y, placement, middlewareData }) => {
|
|
7902
8200
|
if (destroyed || currentUpdateId !== updateId)
|
|
7903
8201
|
return;
|
|
@@ -7964,10 +8262,10 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
7964
8262
|
...(ngDevMode ? [{ debugName: "_componentInstance" }] : /* istanbul ignore next */ []));
|
|
7965
8263
|
let positionUpdater = null;
|
|
7966
8264
|
let backdropUpdater = null;
|
|
7967
|
-
const beforeOpenedSubject = new
|
|
7968
|
-
const afterOpenedSubject = new
|
|
7969
|
-
const beforeClosedSubject = new
|
|
7970
|
-
const afterClosedSubject = new
|
|
8265
|
+
const beforeOpenedSubject = new ReplaySubject(1);
|
|
8266
|
+
const afterOpenedSubject = new ReplaySubject(1);
|
|
8267
|
+
const beforeClosedSubject = new ReplaySubject(1);
|
|
8268
|
+
const afterClosedSubject = new ReplaySubject(1);
|
|
7971
8269
|
const closeGuards = new Set();
|
|
7972
8270
|
return {
|
|
7973
8271
|
id,
|
|
@@ -7975,8 +8273,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
7975
8273
|
elements,
|
|
7976
8274
|
state: _state.asReadonly(),
|
|
7977
8275
|
componentInstance: _componentInstance.asReadonly(),
|
|
7978
|
-
/** @internal */
|
|
7979
|
-
beforeOpenedSubject,
|
|
7980
8276
|
close(result, source = 'api') {
|
|
7981
8277
|
if (_state() === 'closing' || _state() === 'closed') {
|
|
7982
8278
|
return;
|
|
@@ -8016,11 +8312,13 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8016
8312
|
afterClosed() {
|
|
8017
8313
|
return afterClosedSubject.asObservable();
|
|
8018
8314
|
},
|
|
8019
|
-
/** @internal */
|
|
8020
8315
|
attachComponentRef(componentRef) {
|
|
8021
8316
|
_componentInstance.set(componentRef.instance);
|
|
8022
8317
|
},
|
|
8023
|
-
|
|
8318
|
+
markBeforeOpened() {
|
|
8319
|
+
beforeOpenedSubject.next();
|
|
8320
|
+
beforeOpenedSubject.complete();
|
|
8321
|
+
},
|
|
8024
8322
|
attachPositionUpdater(updater) {
|
|
8025
8323
|
positionUpdater = updater;
|
|
8026
8324
|
},
|
|
@@ -8030,7 +8328,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8030
8328
|
}
|
|
8031
8329
|
positionUpdater?.(strategy);
|
|
8032
8330
|
},
|
|
8033
|
-
/** @internal */
|
|
8034
8331
|
attachBackdropUpdater(updater) {
|
|
8035
8332
|
backdropUpdater = updater;
|
|
8036
8333
|
},
|
|
@@ -8044,7 +8341,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8044
8341
|
}
|
|
8045
8342
|
backdropUpdater?.(hasBackdrop);
|
|
8046
8343
|
},
|
|
8047
|
-
/** @internal */
|
|
8048
8344
|
markOpened() {
|
|
8049
8345
|
if (_state() !== 'mounting') {
|
|
8050
8346
|
return;
|
|
@@ -8053,7 +8349,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8053
8349
|
afterOpenedSubject.next();
|
|
8054
8350
|
afterOpenedSubject.complete();
|
|
8055
8351
|
},
|
|
8056
|
-
/** @internal */
|
|
8057
8352
|
beginClose(closeEvent) {
|
|
8058
8353
|
if (_state() === 'closing' || _state() === 'closed') {
|
|
8059
8354
|
return false;
|
|
@@ -8063,7 +8358,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8063
8358
|
beforeClosedSubject.complete();
|
|
8064
8359
|
return true;
|
|
8065
8360
|
},
|
|
8066
|
-
/** @internal */
|
|
8067
8361
|
finishClose(closeEvent) {
|
|
8068
8362
|
if (_state() === 'closed') {
|
|
8069
8363
|
return;
|
|
@@ -8093,6 +8387,14 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8093
8387
|
// Synchronous teardown for each currently-mounted overlay, run when the runtime's injector is
|
|
8094
8388
|
// destroyed (app teardown). Registered on mount, removed once the overlay is destroyed normally.
|
|
8095
8389
|
const mountedTeardowns = new Set();
|
|
8390
|
+
const armedReopenSwallows = new Set();
|
|
8391
|
+
const focusRestoreChains = new WeakMap();
|
|
8392
|
+
const resolveFocusRestoreChain = (focused) => {
|
|
8393
|
+
if (!focused)
|
|
8394
|
+
return [];
|
|
8395
|
+
const owner = openEntriesState().find((entry) => entry.elements.hostElement.contains(focused));
|
|
8396
|
+
return [focused, ...((owner && focusRestoreChains.get(owner)) ?? [])];
|
|
8397
|
+
};
|
|
8096
8398
|
const getRootElement = (targetDocument, zIndex) => {
|
|
8097
8399
|
let documentRoots = rootElements.get(targetDocument);
|
|
8098
8400
|
if (!documentRoots) {
|
|
@@ -8165,9 +8467,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8165
8467
|
const paneElement = renderer.createElement('div');
|
|
8166
8468
|
const backdropElement = signal(null, /* @ts-ignore */
|
|
8167
8469
|
...(ngDevMode ? [{ debugName: "backdropElement" }] : /* istanbul ignore next */ []));
|
|
8168
|
-
const
|
|
8169
|
-
? targetDocument.activeElement
|
|
8170
|
-
: null;
|
|
8470
|
+
const focusRestoreChain = resolveFocusRestoreChain(isHTMLOrSVGElement(targetDocument.activeElement) ? targetDocument.activeElement : null);
|
|
8171
8471
|
const autoFocus = config.autoFocus ?? 'first-tabbable';
|
|
8172
8472
|
let currentHasBackdrop = config.hasBackdrop !== false;
|
|
8173
8473
|
let currentPositionStrategy = config.positionStrategy;
|
|
@@ -8210,8 +8510,6 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8210
8510
|
paneElement,
|
|
8211
8511
|
}, (result, source) => beginClose({ result, source }));
|
|
8212
8512
|
animationDebugLog(`runtime ${config.id}`, 'mount');
|
|
8213
|
-
// matches the previous overlay behavior: escape/outside-pointer closes are
|
|
8214
|
-
// ignored until the enter transition has started plus one frame
|
|
8215
8513
|
let interactiveCloseReady = false;
|
|
8216
8514
|
const cleanupFns = [];
|
|
8217
8515
|
const parentInjector = config.injector ?? config.viewContainerRef?.injector ?? environmentInjector;
|
|
@@ -8225,19 +8523,29 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8225
8523
|
// `config.providers`.
|
|
8226
8524
|
providers: [{ provide: EnvironmentInjector, useValue: overlayEnvironmentInjector }],
|
|
8227
8525
|
});
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8526
|
+
let componentRef;
|
|
8527
|
+
try {
|
|
8528
|
+
componentRef = createComponent(config.component, {
|
|
8529
|
+
environmentInjector: overlayEnvironmentInjector,
|
|
8530
|
+
elementInjector,
|
|
8531
|
+
hostElement: paneElement,
|
|
8532
|
+
bindings: config.bindings ?? [],
|
|
8533
|
+
});
|
|
8534
|
+
}
|
|
8535
|
+
catch (error) {
|
|
8536
|
+
elementInjector.destroy();
|
|
8537
|
+
overlayEnvironmentInjector.destroy();
|
|
8538
|
+
renderer.removeChild(root, hostElement);
|
|
8539
|
+
maybeDestroyRootElements(targetDocument);
|
|
8540
|
+
throw error;
|
|
8541
|
+
}
|
|
8234
8542
|
// pane classes are applied after component creation - Angular replaces the host
|
|
8235
8543
|
// element's class attribute with the component's static host class on creation
|
|
8236
8544
|
renderer.addClass(paneElement, 'et-overlay-runtime-pane', ...(config.paneClass ?? []));
|
|
8237
8545
|
appRef.attachView(componentRef.hostView);
|
|
8238
8546
|
overlayRef.attachComponentRef(componentRef);
|
|
8239
|
-
overlayRef.
|
|
8240
|
-
|
|
8547
|
+
overlayRef.markBeforeOpened();
|
|
8548
|
+
focusRestoreChains.set(overlayRef, focusRestoreChain);
|
|
8241
8549
|
openEntriesState.update((entries) => [...entries, overlayRef]);
|
|
8242
8550
|
let positionCleanup = setupPositioning(activeConfig(), hostElement, paneElement, overlayRef, renderer);
|
|
8243
8551
|
cleanupFns.push(() => positionCleanup());
|
|
@@ -8292,8 +8600,8 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8292
8600
|
}
|
|
8293
8601
|
openEntriesState.update((entries) => entries.filter((entry) => entry !== overlayRef));
|
|
8294
8602
|
maybeDestroyRootElements(targetDocument);
|
|
8295
|
-
if (config.restoreFocus !== false && overlayStillOwnsFocus
|
|
8296
|
-
|
|
8603
|
+
if (config.restoreFocus !== false && overlayStillOwnsFocus) {
|
|
8604
|
+
focusRestoreChain.find((element) => element.isConnected)?.focus({ preventScroll: true });
|
|
8297
8605
|
}
|
|
8298
8606
|
overlayRef.finishClose(closeEvent);
|
|
8299
8607
|
};
|
|
@@ -8311,7 +8619,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8311
8619
|
return;
|
|
8312
8620
|
}
|
|
8313
8621
|
const lifecycle = getAnimatedLifecycle(componentRef);
|
|
8314
|
-
animationDebugLog(`runtime ${config.id}`, `close requested (source "${closeEvent.source}", lifecycle state "${lifecycle?.state
|
|
8622
|
+
animationDebugLog(`runtime ${config.id}`, `close requested (source "${closeEvent.source}", lifecycle state "${lifecycle?.state() ?? 'none'}", delegate ${config.animationDelegate?.leave ? 'yes' : 'no'})`);
|
|
8315
8623
|
// The reference element is gone - there is nothing left to animate away from, so tear the
|
|
8316
8624
|
// overlay down synchronously instead of playing a leave transition from a stale position.
|
|
8317
8625
|
if (!lifecycle || closeEvent.source === 'reference-detached') {
|
|
@@ -8325,10 +8633,8 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8325
8633
|
lifecycle.leave();
|
|
8326
8634
|
}
|
|
8327
8635
|
lifecycle.state$
|
|
8328
|
-
.pipe(filter((state) => state === 'left'), take(1))
|
|
8329
|
-
.subscribe(
|
|
8330
|
-
destroyMountedOverlay(closeEvent);
|
|
8331
|
-
});
|
|
8636
|
+
.pipe(filter((state) => state === 'left'), take(1), tap(() => destroyMountedOverlay(closeEvent)))
|
|
8637
|
+
.subscribe();
|
|
8332
8638
|
};
|
|
8333
8639
|
const closeOnEscape = config.closeOnEscape ?? true;
|
|
8334
8640
|
if (closeOnEscape) {
|
|
@@ -8370,12 +8676,22 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8370
8676
|
const originElement = getOriginElement();
|
|
8371
8677
|
if (originElement && originElement.contains(target)) {
|
|
8372
8678
|
const swallowReopenClick = (clickEvent) => {
|
|
8679
|
+
disarmReopenSwallow();
|
|
8373
8680
|
if (isElement(clickEvent.target) && originElement.contains(clickEvent.target)) {
|
|
8374
8681
|
clickEvent.stopImmediatePropagation();
|
|
8375
8682
|
clickEvent.preventDefault();
|
|
8376
8683
|
}
|
|
8377
8684
|
};
|
|
8378
|
-
|
|
8685
|
+
// A press that ends without a click (cancelled, dragged off) must not leave the swallow
|
|
8686
|
+
// armed for the next, unrelated click on the origin.
|
|
8687
|
+
const disarmReopenSwallow = () => {
|
|
8688
|
+
targetDocument.removeEventListener('click', swallowReopenClick, true);
|
|
8689
|
+
targetDocument.removeEventListener('pointerdown', disarmReopenSwallow, true);
|
|
8690
|
+
armedReopenSwallows.delete(disarmReopenSwallow);
|
|
8691
|
+
};
|
|
8692
|
+
targetDocument.addEventListener('click', swallowReopenClick, true);
|
|
8693
|
+
targetDocument.addEventListener('pointerdown', disarmReopenSwallow, true);
|
|
8694
|
+
armedReopenSwallows.add(disarmReopenSwallow);
|
|
8379
8695
|
}
|
|
8380
8696
|
};
|
|
8381
8697
|
targetDocument.addEventListener('pointerdown', onPointerDown, true);
|
|
@@ -8394,14 +8710,14 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8394
8710
|
overlayRef.markOpened();
|
|
8395
8711
|
return;
|
|
8396
8712
|
}
|
|
8397
|
-
animationDebugLog(`runtime ${config.id}`, `enter (delegate ${config.animationDelegate?.enter ? 'yes' : 'no'}, lifecycle state "${lifecycle.state
|
|
8713
|
+
animationDebugLog(`runtime ${config.id}`, `enter (delegate ${config.animationDelegate?.enter ? 'yes' : 'no'}, lifecycle state "${lifecycle.state()}")`);
|
|
8398
8714
|
const readySubscription = lifecycle.state$
|
|
8399
|
-
.pipe(filter((state) => state === 'entering' || state === 'entered'), take(1))
|
|
8400
|
-
.subscribe(() => {
|
|
8715
|
+
.pipe(filter((state) => state === 'entering' || state === 'entered'), take(1), tap(() => {
|
|
8401
8716
|
nextFrame(() => {
|
|
8402
8717
|
interactiveCloseReady = true;
|
|
8403
8718
|
});
|
|
8404
|
-
})
|
|
8719
|
+
}))
|
|
8720
|
+
.subscribe();
|
|
8405
8721
|
cleanupFns.push(() => readySubscription.unsubscribe());
|
|
8406
8722
|
if (config.animationDelegate?.enter) {
|
|
8407
8723
|
config.animationDelegate.enter({ lifecycle, elements: overlayRef.elements });
|
|
@@ -8410,16 +8726,17 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8410
8726
|
lifecycle.enter();
|
|
8411
8727
|
}
|
|
8412
8728
|
lifecycle.state$
|
|
8413
|
-
.pipe(filter((state) => state === 'entered'), take(1))
|
|
8414
|
-
.subscribe(() => {
|
|
8729
|
+
.pipe(filter((state) => state === 'entered'), take(1), tap(() => {
|
|
8415
8730
|
applyInitialFocus(paneElement, autoFocus, targetDocument);
|
|
8416
8731
|
overlayRef.markOpened();
|
|
8417
|
-
})
|
|
8732
|
+
}))
|
|
8733
|
+
.subscribe();
|
|
8418
8734
|
});
|
|
8419
8735
|
return overlayRef;
|
|
8420
8736
|
};
|
|
8421
8737
|
destroyRef.onDestroy(() => {
|
|
8422
8738
|
[...mountedTeardowns].forEach((teardown) => teardown());
|
|
8739
|
+
[...armedReopenSwallows].forEach((disarm) => disarm());
|
|
8423
8740
|
[...rootElements.keys()].forEach((targetDocument) => maybeDestroyRootElements(targetDocument));
|
|
8424
8741
|
});
|
|
8425
8742
|
return {
|
|
@@ -9010,7 +9327,7 @@ const initials = (value, maxLength = 2) => {
|
|
|
9010
9327
|
return '';
|
|
9011
9328
|
return words
|
|
9012
9329
|
.slice(0, maxLength)
|
|
9013
|
-
.map((word) => word[0]?.toUpperCase() ?? '')
|
|
9330
|
+
.map((word) => [...word.normalize()][0]?.toUpperCase() ?? '')
|
|
9014
9331
|
.join('');
|
|
9015
9332
|
};
|
|
9016
9333
|
|
|
@@ -9089,20 +9406,38 @@ const EDGE_CURSORS = {
|
|
|
9089
9406
|
nw: 'nwse-resize',
|
|
9090
9407
|
se: 'nwse-resize',
|
|
9091
9408
|
};
|
|
9092
|
-
|
|
9409
|
+
// jsdom and other non-browser DOMs ship no pointer capture; the document listeners track the
|
|
9410
|
+
// gesture either way, so a failure here only costs events over a cross-origin frame.
|
|
9411
|
+
const capturePointer = (el, pointerId) => {
|
|
9412
|
+
try {
|
|
9413
|
+
el.setPointerCapture(pointerId);
|
|
9414
|
+
}
|
|
9415
|
+
catch {
|
|
9416
|
+
return;
|
|
9417
|
+
}
|
|
9418
|
+
};
|
|
9419
|
+
const setupResizeObservable = (startEvent, handle, edge, doc) => {
|
|
9093
9420
|
const pointerId = startEvent.pointerId;
|
|
9094
9421
|
const startX = startEvent.clientX;
|
|
9095
9422
|
const startY = startEvent.clientY;
|
|
9096
9423
|
let cancelled = false;
|
|
9424
|
+
let last = { edge, totalDx: 0, totalDy: 0, clientX: startX, clientY: startY };
|
|
9097
9425
|
const end$ = merge(fromEvent(doc, 'pointerup'), fromEvent(doc, 'pointercancel')).pipe(filter((e) => e.pointerId === pointerId), take(1),
|
|
9098
9426
|
// Read below by the terminating `defer`, which only runs once this has completed the moves.
|
|
9099
9427
|
tap((e) => (cancelled = e.type === 'pointercancel')));
|
|
9100
9428
|
return defer(() => {
|
|
9101
9429
|
const releaseSelection = suppressTextSelection(doc);
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9430
|
+
capturePointer(handle, pointerId);
|
|
9431
|
+
return concat(of({ type: 'start', edge }), fromEvent(doc, 'pointermove').pipe(filter((e) => e.pointerId === pointerId), map((e) => {
|
|
9432
|
+
last = {
|
|
9433
|
+
edge,
|
|
9434
|
+
totalDx: e.clientX - startX,
|
|
9435
|
+
totalDy: e.clientY - startY,
|
|
9436
|
+
clientX: e.clientX,
|
|
9437
|
+
clientY: e.clientY,
|
|
9438
|
+
};
|
|
9439
|
+
return { type: 'move', data: last };
|
|
9440
|
+
}), takeUntil(end$)), defer(() => of(cancelled ? { type: 'cancelled' } : { type: 'end', data: last }))).pipe(finalize(releaseSelection));
|
|
9106
9441
|
});
|
|
9107
9442
|
};
|
|
9108
9443
|
class ResizeHandlesComponent {
|
|
@@ -9112,10 +9447,10 @@ class ResizeHandlesComponent {
|
|
|
9112
9447
|
...(ngDevMode ? [{ debugName: "edges" }] : /* istanbul ignore next */ []));
|
|
9113
9448
|
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
9114
9449
|
this.gestureStart$ = new Subject();
|
|
9115
|
-
this.gesture$ = this.gestureStart$.pipe(exhaustMap(({ event, edge }) => setupResizeObservable(event, edge, this.el.nativeElement.ownerDocument)), share(), takeUntilDestroyed());
|
|
9450
|
+
this.gesture$ = this.gestureStart$.pipe(exhaustMap(({ event, handle, edge }) => setupResizeObservable(event, handle, edge, this.el.nativeElement.ownerDocument)), share(), takeUntilDestroyed());
|
|
9116
9451
|
this.resizeStarted = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'start'), map((e) => e.edge)));
|
|
9117
9452
|
this.resizeMoved = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'move'), map((e) => e.data)));
|
|
9118
|
-
this.resizeEnded = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'end'), map(() =>
|
|
9453
|
+
this.resizeEnded = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'end'), map((e) => e.data)));
|
|
9119
9454
|
/**
|
|
9120
9455
|
* The browser took the gesture away mid-resize (a system gesture, an incoming call, the tab going
|
|
9121
9456
|
* to the background). Revert to the size the resize started from - the user never let go, so there
|
|
@@ -9136,7 +9471,7 @@ class ResizeHandlesComponent {
|
|
|
9136
9471
|
if (event.button !== 0 || this.disabled())
|
|
9137
9472
|
return;
|
|
9138
9473
|
event.stopPropagation();
|
|
9139
|
-
this.gestureStart$.next({ event, edge });
|
|
9474
|
+
this.gestureStart$.next({ event, handle: event.currentTarget, edge });
|
|
9140
9475
|
}
|
|
9141
9476
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ResizeHandlesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9142
9477
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ResizeHandlesComponent, isStandalone: true, selector: "et-resize-handles", inputs: { edges: { classPropertyName: "edges", publicName: "edges", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resizeStarted: "resizeStarted", resizeMoved: "resizeMoved", resizeEnded: "resizeEnded", resizeCancelled: "resizeCancelled" }, host: { properties: { "attr.inert": "disabled() ? \"\" : null", "attr.data-active-edge": "activeEdge()" }, classAttribute: "et-resize-handles" }, ngImport: i0, template: `
|
|
@@ -9398,66 +9733,300 @@ const injectDefaultSurfaceTheme = (type) => {
|
|
|
9398
9733
|
return defaults.length === 1 ? (defaults[0] ?? null) : null;
|
|
9399
9734
|
};
|
|
9400
9735
|
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9736
|
+
/**
|
|
9737
|
+
* Reserved color name. Resolves the color tokens from the ambient surface's neutral swatch
|
|
9738
|
+
* (`SurfaceTheme['interactionColor']`) instead of a registered accent, so a secondary action can
|
|
9739
|
+
* read as chrome without the app registering a neutral color theme. Apps must not register a color
|
|
9740
|
+
* theme under this name.
|
|
9741
|
+
*/
|
|
9742
|
+
const SURFACE_COLOR = 'surface';
|
|
9743
|
+
const createCssColorThemeName = (name) => name.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
9744
|
+
const COLOR_THEMES_DEF = /* @__PURE__ */ defineStaticProvider(undefined, {
|
|
9745
|
+
name: 'Color Themes',
|
|
9746
|
+
});
|
|
9747
|
+
const ɵProvideColorThemes = /* @__PURE__ */ toProvideFn(COLOR_THEMES_DEF);
|
|
9748
|
+
const injectColorThemes = /* @__PURE__ */ toInjectFn(COLOR_THEMES_DEF);
|
|
9749
|
+
const COLOR_THEMES_PREFIX_DEF = /* @__PURE__ */ defineStaticProvider('et', {
|
|
9750
|
+
name: 'Themes Prefix',
|
|
9751
|
+
});
|
|
9752
|
+
const ɵProvideColorThemesPrefix = /* @__PURE__ */ toProvideFn(COLOR_THEMES_PREFIX_DEF);
|
|
9753
|
+
const injectColorThemesPrefix = /* @__PURE__ */ toInjectFn(COLOR_THEMES_PREFIX_DEF);
|
|
9754
|
+
const provideColorThemesWithTailwind4 = (themes, prefix = 'et') => {
|
|
9755
|
+
if (isDevMode() && themes.some((theme) => theme.name === SURFACE_COLOR)) {
|
|
9756
|
+
console.error(`[provideColorThemesWithTailwind4] "${SURFACE_COLOR}" is a reserved color name that resolves to the ambient surface's neutral swatch. Rename your theme.`);
|
|
9757
|
+
}
|
|
9758
|
+
return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix(prefix)];
|
|
9759
|
+
};
|
|
9760
|
+
const findColorThemeByType = (themes, type, caller) => {
|
|
9761
|
+
if (!themes) {
|
|
9762
|
+
throw new Error(`[${caller}] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9763
|
+
}
|
|
9764
|
+
const theme = themes.find((t) => t.type === type);
|
|
9765
|
+
if (!theme) {
|
|
9766
|
+
throw new Error(`[${caller}] No color theme with type "${type}" found. Add a theme with type: "${type}" to provideColorThemesWithTailwind4().`);
|
|
9767
|
+
}
|
|
9768
|
+
if (isDevMode()) {
|
|
9769
|
+
const duplicates = themes.filter((t) => t.type === type);
|
|
9770
|
+
if (duplicates.length > 1) {
|
|
9771
|
+
console.error(`[${caller}] Multiple themes with type "${type}" found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9772
|
+
}
|
|
9773
|
+
}
|
|
9774
|
+
return theme;
|
|
9775
|
+
};
|
|
9776
|
+
const injectColorThemeByType = (type) => findColorThemeByType(injectColorThemes({ optional: true }), type, 'injectColorThemeByType');
|
|
9777
|
+
/**
|
|
9778
|
+
* A signal of the color theme for `type` on the surface this element renders on: the theme the nearest
|
|
9779
|
+
* surface names in `semanticColorThemes`, else the first registered theme of that `type`. Reading it throws
|
|
9780
|
+
* when the app registered no theme of that `type`, so read it only for a state that renders.
|
|
9781
|
+
*
|
|
9782
|
+
* @example
|
|
9783
|
+
* protected successTheme = injectSemanticColorTheme('success');
|
|
9784
|
+
* // <span [etProvideColor]="successTheme()">+12%</span>
|
|
9785
|
+
*/
|
|
9786
|
+
const injectSemanticColorTheme = (type) => {
|
|
9787
|
+
const themes = injectColorThemes({ optional: true });
|
|
9788
|
+
const surfaceProvider = inject(SURFACE_PROVIDER, { optional: true });
|
|
9789
|
+
const defaultSurface = injectDefaultSurfaceTheme();
|
|
9790
|
+
return computed(() => {
|
|
9791
|
+
const surface = surfaceProvider?.activeTheme() ?? defaultSurface;
|
|
9792
|
+
const mappedName = surface?.semanticColorThemes?.[type];
|
|
9793
|
+
const mapped = mappedName ? themes?.find((t) => t.name === mappedName) : undefined;
|
|
9794
|
+
if (mapped)
|
|
9795
|
+
return mapped;
|
|
9796
|
+
if (mappedName && isDevMode()) {
|
|
9797
|
+
console.error(`[injectSemanticColorTheme] Surface theme "${surface?.name}" maps "${type}" to the color theme "${mappedName}", which is not registered. Add it to provideColorThemesWithTailwind4().`);
|
|
9798
|
+
}
|
|
9799
|
+
return findColorThemeByType(themes, type, 'injectSemanticColorTheme');
|
|
9800
|
+
});
|
|
9801
|
+
};
|
|
9802
|
+
/** @deprecated Use `injectSemanticColorTheme('error')`, which follows the surface the element renders on. */
|
|
9803
|
+
const injectErrorTheme = () => injectColorThemeByType('error');
|
|
9804
|
+
/** @deprecated Use `injectSemanticColorTheme('warning')`, which follows the surface the element renders on. */
|
|
9805
|
+
const injectWarningTheme = () => injectColorThemeByType('warning');
|
|
9806
|
+
/** @deprecated Use `injectSemanticColorTheme('success')`, which follows the surface the element renders on. */
|
|
9807
|
+
const injectSuccessTheme = () => injectColorThemeByType('success');
|
|
9808
|
+
const injectDefaultColorTheme = () => {
|
|
9809
|
+
const themes = injectColorThemes({ optional: true });
|
|
9810
|
+
if (!themes) {
|
|
9811
|
+
throw new Error(`[injectDefaultColorTheme] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9812
|
+
}
|
|
9813
|
+
const theme = themes.find((t) => t.isDefault);
|
|
9814
|
+
if (!theme) {
|
|
9815
|
+
throw new Error(`[injectDefaultColorTheme] No color theme with isDefault: true found. Mark one theme as the default in provideColorThemesWithTailwind4().`);
|
|
9816
|
+
}
|
|
9817
|
+
if (isDevMode()) {
|
|
9818
|
+
const duplicates = themes.filter((t) => t.isDefault);
|
|
9819
|
+
if (duplicates.length > 1) {
|
|
9820
|
+
console.error(`[injectDefaultColorTheme] Multiple themes with isDefault: true found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9821
|
+
}
|
|
9822
|
+
}
|
|
9823
|
+
return theme;
|
|
9824
|
+
};
|
|
9825
|
+
|
|
9826
|
+
const COLOR_PROVIDER = new InjectionToken('ColorProvider');
|
|
9827
|
+
/**
|
|
9828
|
+
* The color provider attached to a bootstrapped root component (e.g. `ProvideColorDirective` in
|
|
9829
|
+
* the app component's `hostDirectives`), if any. Detached DOM such as overlay panes can't reach
|
|
9830
|
+
* it through element DI, so overlay containers fall back to this when resolving the provider to
|
|
9831
|
+
* `syncWithProvider()` - resolve at sync time, not at injection time, since root components only
|
|
9832
|
+
* register with `ApplicationRef` once bootstrap completes.
|
|
9833
|
+
*/
|
|
9834
|
+
const resolveAppRootColorProvider = (appRef) => {
|
|
9835
|
+
for (const componentRef of appRef.components) {
|
|
9836
|
+
const provider = componentRef.injector.get(COLOR_PROVIDER, null);
|
|
9837
|
+
if (provider) {
|
|
9838
|
+
return provider;
|
|
9839
|
+
}
|
|
9840
|
+
}
|
|
9841
|
+
return null;
|
|
9842
|
+
};
|
|
9843
|
+
const FORCED_COLOR_UNSET = /* @__PURE__ */ Symbol('FORCED_COLOR_UNSET');
|
|
9844
|
+
const appsWarnedAboutMissingThemes = /* @__PURE__ */ new WeakSet();
|
|
9845
|
+
class ProvideColorDirective {
|
|
9846
|
+
constructor() {
|
|
9847
|
+
this.themes = injectColorThemes({ optional: true });
|
|
9848
|
+
this.prefix = injectColorThemesPrefix({ optional: true });
|
|
9849
|
+
this.injector = inject(Injector);
|
|
9850
|
+
this.parentProvider = inject(COLOR_PROVIDER, { optional: true, skipSelf: true });
|
|
9851
|
+
this.appRef = inject(ApplicationRef);
|
|
9852
|
+
this.currentProviderSync = null;
|
|
9853
|
+
this.forcedColor = signal(FORCED_COLOR_UNSET, /* @ts-ignore */
|
|
9854
|
+
...(ngDevMode ? [{ debugName: "forcedColor" }] : /* istanbul ignore next */ []));
|
|
9855
|
+
this.color = input(undefined, { ...(ngDevMode ? { debugName: "color" } : /* istanbul ignore next */ {}), alias: 'etProvideColor' });
|
|
9856
|
+
this.effectiveColor = computed(() => {
|
|
9857
|
+
const forcedColor = this.forcedColor();
|
|
9858
|
+
if (forcedColor !== FORCED_COLOR_UNSET) {
|
|
9859
|
+
return forcedColor;
|
|
9860
|
+
}
|
|
9861
|
+
return this.color();
|
|
9862
|
+
}, /* @ts-ignore */
|
|
9863
|
+
...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
|
|
9864
|
+
/**
|
|
9865
|
+
* The color that actually applies at this provider's location: its own effective color, or -
|
|
9866
|
+
* when this provider is passive (no input, nothing forced) - the nearest ancestor provider's.
|
|
9867
|
+
* Mirrors the CSS cascade, which falls through a provider's `-color--inherited` scope class;
|
|
9868
|
+
* consumers that re-apply context across detached DOM (overlay panes) must use this instead
|
|
9869
|
+
* of `effectiveColor` or a passive in-between provider (e.g. a form field's) erases the theme.
|
|
9870
|
+
*/
|
|
9871
|
+
this.resolvedColor = computed(() => {
|
|
9872
|
+
const own = this.effectiveColor();
|
|
9873
|
+
const ownName = typeof own === 'object' && own !== null ? own.name : own;
|
|
9874
|
+
if (ownName) {
|
|
9875
|
+
return own;
|
|
9876
|
+
}
|
|
9877
|
+
return this.parentProvider?.resolvedColor();
|
|
9878
|
+
}, /* @ts-ignore */
|
|
9879
|
+
...(ngDevMode ? [{ debugName: "resolvedColor" }] : /* istanbul ignore next */ []));
|
|
9880
|
+
this.colorName = computed(() => {
|
|
9881
|
+
const raw = this.effectiveColor();
|
|
9882
|
+
const value = typeof raw === 'object' && raw !== null ? raw.name : raw;
|
|
9883
|
+
if (value === SURFACE_COLOR)
|
|
9884
|
+
return SURFACE_COLOR;
|
|
9885
|
+
if (!value)
|
|
9886
|
+
return;
|
|
9887
|
+
if (!this.themes) {
|
|
9888
|
+
if (isDevMode() && !appsWarnedAboutMissingThemes.has(this.appRef)) {
|
|
9889
|
+
appsWarnedAboutMissingThemes.add(this.appRef);
|
|
9890
|
+
console.warn(`etProvideColor="${value}" has no effect because no color themes are registered. Please make sure to add provideColorThemesWithTailwind4()`);
|
|
9891
|
+
}
|
|
9892
|
+
return;
|
|
9893
|
+
}
|
|
9894
|
+
if (isDevMode() && !this.themes.some((theme) => theme.name === value)) {
|
|
9895
|
+
console.error(`Theme ${value} does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
|
|
9896
|
+
}
|
|
9897
|
+
return createCssColorThemeName(value);
|
|
9898
|
+
}, /* @ts-ignore */
|
|
9899
|
+
...(ngDevMode ? [{ debugName: "colorName" }] : /* istanbul ignore next */ []));
|
|
9900
|
+
this.themeClass = computed(() => {
|
|
9456
9901
|
const prefix = this.prefix || 'et';
|
|
9457
|
-
if (this.
|
|
9458
|
-
return `${prefix}-
|
|
9902
|
+
if (this.colorName()) {
|
|
9903
|
+
return `${prefix}-color--${this.colorName()}`;
|
|
9904
|
+
}
|
|
9905
|
+
return `${prefix}-color--inherited`;
|
|
9906
|
+
}, /* @ts-ignore */
|
|
9907
|
+
...(ngDevMode ? [{ debugName: "themeClass" }] : /* istanbul ignore next */ []));
|
|
9908
|
+
}
|
|
9909
|
+
syncWithProvider(provider) {
|
|
9910
|
+
this.currentProviderSync?.destroy();
|
|
9911
|
+
runInInjectionContext(this.injector, () => {
|
|
9912
|
+
this.currentProviderSync = effect(() => {
|
|
9913
|
+
const provideColor = provider.resolvedColor();
|
|
9914
|
+
untracked(() => {
|
|
9915
|
+
if (provideColor === undefined) {
|
|
9916
|
+
this.clearForcedColor();
|
|
9917
|
+
return;
|
|
9918
|
+
}
|
|
9919
|
+
this.forceColor(provideColor);
|
|
9920
|
+
});
|
|
9921
|
+
}, /* @ts-ignore */
|
|
9922
|
+
...(ngDevMode ? [{ debugName: "currentProviderSync" }] : /* istanbul ignore next */ []));
|
|
9923
|
+
});
|
|
9924
|
+
}
|
|
9925
|
+
stopSyncWithProvider() {
|
|
9926
|
+
const hadProviderSync = !!this.currentProviderSync;
|
|
9927
|
+
this.currentProviderSync?.destroy();
|
|
9928
|
+
this.currentProviderSync = null;
|
|
9929
|
+
if (hadProviderSync) {
|
|
9930
|
+
this.clearForcedColor();
|
|
9931
|
+
}
|
|
9932
|
+
}
|
|
9933
|
+
forceColor(color) {
|
|
9934
|
+
this.forcedColor.set(color);
|
|
9935
|
+
}
|
|
9936
|
+
clearForcedColor() {
|
|
9937
|
+
if (this.forcedColor() === FORCED_COLOR_UNSET) {
|
|
9938
|
+
return;
|
|
9939
|
+
}
|
|
9940
|
+
this.forcedColor.set(FORCED_COLOR_UNSET);
|
|
9941
|
+
}
|
|
9942
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
9943
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: ProvideColorDirective, isStandalone: true, selector: "[etProvideColor]", inputs: { color: { classPropertyName: "color", publicName: "etProvideColor", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "themeClass()" } }, providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }], ngImport: i0 }); }
|
|
9944
|
+
}
|
|
9945
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, decorators: [{
|
|
9946
|
+
type: Directive,
|
|
9947
|
+
args: [{
|
|
9948
|
+
selector: '[etProvideColor]',
|
|
9949
|
+
providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }],
|
|
9950
|
+
host: {
|
|
9951
|
+
'[class]': 'themeClass()',
|
|
9952
|
+
},
|
|
9953
|
+
}]
|
|
9954
|
+
}], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "etProvideColor", required: false }] }] } });
|
|
9955
|
+
|
|
9956
|
+
const SURFACE_PROVIDER = new InjectionToken('SurfaceProvider');
|
|
9957
|
+
const FORCED_SURFACE_UNSET = /* @__PURE__ */ Symbol('FORCED_SURFACE_UNSET');
|
|
9958
|
+
class ProvideSurfaceDirective {
|
|
9959
|
+
constructor() {
|
|
9960
|
+
this.themes = injectSurfaceThemes({ optional: true });
|
|
9961
|
+
this.prefix = injectSurfaceThemesPrefix({ optional: true });
|
|
9962
|
+
this.injector = inject(Injector);
|
|
9963
|
+
this.parentProvider = inject(SURFACE_PROVIDER, { optional: true, skipSelf: true });
|
|
9964
|
+
this.defaultTheme = injectDefaultSurfaceTheme();
|
|
9965
|
+
this.colorThemes = injectColorThemes({ optional: true });
|
|
9966
|
+
this.colorPrefix = injectColorThemesPrefix({ optional: true });
|
|
9967
|
+
this.colorProvider = inject(COLOR_PROVIDER, { optional: true });
|
|
9968
|
+
this.currentProviderSync = null;
|
|
9969
|
+
this.forcedSurface = signal(FORCED_SURFACE_UNSET, /* @ts-ignore */
|
|
9970
|
+
...(ngDevMode ? [{ debugName: "forcedSurface" }] : /* istanbul ignore next */ []));
|
|
9971
|
+
this.surface = input(undefined, { ...(ngDevMode ? { debugName: "surface" } : /* istanbul ignore next */ {}), alias: 'etProvideSurface' });
|
|
9972
|
+
this.effectiveSurface = computed(() => {
|
|
9973
|
+
const forcedSurface = this.forcedSurface();
|
|
9974
|
+
if (forcedSurface !== FORCED_SURFACE_UNSET) {
|
|
9975
|
+
return forcedSurface;
|
|
9976
|
+
}
|
|
9977
|
+
return this.surface();
|
|
9978
|
+
}, /* @ts-ignore */
|
|
9979
|
+
...(ngDevMode ? [{ debugName: "effectiveSurface" }] : /* istanbul ignore next */ []));
|
|
9980
|
+
this.resolvedTheme = computed(() => {
|
|
9981
|
+
const value = this.effectiveSurface();
|
|
9982
|
+
if (!this.themes)
|
|
9983
|
+
return null;
|
|
9984
|
+
// Only the outermost provider adopts the app default. A nested one left unset must keep
|
|
9985
|
+
// resolving to `null` so it reports the surface it inherits instead of resetting elevation.
|
|
9986
|
+
if (!value)
|
|
9987
|
+
return this.parentProvider ? null : this.defaultTheme;
|
|
9988
|
+
const theme = this.themes.find((t) => t.name === value) ?? null;
|
|
9989
|
+
if (isDevMode() && !theme) {
|
|
9990
|
+
console.error(`Surface theme ${value} does not exist. Please make sure to add it to provideSurfaceThemesWithTailwind4()`);
|
|
9991
|
+
}
|
|
9992
|
+
return theme;
|
|
9993
|
+
}, /* @ts-ignore */
|
|
9994
|
+
...(ngDevMode ? [{ debugName: "resolvedTheme" }] : /* istanbul ignore next */ []));
|
|
9995
|
+
/**
|
|
9996
|
+
* The surface theme in effect on this element - the theme this provider resolves itself, or,
|
|
9997
|
+
* where it is left unset, the one it inherits from the closest ancestor provider. The outermost
|
|
9998
|
+
* provider resolves the app default, so the chain ends there.
|
|
9999
|
+
*/
|
|
10000
|
+
this.activeTheme = computed(() => this.resolvedTheme() ?? this.parentProvider?.activeTheme() ?? null, /* @ts-ignore */
|
|
10001
|
+
...(ngDevMode ? [{ debugName: "activeTheme" }] : /* istanbul ignore next */ []));
|
|
10002
|
+
this.elevation = computed(() => this.activeTheme()?.elevation ?? 0, /* @ts-ignore */
|
|
10003
|
+
...(ngDevMode ? [{ debugName: "elevation" }] : /* istanbul ignore next */ []));
|
|
10004
|
+
this.surfaceType = computed(() => this.activeTheme()?.type ?? null, /* @ts-ignore */
|
|
10005
|
+
...(ngDevMode ? [{ debugName: "surfaceType" }] : /* istanbul ignore next */ []));
|
|
10006
|
+
this.surfaceName = computed(() => {
|
|
10007
|
+
const value = this.effectiveSurface();
|
|
10008
|
+
if (!this.themes || !value)
|
|
10009
|
+
return;
|
|
10010
|
+
return createCssSurfaceName(value);
|
|
10011
|
+
}, /* @ts-ignore */
|
|
10012
|
+
...(ngDevMode ? [{ debugName: "surfaceName" }] : /* istanbul ignore next */ []));
|
|
10013
|
+
this.colorThemeClass = computed(() => {
|
|
10014
|
+
const colorTheme = this.resolvedTheme()?.colorTheme;
|
|
10015
|
+
if (!colorTheme || this.colorProvider?.resolvedColor())
|
|
10016
|
+
return null;
|
|
10017
|
+
if (isDevMode() && !this.colorThemes?.some((theme) => theme.name === colorTheme)) {
|
|
10018
|
+
console.error(`Surface theme ${this.resolvedTheme()?.name} names the color theme ${colorTheme}, which does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
|
|
9459
10019
|
}
|
|
9460
|
-
return `${
|
|
10020
|
+
return `${this.colorPrefix || 'et'}-color--${createCssColorThemeName(colorTheme)}`;
|
|
10021
|
+
}, /* @ts-ignore */
|
|
10022
|
+
...(ngDevMode ? [{ debugName: "colorThemeClass" }] : /* istanbul ignore next */ []));
|
|
10023
|
+
this.surfaceClass = computed(() => {
|
|
10024
|
+
const prefix = this.prefix || 'et';
|
|
10025
|
+
const surfaceClass = this.surfaceName()
|
|
10026
|
+
? `${prefix}-surface--${this.surfaceName()}`
|
|
10027
|
+
: `${prefix}-surface--inherited`;
|
|
10028
|
+
const colorThemeClass = this.colorThemeClass();
|
|
10029
|
+
return colorThemeClass ? `${surfaceClass} ${colorThemeClass}` : surfaceClass;
|
|
9461
10030
|
}, /* @ts-ignore */
|
|
9462
10031
|
...(ngDevMode ? [{ debugName: "surfaceClass" }] : /* istanbul ignore next */ []));
|
|
9463
10032
|
}
|
|
@@ -9485,11 +10054,9 @@ class ProvideSurfaceDirective {
|
|
|
9485
10054
|
this.clearForcedSurface();
|
|
9486
10055
|
}
|
|
9487
10056
|
}
|
|
9488
|
-
/** @internal */
|
|
9489
10057
|
forceSurface(surface) {
|
|
9490
10058
|
this.forcedSurface.set(surface);
|
|
9491
10059
|
}
|
|
9492
|
-
/** @internal */
|
|
9493
10060
|
clearForcedSurface() {
|
|
9494
10061
|
if (this.forcedSurface() === FORCED_SURFACE_UNSET) {
|
|
9495
10062
|
return;
|
|
@@ -9586,15 +10153,9 @@ class AutoSurfaceDirective {
|
|
|
9586
10153
|
*/
|
|
9587
10154
|
this.surfaceProvider = input(null, /* @ts-ignore */
|
|
9588
10155
|
...(ngDevMode ? [{ debugName: "surfaceProvider" }] : /* istanbul ignore next */ []));
|
|
9589
|
-
// An overlay *panel* that IS its overlay's own painted surface opts in (see
|
|
9590
|
-
// matchOverlaySurface) so it adopts the overlay's registered elevation exactly,
|
|
9591
|
-
// rather than layering one level above a parent surface.
|
|
9592
10156
|
this.isOverlaySurface = signal(false, /* @ts-ignore */
|
|
9593
10157
|
...(ngDevMode ? [{ debugName: "isOverlaySurface" }] : /* istanbul ignore next */ []));
|
|
9594
|
-
//
|
|
9595
|
-
// containment changes. surfaceForElement() below does a non-reactive element.contains() read,
|
|
9596
|
-
// so the computed must be re-triggered when this element is (re)grafted into an overlay pane -
|
|
9597
|
-
// otherwise a value cached from before the graft (one elevation too low) sticks forever.
|
|
10158
|
+
// surfaceForElement() reads the DOM non-reactively; bumping this re-runs resolvedSurface after a re-graft.
|
|
9598
10159
|
this.domSettleTick = signal(0, /* @ts-ignore */
|
|
9599
10160
|
...(ngDevMode ? [{ debugName: "domSettleTick" }] : /* istanbul ignore next */ []));
|
|
9600
10161
|
this.resolvedSurface = computed(() => {
|
|
@@ -9602,40 +10163,25 @@ class AutoSurfaceDirective {
|
|
|
9602
10163
|
if (!themes) {
|
|
9603
10164
|
return null;
|
|
9604
10165
|
}
|
|
9605
|
-
// Establish a dependency on the DOM-settle tick so the non-reactive containment read below
|
|
9606
|
-
// is re-evaluated after the element is grafted into its final overlay pane.
|
|
9607
10166
|
this.domSettleTick();
|
|
9608
10167
|
const explicitProvider = this.surfaceProvider();
|
|
9609
10168
|
const contextTheme = explicitProvider ? explicitProvider.activeTheme() : this.parentSurface();
|
|
9610
10169
|
const contextElevation = contextTheme?.elevation ?? null;
|
|
9611
10170
|
const contextType = contextTheme?.type ?? null;
|
|
9612
|
-
//
|
|
9613
|
-
// *declared* (the trigger location), not the pane it renders into - so the
|
|
9614
|
-
// injector-derived parent surface is one elevation too low. The surface-context
|
|
9615
|
-
// tracker records each open overlay's surface *and* its pane element, so we consult
|
|
9616
|
-
// only the overlay whose pane actually contains this element in the DOM (portaling
|
|
9617
|
-
// moves the DOM into the pane even though the injector stays at the trigger). An
|
|
9618
|
-
// auto-surface on the base page therefore stays put when an overlay opens elsewhere.
|
|
10171
|
+
// Portaled content keeps the trigger's injector, so only the pane that contains this element in the DOM counts.
|
|
9619
10172
|
const overlaySurface = this.surfaceContextTracker.surfaceForElement(this.elementRef.nativeElement);
|
|
9620
10173
|
const overlayElevation = overlaySurface?.elevation ?? null;
|
|
9621
10174
|
const overlayType = overlaySurface?.type ?? null;
|
|
9622
|
-
//
|
|
9623
|
-
// overlay's elevation: the overlay container already resolved it (one above the trigger) and
|
|
9624
|
-
// registered it in the tracker. It must NOT re-derive from its declaration injector - that
|
|
9625
|
-
// points back at the trigger location and disagrees with the pane whenever the trigger itself
|
|
9626
|
-
// sits on a surface, which would double-elevate the panel relative to its own pane.
|
|
10175
|
+
// Must not re-derive from the declaration injector: it points at the trigger and would double-elevate the panel.
|
|
9627
10176
|
if (this.isOverlaySurface()) {
|
|
9628
10177
|
if (overlayElevation !== null) {
|
|
9629
10178
|
return resolveSurfaceByElevation(themes, overlayType ?? contextType ?? 'dark', overlayElevation)?.name ?? null;
|
|
9630
10179
|
}
|
|
9631
|
-
// Rendered outside any tracked overlay: fall back to the injector context unchanged.
|
|
9632
10180
|
if (contextElevation !== null) {
|
|
9633
10181
|
return resolveSurfaceByElevation(themes, contextType ?? 'dark', contextElevation)?.name ?? null;
|
|
9634
10182
|
}
|
|
9635
10183
|
return null;
|
|
9636
10184
|
}
|
|
9637
|
-
// A regular auto-surface sits one elevation *above* the deeper of its injector context and the
|
|
9638
|
-
// overlay pane it renders into (content painted onto the surface it lives on).
|
|
9639
10185
|
let parentElevation;
|
|
9640
10186
|
let parentType;
|
|
9641
10187
|
if (overlayElevation !== null && (contextElevation === null || overlayElevation > contextElevation)) {
|
|
@@ -9652,14 +10198,8 @@ class AutoSurfaceDirective {
|
|
|
9652
10198
|
return resolveSurfaceByElevation(themes, parentType, parentElevation + 1)?.name ?? null;
|
|
9653
10199
|
}, /* @ts-ignore */
|
|
9654
10200
|
...(ngDevMode ? [{ debugName: "resolvedSurface" }] : /* istanbul ignore next */ []));
|
|
9655
|
-
//
|
|
9656
|
-
//
|
|
9657
|
-
// in - or while it is briefly mounted in an off-pane measuring container, as windowed lists do.
|
|
9658
|
-
// surfaceForElement()'s element.contains() check then returns the wrong (or no) overlay, so it
|
|
9659
|
-
// resolves off the declaration injector (the trigger location) one elevation too low. That check
|
|
9660
|
-
// is a plain DOM read, not a signal, so nothing re-runs the computed once the element reaches its
|
|
9661
|
-
// final pane. Watch the tracker result across renders and re-trigger the computed whenever the
|
|
9662
|
-
// element's overlay containment changes, settling (and unsubscribing) once it stops moving.
|
|
10201
|
+
// The containment check is a plain DOM read, so nothing re-runs resolvedSurface once the element
|
|
10202
|
+
// reaches its final pane (windowed lists mount it in an off-pane container first). Watch it across renders.
|
|
9663
10203
|
let lastElevation = this.surfaceContextTracker.surfaceForElement(this.elementRef.nativeElement)?.elevation ?? null;
|
|
9664
10204
|
let stableRenders = 0;
|
|
9665
10205
|
const settleWatcher = afterEveryRender(() => {
|
|
@@ -9671,7 +10211,6 @@ class AutoSurfaceDirective {
|
|
|
9671
10211
|
this.domSettleTick.update((v) => v + 1);
|
|
9672
10212
|
}
|
|
9673
10213
|
else if (el.isConnected && ++stableRenders >= 2) {
|
|
9674
|
-
// containment has held for a couple of renders and the element is in the DOM - done moving
|
|
9675
10214
|
settleWatcher.destroy();
|
|
9676
10215
|
}
|
|
9677
10216
|
});
|
|
@@ -9696,87 +10235,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
9696
10235
|
const COLOR_PALETTE_DEF = /* @__PURE__ */ defineStaticProvider(undefined, {
|
|
9697
10236
|
name: 'Color Palette',
|
|
9698
10237
|
});
|
|
10238
|
+
const injectColorPaletteConfig = /* @__PURE__ */ toInjectFn(COLOR_PALETTE_DEF);
|
|
10239
|
+
const selectPalette = (config, surface) => {
|
|
10240
|
+
if (Array.isArray(config))
|
|
10241
|
+
return config;
|
|
10242
|
+
return (surface === null ? undefined : config[surface]) ?? config.default;
|
|
10243
|
+
};
|
|
9699
10244
|
/**
|
|
9700
10245
|
* The color themes a subtree may offer a user as pickable choices, in the order they should appear.
|
|
9701
10246
|
* A curated slice of what `provideColorThemesWithTailwind4` registered: an app registers every theme
|
|
9702
10247
|
* it renders with, including ones nobody should pick by hand (`error`, `warning`), and adds the
|
|
9703
10248
|
* user-facing label a swatch needs.
|
|
9704
10249
|
*
|
|
9705
|
-
*
|
|
9706
|
-
*
|
|
10250
|
+
* Pass one list, or one list per registered surface theme name plus a `default`. The charts draw with
|
|
10251
|
+
* the list of the surface they sit on; a color picker always offers the `default` list.
|
|
9707
10252
|
*
|
|
9708
10253
|
* @example
|
|
9709
10254
|
* provideColorPalette([
|
|
9710
10255
|
* { token: 'brand', label: 'Team green' },
|
|
9711
10256
|
* { token: 'ocean', label: 'Training blue' },
|
|
9712
10257
|
* ]);
|
|
10258
|
+
*
|
|
10259
|
+
* @example
|
|
10260
|
+
* provideColorPalette({
|
|
10261
|
+
* default: [{ token: 'ocean', label: 'Training' }],
|
|
10262
|
+
* 'dark-card': [{ token: 'ocean-bright', label: 'Training' }],
|
|
10263
|
+
* });
|
|
9713
10264
|
*/
|
|
9714
|
-
const provideColorPalette =
|
|
9715
|
-
|
|
9716
|
-
const
|
|
9717
|
-
|
|
9718
|
-
|
|
10265
|
+
const provideColorPalette = (palette) => COLOR_PALETTE_DEF.provide(palette);
|
|
10266
|
+
function injectColorPalette(options) {
|
|
10267
|
+
const config = options ? injectColorPaletteConfig(options) : injectColorPaletteConfig();
|
|
10268
|
+
return config ? selectPalette(config, null) : null;
|
|
10269
|
+
}
|
|
9719
10270
|
/**
|
|
9720
|
-
*
|
|
9721
|
-
*
|
|
9722
|
-
* read as chrome without the app registering a neutral color theme. Apps must not register a color
|
|
9723
|
-
* theme under this name.
|
|
10271
|
+
* The palette from {@link provideColorPalette} for the surface the current subtree renders on: that
|
|
10272
|
+
* surface theme's list, else the `default` list. `null` when no palette is provided.
|
|
9724
10273
|
*/
|
|
9725
|
-
const
|
|
9726
|
-
const
|
|
9727
|
-
const
|
|
9728
|
-
name:
|
|
9729
|
-
});
|
|
9730
|
-
const ɵProvideColorThemes = /* @__PURE__ */ toProvideFn(COLOR_THEMES_DEF);
|
|
9731
|
-
const injectColorThemes = /* @__PURE__ */ toInjectFn(COLOR_THEMES_DEF);
|
|
9732
|
-
const COLOR_THEMES_PREFIX_DEF = /* @__PURE__ */ defineStaticProvider('et', {
|
|
9733
|
-
name: 'Themes Prefix',
|
|
9734
|
-
});
|
|
9735
|
-
const ɵProvideColorThemesPrefix = /* @__PURE__ */ toProvideFn(COLOR_THEMES_PREFIX_DEF);
|
|
9736
|
-
const injectColorThemesPrefix = /* @__PURE__ */ toInjectFn(COLOR_THEMES_PREFIX_DEF);
|
|
9737
|
-
const provideColorThemesWithTailwind4 = (themes, prefix = 'et') => {
|
|
9738
|
-
if (isDevMode() && themes.some((theme) => theme.name === SURFACE_COLOR)) {
|
|
9739
|
-
console.error(`[provideColorThemesWithTailwind4] "${SURFACE_COLOR}" is a reserved color name that resolves to the ambient surface's neutral swatch. Rename your theme.`);
|
|
9740
|
-
}
|
|
9741
|
-
return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix(prefix)];
|
|
9742
|
-
};
|
|
9743
|
-
const injectColorThemeByType = (type) => {
|
|
9744
|
-
const themes = injectColorThemes({ optional: true });
|
|
9745
|
-
if (!themes) {
|
|
9746
|
-
throw new Error(`[injectColorThemeByType] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9747
|
-
}
|
|
9748
|
-
const theme = themes.find((t) => t.type === type);
|
|
9749
|
-
if (!theme) {
|
|
9750
|
-
throw new Error(`[injectColorThemeByType] No color theme with type "${type}" found. Add a theme with type: "${type}" to provideColorThemesWithTailwind4().`);
|
|
9751
|
-
}
|
|
9752
|
-
if (isDevMode()) {
|
|
9753
|
-
const duplicates = themes.filter((t) => t.type === type);
|
|
9754
|
-
if (duplicates.length > 1) {
|
|
9755
|
-
console.error(`[injectColorThemeByType] Multiple themes with type "${type}" found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9756
|
-
}
|
|
9757
|
-
}
|
|
9758
|
-
return theme;
|
|
9759
|
-
};
|
|
9760
|
-
const injectErrorTheme = () => injectColorThemeByType('error');
|
|
9761
|
-
const injectWarningTheme = () => injectColorThemeByType('warning');
|
|
9762
|
-
const injectSuccessTheme = () => injectColorThemeByType('success');
|
|
9763
|
-
const injectDefaultColorTheme = () => {
|
|
9764
|
-
const themes = injectColorThemes({ optional: true });
|
|
9765
|
-
if (!themes) {
|
|
9766
|
-
throw new Error(`[injectDefaultColorTheme] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9767
|
-
}
|
|
9768
|
-
const theme = themes.find((t) => t.isDefault);
|
|
9769
|
-
if (!theme) {
|
|
9770
|
-
throw new Error(`[injectDefaultColorTheme] No color theme with isDefault: true found. Mark one theme as the default in provideColorThemesWithTailwind4().`);
|
|
9771
|
-
}
|
|
9772
|
-
if (isDevMode()) {
|
|
9773
|
-
const duplicates = themes.filter((t) => t.isDefault);
|
|
9774
|
-
if (duplicates.length > 1) {
|
|
9775
|
-
console.error(`[injectDefaultColorTheme] Multiple themes with isDefault: true found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9776
|
-
}
|
|
9777
|
-
}
|
|
9778
|
-
return theme;
|
|
10274
|
+
const injectSurfaceColorPalette = () => {
|
|
10275
|
+
const config = injectColorPaletteConfig({ optional: true });
|
|
10276
|
+
const surface = injectParentSurface();
|
|
10277
|
+
return computed(() => (config ? selectPalette(config, surface()?.name ?? null) : null));
|
|
9779
10278
|
};
|
|
10279
|
+
const COLOR_PALETTE = /* @__PURE__ */ toToken(COLOR_PALETTE_DEF);
|
|
9780
10280
|
|
|
9781
10281
|
/** Reads the nonce where Angular's default `CSP_NONCE` does, since these run outside any injector. */
|
|
9782
10282
|
const createNoncedStyle = () => {
|
|
@@ -9979,131 +10479,6 @@ const provideColorThemes = (themes) => {
|
|
|
9979
10479
|
return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix('et')];
|
|
9980
10480
|
};
|
|
9981
10481
|
|
|
9982
|
-
const COLOR_PROVIDER = new InjectionToken('ColorProvider');
|
|
9983
|
-
/**
|
|
9984
|
-
* The color provider attached to a bootstrapped root component (e.g. `ProvideColorDirective` in
|
|
9985
|
-
* the app component's `hostDirectives`), if any. Detached DOM such as overlay panes can't reach
|
|
9986
|
-
* it through element DI, so overlay containers fall back to this when resolving the provider to
|
|
9987
|
-
* `syncWithProvider()` - resolve at sync time, not at injection time, since root components only
|
|
9988
|
-
* register with `ApplicationRef` once bootstrap completes.
|
|
9989
|
-
*
|
|
9990
|
-
* @internal
|
|
9991
|
-
*/
|
|
9992
|
-
const resolveAppRootColorProvider = (appRef) => {
|
|
9993
|
-
for (const componentRef of appRef.components) {
|
|
9994
|
-
const provider = componentRef.injector.get(COLOR_PROVIDER, null);
|
|
9995
|
-
if (provider) {
|
|
9996
|
-
return provider;
|
|
9997
|
-
}
|
|
9998
|
-
}
|
|
9999
|
-
return null;
|
|
10000
|
-
};
|
|
10001
|
-
const FORCED_COLOR_UNSET = /* @__PURE__ */ Symbol('FORCED_COLOR_UNSET');
|
|
10002
|
-
class ProvideColorDirective {
|
|
10003
|
-
constructor() {
|
|
10004
|
-
this.themes = injectColorThemes({ optional: true });
|
|
10005
|
-
this.prefix = injectColorThemesPrefix({ optional: true });
|
|
10006
|
-
this.injector = inject(Injector);
|
|
10007
|
-
this.parentProvider = inject(COLOR_PROVIDER, { optional: true, skipSelf: true });
|
|
10008
|
-
this.currentProviderSync = null;
|
|
10009
|
-
this.forcedColor = signal(FORCED_COLOR_UNSET, /* @ts-ignore */
|
|
10010
|
-
...(ngDevMode ? [{ debugName: "forcedColor" }] : /* istanbul ignore next */ []));
|
|
10011
|
-
this.color = input(undefined, { ...(ngDevMode ? { debugName: "color" } : /* istanbul ignore next */ {}), alias: 'etProvideColor' });
|
|
10012
|
-
this.effectiveColor = computed(() => {
|
|
10013
|
-
const forcedColor = this.forcedColor();
|
|
10014
|
-
if (forcedColor !== FORCED_COLOR_UNSET) {
|
|
10015
|
-
return forcedColor;
|
|
10016
|
-
}
|
|
10017
|
-
return this.color();
|
|
10018
|
-
}, /* @ts-ignore */
|
|
10019
|
-
...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
|
|
10020
|
-
/**
|
|
10021
|
-
* The color that actually applies at this provider's location: its own effective color, or -
|
|
10022
|
-
* when this provider is passive (no input, nothing forced) - the nearest ancestor provider's.
|
|
10023
|
-
* Mirrors the CSS cascade, which falls through a provider's `-color--inherited` scope class;
|
|
10024
|
-
* consumers that re-apply context across detached DOM (overlay panes) must use this instead
|
|
10025
|
-
* of `effectiveColor` or a passive in-between provider (e.g. a form field's) erases the theme.
|
|
10026
|
-
*/
|
|
10027
|
-
this.resolvedColor = computed(() => {
|
|
10028
|
-
const own = this.effectiveColor();
|
|
10029
|
-
const ownName = typeof own === 'object' && own !== null ? own.name : own;
|
|
10030
|
-
if (ownName) {
|
|
10031
|
-
return own;
|
|
10032
|
-
}
|
|
10033
|
-
return this.parentProvider?.resolvedColor();
|
|
10034
|
-
}, /* @ts-ignore */
|
|
10035
|
-
...(ngDevMode ? [{ debugName: "resolvedColor" }] : /* istanbul ignore next */ []));
|
|
10036
|
-
this.colorName = computed(() => {
|
|
10037
|
-
const raw = this.effectiveColor();
|
|
10038
|
-
const value = typeof raw === 'object' && raw !== null ? raw.name : raw;
|
|
10039
|
-
if (value === SURFACE_COLOR)
|
|
10040
|
-
return SURFACE_COLOR;
|
|
10041
|
-
if (!this.themes || !value)
|
|
10042
|
-
return;
|
|
10043
|
-
if (isDevMode() && !this.themes.some((theme) => theme.name === value) && value !== null) {
|
|
10044
|
-
console.error(`Theme ${value} does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
|
|
10045
|
-
}
|
|
10046
|
-
return createCssColorThemeName(value);
|
|
10047
|
-
}, /* @ts-ignore */
|
|
10048
|
-
...(ngDevMode ? [{ debugName: "colorName" }] : /* istanbul ignore next */ []));
|
|
10049
|
-
this.themeClass = computed(() => {
|
|
10050
|
-
const prefix = this.prefix || 'et';
|
|
10051
|
-
if (this.colorName()) {
|
|
10052
|
-
return `${prefix}-color--${this.colorName()}`;
|
|
10053
|
-
}
|
|
10054
|
-
return `${prefix}-color--inherited`;
|
|
10055
|
-
}, /* @ts-ignore */
|
|
10056
|
-
...(ngDevMode ? [{ debugName: "themeClass" }] : /* istanbul ignore next */ []));
|
|
10057
|
-
}
|
|
10058
|
-
syncWithProvider(provider) {
|
|
10059
|
-
this.currentProviderSync?.destroy();
|
|
10060
|
-
runInInjectionContext(this.injector, () => {
|
|
10061
|
-
this.currentProviderSync = effect(() => {
|
|
10062
|
-
const provideColor = provider.resolvedColor();
|
|
10063
|
-
untracked(() => {
|
|
10064
|
-
if (provideColor === undefined) {
|
|
10065
|
-
this.clearForcedColor();
|
|
10066
|
-
return;
|
|
10067
|
-
}
|
|
10068
|
-
this.forceColor(provideColor);
|
|
10069
|
-
});
|
|
10070
|
-
}, /* @ts-ignore */
|
|
10071
|
-
...(ngDevMode ? [{ debugName: "currentProviderSync" }] : /* istanbul ignore next */ []));
|
|
10072
|
-
});
|
|
10073
|
-
}
|
|
10074
|
-
stopSyncWithProvider() {
|
|
10075
|
-
const hadProviderSync = !!this.currentProviderSync;
|
|
10076
|
-
this.currentProviderSync?.destroy();
|
|
10077
|
-
this.currentProviderSync = null;
|
|
10078
|
-
if (hadProviderSync) {
|
|
10079
|
-
this.clearForcedColor();
|
|
10080
|
-
}
|
|
10081
|
-
}
|
|
10082
|
-
/** @internal */
|
|
10083
|
-
forceColor(color) {
|
|
10084
|
-
this.forcedColor.set(color);
|
|
10085
|
-
}
|
|
10086
|
-
/** @internal */
|
|
10087
|
-
clearForcedColor() {
|
|
10088
|
-
if (this.forcedColor() === FORCED_COLOR_UNSET) {
|
|
10089
|
-
return;
|
|
10090
|
-
}
|
|
10091
|
-
this.forcedColor.set(FORCED_COLOR_UNSET);
|
|
10092
|
-
}
|
|
10093
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
10094
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: ProvideColorDirective, isStandalone: true, selector: "[etProvideColor]", inputs: { color: { classPropertyName: "color", publicName: "etProvideColor", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "themeClass()" } }, providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }], ngImport: i0 }); }
|
|
10095
|
-
}
|
|
10096
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, decorators: [{
|
|
10097
|
-
type: Directive,
|
|
10098
|
-
args: [{
|
|
10099
|
-
selector: '[etProvideColor]',
|
|
10100
|
-
providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }],
|
|
10101
|
-
host: {
|
|
10102
|
-
'[class]': 'themeClass()',
|
|
10103
|
-
},
|
|
10104
|
-
}]
|
|
10105
|
-
}], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "etProvideColor", required: false }] }] } });
|
|
10106
|
-
|
|
10107
10482
|
class SurfaceInteractiveStylesComponent {
|
|
10108
10483
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: SurfaceInteractiveStylesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10109
10484
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.6", type: SurfaceInteractiveStylesComponent, isStandalone: true, selector: "et-surface-interactive-styles", ngImport: i0, template: '', isInline: true, styles: ["@layer components{:where(.et-surface-interactive){--et-surface-interaction-rgb: var(--et-surface-interaction);--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}@media(hover:hover){:where(.et-surface-interactive:hover){--et-surface-interaction-rgb: var(--et-surface-interaction-hover, var(--et-surface-interaction));--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}}:where(.et-surface-interactive:focus-visible){--et-surface-interaction-rgb: var( --et-surface-interaction-focus, var(--et-surface-interaction-hover, var(--et-surface-interaction)) );--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}:where(.et-surface-interactive:active){--et-surface-interaction-rgb: var( --et-surface-interaction-active, var(--et-surface-interaction-focus, var(--et-surface-interaction-hover, var(--et-surface-interaction))) );--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}:where(.et-surface-interactive[disabled]),:where(.et-surface-interactive[aria-disabled=true]){--et-surface-interaction-rgb: var( --et-surface-interaction-disabled, var( --et-surface-interaction-active, var(--et-surface-interaction-focus, var(--et-surface-interaction-hover, var(--et-surface-interaction))) ) );--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}}\n"], encapsulation: i0.ViewEncapsulation.None }); }
|
|
@@ -10144,11 +10519,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
10144
10519
|
|
|
10145
10520
|
// Generated from package.json by tools/scripts/generate-version.js - do not edit.
|
|
10146
10521
|
/** The version of `@ethlete/core` this build was cut from. */
|
|
10147
|
-
const CORE_VERSION = '5.0.0-next.
|
|
10522
|
+
const CORE_VERSION = '5.0.0-next.62';
|
|
10148
10523
|
|
|
10149
10524
|
/**
|
|
10150
10525
|
* Generated bundle index. Do not edit.
|
|
10151
10526
|
*/
|
|
10152
10527
|
|
|
10153
|
-
export { ANIMATABLE_TOKEN, ANIMATED_IF_TOKEN, ANIMATED_LIFECYCLE_TOKEN, ANIMATION_DEBUG_STORAGE_KEY, AT_LEAST_ONE_REQUIRED, AnimatableDirective, AnimatedIfDirective, AnimatedLifecycleDirective, AutoSurfaceDirective, BOUNDARY_ELEMENT_TOKEN, BREAKPOINT_INSTANCE_TOKEN, BREAKPOINT_ORDER, COLOR_PALETTE, COLOR_PROVIDER, CORE_VERSION, ClickOutsideDirective, ColorInteractiveContainerDirective, ColorInteractiveDirective, ColorInteractiveExcludeDirective, ColorInteractiveHasFocusDirective, DEFAULT_MULTI_INSTANCE_RELS, DEFAULT_MULTI_INSTANCE_TAGS, DEFAULT_OVERLAY_LAYER, DEFAULT_VIEWPORT_CONFIG, DISABLE_LOGGER_PARAM, DragHandleDirective, ET_DISABLE_SCROLL_TOP, ET_DISABLE_SCROLL_TOP_AS_RETURN_ROUTE, ET_DISABLE_SCROLL_TOP_ON_PATH_PARAM_CHANGE, ET_PROPERTY_REMOVED, ET_RESTORE_SCROLL, EasingTokensStylesComponent, EthleteValidators, FOCUSABLE_SELECTOR, FOCUS_VISIBLE_TRACKER_TOKEN, HtmlToMarkdownPipe, IS_ARRAY_NOT_EMPTY, IS_EMAIL, InferMimeTypePipe, InitialsPipe, IsArrayNotEmpty, IsEmail, jsonLd as JsonLD, KeyPressManager, MUST_MATCH, MarkdownToHtmlPipe, MatchStateType, MustMatch, NOTIFICATION_CLICK_MESSAGE_TYPE, NormalizeGameResultTypePipe, NormalizeMatchParticipantsPipe, NormalizeMatchScorePipe, NormalizeMatchStatePipe, NormalizeMatchTypePipe, OVERLAY_LAYER_ATTRIBUTE, ProvideColorDirective, ProvideSurfaceDirective, RUNTIME_ERROR_NO_DATA, RepeatDirective, ResizeHandlesComponent, RestoreScrollDirective, RuntimeError, SURFACE_COLOR, SURFACE_PROVIDER, SURFACE_TYPE, SWIPE_VELOCITY_WINDOW_MS, ScrollObserverDirective, ScrollObserverEndDirective, ScrollObserverStartDirective, SlugifyPipe, StructuredDataComponent, SurfaceInteractiveDirective, ToArrayPipe, TypedQueryList, UNSAVED_CHANGES_TITLE_MARKER, ValidateAtLeastOneRequired, anchoredOverlayPosition, animationDebugLog, applyAlternateBinding, applyAlternateLanguagesBindings, applyArticleBindings, applyAuthorBinding, applyCanonicalBinding, applyCenteredPosition, applyDescriptionBinding, applyFaviconOverlay, applyGlobalPosition, applyHeadBinding, applyHeadTitleBinding, applyHeadTitleMarker, applyHostListener, applyHostListeners, applyInitialFocus, applyKeywordsBinding, applyLinkBinding, applyMetaBinding, applyNextBinding, applyOgBinding, applyOpenGraphBindings, applyPrevBinding, applyResourceHintsBindings, applyRobotsBinding, applySocialMediaBindings, applyStructuredDataBinding, applyTwitterCardBindings, boolBreakpointTransform, boundingClientRectToElementRect, breakpointTransformBase, buildElementSignal, buildSignalEffects, canUseSessionMemory, clamp, clone, cloneFormGroup, computedTillFalsy, computedTillTruthy, controlValueSignal, controlValueSignalWithPrevious, copyToClipboard, createAnchoredPositionCleanup, createArrayPropertyBinding, createAutoSessionMemoryKey, createCanAnimateSignal, createComponentId, createCssColorThemeName, createCssSurfaceName, createDestroy, createDocumentElementSignal, createElementDimensions, createEmptyElementSignal, createFlipAnimation, createFlipAnimationGroup, createIsRenderedSignal, createLogger, createObjectUrlHandle,
|
|
10528
|
+
export { ANIMATABLE_TOKEN, ANIMATED_IF_TOKEN, ANIMATED_LIFECYCLE_TOKEN, ANIMATION_DEBUG_STORAGE_KEY, ANIMATION_ERROR_CODES, AT_LEAST_ONE_REQUIRED, AnimatableDirective, AnimatedIfDirective, AnimatedLifecycleDirective, AutoSurfaceDirective, BOUNDARY_ELEMENT_TOKEN, BREAKPOINT_INSTANCE_TOKEN, BREAKPOINT_ORDER, COLOR_PALETTE, COLOR_PROVIDER, CORE_VERSION, ClickOutsideDirective, ColorInteractiveContainerDirective, ColorInteractiveDirective, ColorInteractiveExcludeDirective, ColorInteractiveHasFocusDirective, DEFAULT_MULTI_INSTANCE_RELS, DEFAULT_MULTI_INSTANCE_TAGS, DEFAULT_OVERLAY_LAYER, DEFAULT_VIEWPORT_CONFIG, DISABLE_LOGGER_PARAM, DragHandleDirective, ET_DISABLE_SCROLL_TOP, ET_DISABLE_SCROLL_TOP_AS_RETURN_ROUTE, ET_DISABLE_SCROLL_TOP_ON_PATH_PARAM_CHANGE, ET_PROPERTY_REMOVED, ET_RESTORE_SCROLL, EasingTokensStylesComponent, EthleteValidators, FOCUSABLE_SELECTOR, FOCUS_VISIBLE_TRACKER_TOKEN, HtmlToMarkdownPipe, IS_ARRAY_NOT_EMPTY, IS_EMAIL, InferMimeTypePipe, InitialsPipe, IsArrayNotEmpty, IsEmail, jsonLd as JsonLD, KeyPressManager, MARKDOWN_VERBATIM_ATTR, MUST_MATCH, MarkdownToHtmlPipe, MatchStateType, MustMatch, NOTIFICATION_CLICK_MESSAGE_TYPE, NormalizeGameResultTypePipe, NormalizeMatchParticipantsPipe, NormalizeMatchScorePipe, NormalizeMatchStatePipe, NormalizeMatchTypePipe, OVERLAY_LAYER_ATTRIBUTE, ProvideColorDirective, ProvideSurfaceDirective, RUNTIME_ERROR_NO_DATA, RepeatDirective, ResizeHandlesComponent, RestoreScrollDirective, RuntimeError, SURFACE_COLOR, SURFACE_PROVIDER, SURFACE_TYPE, SWIPE_VELOCITY_WINDOW_MS, ScrollObserverDirective, ScrollObserverEndDirective, ScrollObserverStartDirective, SlugifyPipe, StructuredDataComponent, SurfaceInteractiveDirective, ToArrayPipe, TypedQueryList, UNSAVED_CHANGES_CONFIRM, UNSAVED_CHANGES_ERROR_CODES, UNSAVED_CHANGES_TITLE_MARKER, ValidateAtLeastOneRequired, VisuallyHiddenStylesComponent, anchoredOverlayPosition, animationDebugLog, applyAlternateBinding, applyAlternateLanguagesBindings, applyArticleBindings, applyAuthorBinding, applyCanonicalBinding, applyCenteredPosition, applyDescriptionBinding, applyFaviconOverlay, applyGlobalPosition, applyHeadBinding, applyHeadTitleBinding, applyHeadTitleMarker, applyHostListener, applyHostListeners, applyInitialFocus, applyKeywordsBinding, applyLinkBinding, applyMetaBinding, applyNextBinding, applyOgBinding, applyOpenGraphBindings, applyPrevBinding, applyResourceHintsBindings, applyRobotsBinding, applySocialMediaBindings, applyStructuredDataBinding, applyTwitterCardBindings, boolBreakpointTransform, boundingClientRectToElementRect, breakpointTransformBase, buildElementSignal, buildSignalEffects, canUseSessionMemory, clamp, clone, cloneFormGroup, computedTillFalsy, computedTillTruthy, controlValueSignal, controlValueSignalWithPrevious, copyToClipboard, createAnchoredPositionCleanup, createArrayPropertyBinding, createAutoSessionMemoryKey, createCanAnimateSignal, createComponentId, createCssColorThemeName, createCssSurfaceName, createDestroy, createDocumentElementSignal, createElementDimensions, createEmptyElementSignal, createFlipAnimation, createFlipAnimationGroup, createIsRenderedSignal, createLogger, createObjectUrlHandle, createPropertyBinding, createRootThemeCss, createRoute, createRouterState, createRxHostListener, createSessionMemory, createSwatchCss, createSwipeTracker, createTailwindColorThemes, createTailwindCssVar, createTailwindRgbVar, createThemeStyle, createUnsavedChangesGuard, createUnsavedChangesTabLock, createUnsavedChangesTracker, createUserConsentProvider, deferredSignal, defineLabels, defineProvider, defineRootProvider, defineStaticProvider, defineStaticRootProvider, deleteCookie, dragGestureFrom, easeElastic, easeIn, easeInOut, easeLinear, easeOut, easeOutBack, easeOutBackStrong, elementCanScroll, enableAnchoredOverlayPositionExtras, equal, fetchDeployedBuildFingerprint$, firstElementSignal, focusElement, forceReflow, formatRuntimeError, fromNextFrame, getCookie, getDomain, getElementScrollCoordinates, getFocusableElements, getFormGroupValue, getGroupMatchPoints, getGroupMatchScore, getHeadingElement, getKnockoutMatchScore, getMatchScoreSubLine, getObjectProperty, getScrollContainerTarget, getScrollItemTarget, getScrollSnapTarget, hasCookie, holdScrollRestoration, htmlToMarkdown, inferMimeType, initials, injectAngularRootElement, injectAnimatedBlockSize, injectAppUpdates, injectAppUpdatesConfig, injectBoundaryElement, injectBreakpointInput, injectBreakpointIsMatched, injectBreakpointObserver, injectCanHover, injectColorPalette, injectColorThemes, injectColorThemesPrefix, injectCurrentBreakpoint, injectDefaultColorTheme, injectDefaultSurfaceTheme, injectDeviceInputType, injectDisplayOrientation, injectErrorTheme, injectFaviconStore, injectFileDownload, injectFocusVisibleTracker, injectFragment, injectHasPrecisionInput, injectHasTouchInput, injectHostElement, injectIs2Xl, injectIsDocumentVisible, injectIsLandscape, injectIsLg, injectIsMd, injectIsPortrait, injectIsRouterInitialized, injectIsSm, injectIsXl, injectIsXs, injectLinkStore, injectLinkStoreConfig, injectLocale, injectMediaQueryIsMatched, injectMetaConfig, injectMetaStore, injectNotifications, injectObserveBreakpoint, injectObserveMediaQuery, injectOverlayRuntime, injectParentSurface, injectPathParam, injectPathParamChanges, injectPathParams, injectPrefersReducedMotion, injectQueryParam, injectQueryParamAll, injectQueryParamChanges, injectQueryParams, injectRenderer, injectRoute, injectRouteData, injectRouteDataItem, injectRouteTitle, injectRouterEvent, injectRouterNavigationState, injectRouterState, injectScrollbarDimensions, injectSemanticColorTheme, injectStructuredDataConfig, injectStructuredDataStore, injectStyleManager, injectSuccessTheme, injectSurfaceColorPalette, injectSurfaceContextTracker, injectSurfaceThemes, injectSurfaceThemesPrefix, injectSurfaceType, injectTemplateRef, injectTitleConfig, injectTitleStore, injectUnsavedChangesCoordinator, injectUrl, injectViewportConfig, injectViewportDimensions, injectViewportSize, injectWarningTheme, isAnimationDebugEnabled, isArray, isElement, isElementSignal, isElementVisible, isFocusable, isGroupMatch, isHTMLElement, isHTMLOrSVGElement, isKnockoutMatch, isObject, isOnHigherOverlayLayer, isSafeLinkUrl, isSafeUrl, isStaleBuildError, markdownToHtml, matchesReducedMotion, maybeSignalValue, memoizeSignal, mountEasingTokens, mountVisuallyHidden, nextFrame, normalizeGameResultType, normalizeMatchParticipant, normalizeMatchParticipants, normalizeMatchScore, normalizeMatchState, normalizeMatchType, normalizeUnsavedChangesSource, numberBreakpointTransform, onOverlayViewportInsetsChange, optionalBooleanAttribute, overlayViewportInsets, overlayViewportInsetsFor, ownsActiveElement, previousSignalValue, provideAppUpdates, provideAppUpdatesConfig, provideAppUpdatesInstance, provideBoundaryElement, provideBreakpointInstance, provideBreakpointObserver, provideColorPalette, provideColorThemes, provideColorThemesWithTailwind4, provideFaviconStore, provideFocusVisibleTracker, provideLinkStore, provideLinkStoreConfig, provideLocale, provideMetaConfig, provideMetaStore, provideNotifications, provideOverlayRuntime, provideRenderer, provideStructuredDataConfig, provideStructuredDataStore, provideStyleManager, provideSurfaceContextTracker, provideSurfaceThemesWithTailwind4, provideTitleConfig, provideTitleStore, provideUnsavedChangesConfirm, provideUnsavedChangesCoordinator, provideViewportConfig, randomId, readBuildFingerprint, readFromClipboard, reserveOverlayViewportSpace, resolveAppRootColorProvider, resolveOverlayLayer, resolveSurfaceByElevation, round, routerDisableScrollTop, routerRestoreScroll, scrollToElement, setCookie, setInputSignal, setupFocusTrap, setupPositioning, setupScrollRestoration, signalAnimatedNumber, signalAttributes, signalClasses, signalCountdown, signalDeferredLoading, signalElementChildren, signalElementDimensions, signalElementIntersection, signalElementLastScrollDirection, signalElementMutations, signalElementScrollState, signalHostAttributes, signalHostClasses, signalHostElementDimensions, signalHostElementIntersection, signalHostElementLastScrollDirection, signalHostElementMutations, signalHostElementScrollState, signalHostStyles, signalIsRendered, signalStyles, slugify, suppressTextSelection, switchQueryListChanges, syncSignal, toArray, toArrayTrackByFn, toInjectFn, toProvideFn, toStringBinding, toToken, transformOrReturn, typedBreakpointTransform, useCursorDragScroll, writeScrollbarSizeToCssVariables, writeViewportSizeToCssVariables, ɵProvideColorThemes, ɵProvideColorThemesPrefix, ɵProvideSurfaceThemes, ɵProvideSurfaceThemesPrefix };
|
|
10154
10529
|
//# sourceMappingURL=ethlete-core.mjs.map
|