@ethlete/core 5.0.0-next.61 → 5.0.0-next.63
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 +56 -0
- package/fesm2022/ethlete-core.mjs +774 -405
- 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/removed-exports.js +94 -6
- 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 +269 -174
|
@@ -1,10 +1,10 @@
|
|
|
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, tap, pairwise, debounceTime, merge, distinctUntilChanged, EMPTY, expand, skip, take, filter, BehaviorSubject, combineLatest, Observable, isObservable, firstValueFrom, defaultIfEmpty, concatMap, concat, finalize, exhaustMap, share, shareReplay } from 'rxjs';
|
|
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
10
|
import { platform, offset, flip, shift, limitShift, computePosition, autoUpdate, hide, arrow, size } from '@floating-ui/dom';
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -1699,28 +1740,41 @@ const breakpointTransformBase = (coerce, defaultValue) => {
|
|
|
1699
1740
|
const injector = inject(Injector);
|
|
1700
1741
|
const raw = signal(undefined, /* @ts-ignore */
|
|
1701
1742
|
...(ngDevMode ? [{ debugName: "raw" }] : /* istanbul ignore next */ []));
|
|
1743
|
+
let warnedMissingInstance = false;
|
|
1702
1744
|
let cachedSig = null;
|
|
1703
1745
|
const transformFn = (value) => {
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
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);
|
|
1707
1753
|
};
|
|
1708
1754
|
effect(() => {
|
|
1709
1755
|
const bp = currentBp();
|
|
1710
1756
|
const r = raw();
|
|
1711
1757
|
if (!cachedSig) {
|
|
1712
|
-
const instance = injector.get(BREAKPOINT_INSTANCE_TOKEN, null);
|
|
1713
|
-
if (
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1758
|
+
const instance = injector.get(BREAKPOINT_INSTANCE_TOKEN, null, { self: true });
|
|
1759
|
+
if (instance) {
|
|
1760
|
+
for (const key of Object.keys(instance)) {
|
|
1761
|
+
const val = instance[key];
|
|
1762
|
+
if (val && typeof val === 'function' && val[SIGNAL]?.transformFn === transformFn) {
|
|
1763
|
+
cachedSig = val;
|
|
1764
|
+
break;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
if (!cachedSig) {
|
|
1769
|
+
if (ngDevMode && r !== undefined && !warnedMissingInstance) {
|
|
1770
|
+
warnedMissingInstance = true;
|
|
1771
|
+
console.warn('[ethlete] A breakpoint map was bound to an input, but its component does not call ' +
|
|
1772
|
+
'provideBreakpointInstance(), so the value will not follow the breakpoint after the first render.');
|
|
1720
1773
|
}
|
|
1774
|
+
return;
|
|
1721
1775
|
}
|
|
1722
1776
|
}
|
|
1723
|
-
if (!cachedSig || r === undefined
|
|
1777
|
+
if (!cachedSig || r === undefined)
|
|
1724
1778
|
return;
|
|
1725
1779
|
const resolved = resolveFromMap(r, bp, defaultValue);
|
|
1726
1780
|
untracked(() => setInputSignal(cachedSig, resolved));
|
|
@@ -1966,14 +2020,13 @@ const signalElementChildren = (el, options) => {
|
|
|
1966
2020
|
const buildSignalEffects = (el, config) => {
|
|
1967
2021
|
const elements = buildElementSignal(el);
|
|
1968
2022
|
const injector = inject(Injector);
|
|
2023
|
+
const liveTokens = new Map(Object.entries(config.tokenMap));
|
|
1969
2024
|
effect(() => {
|
|
1970
2025
|
const { currentElements, previousElements } = elements();
|
|
1971
2026
|
for (const previousEl of previousElements) {
|
|
1972
2027
|
if (currentElements.includes(previousEl))
|
|
1973
2028
|
continue;
|
|
1974
|
-
const tokens =
|
|
1975
|
-
.map((key) => key.split(' '))
|
|
1976
|
-
.flat();
|
|
2029
|
+
const tokens = [...liveTokens.keys()].flatMap((key) => key.split(' '));
|
|
1977
2030
|
if (!tokens.length)
|
|
1978
2031
|
continue;
|
|
1979
2032
|
config.cleanupFn(previousEl, tokens);
|
|
@@ -1981,7 +2034,7 @@ const buildSignalEffects = (el, config) => {
|
|
|
1981
2034
|
for (const currentEl of currentElements) {
|
|
1982
2035
|
if (previousElements.includes(currentEl))
|
|
1983
2036
|
continue;
|
|
1984
|
-
for (const [tokens, condition] of
|
|
2037
|
+
for (const [tokens, condition] of liveTokens) {
|
|
1985
2038
|
untracked(() => {
|
|
1986
2039
|
const tokenArray = tokens.split(' ');
|
|
1987
2040
|
if (!tokenArray.length)
|
|
@@ -1996,6 +2049,7 @@ const buildSignalEffects = (el, config) => {
|
|
|
1996
2049
|
const push = (tokens, signal) => {
|
|
1997
2050
|
if (has(tokens))
|
|
1998
2051
|
return;
|
|
2052
|
+
liveTokens.set(tokens, signal);
|
|
1999
2053
|
runInInjectionContext(injector, () => {
|
|
2000
2054
|
effects[tokens] = effect(() => {
|
|
2001
2055
|
const { currentElements } = untracked(() => elements());
|
|
@@ -2017,6 +2071,7 @@ const buildSignalEffects = (el, config) => {
|
|
|
2017
2071
|
const remove = (tokens) => {
|
|
2018
2072
|
effects[tokens]?.destroy();
|
|
2019
2073
|
delete effects[tokens];
|
|
2074
|
+
liveTokens.delete(tokens);
|
|
2020
2075
|
for (const el of elements().currentElements) {
|
|
2021
2076
|
const tokenArray = tokens.split(' ');
|
|
2022
2077
|
if (!tokenArray.length)
|
|
@@ -2160,12 +2215,25 @@ const isElementVisible = (options) => {
|
|
|
2160
2215
|
containerRect,
|
|
2161
2216
|
elementRect,
|
|
2162
2217
|
// Round the intersection ratio to the nearest 0.01 to avoid floating point errors and system scaling issues.
|
|
2163
|
-
intersectionRatio: Math.round(
|
|
2218
|
+
intersectionRatio: Math.round(inlineIntersectionPercentage * blockIntersectionPercentage * 100) / 100,
|
|
2164
2219
|
};
|
|
2165
2220
|
};
|
|
2221
|
+
const nearestAlignment = (elStart, elEnd, conStart, conEnd) => {
|
|
2222
|
+
const startOutside = elStart < conStart;
|
|
2223
|
+
const endOutside = elEnd > conEnd;
|
|
2224
|
+
const fits = elEnd - elStart <= conEnd - conStart;
|
|
2225
|
+
if (startOutside === endOutside)
|
|
2226
|
+
return null;
|
|
2227
|
+
if (startOutside === fits)
|
|
2228
|
+
return 'start';
|
|
2229
|
+
return 'end';
|
|
2230
|
+
};
|
|
2166
2231
|
const getElementScrollCoordinates = (options) => {
|
|
2167
2232
|
const { container, element, direction, behavior = 'smooth', origin = 'nearest', scrollBlockMargin = 0, scrollInlineMargin = 0, } = options;
|
|
2168
|
-
if (!element ||
|
|
2233
|
+
if (!element ||
|
|
2234
|
+
!container ||
|
|
2235
|
+
(container.isConnected && !container.contains(element)) ||
|
|
2236
|
+
!elementCanScroll(container)) {
|
|
2169
2237
|
return {
|
|
2170
2238
|
behavior,
|
|
2171
2239
|
left: undefined,
|
|
@@ -2193,33 +2261,33 @@ const getElementScrollCoordinates = (options) => {
|
|
|
2193
2261
|
let scrollTopTo = scrollTop;
|
|
2194
2262
|
const relativeTop = elTop - conTop;
|
|
2195
2263
|
const relativeLeft = elLeft - conLeft;
|
|
2264
|
+
const calculateInlineStart = () => (scrollLeftTo = scrollLeft + relativeLeft - scrollInlineMargin);
|
|
2265
|
+
const calculateBlockStart = () => (scrollTopTo = scrollTop + relativeTop - scrollBlockMargin);
|
|
2266
|
+
const calculateInlineEnd = () => (scrollLeftTo = scrollLeft + relativeLeft - conWidth + elWidth + scrollInlineMargin);
|
|
2267
|
+
const calculateBlockEnd = () => (scrollTopTo = scrollTop + relativeTop - conHeight + elHeight + scrollBlockMargin);
|
|
2196
2268
|
const calculateScrollToStart = () => {
|
|
2197
|
-
|
|
2198
|
-
|
|
2269
|
+
calculateInlineStart();
|
|
2270
|
+
calculateBlockStart();
|
|
2199
2271
|
};
|
|
2200
2272
|
const calculateScrollToEnd = () => {
|
|
2201
|
-
|
|
2202
|
-
|
|
2273
|
+
calculateInlineEnd();
|
|
2274
|
+
calculateBlockEnd();
|
|
2203
2275
|
};
|
|
2204
2276
|
const calculateScrollToCenter = () => {
|
|
2205
2277
|
scrollLeftTo = scrollLeft + relativeLeft - conWidth / 2 + elWidth / 2;
|
|
2206
2278
|
scrollTopTo = scrollTop + relativeTop - conHeight / 2 + elHeight / 2;
|
|
2207
2279
|
};
|
|
2208
2280
|
const calculateScrollToNearest = () => {
|
|
2209
|
-
const
|
|
2210
|
-
const
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
if (
|
|
2218
|
-
|
|
2219
|
-
}
|
|
2220
|
-
else if (isBelow || isPartialBelow || isRight || isPartialRight) {
|
|
2221
|
-
calculateScrollToEnd();
|
|
2222
|
-
}
|
|
2281
|
+
const inline = nearestAlignment(elLeft, elRight, conLeft, conRight);
|
|
2282
|
+
const block = nearestAlignment(elTop, elBottom, conTop, conBottom);
|
|
2283
|
+
if (inline === 'start')
|
|
2284
|
+
calculateInlineStart();
|
|
2285
|
+
else if (inline === 'end')
|
|
2286
|
+
calculateInlineEnd();
|
|
2287
|
+
if (block === 'start')
|
|
2288
|
+
calculateBlockStart();
|
|
2289
|
+
else if (block === 'end')
|
|
2290
|
+
calculateBlockEnd();
|
|
2223
2291
|
};
|
|
2224
2292
|
switch (origin) {
|
|
2225
2293
|
case 'start':
|
|
@@ -2265,6 +2333,8 @@ const getScrollSnapTarget = (items, container, direction, origin, margin = 0) =>
|
|
|
2265
2333
|
};
|
|
2266
2334
|
for (const item of items) {
|
|
2267
2335
|
const itemRect = item.getBoundingClientRect();
|
|
2336
|
+
if (!itemRect.width && !itemRect.height)
|
|
2337
|
+
continue;
|
|
2268
2338
|
const itemSize = direction === 'horizontal' ? itemRect.width : itemRect.height;
|
|
2269
2339
|
const relativeStart = direction === 'horizontal' ? itemRect.left - containerRect.left : itemRect.top - containerRect.top;
|
|
2270
2340
|
const relativeEnd = relativeStart + itemSize;
|
|
@@ -2403,8 +2473,10 @@ const createPositionObject = (entry) => {
|
|
|
2403
2473
|
isVisible,
|
|
2404
2474
|
};
|
|
2405
2475
|
};
|
|
2476
|
+
const unwrap = (value) => (isSignal(value) ? value() : value);
|
|
2477
|
+
const isZeroMargin = (margin) => !margin || /^(0(px|%)?\s*)+$/.test(margin.trim());
|
|
2406
2478
|
const signalElementIntersection = (el, options) => {
|
|
2407
|
-
const { root: rootBinding, enabled, ...observerOptions } = options ?? {};
|
|
2479
|
+
const { root: rootBinding, enabled, rootMargin, threshold, ...observerOptions } = options ?? {};
|
|
2408
2480
|
const destroyRef = inject(DestroyRef);
|
|
2409
2481
|
const elements = buildElementSignal(el);
|
|
2410
2482
|
const root = firstElementSignal(rootBinding ? buildElementSignal(rootBinding) : createEmptyElementSignal());
|
|
@@ -2416,6 +2488,7 @@ const signalElementIntersection = (el, options) => {
|
|
|
2416
2488
|
const observer = signal(null, /* @ts-ignore */
|
|
2417
2489
|
...(ngDevMode ? [{ debugName: "observer" }] : /* istanbul ignore next */ []));
|
|
2418
2490
|
const currentlyObservedElements = new Set();
|
|
2491
|
+
let observedRootMargin;
|
|
2419
2492
|
const updateIntersections = (entries) => {
|
|
2420
2493
|
let currentValues = [...elementIntersectionSignal()];
|
|
2421
2494
|
for (const entry of entries) {
|
|
@@ -2443,9 +2516,15 @@ const signalElementIntersection = (el, options) => {
|
|
|
2443
2516
|
currentValues = [...currentValues, intersectionEntry];
|
|
2444
2517
|
}
|
|
2445
2518
|
}
|
|
2519
|
+
const domOrder = untracked(elements).currentElements;
|
|
2520
|
+
const orderOf = (target) => {
|
|
2521
|
+
const index = domOrder.indexOf(target);
|
|
2522
|
+
return index === -1 ? domOrder.length : index;
|
|
2523
|
+
};
|
|
2524
|
+
currentValues.sort((a, b) => orderOf(a.target) - orderOf(b.target));
|
|
2446
2525
|
zone.run(() => elementIntersectionSignal.set(currentValues));
|
|
2447
2526
|
};
|
|
2448
|
-
const updateIntersectionObserver = (rendered, enabled, rootEl) => {
|
|
2527
|
+
const updateIntersectionObserver = (rendered, enabled, rootEl, margin, thresholdValue) => {
|
|
2449
2528
|
observer()?.disconnect();
|
|
2450
2529
|
currentlyObservedElements.clear();
|
|
2451
2530
|
if (!rendered || !enabled) {
|
|
@@ -2455,8 +2534,11 @@ const signalElementIntersection = (el, options) => {
|
|
|
2455
2534
|
}
|
|
2456
2535
|
const newObserver = new IntersectionObserver((entries) => updateIntersections(entries), {
|
|
2457
2536
|
...observerOptions,
|
|
2537
|
+
...(margin !== undefined && { rootMargin: margin }),
|
|
2538
|
+
...(thresholdValue !== undefined && { threshold: thresholdValue }),
|
|
2458
2539
|
root: rootEl,
|
|
2459
2540
|
});
|
|
2541
|
+
observedRootMargin = margin;
|
|
2460
2542
|
observer.set(newObserver);
|
|
2461
2543
|
};
|
|
2462
2544
|
const updateObservedElements = (observer, elements) => {
|
|
@@ -2469,15 +2551,16 @@ const signalElementIntersection = (el, options) => {
|
|
|
2469
2551
|
const newIntersectionValue = [];
|
|
2470
2552
|
for (const el of elements.currentElements) {
|
|
2471
2553
|
if (currentlyObservedElements.has(el)) {
|
|
2472
|
-
const
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
console.warn('Could not find existing entry for element. The intersection observer might be broken now.', el);
|
|
2476
|
-
continue;
|
|
2477
|
-
}
|
|
2478
|
-
newIntersectionValue.push(existingEntry);
|
|
2554
|
+
const existingEntry = currIntersectionValue.find((v) => v.target === el);
|
|
2555
|
+
if (existingEntry)
|
|
2556
|
+
newIntersectionValue.push(existingEntry);
|
|
2479
2557
|
continue;
|
|
2480
2558
|
}
|
|
2559
|
+
currentlyObservedElements.add(el);
|
|
2560
|
+
observer.observe(el);
|
|
2561
|
+
// isElementVisible ignores rootMargin, so with one set the observer's own first entry is the only correct one.
|
|
2562
|
+
if (!isZeroMargin(observedRootMargin))
|
|
2563
|
+
continue;
|
|
2481
2564
|
const initialElementVisibility = isElementVisible({
|
|
2482
2565
|
container: rootEl,
|
|
2483
2566
|
element: el,
|
|
@@ -2497,8 +2580,6 @@ const signalElementIntersection = (el, options) => {
|
|
|
2497
2580
|
...intersectionEntry,
|
|
2498
2581
|
...createPositionObject(intersectionEntry),
|
|
2499
2582
|
});
|
|
2500
|
-
currentlyObservedElements.add(el);
|
|
2501
|
-
observer.observe(el);
|
|
2502
2583
|
}
|
|
2503
2584
|
for (const el of elements.previousElements) {
|
|
2504
2585
|
if (elements.currentElements.includes(el))
|
|
@@ -2512,9 +2593,13 @@ const signalElementIntersection = (el, options) => {
|
|
|
2512
2593
|
const rootEl = root().currentElement;
|
|
2513
2594
|
const rendered = isRendered();
|
|
2514
2595
|
const enabled = isEnabled();
|
|
2515
|
-
|
|
2596
|
+
const margin = rootMargin === undefined ? undefined : unwrap(rootMargin);
|
|
2597
|
+
const thresholdValue = threshold === undefined ? undefined : unwrap(threshold);
|
|
2598
|
+
untracked(() => updateIntersectionObserver(rendered, enabled, rootEl, margin, thresholdValue));
|
|
2516
2599
|
});
|
|
2517
|
-
effect
|
|
2600
|
+
// The initial entries are measured here, not in an effect: an effect runs during change detection,
|
|
2601
|
+
// before elements rendered in the same pass have their layout.
|
|
2602
|
+
afterRenderEffect(() => {
|
|
2518
2603
|
const els = elements();
|
|
2519
2604
|
const obs = observer();
|
|
2520
2605
|
untracked(() => updateObservedElements(obs, els));
|
|
@@ -2540,6 +2625,8 @@ const signalElementLastScrollDirection = (el) => {
|
|
|
2540
2625
|
lastScrollLeft = 0;
|
|
2541
2626
|
return of(null);
|
|
2542
2627
|
}
|
|
2628
|
+
lastScrollTop = currentElement.scrollTop;
|
|
2629
|
+
lastScrollLeft = currentElement.scrollLeft;
|
|
2543
2630
|
return fromEvent(currentElement, 'scroll').pipe(tap(() => {
|
|
2544
2631
|
const { scrollTop, scrollLeft } = currentElement;
|
|
2545
2632
|
const time = Date.now();
|
|
@@ -2644,11 +2731,12 @@ const writeScrollbarSizeToCssVariables = () => {
|
|
|
2644
2731
|
if (!isPlatformBrowser(inject(PLATFORM_ID))) {
|
|
2645
2732
|
return;
|
|
2646
2733
|
}
|
|
2647
|
-
|
|
2648
|
-
|
|
2734
|
+
// The app's injector, not the caller's: a writer started from a component must outlive it.
|
|
2735
|
+
const appInjector = inject(ApplicationRef).injector;
|
|
2736
|
+
if (scrollbarSizeWriters.has(appInjector)) {
|
|
2649
2737
|
return;
|
|
2650
2738
|
}
|
|
2651
|
-
scrollbarSizeWriters.add(
|
|
2739
|
+
scrollbarSizeWriters.add(appInjector);
|
|
2652
2740
|
const document = inject(DOCUMENT);
|
|
2653
2741
|
const renderer = injectRenderer();
|
|
2654
2742
|
const scrollbarDimensions = injectScrollbarDimensions();
|
|
@@ -2660,7 +2748,7 @@ const writeScrollbarSizeToCssVariables = () => {
|
|
|
2660
2748
|
'--et-sw': `${dimensions.width}px`,
|
|
2661
2749
|
'--et-sh': `${dimensions.height}px`,
|
|
2662
2750
|
});
|
|
2663
|
-
});
|
|
2751
|
+
}, { injector: appInjector });
|
|
2664
2752
|
};
|
|
2665
2753
|
const viewportSizeWriters = /* @__PURE__ */ new WeakSet();
|
|
2666
2754
|
/**
|
|
@@ -2672,11 +2760,11 @@ const writeViewportSizeToCssVariables = () => {
|
|
|
2672
2760
|
if (!isPlatformBrowser(inject(PLATFORM_ID))) {
|
|
2673
2761
|
return;
|
|
2674
2762
|
}
|
|
2675
|
-
const
|
|
2676
|
-
if (viewportSizeWriters.has(
|
|
2763
|
+
const appInjector = inject(ApplicationRef).injector;
|
|
2764
|
+
if (viewportSizeWriters.has(appInjector)) {
|
|
2677
2765
|
return;
|
|
2678
2766
|
}
|
|
2679
|
-
viewportSizeWriters.add(
|
|
2767
|
+
viewportSizeWriters.add(appInjector);
|
|
2680
2768
|
const document = inject(DOCUMENT);
|
|
2681
2769
|
const renderer = injectRenderer();
|
|
2682
2770
|
const htmlElementDimensions = injectViewportDimensions();
|
|
@@ -2688,7 +2776,7 @@ const writeViewportSizeToCssVariables = () => {
|
|
|
2688
2776
|
'--et-vw': `${dimensions.width}px`,
|
|
2689
2777
|
'--et-vh': `${dimensions.height}px`,
|
|
2690
2778
|
});
|
|
2691
|
-
});
|
|
2779
|
+
}, { injector: appInjector });
|
|
2692
2780
|
};
|
|
2693
2781
|
|
|
2694
2782
|
/** The deadzone in pixels after which the cursor drag scroll will take effect. */
|
|
@@ -2876,7 +2964,11 @@ const createInitialRoute = () => {
|
|
|
2876
2964
|
if (!isBrowser) {
|
|
2877
2965
|
return router.url;
|
|
2878
2966
|
}
|
|
2879
|
-
return
|
|
2967
|
+
return readLocationPath(inject(Location));
|
|
2968
|
+
};
|
|
2969
|
+
const readLocationPath = (location) => {
|
|
2970
|
+
const path = location.path(true);
|
|
2971
|
+
return path.startsWith('/') ? path : `/${path}`;
|
|
2880
2972
|
};
|
|
2881
2973
|
/** Inject the current router event */
|
|
2882
2974
|
const injectRouterEvent = /* @__PURE__ */ memoizeSignal(() => {
|
|
@@ -2943,6 +3035,7 @@ const injectRoute = /* @__PURE__ */ memoizeSignal(() => {
|
|
|
2943
3035
|
return withoutFragment;
|
|
2944
3036
|
});
|
|
2945
3037
|
});
|
|
3038
|
+
const withInjector = (config, fn) => config?.injector ? runInInjectionContext(config.injector, fn) : fn();
|
|
2946
3039
|
const createRouterState = (router) => {
|
|
2947
3040
|
let route = router.routerState.snapshot.root;
|
|
2948
3041
|
while (route.firstChild) {
|
|
@@ -2960,14 +3053,20 @@ const createRouterState = (router) => {
|
|
|
2960
3053
|
/**
|
|
2961
3054
|
* Read the current route (the committed url without query params and fragment) straight from the router.
|
|
2962
3055
|
* Unlike the `injectRoute` signal this reads synchronously, so it can be used inside a `router.events` handler
|
|
2963
|
-
* where `router.url` is already committed.
|
|
3056
|
+
* where `router.url` is already committed. Before the first navigation commits it falls back to `location`
|
|
3057
|
+
* (without the base href) when given, else to the browser location.
|
|
2964
3058
|
*/
|
|
2965
|
-
const createRoute = (router) => {
|
|
2966
|
-
const
|
|
3059
|
+
const createRoute = (router, location) => {
|
|
3060
|
+
const url = router.navigated ? router.url : readUncommittedUrl(router, location);
|
|
3061
|
+
return url.split('?')[0]?.split('#')[0] ?? '';
|
|
3062
|
+
};
|
|
3063
|
+
const readUncommittedUrl = (router, location) => {
|
|
3064
|
+
if (location) {
|
|
3065
|
+
return readLocationPath(location);
|
|
3066
|
+
}
|
|
3067
|
+
return typeof window === 'undefined'
|
|
2967
3068
|
? router.url
|
|
2968
3069
|
: window.location.pathname + window.location.search + window.location.hash;
|
|
2969
|
-
const url = router.navigated ? router.url : browserUrl;
|
|
2970
|
-
return url.split('?')[0]?.split('#')[0] ?? '';
|
|
2971
3070
|
};
|
|
2972
3071
|
/**
|
|
2973
3072
|
* Inject the complete router state. This includes the current route data, path params, query params, title and fragment.
|
|
@@ -2982,37 +3081,35 @@ const injectRouterState = /* @__PURE__ */ memoizeSignal(() => {
|
|
|
2982
3081
|
});
|
|
2983
3082
|
/** Inject a signal containing the current route fragment (the part after the # inside the url if present) */
|
|
2984
3083
|
const injectFragment = (config) => {
|
|
2985
|
-
const routerState = config
|
|
2986
|
-
? runInInjectionContext(config.injector, injectRouterState)
|
|
2987
|
-
: injectRouterState();
|
|
3084
|
+
const routerState = withInjector(config, injectRouterState);
|
|
2988
3085
|
const fragment = computed(() => routerState().fragment, /* @ts-ignore */
|
|
2989
3086
|
...(ngDevMode ? [{ debugName: "fragment" }] : /* istanbul ignore next */ []));
|
|
2990
3087
|
return transformOrReturn(fragment, config);
|
|
2991
3088
|
};
|
|
2992
3089
|
/** Inject all currently available query parameters as a signal */
|
|
2993
|
-
const injectQueryParams = () => {
|
|
2994
|
-
const routerState = injectRouterState
|
|
3090
|
+
const injectQueryParams = (config) => {
|
|
3091
|
+
const routerState = withInjector(config, injectRouterState);
|
|
2995
3092
|
const queryParams = computed(() => routerState().queryParams, /* @ts-ignore */
|
|
2996
3093
|
...(ngDevMode ? [{ debugName: "queryParams" }] : /* istanbul ignore next */ []));
|
|
2997
3094
|
return queryParams;
|
|
2998
3095
|
};
|
|
2999
3096
|
/** Inject all currently available route data as a signal */
|
|
3000
|
-
const injectRouteData = () => {
|
|
3001
|
-
const routerState = injectRouterState
|
|
3097
|
+
const injectRouteData = (config) => {
|
|
3098
|
+
const routerState = withInjector(config, injectRouterState);
|
|
3002
3099
|
const data = computed(() => routerState().data, /* @ts-ignore */
|
|
3003
3100
|
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
3004
3101
|
return data;
|
|
3005
3102
|
};
|
|
3006
3103
|
/** Inject the current route title as a signal */
|
|
3007
3104
|
const injectRouteTitle = (config) => {
|
|
3008
|
-
const routerState = injectRouterState
|
|
3105
|
+
const routerState = withInjector(config, injectRouterState);
|
|
3009
3106
|
const title = computed(() => routerState().title, /* @ts-ignore */
|
|
3010
3107
|
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
3011
3108
|
return transformOrReturn(title, config);
|
|
3012
3109
|
};
|
|
3013
3110
|
/** Inject all currently available path parameters as a signal */
|
|
3014
|
-
const injectPathParams = () => {
|
|
3015
|
-
const routerState = injectRouterState
|
|
3111
|
+
const injectPathParams = (config) => {
|
|
3112
|
+
const routerState = withInjector(config, injectRouterState);
|
|
3016
3113
|
const pathParams = computed(() => routerState().pathParams, /* @ts-ignore */
|
|
3017
3114
|
...(ngDevMode ? [{ debugName: "pathParams" }] : /* istanbul ignore next */ []));
|
|
3018
3115
|
return pathParams;
|
|
@@ -3023,21 +3120,48 @@ const getQueryParamFromUrl = (key) => {
|
|
|
3023
3120
|
const urlParams = new URLSearchParams(window.location.search);
|
|
3024
3121
|
return urlParams.get(key);
|
|
3025
3122
|
};
|
|
3026
|
-
|
|
3123
|
+
const getAllQueryParamsFromUrl = (key) => {
|
|
3124
|
+
if (typeof window === 'undefined')
|
|
3125
|
+
return [];
|
|
3126
|
+
return new URLSearchParams(window.location.search).getAll(key);
|
|
3127
|
+
};
|
|
3128
|
+
const toValues = (value) => {
|
|
3129
|
+
if (value === undefined || value === null)
|
|
3130
|
+
return [];
|
|
3131
|
+
return Array.isArray(value) ? value : [value];
|
|
3132
|
+
};
|
|
3133
|
+
/**
|
|
3134
|
+
* Inject a specific query parameter as a signal. A repeated parameter (`?tag=a&tag=b`) resolves to its first value;
|
|
3135
|
+
* use `injectQueryParamAll` to read every value.
|
|
3136
|
+
*/
|
|
3027
3137
|
const injectQueryParam = (key, config) => {
|
|
3028
|
-
const queryParams = injectQueryParams();
|
|
3029
|
-
const src = computed(() =>
|
|
3138
|
+
const queryParams = injectQueryParams(config);
|
|
3139
|
+
const src = computed(() => {
|
|
3140
|
+
const first = toValues(queryParams()[key])[0];
|
|
3141
|
+
return first ?? (config?.requireSync ? getQueryParamFromUrl(key) : null);
|
|
3142
|
+
}, /* @ts-ignore */
|
|
3143
|
+
...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
3144
|
+
return transformOrReturn(src, config);
|
|
3145
|
+
};
|
|
3146
|
+
/** Inject every value of a specific query parameter as a signal. Resolves to an empty array when it is absent. */
|
|
3147
|
+
const injectQueryParamAll = (key, config) => {
|
|
3148
|
+
const queryParams = injectQueryParams(config);
|
|
3149
|
+
const src = computed(() => {
|
|
3150
|
+
const values = toValues(queryParams()[key]);
|
|
3151
|
+
return values.length || !config?.requireSync ? values : getAllQueryParamsFromUrl(key);
|
|
3152
|
+
}, /* @ts-ignore */
|
|
3153
|
+
...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
3030
3154
|
return transformOrReturn(src, config);
|
|
3031
3155
|
};
|
|
3032
3156
|
/** Inject a specific route data item as a signal */
|
|
3033
3157
|
const injectRouteDataItem = (key, config) => {
|
|
3034
|
-
const data = injectRouteData();
|
|
3158
|
+
const data = injectRouteData(config);
|
|
3035
3159
|
const src = computed(() => data()[key] ?? null);
|
|
3036
3160
|
return transformOrReturn(src, config);
|
|
3037
3161
|
};
|
|
3038
3162
|
/** Inject a specific path parameter as a signal */
|
|
3039
3163
|
const injectPathParam = (key, config) => {
|
|
3040
|
-
const pathParams = injectPathParams();
|
|
3164
|
+
const pathParams = injectPathParams(config);
|
|
3041
3165
|
const src = computed(() => pathParams()[key] ?? null);
|
|
3042
3166
|
return transformOrReturn(src, config);
|
|
3043
3167
|
};
|
|
@@ -3110,9 +3234,9 @@ const injectPathParamChanges = /* @__PURE__ */ memoizeSignal(() => {
|
|
|
3110
3234
|
* protected cameFromCart = this.navState?.fromCart ?? false;
|
|
3111
3235
|
* }
|
|
3112
3236
|
*/
|
|
3113
|
-
const injectRouterNavigationState = () => {
|
|
3114
|
-
const router = inject(Router);
|
|
3115
|
-
return router.
|
|
3237
|
+
const injectRouterNavigationState = (config) => {
|
|
3238
|
+
const router = withInjector(config, () => inject(Router));
|
|
3239
|
+
return router.currentNavigation()?.extras.state ?? null;
|
|
3116
3240
|
};
|
|
3117
3241
|
|
|
3118
3242
|
const ET_DISABLE_SCROLL_TOP = /* @__PURE__ */ Symbol('ET_DISABLE_SCROLL_TOP');
|
|
@@ -3201,6 +3325,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3201
3325
|
return;
|
|
3202
3326
|
}
|
|
3203
3327
|
const router = inject(Router);
|
|
3328
|
+
const location = inject(Location);
|
|
3204
3329
|
const document = inject(DOCUMENT);
|
|
3205
3330
|
const injector = inject(Injector);
|
|
3206
3331
|
const destroyRef = inject(DestroyRef);
|
|
@@ -3229,7 +3354,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3229
3354
|
let wantsRestore = false;
|
|
3230
3355
|
let prev = {
|
|
3231
3356
|
state: createRouterState(router),
|
|
3232
|
-
route: createRoute(router),
|
|
3357
|
+
route: createRoute(router, location),
|
|
3233
3358
|
routeConfig: getDeepestRouteConfig(router),
|
|
3234
3359
|
};
|
|
3235
3360
|
let pendingRestore = null;
|
|
@@ -3341,13 +3466,13 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3341
3466
|
// Read off the navigation rather than `history.state`: a symbol key does not survive being
|
|
3342
3467
|
// structured-cloned into the history entry, which is what keeps the mark from outliving this
|
|
3343
3468
|
// navigation and re-firing when the user later pops back to the entry it created.
|
|
3344
|
-
const navigationState = router.
|
|
3469
|
+
const navigationState = router.currentNavigation()?.extras.state;
|
|
3345
3470
|
wantsRestore = !isPopstate && navigationState?.[ET_RESTORE_SCROLL] === true;
|
|
3346
3471
|
};
|
|
3347
3472
|
const onNavigationEnd = (event) => {
|
|
3348
3473
|
const curr = {
|
|
3349
3474
|
state: createRouterState(router),
|
|
3350
|
-
route: createRoute(router),
|
|
3475
|
+
route: createRoute(router, location),
|
|
3351
3476
|
routeConfig: getDeepestRouteConfig(router),
|
|
3352
3477
|
};
|
|
3353
3478
|
const prevState = prev.state;
|
|
@@ -3434,7 +3559,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3434
3559
|
const DISABLE_LOGGER_PARAM = 'et-logger-quiet';
|
|
3435
3560
|
const createLogger = (config) => {
|
|
3436
3561
|
const { scope, feature } = config;
|
|
3437
|
-
const disableLogging = injectQueryParam(DISABLE_LOGGER_PARAM);
|
|
3562
|
+
const disableLogging = injectQueryParam(DISABLE_LOGGER_PARAM, { transform: (value) => value !== null });
|
|
3438
3563
|
return {
|
|
3439
3564
|
log: (...args) => {
|
|
3440
3565
|
if (!disableLogging())
|
|
@@ -3465,9 +3590,10 @@ const escapeHtmlPreservingEntities = (str) => str
|
|
|
3465
3590
|
* text only. */
|
|
3466
3591
|
const MARKDOWN_VERBATIM_ATTR = 'data-markdown-verbatim';
|
|
3467
3592
|
const VERBATIM_RE = /* @__PURE__ */ new RegExp(`<span\\b[^>]*\\b${MARKDOWN_VERBATIM_ATTR}\\b[^>]*>([^<]*)<\\/span>`, 'gi');
|
|
3593
|
+
const codePointToString = (codePoint) => (codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : '\ufffd');
|
|
3468
3594
|
const decodeUrlCharacterReferences = (url) => url
|
|
3469
|
-
.replace(/&#(\d+);?/g, (_, value) =>
|
|
3470
|
-
.replace(/&#x([0-9a-f]+);?/gi, (_, value) =>
|
|
3595
|
+
.replace(/&#(\d+);?/g, (_, value) => codePointToString(Number(value)))
|
|
3596
|
+
.replace(/&#x([0-9a-f]+);?/gi, (_, value) => codePointToString(Number.parseInt(value, 16)))
|
|
3471
3597
|
.replace(/&(colon|tab|newline);/gi, (_, entity) => entity.toLowerCase() === 'colon' ? ':' : entity.toLowerCase() === 'tab' ? '\t' : '\n');
|
|
3472
3598
|
/** Whether `url` may be embedded as an image `src`: everything except `javascript:` and `vbscript:`
|
|
3473
3599
|
* URLs and non-image `data:` URLs, however they are cased, spaced or entity-encoded. */
|
|
@@ -3507,7 +3633,13 @@ const parseSeparatorAligns = (line) => parseTableRow(line).map((cell) => {
|
|
|
3507
3633
|
const separatorFor = (align) => align === 'center' ? ':---:' : align === 'right' ? '---:' : align === 'left' ? ':---' : '---';
|
|
3508
3634
|
const alignClass = (align) => (align ? ` class="et-rte-align-${align}"` : '');
|
|
3509
3635
|
const alignOf = (attrs) => (/\bclass\s*=\s*["'][^"']*\bet-rte-align-([a-z]+)/i.exec(attrs)?.[1] ??
|
|
3510
|
-
/\bstyle\s*=\s*["'][^"']*text-align:\s*([a-z]+)/i.exec(attrs)?.[1])?.toLowerCase() ?? null;
|
|
3636
|
+
/\bstyle\s*=\s*["'][^"']*text-align:\s*(?:-[a-z]+-)?([a-z]+)/i.exec(attrs)?.[1])?.toLowerCase() ?? null;
|
|
3637
|
+
const BLOCK_ALIGNS = /* @__PURE__ */ new Set(['center', 'right', 'justify']);
|
|
3638
|
+
const blockAlignOf = (attrs) => {
|
|
3639
|
+
const align = alignOf(attrs);
|
|
3640
|
+
return align && BLOCK_ALIGNS.has(align) ? align : null;
|
|
3641
|
+
};
|
|
3642
|
+
const isBlankHtml = (html) => !/<img\b/i.test(html) && !stripTags(html.replace(/ /gi, ' ')).trim();
|
|
3511
3643
|
/** Wraps `content` in an emphasis `marker`, hoisting boundary whitespace outside the delimiters -
|
|
3512
3644
|
* CommonMark emphasis must not face whitespace on the inside (`** fett**` doesn't parse), and
|
|
3513
3645
|
* ` ` counts as whitespace there too. Whitespace-only content stays unwrapped. */
|
|
@@ -3584,6 +3716,10 @@ const processInline = (text) => {
|
|
|
3584
3716
|
.replace(placeholderRe('IC'), (match, i) => inlineCodes[+i] ?? match)
|
|
3585
3717
|
.replace(placeholderRe('ESC'), (match, i) => escapes[+i] ?? match);
|
|
3586
3718
|
};
|
|
3719
|
+
const parseStartNumber = (openTag) => {
|
|
3720
|
+
const value = /\bstart\s*=\s*["']?(-?\d+)/i.exec(openTag)?.[1];
|
|
3721
|
+
return value === undefined ? 1 : Number(value);
|
|
3722
|
+
};
|
|
3587
3723
|
/** The first top-level `<ul>`/`<ol>` in `html`, matched with balanced nesting. */
|
|
3588
3724
|
const findList = (html) => {
|
|
3589
3725
|
const open = /<(ul|ol)\b[^>]*>/i.exec(html);
|
|
@@ -3602,6 +3738,7 @@ const findList = (html) => {
|
|
|
3602
3738
|
start: open.index,
|
|
3603
3739
|
end: tag.lastIndex,
|
|
3604
3740
|
ordered: openTagName.toLowerCase() === 'ol',
|
|
3741
|
+
startNumber: parseStartNumber(open[0]),
|
|
3605
3742
|
inner: html.slice(innerStart, match.index),
|
|
3606
3743
|
};
|
|
3607
3744
|
}
|
|
@@ -3701,20 +3838,24 @@ const buildBlockquoteHtml = (lines) => {
|
|
|
3701
3838
|
return `<blockquote>${html || '<br>'}</blockquote>`;
|
|
3702
3839
|
};
|
|
3703
3840
|
/** Serializes a list's inner HTML to Markdown, indenting nested lists two spaces per level. */
|
|
3704
|
-
const listToMarkdown = (inner, ordered, depth) => {
|
|
3841
|
+
const listToMarkdown = (inner, ordered, startNumber, depth) => {
|
|
3705
3842
|
const indent = ' '.repeat(depth);
|
|
3706
|
-
let n =
|
|
3843
|
+
let n = startNumber;
|
|
3707
3844
|
return findListItems(inner)
|
|
3708
|
-
.
|
|
3845
|
+
.flatMap((itemInner) => {
|
|
3709
3846
|
let content = itemInner;
|
|
3710
3847
|
let nestedMarkdown = '';
|
|
3711
3848
|
for (let nested = findList(content); nested; nested = findList(content)) {
|
|
3712
|
-
|
|
3849
|
+
const nestedItems = listToMarkdown(nested.inner, nested.ordered, nested.startNumber, depth + 1);
|
|
3850
|
+
if (nestedItems)
|
|
3851
|
+
nestedMarkdown += `\n${nestedItems}`;
|
|
3713
3852
|
content = content.slice(0, nested.start) + content.slice(nested.end);
|
|
3714
3853
|
}
|
|
3854
|
+
if (!nestedMarkdown && isBlankHtml(content))
|
|
3855
|
+
return [];
|
|
3715
3856
|
const marker = ordered ? `${n++}. ` : '- ';
|
|
3716
3857
|
// list items are single-line in this serializer, so a <br> degrades to a space
|
|
3717
|
-
return `${indent}${marker}${stripTags(content.replace(/<br\s*\/?>/gi, ' ')).trim()}${nestedMarkdown}
|
|
3858
|
+
return [`${indent}${marker}${stripTags(content.replace(/<br\s*\/?>/gi, ' ')).trim()}${nestedMarkdown}`];
|
|
3718
3859
|
})
|
|
3719
3860
|
.join('\n');
|
|
3720
3861
|
};
|
|
@@ -3723,7 +3864,12 @@ const parseListLines = (lines) => lines
|
|
|
3723
3864
|
.map((line) => {
|
|
3724
3865
|
const match = /^(\s*)([-*+]|\d+\.)\s+(.*)$/.exec(line);
|
|
3725
3866
|
return match
|
|
3726
|
-
? {
|
|
3867
|
+
? {
|
|
3868
|
+
indent: Math.floor((match[1] ?? '').length / 2),
|
|
3869
|
+
ordered: /\d/.test(match[2] ?? ''),
|
|
3870
|
+
number: Number.parseInt(match[2] ?? '', 10),
|
|
3871
|
+
text: match[3] ?? '',
|
|
3872
|
+
}
|
|
3727
3873
|
: null;
|
|
3728
3874
|
})
|
|
3729
3875
|
.filter((line) => line !== null);
|
|
@@ -3744,7 +3890,9 @@ const buildListHtml = (lines, start, baseIndent) => {
|
|
|
3744
3890
|
items += `<li>${item}</li>`;
|
|
3745
3891
|
}
|
|
3746
3892
|
const tag = ordered ? 'ol' : 'ul';
|
|
3747
|
-
|
|
3893
|
+
const startNumber = ordered ? (lines[start]?.number ?? 1) : 1;
|
|
3894
|
+
const startAttr = startNumber === 1 ? '' : ` start="${startNumber}"`;
|
|
3895
|
+
return { html: `<${tag}${startAttr}>${items}</${tag}>`, next: i };
|
|
3748
3896
|
};
|
|
3749
3897
|
const escapeMarkdownText = (text) => text
|
|
3750
3898
|
.replace(/[\\`[\]]/g, '\\$&')
|
|
@@ -3816,7 +3964,7 @@ const markdownToHtml = (markdown, options = {}) => {
|
|
|
3816
3964
|
const align = aligned ? alignOf(aligned[2] ?? '') : null;
|
|
3817
3965
|
if (aligned && align) {
|
|
3818
3966
|
const tag = (aligned[1] ?? 'p').toLowerCase();
|
|
3819
|
-
return `<${tag}${alignClass(align)}>${sanitizeInlineHtml(aligned[3] ?? '')}</${tag}>`;
|
|
3967
|
+
return `<${tag}${alignClass(BLOCK_ALIGNS.has(align) ? align : null)}>${sanitizeInlineHtml(aligned[3] ?? '')}</${tag}>`;
|
|
3820
3968
|
}
|
|
3821
3969
|
// Heading
|
|
3822
3970
|
const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
|
|
@@ -3886,9 +4034,12 @@ const htmlToMarkdown = (html) => {
|
|
|
3886
4034
|
// stays HTML) and round-trip via a placeholder - extracted before the block passes below rewrite
|
|
3887
4035
|
// them, restored after the final tag-strip.
|
|
3888
4036
|
const alignedBlocks = [];
|
|
3889
|
-
md = md.replace(/<(p|h[1-6]|div)\b([^>]*\b(?:style="[^"]*text-align|class="[^"]*\bet-rte-align-)[^>]*)>([\s\S]*?)<\/\1>/gi, (
|
|
4037
|
+
md = md.replace(/<(p|h[1-6]|div)\b([^>]*\b(?:style="[^"]*text-align|class="[^"]*\bet-rte-align-)[^>]*)>([\s\S]*?)<\/\1>/gi, (block, tag, attrs, inner) => {
|
|
4038
|
+
const align = blockAlignOf(attrs);
|
|
4039
|
+
if (!align)
|
|
4040
|
+
return block.replace(/^<[a-z0-9]+\b[^>]*>/i, `<${tag}>`);
|
|
3890
4041
|
const name = tag.toLowerCase();
|
|
3891
|
-
return makePlaceholder('ALIGN', alignedBlocks.push(`<${name}${alignClass(
|
|
4042
|
+
return makePlaceholder('ALIGN', alignedBlocks.push(`<${name}${alignClass(align)}>${inner}</${name}>`) - 1);
|
|
3892
4043
|
});
|
|
3893
4044
|
md = escapeTextNodes(md);
|
|
3894
4045
|
// Code blocks - process before inline code
|
|
@@ -3898,7 +4049,7 @@ const htmlToMarkdown = (html) => {
|
|
|
3898
4049
|
// Headings - keep the inner markup so the inline passes below turn any nested
|
|
3899
4050
|
// bold/italic/link/code into markdown; leftover tags are stripped at the end.
|
|
3900
4051
|
for (let i = 6; i >= 1; i--) {
|
|
3901
|
-
md = md.replace(new RegExp(`<h${i}[^>]*>([\\s\\S]*?)<\\/h${i}>`, 'gi'), (_, content) => `\n${'#'.repeat(i)} ${content.trim()}\n`);
|
|
4052
|
+
md = md.replace(new RegExp(`<h${i}[^>]*>([\\s\\S]*?)<\\/h${i}>`, 'gi'), (_, content) => isBlankHtml(content) ? '\n' : `\n${'#'.repeat(i)} ${content.trim()}\n`);
|
|
3902
4053
|
}
|
|
3903
4054
|
// Bold + italic (combined before individual). Boundary whitespace is hoisted out of the
|
|
3904
4055
|
// delimiters (emphasize) - `<strong> x</strong>` must become ` **x**`, not the invalid `** x**`.
|
|
@@ -3938,7 +4089,7 @@ const htmlToMarkdown = (html) => {
|
|
|
3938
4089
|
}
|
|
3939
4090
|
// Lists - replace each top-level list with its recursively-serialized Markdown (handles nesting).
|
|
3940
4091
|
for (let list = findList(md); list; list = findList(md)) {
|
|
3941
|
-
md = `${md.slice(0, list.start)}\n${listToMarkdown(list.inner, list.ordered, 0)}\n${md.slice(list.end)}`;
|
|
4092
|
+
md = `${md.slice(0, list.start)}\n${listToMarkdown(list.inner, list.ordered, list.startNumber, 0)}\n${md.slice(list.end)}`;
|
|
3942
4093
|
}
|
|
3943
4094
|
// Tables
|
|
3944
4095
|
md = md.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, tableContent) => {
|
|
@@ -4536,6 +4687,22 @@ const createUserConsentProvider = (options) => ({
|
|
|
4536
4687
|
}),
|
|
4537
4688
|
});
|
|
4538
4689
|
|
|
4690
|
+
/**
|
|
4691
|
+
* Declares the `et-visually-hidden` class as a styles-only component: content that stays in the
|
|
4692
|
+
* accessibility tree but is clipped out of the layout. Mount it from any component whose template
|
|
4693
|
+
* uses the class.
|
|
4694
|
+
*/
|
|
4695
|
+
class VisuallyHiddenStylesComponent {
|
|
4696
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: VisuallyHiddenStylesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4697
|
+
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 }); }
|
|
4698
|
+
}
|
|
4699
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: VisuallyHiddenStylesComponent, decorators: [{
|
|
4700
|
+
type: Component,
|
|
4701
|
+
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"] }]
|
|
4702
|
+
}] });
|
|
4703
|
+
/** Mounts {@link VisuallyHiddenStylesComponent}. Call from an injection context. */
|
|
4704
|
+
const mountVisuallyHidden = () => injectStyleManager().mount(VisuallyHiddenStylesComponent);
|
|
4705
|
+
|
|
4539
4706
|
/**
|
|
4540
4707
|
* Declares the `--ease-*` custom properties that every animated stylesheet in the SDK
|
|
4541
4708
|
* transitions with, as a styles-only component. Mount it from any component or directive whose
|
|
@@ -4670,6 +4837,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
4670
4837
|
}]
|
|
4671
4838
|
}], ctorParameters: () => [] });
|
|
4672
4839
|
|
|
4840
|
+
// codes 9000-9099
|
|
4841
|
+
const ANIMATION_ERROR_CODES = {
|
|
4842
|
+
/** `*etAnimatedIf` was used without an `[etAnimatedLifecycle]` element around it. */
|
|
4843
|
+
MISSING_ANIMATED_LIFECYCLE: 9000,
|
|
4844
|
+
};
|
|
4845
|
+
|
|
4673
4846
|
/** Runs `cb` two frames from now. Returns a function that cancels it if it has not run yet. */
|
|
4674
4847
|
const nextFrame = (cb) => {
|
|
4675
4848
|
if (typeof requestAnimationFrame === 'undefined') {
|
|
@@ -4716,6 +4889,7 @@ const ANIMATION_CLASSES = {
|
|
|
4716
4889
|
leaveDone: 'et-animation-leave-done',
|
|
4717
4890
|
leaveInterrupt: 'et-animation-leave-interrupt',
|
|
4718
4891
|
};
|
|
4892
|
+
const ALL_ANIMATION_CLASSES = /* @__PURE__ */ Object.values(ANIMATION_CLASSES);
|
|
4719
4893
|
const FORCE_INVISIBLE_CLASS = 'et-force-invisible';
|
|
4720
4894
|
class AnimatedLifecycleDirective {
|
|
4721
4895
|
constructor() {
|
|
@@ -4729,7 +4903,12 @@ class AnimatedLifecycleDirective {
|
|
|
4729
4903
|
this.transitionIdCounter = 0;
|
|
4730
4904
|
this.forcedThisFrame = false;
|
|
4731
4905
|
this.forcedFrameResetId = null;
|
|
4732
|
-
this.
|
|
4906
|
+
this.stateSubject$ = new BehaviorSubject('init');
|
|
4907
|
+
this._state = signal('init', /* @ts-ignore */
|
|
4908
|
+
...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
|
|
4909
|
+
this.state = this._state.asReadonly();
|
|
4910
|
+
/** Emits the current lifecycle state synchronously on subscribe, then every change. */
|
|
4911
|
+
this.state$ = this.stateSubject$.asObservable();
|
|
4733
4912
|
this.stateChange = outputFromObservable(this.state$);
|
|
4734
4913
|
this.skipNextEnter = model(false, /* @ts-ignore */
|
|
4735
4914
|
...(ngDevMode ? [{ debugName: "skipNextEnter" }] : /* istanbul ignore next */ []));
|
|
@@ -4752,14 +4931,14 @@ class AnimatedLifecycleDirective {
|
|
|
4752
4931
|
this.isConstructed = true;
|
|
4753
4932
|
}
|
|
4754
4933
|
enter() {
|
|
4755
|
-
const currentState = this.state
|
|
4934
|
+
const currentState = this.state();
|
|
4756
4935
|
if (currentState === 'entering')
|
|
4757
4936
|
return;
|
|
4758
4937
|
if ((currentState === 'init' && !this.isConstructed) || this.skipNextEnter()) {
|
|
4759
4938
|
this.debugLog('enter: instant (not constructed or skipNextEnter)', { currentState });
|
|
4760
|
-
this.
|
|
4939
|
+
this.cancelCurrentAnimation$.next();
|
|
4940
|
+
this.settleInstantly('entered');
|
|
4761
4941
|
this.skipNextEnter.set(false);
|
|
4762
|
-
this.addClass(ANIMATION_CLASSES.enterDone);
|
|
4763
4942
|
this.forcedThisFrame = false;
|
|
4764
4943
|
return;
|
|
4765
4944
|
}
|
|
@@ -4777,9 +4956,7 @@ class AnimatedLifecycleDirective {
|
|
|
4777
4956
|
transitionId,
|
|
4778
4957
|
});
|
|
4779
4958
|
if (skipAnimation) {
|
|
4780
|
-
this.
|
|
4781
|
-
this.updateState('entered');
|
|
4782
|
-
this.addClass(ANIMATION_CLASSES.enterDone);
|
|
4959
|
+
this.settleInstantly('entered');
|
|
4783
4960
|
previousCancel$.next();
|
|
4784
4961
|
previousCancel$.complete();
|
|
4785
4962
|
return;
|
|
@@ -4822,13 +4999,12 @@ class AnimatedLifecycleDirective {
|
|
|
4822
4999
|
previousCancel$.complete();
|
|
4823
5000
|
}
|
|
4824
5001
|
leave() {
|
|
4825
|
-
const currentState = this.state
|
|
5002
|
+
const currentState = this.state();
|
|
4826
5003
|
if (currentState === 'leaving')
|
|
4827
5004
|
return;
|
|
4828
5005
|
if (currentState === 'init') {
|
|
4829
5006
|
this.debugLog('leave: instant (state is init, enter never ran)');
|
|
4830
|
-
this.
|
|
4831
|
-
this.addClass(ANIMATION_CLASSES.leaveDone);
|
|
5007
|
+
this.settleInstantly('left');
|
|
4832
5008
|
this.forcedThisFrame = false;
|
|
4833
5009
|
return;
|
|
4834
5010
|
}
|
|
@@ -4846,9 +5022,7 @@ class AnimatedLifecycleDirective {
|
|
|
4846
5022
|
transitionId,
|
|
4847
5023
|
});
|
|
4848
5024
|
if (skipAnimation) {
|
|
4849
|
-
this.
|
|
4850
|
-
this.updateState('left');
|
|
4851
|
-
this.addClass(ANIMATION_CLASSES.leaveDone);
|
|
5025
|
+
this.settleInstantly('left');
|
|
4852
5026
|
previousCancel$.next();
|
|
4853
5027
|
previousCancel$.complete();
|
|
4854
5028
|
return;
|
|
@@ -4892,18 +5066,20 @@ class AnimatedLifecycleDirective {
|
|
|
4892
5066
|
}
|
|
4893
5067
|
forceEnteredState() {
|
|
4894
5068
|
this.cancelCurrentAnimation$.next();
|
|
4895
|
-
this.
|
|
4896
|
-
this.removeClasses(ANIMATION_CLASSES.enterFrom, ANIMATION_CLASSES.enterActive, ANIMATION_CLASSES.enterTo, ANIMATION_CLASSES.leaveFrom, ANIMATION_CLASSES.leaveActive, ANIMATION_CLASSES.leaveTo);
|
|
4897
|
-
this.addClass(ANIMATION_CLASSES.enterDone);
|
|
5069
|
+
this.settleInstantly('entered');
|
|
4898
5070
|
this.markForcedThisFrame();
|
|
4899
5071
|
}
|
|
4900
5072
|
forceLeftState() {
|
|
4901
5073
|
this.cancelCurrentAnimation$.next();
|
|
4902
|
-
this.
|
|
4903
|
-
this.removeClasses(ANIMATION_CLASSES.enterFrom, ANIMATION_CLASSES.enterActive, ANIMATION_CLASSES.enterTo, ANIMATION_CLASSES.leaveFrom, ANIMATION_CLASSES.leaveActive, ANIMATION_CLASSES.leaveTo);
|
|
4904
|
-
this.addClass(ANIMATION_CLASSES.leaveDone);
|
|
5074
|
+
this.settleInstantly('left');
|
|
4905
5075
|
this.markForcedThisFrame();
|
|
4906
5076
|
}
|
|
5077
|
+
settleInstantly(state) {
|
|
5078
|
+
const doneClass = state === 'entered' ? ANIMATION_CLASSES.enterDone : ANIMATION_CLASSES.leaveDone;
|
|
5079
|
+
this.updateState(state);
|
|
5080
|
+
this.removeClasses(...ALL_ANIMATION_CLASSES.filter((cls) => cls !== doneClass));
|
|
5081
|
+
this.addClass(doneClass);
|
|
5082
|
+
}
|
|
4907
5083
|
markForcedThisFrame() {
|
|
4908
5084
|
this.forcedThisFrame = true;
|
|
4909
5085
|
if (typeof requestAnimationFrame === 'undefined')
|
|
@@ -4923,11 +5099,11 @@ class AnimatedLifecycleDirective {
|
|
|
4923
5099
|
this.addClass(activeClass);
|
|
4924
5100
|
fromNextFrame()
|
|
4925
5101
|
.pipe(tap(() => {
|
|
4926
|
-
if (this.state
|
|
5102
|
+
if (this.state() === expectedState) {
|
|
4927
5103
|
this.removeClass(fromClass);
|
|
4928
5104
|
this.addClass(toClass);
|
|
4929
5105
|
}
|
|
4930
|
-
}), switchMap(() => this.whenAnimationsSettled()), tap(() => this.debugLog(`${transitionId}: settled`)), filter(() => this.state
|
|
5106
|
+
}), switchMap(() => this.whenAnimationsSettled()), tap(() => this.debugLog(`${transitionId}: settled`)), filter(() => this.state() === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
|
|
4931
5107
|
.subscribe();
|
|
4932
5108
|
}
|
|
4933
5109
|
handleInterruptedTransition(config) {
|
|
@@ -4935,7 +5111,7 @@ class AnimatedLifecycleDirective {
|
|
|
4935
5111
|
this.removeClasses(...removeClasses);
|
|
4936
5112
|
addClasses.forEach((cls) => this.addClass(cls));
|
|
4937
5113
|
this.whenAnimationsSettled()
|
|
4938
|
-
.pipe(tap(() => this.debugLog(`${transitionId}: interrupt settled`)), filter(() => this.state
|
|
5114
|
+
.pipe(tap(() => this.debugLog(`${transitionId}: interrupt settled`)), filter(() => this.state() === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
|
|
4939
5115
|
.subscribe();
|
|
4940
5116
|
}
|
|
4941
5117
|
/**
|
|
@@ -4970,8 +5146,9 @@ class AnimatedLifecycleDirective {
|
|
|
4970
5146
|
this.renderer.removeClass(this.element, ...classNames);
|
|
4971
5147
|
}
|
|
4972
5148
|
updateState(newState) {
|
|
4973
|
-
this.debugLog(`state: ${this.state
|
|
4974
|
-
this.
|
|
5149
|
+
this.debugLog(`state: ${this.state()} → ${newState}`);
|
|
5150
|
+
this._state.set(newState);
|
|
5151
|
+
this.stateSubject$.next(newState);
|
|
4975
5152
|
}
|
|
4976
5153
|
debugLog(message, data) {
|
|
4977
5154
|
const el = this.element;
|
|
@@ -5011,27 +5188,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
5011
5188
|
|
|
5012
5189
|
const ANIMATED_IF_TOKEN = new InjectionToken('ANIMATED_IF_TOKEN');
|
|
5013
5190
|
class AnimatedIfDirective {
|
|
5191
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
5192
|
+
return true;
|
|
5193
|
+
}
|
|
5014
5194
|
constructor() {
|
|
5015
5195
|
this.templateRef = inject(TemplateRef);
|
|
5016
5196
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
5017
|
-
this.animatedLifecycle = inject(ANIMATED_LIFECYCLE_TOKEN);
|
|
5197
|
+
this.animatedLifecycle = inject(ANIMATED_LIFECYCLE_TOKEN, { optional: true });
|
|
5018
5198
|
this.hasView = false;
|
|
5199
|
+
this.context = { $implicit: null, etAnimatedIf: null };
|
|
5019
5200
|
this.ifValue = input(null, { ...(ngDevMode ? { debugName: "ifValue" } : /* istanbul ignore next */ {}), alias: 'etAnimatedIf' });
|
|
5201
|
+
const animatedLifecycle = this.animatedLifecycle;
|
|
5202
|
+
if (!animatedLifecycle) {
|
|
5203
|
+
throw new RuntimeError(ANIMATION_ERROR_CODES.MISSING_ANIMATED_LIFECYCLE, '*etAnimatedIf needs an element with [etAnimatedLifecycle] around it to animate against.');
|
|
5204
|
+
}
|
|
5020
5205
|
toObservable(this.ifValue)
|
|
5021
5206
|
.pipe(switchMap((value) => {
|
|
5022
5207
|
if (value) {
|
|
5208
|
+
this.context.$implicit = value;
|
|
5209
|
+
this.context.etAnimatedIf = value;
|
|
5023
5210
|
if (!this.hasView) {
|
|
5024
|
-
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
|
5211
|
+
this.viewContainerRef.createEmbeddedView(this.templateRef, this.context);
|
|
5025
5212
|
this.hasView = true;
|
|
5026
5213
|
}
|
|
5027
|
-
|
|
5214
|
+
if (animatedLifecycle.state() !== 'entered')
|
|
5215
|
+
animatedLifecycle.enter();
|
|
5028
5216
|
return of(null);
|
|
5029
5217
|
}
|
|
5030
5218
|
if (!this.hasView) {
|
|
5031
5219
|
return of(null);
|
|
5032
5220
|
}
|
|
5033
|
-
|
|
5034
|
-
return
|
|
5221
|
+
animatedLifecycle.leave();
|
|
5222
|
+
return animatedLifecycle.state$.pipe(filter((state) => state === 'left'), take(1), tap(() => {
|
|
5035
5223
|
this.viewContainerRef.clear();
|
|
5036
5224
|
this.hasView = false;
|
|
5037
5225
|
}));
|
|
@@ -5207,6 +5395,23 @@ const createFlipAnimation = (config) => {
|
|
|
5207
5395
|
};
|
|
5208
5396
|
};
|
|
5209
5397
|
|
|
5398
|
+
/** The confirm an unsaved-changes tracker or guard runs when its own config has none. */
|
|
5399
|
+
const UNSAVED_CHANGES_CONFIRM = /* @__PURE__ */ new InjectionToken('UNSAVED_CHANGES_CONFIRM');
|
|
5400
|
+
/**
|
|
5401
|
+
* Sets the confirm every unsaved-changes tracker and guard below this injector runs when its config has no
|
|
5402
|
+
* `confirm`. `factory` runs once, in an injection context, and returns the confirm.
|
|
5403
|
+
*
|
|
5404
|
+
* @example
|
|
5405
|
+
* provideUnsavedChangesConfirm(() => {
|
|
5406
|
+
* const dialogs = inject(MyDialogs);
|
|
5407
|
+
* return () => dialogs.confirmDiscard();
|
|
5408
|
+
* });
|
|
5409
|
+
*/
|
|
5410
|
+
const provideUnsavedChangesConfirm = (factory) => ({
|
|
5411
|
+
provide: UNSAVED_CHANGES_CONFIRM,
|
|
5412
|
+
useFactory: factory,
|
|
5413
|
+
});
|
|
5414
|
+
|
|
5210
5415
|
const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
5211
5416
|
const _registrations = signal([], /* @ts-ignore */
|
|
5212
5417
|
...(ngDevMode ? [{ debugName: "_registrations" }] : /* istanbul ignore next */ []));
|
|
@@ -5290,6 +5495,11 @@ const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() =>
|
|
|
5290
5495
|
const provideUnsavedChangesCoordinator = /* @__PURE__ */ toProvideFn(UNSAVED_CHANGES_COORDINATOR_DEF);
|
|
5291
5496
|
const injectUnsavedChangesCoordinator = /* @__PURE__ */ toInjectFn(UNSAVED_CHANGES_COORDINATOR_DEF);
|
|
5292
5497
|
|
|
5498
|
+
const UNSAVED_CHANGES_ERROR_CODES = {
|
|
5499
|
+
/** An unsaved-changes tracker or guard has no `confirm`, and no `provideUnsavedChangesConfirm()` is in scope. */
|
|
5500
|
+
MISSING_CONFIRM: 9100,
|
|
5501
|
+
};
|
|
5502
|
+
|
|
5293
5503
|
const isAbstractControl = (value) => !!value &&
|
|
5294
5504
|
typeof value === 'object' &&
|
|
5295
5505
|
typeof value.getRawValue === 'function' &&
|
|
@@ -5313,22 +5523,25 @@ const normalizeUnsavedChangesSource = (source) => {
|
|
|
5313
5523
|
const peek = untracked(source);
|
|
5314
5524
|
// A signal that currently holds a FieldTree - or nothing yet (the late/async form case).
|
|
5315
5525
|
if (peek === null || peek === undefined || isFieldTree(peek)) {
|
|
5316
|
-
const
|
|
5526
|
+
const lateSource = source;
|
|
5317
5527
|
return {
|
|
5318
5528
|
value: computed(() => {
|
|
5319
|
-
const
|
|
5320
|
-
|
|
5529
|
+
const current = lateSource();
|
|
5530
|
+
if (isFieldTree(current))
|
|
5531
|
+
return current().value();
|
|
5532
|
+
return (current ?? null);
|
|
5321
5533
|
}),
|
|
5322
5534
|
setValue: (next) => {
|
|
5323
|
-
const
|
|
5324
|
-
if (isFieldTree(
|
|
5325
|
-
|
|
5535
|
+
const current = untracked(lateSource);
|
|
5536
|
+
if (isFieldTree(current)) {
|
|
5537
|
+
current().value.set(next);
|
|
5538
|
+
}
|
|
5539
|
+
else if (current !== null && current !== undefined && 'set' in source) {
|
|
5540
|
+
source.set(next);
|
|
5326
5541
|
}
|
|
5327
5542
|
},
|
|
5328
5543
|
};
|
|
5329
5544
|
}
|
|
5330
|
-
// A plain writable value signal (escape hatch). Initialize it with a non-null value so it isn't
|
|
5331
|
-
// mistaken for a late FieldTree source above.
|
|
5332
5545
|
const writable = source;
|
|
5333
5546
|
return { value: writable, setValue: (next) => writable.set(next) };
|
|
5334
5547
|
}
|
|
@@ -5497,6 +5710,7 @@ const FAVICON_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
5497
5710
|
}
|
|
5498
5711
|
return new Promise((resolve) => {
|
|
5499
5712
|
const image = new Image();
|
|
5713
|
+
image.crossOrigin = 'anonymous';
|
|
5500
5714
|
image.onload = () => {
|
|
5501
5715
|
baseImage = image;
|
|
5502
5716
|
resolve(image);
|
|
@@ -6488,12 +6702,12 @@ const createUnsavedChangesTabLock = (config) => {
|
|
|
6488
6702
|
return { destroy };
|
|
6489
6703
|
};
|
|
6490
6704
|
|
|
6491
|
-
const toBooleanPromise = (result) => {
|
|
6705
|
+
const toBooleanPromise = (result, signal) => {
|
|
6492
6706
|
if (result instanceof Promise) {
|
|
6493
6707
|
return result.then(Boolean);
|
|
6494
6708
|
}
|
|
6495
6709
|
if (isObservable(result)) {
|
|
6496
|
-
return firstValueFrom(result.pipe(take(1), map(Boolean), defaultIfEmpty(false), catchError(() => of(false))));
|
|
6710
|
+
return firstValueFrom(result.pipe(take(1), map(Boolean), takeUntil(fromEvent(signal, 'abort')), defaultIfEmpty(false), catchError(() => of(false))));
|
|
6497
6711
|
}
|
|
6498
6712
|
return Promise.resolve(Boolean(result));
|
|
6499
6713
|
};
|
|
@@ -6514,14 +6728,15 @@ const toBooleanPromise = (result) => {
|
|
|
6514
6728
|
*/
|
|
6515
6729
|
const createUnsavedChangesTracker = (config) => {
|
|
6516
6730
|
assertInInjectionContext(createUnsavedChangesTracker);
|
|
6517
|
-
const { compareFn
|
|
6731
|
+
const { compareFn } = config;
|
|
6732
|
+
const confirm = config.confirm ?? inject(UNSAVED_CHANGES_CONFIRM, { optional: true });
|
|
6733
|
+
if (!confirm && isDevMode()) {
|
|
6734
|
+
throw new RuntimeError(UNSAVED_CHANGES_ERROR_CODES.MISSING_CONFIRM, '[createUnsavedChangesTracker] No confirm: pass `confirm` or call `provideUnsavedChangesConfirm()` in a parent injector.');
|
|
6735
|
+
}
|
|
6518
6736
|
const normalized = normalizeUnsavedChangesSource(config.source);
|
|
6519
|
-
const
|
|
6520
|
-
const
|
|
6521
|
-
|
|
6522
|
-
? config.defaultValue()
|
|
6523
|
-
: config.defaultValue
|
|
6524
|
-
: untracked(normalized.value);
|
|
6737
|
+
const explicitDefault = typeof config.defaultValue === 'function' ? config.defaultValue() : config.defaultValue;
|
|
6738
|
+
const hasExplicitDefault = explicitDefault !== undefined && explicitDefault !== null;
|
|
6739
|
+
const initialDefault = hasExplicitDefault ? explicitDefault : untracked(normalized.value);
|
|
6525
6740
|
const _defaultValue = signal(initialDefault, /* @ts-ignore */
|
|
6526
6741
|
...(ngDevMode ? [{ debugName: "_defaultValue" }] : /* istanbul ignore next */ []));
|
|
6527
6742
|
// No explicit default → capture the first non-null value the source produces as the baseline.
|
|
@@ -6565,7 +6780,10 @@ const createUnsavedChangesTracker = (config) => {
|
|
|
6565
6780
|
if (untracked(_isAbandoned) || !untracked(hasChanges)) {
|
|
6566
6781
|
return Promise.resolve(true);
|
|
6567
6782
|
}
|
|
6568
|
-
|
|
6783
|
+
if (!confirm) {
|
|
6784
|
+
return Promise.resolve(true);
|
|
6785
|
+
}
|
|
6786
|
+
return coordinator.runCheck((context) => toBooleanPromise(confirm(untracked(normalized.value), context), context.signal));
|
|
6569
6787
|
};
|
|
6570
6788
|
const refreshDefaultValue = () => _defaultValue.set(untracked(normalized.value));
|
|
6571
6789
|
const restoreDefaultValue = () => {
|
|
@@ -6877,12 +7095,22 @@ class ClickOutsideDirective {
|
|
|
6877
7095
|
this.elementRef = inject(ElementRef);
|
|
6878
7096
|
this.document = inject(DOCUMENT);
|
|
6879
7097
|
this.didClickOutside = output({ alias: 'etClickOutside' });
|
|
7098
|
+
this.pressStartedInside = false;
|
|
7099
|
+
fromEvent(this.document.documentElement, 'pointerdown', { capture: true })
|
|
7100
|
+
.pipe(tap((event) => {
|
|
7101
|
+
this.pressStartedInside = event.composedPath().includes(this.elementRef.nativeElement);
|
|
7102
|
+
}), takeUntilDestroyed())
|
|
7103
|
+
.subscribe();
|
|
7104
|
+
fromEvent(this.document.documentElement, 'pointercancel', { capture: true })
|
|
7105
|
+
.pipe(tap(() => (this.pressStartedInside = false)), takeUntilDestroyed())
|
|
7106
|
+
.subscribe();
|
|
6880
7107
|
fromEvent(this.document.documentElement, 'click')
|
|
6881
7108
|
.pipe(tap((event) => {
|
|
6882
7109
|
const hostElement = this.elementRef.nativeElement;
|
|
6883
7110
|
const activeElement = event.target;
|
|
6884
|
-
const
|
|
6885
|
-
|
|
7111
|
+
const startedInside = this.pressStartedInside;
|
|
7112
|
+
this.pressStartedInside = false;
|
|
7113
|
+
if (startedInside || event.composedPath().includes(hostElement))
|
|
6886
7114
|
return;
|
|
6887
7115
|
if (isOnHigherOverlayLayer(activeElement, resolveOverlayLayer(hostElement)))
|
|
6888
7116
|
return;
|
|
@@ -6906,7 +7134,7 @@ class RepeatDirective {
|
|
|
6906
7134
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
6907
7135
|
this.repeatCount = input(2, { ...(ngDevMode ? { debugName: "repeatCount" } : /* istanbul ignore next */ {}), alias: 'etRepeat', transform: numberAttribute });
|
|
6908
7136
|
effect(() => {
|
|
6909
|
-
const count = Math.max(0, this.repeatCount());
|
|
7137
|
+
const count = Math.max(0, this.repeatCount()) || 0;
|
|
6910
7138
|
while (this.viewContainerRef.length < count) {
|
|
6911
7139
|
this.viewContainerRef.createEmbeddedView(this.mainTemplateRef);
|
|
6912
7140
|
}
|
|
@@ -7036,7 +7264,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
7036
7264
|
|
|
7037
7265
|
// jsdom and other non-browser DOMs ship no pointer capture; the document listeners track the
|
|
7038
7266
|
// gesture either way, so a failure here only costs events over a cross-origin frame.
|
|
7039
|
-
const capturePointer = (el, pointerId) => {
|
|
7267
|
+
const capturePointer$1 = (el, pointerId) => {
|
|
7040
7268
|
try {
|
|
7041
7269
|
el.setPointerCapture(pointerId);
|
|
7042
7270
|
}
|
|
@@ -7066,7 +7294,7 @@ const setupDragObservable = (startEvent, el, commitThreshold) => {
|
|
|
7066
7294
|
return EMPTY;
|
|
7067
7295
|
}
|
|
7068
7296
|
committed = true;
|
|
7069
|
-
capturePointer(el, pointerId);
|
|
7297
|
+
capturePointer$1(el, pointerId);
|
|
7070
7298
|
lastX = e.clientX;
|
|
7071
7299
|
lastY = e.clientY;
|
|
7072
7300
|
// Emit the catch-up move together with start so consumers snap to the pointer
|
|
@@ -7442,8 +7670,11 @@ const FOCUSABLE_SELECTOR = /* @__PURE__ */ [
|
|
|
7442
7670
|
'select:not([disabled])',
|
|
7443
7671
|
'textarea:not([disabled])',
|
|
7444
7672
|
'iframe',
|
|
7673
|
+
'audio[controls]',
|
|
7674
|
+
'video[controls]',
|
|
7675
|
+
'details > summary:first-of-type',
|
|
7445
7676
|
'[tabindex]:not([tabindex="-1"])',
|
|
7446
|
-
'[contenteditable="
|
|
7677
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
7447
7678
|
].join(',');
|
|
7448
7679
|
const isHTMLElement = (value) => {
|
|
7449
7680
|
// `instanceof` is realm-bound: an element adopted by a same-origin pop-up (the `document` mount
|
|
@@ -7473,10 +7704,24 @@ const isFocusable = (element, document) => {
|
|
|
7473
7704
|
const view = document.defaultView;
|
|
7474
7705
|
const style = view?.getComputedStyle(element);
|
|
7475
7706
|
const isVisible = style?.display !== 'none' && style?.visibility !== 'hidden' && element.getClientRects().length > 0;
|
|
7476
|
-
return isVisible &&
|
|
7477
|
-
|
|
7707
|
+
return (isVisible &&
|
|
7708
|
+
!element.hasAttribute('disabled') &&
|
|
7709
|
+
!element.matches(':disabled') &&
|
|
7710
|
+
!element.closest('[inert]') &&
|
|
7711
|
+
element.tabIndex >= 0);
|
|
7712
|
+
};
|
|
7713
|
+
const isNamedRadio = (element) => element.tagName === 'INPUT' && element.type === 'radio' && !!element.name;
|
|
7714
|
+
const isSameTabStop = (a, b) => a === b || (isNamedRadio(a) && isNamedRadio(b) && a.name === b.name && a.form === b.form);
|
|
7715
|
+
/** The elements Tab stops on inside `container`, with each named radio group collapsed to its checked (else first) radio. */
|
|
7478
7716
|
const getFocusableElements = (container, document) => {
|
|
7479
|
-
|
|
7717
|
+
const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter((el) => isFocusable(el, document));
|
|
7718
|
+
return elements.filter((element) => {
|
|
7719
|
+
if (!isNamedRadio(element)) {
|
|
7720
|
+
return true;
|
|
7721
|
+
}
|
|
7722
|
+
const group = elements.filter((other) => isSameTabStop(element, other));
|
|
7723
|
+
return (group.find((radio) => radio.checked) ?? group[0]) === element;
|
|
7724
|
+
});
|
|
7480
7725
|
};
|
|
7481
7726
|
const getHeadingElement = (container) => {
|
|
7482
7727
|
return container.querySelector('h1, h2, h3, h4, h5, h6, [role="heading"]');
|
|
@@ -7532,12 +7777,12 @@ const setupFocusTrap = (paneElement, overlayRef, enabled, isTopMost, document) =
|
|
|
7532
7777
|
focusElement(event.shiftKey ? (lastElement ?? null) : (firstElement ?? null));
|
|
7533
7778
|
return;
|
|
7534
7779
|
}
|
|
7535
|
-
if (event.shiftKey && activeElement
|
|
7780
|
+
if (event.shiftKey && firstElement && isSameTabStop(activeElement, firstElement)) {
|
|
7536
7781
|
event.preventDefault();
|
|
7537
7782
|
focusElement(lastElement ?? null);
|
|
7538
7783
|
return;
|
|
7539
7784
|
}
|
|
7540
|
-
if (!event.shiftKey && activeElement
|
|
7785
|
+
if (!event.shiftKey && lastElement && isSameTabStop(activeElement, lastElement)) {
|
|
7541
7786
|
event.preventDefault();
|
|
7542
7787
|
focusElement(firstElement ?? null);
|
|
7543
7788
|
}
|
|
@@ -8065,10 +8310,10 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8065
8310
|
...(ngDevMode ? [{ debugName: "_componentInstance" }] : /* istanbul ignore next */ []));
|
|
8066
8311
|
let positionUpdater = null;
|
|
8067
8312
|
let backdropUpdater = null;
|
|
8068
|
-
const beforeOpenedSubject = new
|
|
8069
|
-
const afterOpenedSubject = new
|
|
8070
|
-
const beforeClosedSubject = new
|
|
8071
|
-
const afterClosedSubject = new
|
|
8313
|
+
const beforeOpenedSubject = new ReplaySubject(1);
|
|
8314
|
+
const afterOpenedSubject = new ReplaySubject(1);
|
|
8315
|
+
const beforeClosedSubject = new ReplaySubject(1);
|
|
8316
|
+
const afterClosedSubject = new ReplaySubject(1);
|
|
8072
8317
|
const closeGuards = new Set();
|
|
8073
8318
|
return {
|
|
8074
8319
|
id,
|
|
@@ -8076,7 +8321,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8076
8321
|
elements,
|
|
8077
8322
|
state: _state.asReadonly(),
|
|
8078
8323
|
componentInstance: _componentInstance.asReadonly(),
|
|
8079
|
-
beforeOpenedSubject,
|
|
8080
8324
|
close(result, source = 'api') {
|
|
8081
8325
|
if (_state() === 'closing' || _state() === 'closed') {
|
|
8082
8326
|
return;
|
|
@@ -8119,6 +8363,10 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8119
8363
|
attachComponentRef(componentRef) {
|
|
8120
8364
|
_componentInstance.set(componentRef.instance);
|
|
8121
8365
|
},
|
|
8366
|
+
markBeforeOpened() {
|
|
8367
|
+
beforeOpenedSubject.next();
|
|
8368
|
+
beforeOpenedSubject.complete();
|
|
8369
|
+
},
|
|
8122
8370
|
attachPositionUpdater(updater) {
|
|
8123
8371
|
positionUpdater = updater;
|
|
8124
8372
|
},
|
|
@@ -8164,6 +8412,7 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8164
8412
|
}
|
|
8165
8413
|
_state.set('closed');
|
|
8166
8414
|
_componentInstance.set(null);
|
|
8415
|
+
afterOpenedSubject.complete();
|
|
8167
8416
|
afterClosedSubject.next(closeEvent);
|
|
8168
8417
|
afterClosedSubject.complete();
|
|
8169
8418
|
},
|
|
@@ -8187,6 +8436,14 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8187
8436
|
// Synchronous teardown for each currently-mounted overlay, run when the runtime's injector is
|
|
8188
8437
|
// destroyed (app teardown). Registered on mount, removed once the overlay is destroyed normally.
|
|
8189
8438
|
const mountedTeardowns = new Set();
|
|
8439
|
+
const armedReopenSwallows = new Set();
|
|
8440
|
+
const focusRestoreChains = new WeakMap();
|
|
8441
|
+
const resolveFocusRestoreChain = (focused) => {
|
|
8442
|
+
if (!focused)
|
|
8443
|
+
return [];
|
|
8444
|
+
const owner = openEntriesState().find((entry) => entry.elements.hostElement.contains(focused));
|
|
8445
|
+
return [focused, ...((owner && focusRestoreChains.get(owner)) ?? [])];
|
|
8446
|
+
};
|
|
8190
8447
|
const getRootElement = (targetDocument, zIndex) => {
|
|
8191
8448
|
let documentRoots = rootElements.get(targetDocument);
|
|
8192
8449
|
if (!documentRoots) {
|
|
@@ -8235,14 +8492,18 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8235
8492
|
}
|
|
8236
8493
|
};
|
|
8237
8494
|
const isTopMost = (overlayRef) => {
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
if (
|
|
8495
|
+
let topMost = null;
|
|
8496
|
+
let topMostLayer = -Infinity;
|
|
8497
|
+
for (const entry of openEntriesState()) {
|
|
8498
|
+
if (entry.config.passive || entry.state() === 'closing' || entry.state() === 'closed')
|
|
8242
8499
|
continue;
|
|
8243
|
-
|
|
8500
|
+
const entryLayer = entry.config.zIndex ?? DEFAULT_OVERLAY_LAYER;
|
|
8501
|
+
if (entryLayer >= topMostLayer) {
|
|
8502
|
+
topMost = entry;
|
|
8503
|
+
topMostLayer = entryLayer;
|
|
8504
|
+
}
|
|
8244
8505
|
}
|
|
8245
|
-
return
|
|
8506
|
+
return topMost === overlayRef;
|
|
8246
8507
|
};
|
|
8247
8508
|
const getAnimatedLifecycle = (componentRef) => {
|
|
8248
8509
|
const componentLifecycle = componentRef.instance.animatedLifecycle?.();
|
|
@@ -8259,9 +8520,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8259
8520
|
const paneElement = renderer.createElement('div');
|
|
8260
8521
|
const backdropElement = signal(null, /* @ts-ignore */
|
|
8261
8522
|
...(ngDevMode ? [{ debugName: "backdropElement" }] : /* istanbul ignore next */ []));
|
|
8262
|
-
const
|
|
8263
|
-
? targetDocument.activeElement
|
|
8264
|
-
: null;
|
|
8523
|
+
const focusRestoreChain = resolveFocusRestoreChain(isHTMLOrSVGElement(targetDocument.activeElement) ? targetDocument.activeElement : null);
|
|
8265
8524
|
const autoFocus = config.autoFocus ?? 'first-tabbable';
|
|
8266
8525
|
let currentHasBackdrop = config.hasBackdrop !== false;
|
|
8267
8526
|
let currentPositionStrategy = config.positionStrategy;
|
|
@@ -8338,8 +8597,8 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8338
8597
|
renderer.addClass(paneElement, 'et-overlay-runtime-pane', ...(config.paneClass ?? []));
|
|
8339
8598
|
appRef.attachView(componentRef.hostView);
|
|
8340
8599
|
overlayRef.attachComponentRef(componentRef);
|
|
8341
|
-
overlayRef.
|
|
8342
|
-
|
|
8600
|
+
overlayRef.markBeforeOpened();
|
|
8601
|
+
focusRestoreChains.set(overlayRef, focusRestoreChain);
|
|
8343
8602
|
openEntriesState.update((entries) => [...entries, overlayRef]);
|
|
8344
8603
|
let positionCleanup = setupPositioning(activeConfig(), hostElement, paneElement, overlayRef, renderer);
|
|
8345
8604
|
cleanupFns.push(() => positionCleanup());
|
|
@@ -8394,8 +8653,14 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8394
8653
|
}
|
|
8395
8654
|
openEntriesState.update((entries) => entries.filter((entry) => entry !== overlayRef));
|
|
8396
8655
|
maybeDestroyRootElements(targetDocument);
|
|
8397
|
-
if (config.restoreFocus !== false && overlayStillOwnsFocus
|
|
8398
|
-
|
|
8656
|
+
if (config.restoreFocus !== false && overlayStillOwnsFocus) {
|
|
8657
|
+
for (const element of focusRestoreChain) {
|
|
8658
|
+
if (!element.isConnected)
|
|
8659
|
+
continue;
|
|
8660
|
+
element.focus({ preventScroll: true });
|
|
8661
|
+
if (element.contains(targetDocument.activeElement))
|
|
8662
|
+
break;
|
|
8663
|
+
}
|
|
8399
8664
|
}
|
|
8400
8665
|
overlayRef.finishClose(closeEvent);
|
|
8401
8666
|
};
|
|
@@ -8413,7 +8678,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8413
8678
|
return;
|
|
8414
8679
|
}
|
|
8415
8680
|
const lifecycle = getAnimatedLifecycle(componentRef);
|
|
8416
|
-
animationDebugLog(`runtime ${config.id}`, `close requested (source "${closeEvent.source}", lifecycle state "${lifecycle?.state
|
|
8681
|
+
animationDebugLog(`runtime ${config.id}`, `close requested (source "${closeEvent.source}", lifecycle state "${lifecycle?.state() ?? 'none'}", delegate ${config.animationDelegate?.leave ? 'yes' : 'no'})`);
|
|
8417
8682
|
// The reference element is gone - there is nothing left to animate away from, so tear the
|
|
8418
8683
|
// overlay down synchronously instead of playing a leave transition from a stale position.
|
|
8419
8684
|
if (!lifecycle || closeEvent.source === 'reference-detached') {
|
|
@@ -8470,12 +8735,22 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8470
8735
|
const originElement = getOriginElement();
|
|
8471
8736
|
if (originElement && originElement.contains(target)) {
|
|
8472
8737
|
const swallowReopenClick = (clickEvent) => {
|
|
8738
|
+
disarmReopenSwallow();
|
|
8473
8739
|
if (isElement(clickEvent.target) && originElement.contains(clickEvent.target)) {
|
|
8474
8740
|
clickEvent.stopImmediatePropagation();
|
|
8475
8741
|
clickEvent.preventDefault();
|
|
8476
8742
|
}
|
|
8477
8743
|
};
|
|
8478
|
-
|
|
8744
|
+
// A press that ends without a click (cancelled, dragged off) must not leave the swallow
|
|
8745
|
+
// armed for the next, unrelated click on the origin.
|
|
8746
|
+
const disarmReopenSwallow = () => {
|
|
8747
|
+
targetDocument.removeEventListener('click', swallowReopenClick, true);
|
|
8748
|
+
targetDocument.removeEventListener('pointerdown', disarmReopenSwallow, true);
|
|
8749
|
+
armedReopenSwallows.delete(disarmReopenSwallow);
|
|
8750
|
+
};
|
|
8751
|
+
targetDocument.addEventListener('click', swallowReopenClick, true);
|
|
8752
|
+
targetDocument.addEventListener('pointerdown', disarmReopenSwallow, true);
|
|
8753
|
+
armedReopenSwallows.add(disarmReopenSwallow);
|
|
8479
8754
|
}
|
|
8480
8755
|
};
|
|
8481
8756
|
targetDocument.addEventListener('pointerdown', onPointerDown, true);
|
|
@@ -8494,7 +8769,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8494
8769
|
overlayRef.markOpened();
|
|
8495
8770
|
return;
|
|
8496
8771
|
}
|
|
8497
|
-
animationDebugLog(`runtime ${config.id}`, `enter (delegate ${config.animationDelegate?.enter ? 'yes' : 'no'}, lifecycle state "${lifecycle.state
|
|
8772
|
+
animationDebugLog(`runtime ${config.id}`, `enter (delegate ${config.animationDelegate?.enter ? 'yes' : 'no'}, lifecycle state "${lifecycle.state()}")`);
|
|
8498
8773
|
const readySubscription = lifecycle.state$
|
|
8499
8774
|
.pipe(filter((state) => state === 'entering' || state === 'entered'), take(1), tap(() => {
|
|
8500
8775
|
nextFrame(() => {
|
|
@@ -8509,17 +8784,19 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8509
8784
|
else {
|
|
8510
8785
|
lifecycle.enter();
|
|
8511
8786
|
}
|
|
8512
|
-
lifecycle.state$
|
|
8787
|
+
const enteredSubscription = lifecycle.state$
|
|
8513
8788
|
.pipe(filter((state) => state === 'entered'), take(1), tap(() => {
|
|
8514
8789
|
applyInitialFocus(paneElement, autoFocus, targetDocument);
|
|
8515
8790
|
overlayRef.markOpened();
|
|
8516
8791
|
}))
|
|
8517
8792
|
.subscribe();
|
|
8793
|
+
cleanupFns.push(() => enteredSubscription.unsubscribe());
|
|
8518
8794
|
});
|
|
8519
8795
|
return overlayRef;
|
|
8520
8796
|
};
|
|
8521
8797
|
destroyRef.onDestroy(() => {
|
|
8522
8798
|
[...mountedTeardowns].forEach((teardown) => teardown());
|
|
8799
|
+
[...armedReopenSwallows].forEach((disarm) => disarm());
|
|
8523
8800
|
[...rootElements.keys()].forEach((targetDocument) => maybeDestroyRootElements(targetDocument));
|
|
8524
8801
|
});
|
|
8525
8802
|
return {
|
|
@@ -9110,7 +9387,7 @@ const initials = (value, maxLength = 2) => {
|
|
|
9110
9387
|
return '';
|
|
9111
9388
|
return words
|
|
9112
9389
|
.slice(0, maxLength)
|
|
9113
|
-
.map((word) => word[0]?.toUpperCase() ?? '')
|
|
9390
|
+
.map((word) => [...word.normalize()][0]?.toUpperCase() ?? '')
|
|
9114
9391
|
.join('');
|
|
9115
9392
|
};
|
|
9116
9393
|
|
|
@@ -9189,20 +9466,38 @@ const EDGE_CURSORS = {
|
|
|
9189
9466
|
nw: 'nwse-resize',
|
|
9190
9467
|
se: 'nwse-resize',
|
|
9191
9468
|
};
|
|
9192
|
-
|
|
9469
|
+
// jsdom and other non-browser DOMs ship no pointer capture; the document listeners track the
|
|
9470
|
+
// gesture either way, so a failure here only costs events over a cross-origin frame.
|
|
9471
|
+
const capturePointer = (el, pointerId) => {
|
|
9472
|
+
try {
|
|
9473
|
+
el.setPointerCapture(pointerId);
|
|
9474
|
+
}
|
|
9475
|
+
catch {
|
|
9476
|
+
return;
|
|
9477
|
+
}
|
|
9478
|
+
};
|
|
9479
|
+
const setupResizeObservable = (startEvent, handle, edge, doc) => {
|
|
9193
9480
|
const pointerId = startEvent.pointerId;
|
|
9194
9481
|
const startX = startEvent.clientX;
|
|
9195
9482
|
const startY = startEvent.clientY;
|
|
9196
9483
|
let cancelled = false;
|
|
9484
|
+
let last = { edge, totalDx: 0, totalDy: 0, clientX: startX, clientY: startY };
|
|
9197
9485
|
const end$ = merge(fromEvent(doc, 'pointerup'), fromEvent(doc, 'pointercancel')).pipe(filter((e) => e.pointerId === pointerId), take(1),
|
|
9198
9486
|
// Read below by the terminating `defer`, which only runs once this has completed the moves.
|
|
9199
9487
|
tap((e) => (cancelled = e.type === 'pointercancel')));
|
|
9200
9488
|
return defer(() => {
|
|
9201
9489
|
const releaseSelection = suppressTextSelection(doc);
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9490
|
+
capturePointer(handle, pointerId);
|
|
9491
|
+
return concat(of({ type: 'start', edge }), fromEvent(doc, 'pointermove').pipe(filter((e) => e.pointerId === pointerId), map((e) => {
|
|
9492
|
+
last = {
|
|
9493
|
+
edge,
|
|
9494
|
+
totalDx: e.clientX - startX,
|
|
9495
|
+
totalDy: e.clientY - startY,
|
|
9496
|
+
clientX: e.clientX,
|
|
9497
|
+
clientY: e.clientY,
|
|
9498
|
+
};
|
|
9499
|
+
return { type: 'move', data: last };
|
|
9500
|
+
}), takeUntil(end$)), defer(() => of(cancelled ? { type: 'cancelled' } : { type: 'end', data: last }))).pipe(finalize(releaseSelection));
|
|
9206
9501
|
});
|
|
9207
9502
|
};
|
|
9208
9503
|
class ResizeHandlesComponent {
|
|
@@ -9212,10 +9507,10 @@ class ResizeHandlesComponent {
|
|
|
9212
9507
|
...(ngDevMode ? [{ debugName: "edges" }] : /* istanbul ignore next */ []));
|
|
9213
9508
|
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
9214
9509
|
this.gestureStart$ = new Subject();
|
|
9215
|
-
this.gesture$ = this.gestureStart$.pipe(exhaustMap(({ event, edge }) => setupResizeObservable(event, edge, this.el.nativeElement.ownerDocument)), share(), takeUntilDestroyed());
|
|
9510
|
+
this.gesture$ = this.gestureStart$.pipe(exhaustMap(({ event, handle, edge }) => setupResizeObservable(event, handle, edge, this.el.nativeElement.ownerDocument)), share(), takeUntilDestroyed());
|
|
9216
9511
|
this.resizeStarted = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'start'), map((e) => e.edge)));
|
|
9217
9512
|
this.resizeMoved = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'move'), map((e) => e.data)));
|
|
9218
|
-
this.resizeEnded = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'end'), map(() =>
|
|
9513
|
+
this.resizeEnded = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'end'), map((e) => e.data)));
|
|
9219
9514
|
/**
|
|
9220
9515
|
* The browser took the gesture away mid-resize (a system gesture, an incoming call, the tab going
|
|
9221
9516
|
* to the background). Revert to the size the resize started from - the user never let go, so there
|
|
@@ -9236,7 +9531,7 @@ class ResizeHandlesComponent {
|
|
|
9236
9531
|
if (event.button !== 0 || this.disabled())
|
|
9237
9532
|
return;
|
|
9238
9533
|
event.stopPropagation();
|
|
9239
|
-
this.gestureStart$.next({ event, edge });
|
|
9534
|
+
this.gestureStart$.next({ event, handle: event.currentTarget, edge });
|
|
9240
9535
|
}
|
|
9241
9536
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ResizeHandlesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9242
9537
|
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: `
|
|
@@ -9498,6 +9793,226 @@ const injectDefaultSurfaceTheme = (type) => {
|
|
|
9498
9793
|
return defaults.length === 1 ? (defaults[0] ?? null) : null;
|
|
9499
9794
|
};
|
|
9500
9795
|
|
|
9796
|
+
/**
|
|
9797
|
+
* Reserved color name. Resolves the color tokens from the ambient surface's neutral swatch
|
|
9798
|
+
* (`SurfaceTheme['interactionColor']`) instead of a registered accent, so a secondary action can
|
|
9799
|
+
* read as chrome without the app registering a neutral color theme. Apps must not register a color
|
|
9800
|
+
* theme under this name.
|
|
9801
|
+
*/
|
|
9802
|
+
const SURFACE_COLOR = 'surface';
|
|
9803
|
+
const createCssColorThemeName = (name) => name.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
9804
|
+
const COLOR_THEMES_DEF = /* @__PURE__ */ defineStaticProvider(undefined, {
|
|
9805
|
+
name: 'Color Themes',
|
|
9806
|
+
});
|
|
9807
|
+
const ɵProvideColorThemes = /* @__PURE__ */ toProvideFn(COLOR_THEMES_DEF);
|
|
9808
|
+
const injectColorThemes = /* @__PURE__ */ toInjectFn(COLOR_THEMES_DEF);
|
|
9809
|
+
const COLOR_THEMES_PREFIX_DEF = /* @__PURE__ */ defineStaticProvider('et', {
|
|
9810
|
+
name: 'Themes Prefix',
|
|
9811
|
+
});
|
|
9812
|
+
const ɵProvideColorThemesPrefix = /* @__PURE__ */ toProvideFn(COLOR_THEMES_PREFIX_DEF);
|
|
9813
|
+
const injectColorThemesPrefix = /* @__PURE__ */ toInjectFn(COLOR_THEMES_PREFIX_DEF);
|
|
9814
|
+
const provideColorThemesWithTailwind4 = (themes, prefix = 'et') => {
|
|
9815
|
+
if (isDevMode() && themes.some((theme) => theme.name === SURFACE_COLOR)) {
|
|
9816
|
+
console.error(`[provideColorThemesWithTailwind4] "${SURFACE_COLOR}" is a reserved color name that resolves to the ambient surface's neutral swatch. Rename your theme.`);
|
|
9817
|
+
}
|
|
9818
|
+
return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix(prefix)];
|
|
9819
|
+
};
|
|
9820
|
+
const findColorThemeByType = (themes, type, caller) => {
|
|
9821
|
+
if (!themes) {
|
|
9822
|
+
throw new Error(`[${caller}] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9823
|
+
}
|
|
9824
|
+
const theme = themes.find((t) => t.type === type);
|
|
9825
|
+
if (!theme) {
|
|
9826
|
+
throw new Error(`[${caller}] No color theme with type "${type}" found. Add a theme with type: "${type}" to provideColorThemesWithTailwind4().`);
|
|
9827
|
+
}
|
|
9828
|
+
if (isDevMode()) {
|
|
9829
|
+
const duplicates = themes.filter((t) => t.type === type);
|
|
9830
|
+
if (duplicates.length > 1) {
|
|
9831
|
+
console.error(`[${caller}] Multiple themes with type "${type}" found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9832
|
+
}
|
|
9833
|
+
}
|
|
9834
|
+
return theme;
|
|
9835
|
+
};
|
|
9836
|
+
const injectColorThemeByType = (type) => findColorThemeByType(injectColorThemes({ optional: true }), type, 'injectColorThemeByType');
|
|
9837
|
+
/**
|
|
9838
|
+
* A signal of the color theme for `type` on the surface this element renders on: the theme the nearest
|
|
9839
|
+
* surface names in `semanticColorThemes`, else the first registered theme of that `type`. Reading it throws
|
|
9840
|
+
* when the app registered no theme of that `type`, so read it only for a state that renders.
|
|
9841
|
+
*
|
|
9842
|
+
* @example
|
|
9843
|
+
* protected successTheme = injectSemanticColorTheme('success');
|
|
9844
|
+
* // <span [etProvideColor]="successTheme()">+12%</span>
|
|
9845
|
+
*/
|
|
9846
|
+
const injectSemanticColorTheme = (type) => {
|
|
9847
|
+
const themes = injectColorThemes({ optional: true });
|
|
9848
|
+
const surfaceProvider = inject(SURFACE_PROVIDER, { optional: true });
|
|
9849
|
+
const defaultSurface = injectDefaultSurfaceTheme();
|
|
9850
|
+
return computed(() => {
|
|
9851
|
+
const surface = surfaceProvider?.activeTheme() ?? defaultSurface;
|
|
9852
|
+
const mappedName = surface?.semanticColorThemes?.[type];
|
|
9853
|
+
const mapped = mappedName ? themes?.find((t) => t.name === mappedName) : undefined;
|
|
9854
|
+
if (mapped)
|
|
9855
|
+
return mapped;
|
|
9856
|
+
if (mappedName && isDevMode()) {
|
|
9857
|
+
console.error(`[injectSemanticColorTheme] Surface theme "${surface?.name}" maps "${type}" to the color theme "${mappedName}", which is not registered. Add it to provideColorThemesWithTailwind4().`);
|
|
9858
|
+
}
|
|
9859
|
+
return findColorThemeByType(themes, type, 'injectSemanticColorTheme');
|
|
9860
|
+
});
|
|
9861
|
+
};
|
|
9862
|
+
/** @deprecated Use `injectSemanticColorTheme('error')`, which follows the surface the element renders on. */
|
|
9863
|
+
const injectErrorTheme = () => injectColorThemeByType('error');
|
|
9864
|
+
/** @deprecated Use `injectSemanticColorTheme('warning')`, which follows the surface the element renders on. */
|
|
9865
|
+
const injectWarningTheme = () => injectColorThemeByType('warning');
|
|
9866
|
+
/** @deprecated Use `injectSemanticColorTheme('success')`, which follows the surface the element renders on. */
|
|
9867
|
+
const injectSuccessTheme = () => injectColorThemeByType('success');
|
|
9868
|
+
const injectDefaultColorTheme = () => {
|
|
9869
|
+
const themes = injectColorThemes({ optional: true });
|
|
9870
|
+
if (!themes) {
|
|
9871
|
+
throw new Error(`[injectDefaultColorTheme] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9872
|
+
}
|
|
9873
|
+
const theme = themes.find((t) => t.isDefault);
|
|
9874
|
+
if (!theme) {
|
|
9875
|
+
throw new Error(`[injectDefaultColorTheme] No color theme with isDefault: true found. Mark one theme as the default in provideColorThemesWithTailwind4().`);
|
|
9876
|
+
}
|
|
9877
|
+
if (isDevMode()) {
|
|
9878
|
+
const duplicates = themes.filter((t) => t.isDefault);
|
|
9879
|
+
if (duplicates.length > 1) {
|
|
9880
|
+
console.error(`[injectDefaultColorTheme] Multiple themes with isDefault: true found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9883
|
+
return theme;
|
|
9884
|
+
};
|
|
9885
|
+
|
|
9886
|
+
const COLOR_PROVIDER = new InjectionToken('ColorProvider');
|
|
9887
|
+
/**
|
|
9888
|
+
* The color provider attached to a bootstrapped root component (e.g. `ProvideColorDirective` in
|
|
9889
|
+
* the app component's `hostDirectives`), if any. Detached DOM such as overlay panes can't reach
|
|
9890
|
+
* it through element DI, so overlay containers fall back to this when resolving the provider to
|
|
9891
|
+
* `syncWithProvider()` - resolve at sync time, not at injection time, since root components only
|
|
9892
|
+
* register with `ApplicationRef` once bootstrap completes.
|
|
9893
|
+
*/
|
|
9894
|
+
const resolveAppRootColorProvider = (appRef) => {
|
|
9895
|
+
for (const componentRef of appRef.components) {
|
|
9896
|
+
const provider = componentRef.injector.get(COLOR_PROVIDER, null);
|
|
9897
|
+
if (provider) {
|
|
9898
|
+
return provider;
|
|
9899
|
+
}
|
|
9900
|
+
}
|
|
9901
|
+
return null;
|
|
9902
|
+
};
|
|
9903
|
+
const FORCED_COLOR_UNSET = /* @__PURE__ */ Symbol('FORCED_COLOR_UNSET');
|
|
9904
|
+
const appsWarnedAboutMissingThemes = /* @__PURE__ */ new WeakSet();
|
|
9905
|
+
class ProvideColorDirective {
|
|
9906
|
+
constructor() {
|
|
9907
|
+
this.themes = injectColorThemes({ optional: true });
|
|
9908
|
+
this.prefix = injectColorThemesPrefix({ optional: true });
|
|
9909
|
+
this.injector = inject(Injector);
|
|
9910
|
+
this.parentProvider = inject(COLOR_PROVIDER, { optional: true, skipSelf: true });
|
|
9911
|
+
this.appRef = inject(ApplicationRef);
|
|
9912
|
+
this.currentProviderSync = null;
|
|
9913
|
+
this.forcedColor = signal(FORCED_COLOR_UNSET, /* @ts-ignore */
|
|
9914
|
+
...(ngDevMode ? [{ debugName: "forcedColor" }] : /* istanbul ignore next */ []));
|
|
9915
|
+
this.color = input(undefined, { ...(ngDevMode ? { debugName: "color" } : /* istanbul ignore next */ {}), alias: 'etProvideColor' });
|
|
9916
|
+
this.effectiveColor = computed(() => {
|
|
9917
|
+
const forcedColor = this.forcedColor();
|
|
9918
|
+
if (forcedColor !== FORCED_COLOR_UNSET) {
|
|
9919
|
+
return forcedColor;
|
|
9920
|
+
}
|
|
9921
|
+
return this.color();
|
|
9922
|
+
}, /* @ts-ignore */
|
|
9923
|
+
...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
|
|
9924
|
+
/**
|
|
9925
|
+
* The color that actually applies at this provider's location: its own effective color, or -
|
|
9926
|
+
* when this provider is passive (no input, nothing forced) - the nearest ancestor provider's.
|
|
9927
|
+
* Mirrors the CSS cascade, which falls through a provider's `-color--inherited` scope class;
|
|
9928
|
+
* consumers that re-apply context across detached DOM (overlay panes) must use this instead
|
|
9929
|
+
* of `effectiveColor` or a passive in-between provider (e.g. a form field's) erases the theme.
|
|
9930
|
+
*/
|
|
9931
|
+
this.resolvedColor = computed(() => {
|
|
9932
|
+
const own = this.effectiveColor();
|
|
9933
|
+
const ownName = typeof own === 'object' && own !== null ? own.name : own;
|
|
9934
|
+
if (ownName) {
|
|
9935
|
+
return own;
|
|
9936
|
+
}
|
|
9937
|
+
return this.parentProvider?.resolvedColor();
|
|
9938
|
+
}, /* @ts-ignore */
|
|
9939
|
+
...(ngDevMode ? [{ debugName: "resolvedColor" }] : /* istanbul ignore next */ []));
|
|
9940
|
+
this.colorName = computed(() => {
|
|
9941
|
+
const raw = this.effectiveColor();
|
|
9942
|
+
const value = typeof raw === 'object' && raw !== null ? raw.name : raw;
|
|
9943
|
+
if (value === SURFACE_COLOR)
|
|
9944
|
+
return SURFACE_COLOR;
|
|
9945
|
+
if (!value)
|
|
9946
|
+
return;
|
|
9947
|
+
if (!this.themes) {
|
|
9948
|
+
if (isDevMode() && !appsWarnedAboutMissingThemes.has(this.appRef)) {
|
|
9949
|
+
appsWarnedAboutMissingThemes.add(this.appRef);
|
|
9950
|
+
console.warn(`etProvideColor="${value}" has no effect because no color themes are registered. Please make sure to add provideColorThemesWithTailwind4()`);
|
|
9951
|
+
}
|
|
9952
|
+
return;
|
|
9953
|
+
}
|
|
9954
|
+
if (isDevMode() && !this.themes.some((theme) => theme.name === value)) {
|
|
9955
|
+
console.error(`Theme ${value} does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
|
|
9956
|
+
}
|
|
9957
|
+
return createCssColorThemeName(value);
|
|
9958
|
+
}, /* @ts-ignore */
|
|
9959
|
+
...(ngDevMode ? [{ debugName: "colorName" }] : /* istanbul ignore next */ []));
|
|
9960
|
+
this.themeClass = computed(() => {
|
|
9961
|
+
const prefix = this.prefix || 'et';
|
|
9962
|
+
if (this.colorName()) {
|
|
9963
|
+
return `${prefix}-color--${this.colorName()}`;
|
|
9964
|
+
}
|
|
9965
|
+
return `${prefix}-color--inherited`;
|
|
9966
|
+
}, /* @ts-ignore */
|
|
9967
|
+
...(ngDevMode ? [{ debugName: "themeClass" }] : /* istanbul ignore next */ []));
|
|
9968
|
+
}
|
|
9969
|
+
syncWithProvider(provider) {
|
|
9970
|
+
this.currentProviderSync?.destroy();
|
|
9971
|
+
runInInjectionContext(this.injector, () => {
|
|
9972
|
+
this.currentProviderSync = effect(() => {
|
|
9973
|
+
const provideColor = provider.resolvedColor();
|
|
9974
|
+
untracked(() => {
|
|
9975
|
+
if (provideColor === undefined) {
|
|
9976
|
+
this.clearForcedColor();
|
|
9977
|
+
return;
|
|
9978
|
+
}
|
|
9979
|
+
this.forceColor(provideColor);
|
|
9980
|
+
});
|
|
9981
|
+
}, /* @ts-ignore */
|
|
9982
|
+
...(ngDevMode ? [{ debugName: "currentProviderSync" }] : /* istanbul ignore next */ []));
|
|
9983
|
+
});
|
|
9984
|
+
}
|
|
9985
|
+
stopSyncWithProvider() {
|
|
9986
|
+
const hadProviderSync = !!this.currentProviderSync;
|
|
9987
|
+
this.currentProviderSync?.destroy();
|
|
9988
|
+
this.currentProviderSync = null;
|
|
9989
|
+
if (hadProviderSync) {
|
|
9990
|
+
this.clearForcedColor();
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9993
|
+
forceColor(color) {
|
|
9994
|
+
this.forcedColor.set(color);
|
|
9995
|
+
}
|
|
9996
|
+
clearForcedColor() {
|
|
9997
|
+
if (this.forcedColor() === FORCED_COLOR_UNSET) {
|
|
9998
|
+
return;
|
|
9999
|
+
}
|
|
10000
|
+
this.forcedColor.set(FORCED_COLOR_UNSET);
|
|
10001
|
+
}
|
|
10002
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
10003
|
+
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 }); }
|
|
10004
|
+
}
|
|
10005
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, decorators: [{
|
|
10006
|
+
type: Directive,
|
|
10007
|
+
args: [{
|
|
10008
|
+
selector: '[etProvideColor]',
|
|
10009
|
+
providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }],
|
|
10010
|
+
host: {
|
|
10011
|
+
'[class]': 'themeClass()',
|
|
10012
|
+
},
|
|
10013
|
+
}]
|
|
10014
|
+
}], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "etProvideColor", required: false }] }] } });
|
|
10015
|
+
|
|
9501
10016
|
const SURFACE_PROVIDER = new InjectionToken('SurfaceProvider');
|
|
9502
10017
|
const FORCED_SURFACE_UNSET = /* @__PURE__ */ Symbol('FORCED_SURFACE_UNSET');
|
|
9503
10018
|
class ProvideSurfaceDirective {
|
|
@@ -9507,6 +10022,9 @@ class ProvideSurfaceDirective {
|
|
|
9507
10022
|
this.injector = inject(Injector);
|
|
9508
10023
|
this.parentProvider = inject(SURFACE_PROVIDER, { optional: true, skipSelf: true });
|
|
9509
10024
|
this.defaultTheme = injectDefaultSurfaceTheme();
|
|
10025
|
+
this.colorThemes = injectColorThemes({ optional: true });
|
|
10026
|
+
this.colorPrefix = injectColorThemesPrefix({ optional: true });
|
|
10027
|
+
this.colorProvider = inject(COLOR_PROVIDER, { optional: true });
|
|
9510
10028
|
this.currentProviderSync = null;
|
|
9511
10029
|
this.forcedSurface = signal(FORCED_SURFACE_UNSET, /* @ts-ignore */
|
|
9512
10030
|
...(ngDevMode ? [{ debugName: "forcedSurface" }] : /* istanbul ignore next */ []));
|
|
@@ -9552,12 +10070,23 @@ class ProvideSurfaceDirective {
|
|
|
9552
10070
|
return createCssSurfaceName(value);
|
|
9553
10071
|
}, /* @ts-ignore */
|
|
9554
10072
|
...(ngDevMode ? [{ debugName: "surfaceName" }] : /* istanbul ignore next */ []));
|
|
10073
|
+
this.colorThemeClass = computed(() => {
|
|
10074
|
+
const colorTheme = this.resolvedTheme()?.colorTheme;
|
|
10075
|
+
if (!colorTheme || this.colorProvider?.resolvedColor())
|
|
10076
|
+
return null;
|
|
10077
|
+
if (isDevMode() && !this.colorThemes?.some((theme) => theme.name === colorTheme)) {
|
|
10078
|
+
console.error(`Surface theme ${this.resolvedTheme()?.name} names the color theme ${colorTheme}, which does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
|
|
10079
|
+
}
|
|
10080
|
+
return `${this.colorPrefix || 'et'}-color--${createCssColorThemeName(colorTheme)}`;
|
|
10081
|
+
}, /* @ts-ignore */
|
|
10082
|
+
...(ngDevMode ? [{ debugName: "colorThemeClass" }] : /* istanbul ignore next */ []));
|
|
9555
10083
|
this.surfaceClass = computed(() => {
|
|
9556
10084
|
const prefix = this.prefix || 'et';
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
10085
|
+
const surfaceClass = this.surfaceName()
|
|
10086
|
+
? `${prefix}-surface--${this.surfaceName()}`
|
|
10087
|
+
: `${prefix}-surface--inherited`;
|
|
10088
|
+
const colorThemeClass = this.colorThemeClass();
|
|
10089
|
+
return colorThemeClass ? `${surfaceClass} ${colorThemeClass}` : surfaceClass;
|
|
9561
10090
|
}, /* @ts-ignore */
|
|
9562
10091
|
...(ngDevMode ? [{ debugName: "surfaceClass" }] : /* istanbul ignore next */ []));
|
|
9563
10092
|
}
|
|
@@ -9766,87 +10295,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
9766
10295
|
const COLOR_PALETTE_DEF = /* @__PURE__ */ defineStaticProvider(undefined, {
|
|
9767
10296
|
name: 'Color Palette',
|
|
9768
10297
|
});
|
|
10298
|
+
const injectColorPaletteConfig = /* @__PURE__ */ toInjectFn(COLOR_PALETTE_DEF);
|
|
10299
|
+
const selectPalette = (config, surface) => {
|
|
10300
|
+
if (Array.isArray(config))
|
|
10301
|
+
return config;
|
|
10302
|
+
return (surface === null ? undefined : config[surface]) ?? config.default;
|
|
10303
|
+
};
|
|
9769
10304
|
/**
|
|
9770
10305
|
* The color themes a subtree may offer a user as pickable choices, in the order they should appear.
|
|
9771
10306
|
* A curated slice of what `provideColorThemesWithTailwind4` registered: an app registers every theme
|
|
9772
10307
|
* it renders with, including ones nobody should pick by hand (`error`, `warning`), and adds the
|
|
9773
10308
|
* user-facing label a swatch needs.
|
|
9774
10309
|
*
|
|
9775
|
-
*
|
|
9776
|
-
*
|
|
10310
|
+
* Pass one list, or one list per registered surface theme name plus a `default`. The charts draw with
|
|
10311
|
+
* the list of the surface they sit on; a color picker always offers the `default` list.
|
|
9777
10312
|
*
|
|
9778
10313
|
* @example
|
|
9779
10314
|
* provideColorPalette([
|
|
9780
10315
|
* { token: 'brand', label: 'Team green' },
|
|
9781
10316
|
* { token: 'ocean', label: 'Training blue' },
|
|
9782
10317
|
* ]);
|
|
10318
|
+
*
|
|
10319
|
+
* @example
|
|
10320
|
+
* provideColorPalette({
|
|
10321
|
+
* default: [{ token: 'ocean', label: 'Training' }],
|
|
10322
|
+
* 'dark-card': [{ token: 'ocean-bright', label: 'Training' }],
|
|
10323
|
+
* });
|
|
9783
10324
|
*/
|
|
9784
|
-
const provideColorPalette =
|
|
9785
|
-
|
|
9786
|
-
const
|
|
9787
|
-
|
|
9788
|
-
|
|
10325
|
+
const provideColorPalette = (palette) => COLOR_PALETTE_DEF.provide(palette);
|
|
10326
|
+
function injectColorPalette(options) {
|
|
10327
|
+
const config = options ? injectColorPaletteConfig(options) : injectColorPaletteConfig();
|
|
10328
|
+
return config ? selectPalette(config, null) : null;
|
|
10329
|
+
}
|
|
9789
10330
|
/**
|
|
9790
|
-
*
|
|
9791
|
-
*
|
|
9792
|
-
* read as chrome without the app registering a neutral color theme. Apps must not register a color
|
|
9793
|
-
* theme under this name.
|
|
10331
|
+
* The palette from {@link provideColorPalette} for the surface the current subtree renders on: that
|
|
10332
|
+
* surface theme's list, else the `default` list. `null` when no palette is provided.
|
|
9794
10333
|
*/
|
|
9795
|
-
const
|
|
9796
|
-
const
|
|
9797
|
-
const
|
|
9798
|
-
name:
|
|
9799
|
-
});
|
|
9800
|
-
const ɵProvideColorThemes = /* @__PURE__ */ toProvideFn(COLOR_THEMES_DEF);
|
|
9801
|
-
const injectColorThemes = /* @__PURE__ */ toInjectFn(COLOR_THEMES_DEF);
|
|
9802
|
-
const COLOR_THEMES_PREFIX_DEF = /* @__PURE__ */ defineStaticProvider('et', {
|
|
9803
|
-
name: 'Themes Prefix',
|
|
9804
|
-
});
|
|
9805
|
-
const ɵProvideColorThemesPrefix = /* @__PURE__ */ toProvideFn(COLOR_THEMES_PREFIX_DEF);
|
|
9806
|
-
const injectColorThemesPrefix = /* @__PURE__ */ toInjectFn(COLOR_THEMES_PREFIX_DEF);
|
|
9807
|
-
const provideColorThemesWithTailwind4 = (themes, prefix = 'et') => {
|
|
9808
|
-
if (isDevMode() && themes.some((theme) => theme.name === SURFACE_COLOR)) {
|
|
9809
|
-
console.error(`[provideColorThemesWithTailwind4] "${SURFACE_COLOR}" is a reserved color name that resolves to the ambient surface's neutral swatch. Rename your theme.`);
|
|
9810
|
-
}
|
|
9811
|
-
return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix(prefix)];
|
|
9812
|
-
};
|
|
9813
|
-
const injectColorThemeByType = (type) => {
|
|
9814
|
-
const themes = injectColorThemes({ optional: true });
|
|
9815
|
-
if (!themes) {
|
|
9816
|
-
throw new Error(`[injectColorThemeByType] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9817
|
-
}
|
|
9818
|
-
const theme = themes.find((t) => t.type === type);
|
|
9819
|
-
if (!theme) {
|
|
9820
|
-
throw new Error(`[injectColorThemeByType] No color theme with type "${type}" found. Add a theme with type: "${type}" to provideColorThemesWithTailwind4().`);
|
|
9821
|
-
}
|
|
9822
|
-
if (isDevMode()) {
|
|
9823
|
-
const duplicates = themes.filter((t) => t.type === type);
|
|
9824
|
-
if (duplicates.length > 1) {
|
|
9825
|
-
console.error(`[injectColorThemeByType] Multiple themes with type "${type}" found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9826
|
-
}
|
|
9827
|
-
}
|
|
9828
|
-
return theme;
|
|
9829
|
-
};
|
|
9830
|
-
const injectErrorTheme = () => injectColorThemeByType('error');
|
|
9831
|
-
const injectWarningTheme = () => injectColorThemeByType('warning');
|
|
9832
|
-
const injectSuccessTheme = () => injectColorThemeByType('success');
|
|
9833
|
-
const injectDefaultColorTheme = () => {
|
|
9834
|
-
const themes = injectColorThemes({ optional: true });
|
|
9835
|
-
if (!themes) {
|
|
9836
|
-
throw new Error(`[injectDefaultColorTheme] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
|
|
9837
|
-
}
|
|
9838
|
-
const theme = themes.find((t) => t.isDefault);
|
|
9839
|
-
if (!theme) {
|
|
9840
|
-
throw new Error(`[injectDefaultColorTheme] No color theme with isDefault: true found. Mark one theme as the default in provideColorThemesWithTailwind4().`);
|
|
9841
|
-
}
|
|
9842
|
-
if (isDevMode()) {
|
|
9843
|
-
const duplicates = themes.filter((t) => t.isDefault);
|
|
9844
|
-
if (duplicates.length > 1) {
|
|
9845
|
-
console.error(`[injectDefaultColorTheme] Multiple themes with isDefault: true found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
|
|
9846
|
-
}
|
|
9847
|
-
}
|
|
9848
|
-
return theme;
|
|
10334
|
+
const injectSurfaceColorPalette = () => {
|
|
10335
|
+
const config = injectColorPaletteConfig({ optional: true });
|
|
10336
|
+
const surface = injectParentSurface();
|
|
10337
|
+
return computed(() => (config ? selectPalette(config, surface()?.name ?? null) : null));
|
|
9849
10338
|
};
|
|
10339
|
+
const COLOR_PALETTE = /* @__PURE__ */ toToken(COLOR_PALETTE_DEF);
|
|
9850
10340
|
|
|
9851
10341
|
/** Reads the nonce where Angular's default `CSP_NONCE` does, since these run outside any injector. */
|
|
9852
10342
|
const createNoncedStyle = () => {
|
|
@@ -10049,127 +10539,6 @@ const provideColorThemes = (themes) => {
|
|
|
10049
10539
|
return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix('et')];
|
|
10050
10540
|
};
|
|
10051
10541
|
|
|
10052
|
-
const COLOR_PROVIDER = new InjectionToken('ColorProvider');
|
|
10053
|
-
/**
|
|
10054
|
-
* The color provider attached to a bootstrapped root component (e.g. `ProvideColorDirective` in
|
|
10055
|
-
* the app component's `hostDirectives`), if any. Detached DOM such as overlay panes can't reach
|
|
10056
|
-
* it through element DI, so overlay containers fall back to this when resolving the provider to
|
|
10057
|
-
* `syncWithProvider()` - resolve at sync time, not at injection time, since root components only
|
|
10058
|
-
* register with `ApplicationRef` once bootstrap completes.
|
|
10059
|
-
*/
|
|
10060
|
-
const resolveAppRootColorProvider = (appRef) => {
|
|
10061
|
-
for (const componentRef of appRef.components) {
|
|
10062
|
-
const provider = componentRef.injector.get(COLOR_PROVIDER, null);
|
|
10063
|
-
if (provider) {
|
|
10064
|
-
return provider;
|
|
10065
|
-
}
|
|
10066
|
-
}
|
|
10067
|
-
return null;
|
|
10068
|
-
};
|
|
10069
|
-
const FORCED_COLOR_UNSET = /* @__PURE__ */ Symbol('FORCED_COLOR_UNSET');
|
|
10070
|
-
class ProvideColorDirective {
|
|
10071
|
-
constructor() {
|
|
10072
|
-
this.themes = injectColorThemes({ optional: true });
|
|
10073
|
-
this.prefix = injectColorThemesPrefix({ optional: true });
|
|
10074
|
-
this.injector = inject(Injector);
|
|
10075
|
-
this.parentProvider = inject(COLOR_PROVIDER, { optional: true, skipSelf: true });
|
|
10076
|
-
this.currentProviderSync = null;
|
|
10077
|
-
this.forcedColor = signal(FORCED_COLOR_UNSET, /* @ts-ignore */
|
|
10078
|
-
...(ngDevMode ? [{ debugName: "forcedColor" }] : /* istanbul ignore next */ []));
|
|
10079
|
-
this.color = input(undefined, { ...(ngDevMode ? { debugName: "color" } : /* istanbul ignore next */ {}), alias: 'etProvideColor' });
|
|
10080
|
-
this.effectiveColor = computed(() => {
|
|
10081
|
-
const forcedColor = this.forcedColor();
|
|
10082
|
-
if (forcedColor !== FORCED_COLOR_UNSET) {
|
|
10083
|
-
return forcedColor;
|
|
10084
|
-
}
|
|
10085
|
-
return this.color();
|
|
10086
|
-
}, /* @ts-ignore */
|
|
10087
|
-
...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
|
|
10088
|
-
/**
|
|
10089
|
-
* The color that actually applies at this provider's location: its own effective color, or -
|
|
10090
|
-
* when this provider is passive (no input, nothing forced) - the nearest ancestor provider's.
|
|
10091
|
-
* Mirrors the CSS cascade, which falls through a provider's `-color--inherited` scope class;
|
|
10092
|
-
* consumers that re-apply context across detached DOM (overlay panes) must use this instead
|
|
10093
|
-
* of `effectiveColor` or a passive in-between provider (e.g. a form field's) erases the theme.
|
|
10094
|
-
*/
|
|
10095
|
-
this.resolvedColor = computed(() => {
|
|
10096
|
-
const own = this.effectiveColor();
|
|
10097
|
-
const ownName = typeof own === 'object' && own !== null ? own.name : own;
|
|
10098
|
-
if (ownName) {
|
|
10099
|
-
return own;
|
|
10100
|
-
}
|
|
10101
|
-
return this.parentProvider?.resolvedColor();
|
|
10102
|
-
}, /* @ts-ignore */
|
|
10103
|
-
...(ngDevMode ? [{ debugName: "resolvedColor" }] : /* istanbul ignore next */ []));
|
|
10104
|
-
this.colorName = computed(() => {
|
|
10105
|
-
const raw = this.effectiveColor();
|
|
10106
|
-
const value = typeof raw === 'object' && raw !== null ? raw.name : raw;
|
|
10107
|
-
if (value === SURFACE_COLOR)
|
|
10108
|
-
return SURFACE_COLOR;
|
|
10109
|
-
if (!this.themes || !value)
|
|
10110
|
-
return;
|
|
10111
|
-
if (isDevMode() && !this.themes.some((theme) => theme.name === value)) {
|
|
10112
|
-
console.error(`Theme ${value} does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
|
|
10113
|
-
}
|
|
10114
|
-
return createCssColorThemeName(value);
|
|
10115
|
-
}, /* @ts-ignore */
|
|
10116
|
-
...(ngDevMode ? [{ debugName: "colorName" }] : /* istanbul ignore next */ []));
|
|
10117
|
-
this.themeClass = computed(() => {
|
|
10118
|
-
const prefix = this.prefix || 'et';
|
|
10119
|
-
if (this.colorName()) {
|
|
10120
|
-
return `${prefix}-color--${this.colorName()}`;
|
|
10121
|
-
}
|
|
10122
|
-
return `${prefix}-color--inherited`;
|
|
10123
|
-
}, /* @ts-ignore */
|
|
10124
|
-
...(ngDevMode ? [{ debugName: "themeClass" }] : /* istanbul ignore next */ []));
|
|
10125
|
-
}
|
|
10126
|
-
syncWithProvider(provider) {
|
|
10127
|
-
this.currentProviderSync?.destroy();
|
|
10128
|
-
runInInjectionContext(this.injector, () => {
|
|
10129
|
-
this.currentProviderSync = effect(() => {
|
|
10130
|
-
const provideColor = provider.resolvedColor();
|
|
10131
|
-
untracked(() => {
|
|
10132
|
-
if (provideColor === undefined) {
|
|
10133
|
-
this.clearForcedColor();
|
|
10134
|
-
return;
|
|
10135
|
-
}
|
|
10136
|
-
this.forceColor(provideColor);
|
|
10137
|
-
});
|
|
10138
|
-
}, /* @ts-ignore */
|
|
10139
|
-
...(ngDevMode ? [{ debugName: "currentProviderSync" }] : /* istanbul ignore next */ []));
|
|
10140
|
-
});
|
|
10141
|
-
}
|
|
10142
|
-
stopSyncWithProvider() {
|
|
10143
|
-
const hadProviderSync = !!this.currentProviderSync;
|
|
10144
|
-
this.currentProviderSync?.destroy();
|
|
10145
|
-
this.currentProviderSync = null;
|
|
10146
|
-
if (hadProviderSync) {
|
|
10147
|
-
this.clearForcedColor();
|
|
10148
|
-
}
|
|
10149
|
-
}
|
|
10150
|
-
forceColor(color) {
|
|
10151
|
-
this.forcedColor.set(color);
|
|
10152
|
-
}
|
|
10153
|
-
clearForcedColor() {
|
|
10154
|
-
if (this.forcedColor() === FORCED_COLOR_UNSET) {
|
|
10155
|
-
return;
|
|
10156
|
-
}
|
|
10157
|
-
this.forcedColor.set(FORCED_COLOR_UNSET);
|
|
10158
|
-
}
|
|
10159
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
10160
|
-
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 }); }
|
|
10161
|
-
}
|
|
10162
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, decorators: [{
|
|
10163
|
-
type: Directive,
|
|
10164
|
-
args: [{
|
|
10165
|
-
selector: '[etProvideColor]',
|
|
10166
|
-
providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }],
|
|
10167
|
-
host: {
|
|
10168
|
-
'[class]': 'themeClass()',
|
|
10169
|
-
},
|
|
10170
|
-
}]
|
|
10171
|
-
}], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "etProvideColor", required: false }] }] } });
|
|
10172
|
-
|
|
10173
10542
|
class SurfaceInteractiveStylesComponent {
|
|
10174
10543
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: SurfaceInteractiveStylesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10175
10544
|
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 }); }
|
|
@@ -10210,11 +10579,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
10210
10579
|
|
|
10211
10580
|
// Generated from package.json by tools/scripts/generate-version.js - do not edit.
|
|
10212
10581
|
/** The version of `@ethlete/core` this build was cut from. */
|
|
10213
|
-
const CORE_VERSION = '5.0.0-next.
|
|
10582
|
+
const CORE_VERSION = '5.0.0-next.63';
|
|
10214
10583
|
|
|
10215
10584
|
/**
|
|
10216
10585
|
* Generated bundle index. Do not edit.
|
|
10217
10586
|
*/
|
|
10218
10587
|
|
|
10219
|
-
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, 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_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,
|
|
10588
|
+
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 };
|
|
10220
10589
|
//# sourceMappingURL=ethlete-core.mjs.map
|