@ethlete/core 5.0.0-next.61 → 5.0.0-next.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, booleanAttribute, numberAttribute, PLATFORM_ID, EnvironmentInjector, createComponent, ViewEncapsulation, Component, Directive, model, ViewContainerRef, input, CSP_NONCE, provideEnvironmentInitializer, output, createEnvironmentInjector, Pipe, afterEveryRender } from '@angular/core';
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, index] = prop.split('[').map((part) => part.replace(']', ''));
95
- const arr = value[key];
96
- if (!Array.isArray(arr))
97
- return undefined;
98
- value = arr[+index];
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
- return { ...defaultValue, ...valueOverride };
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
- return cookie?.trim().slice(name.length + 1) ?? null;
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 = getDomain(), path = '/', sameSite = 'lax') => {
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
- if (domain) {
611
- cookieString += `; domain=${domain}`;
612
- }
613
- cookieString += `; SameSite=${sameSiteUpper}`;
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
- cookieString += '; Secure';
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 = getDomain()) => {
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
- (domain ? ';domain=' + domain : '') +
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,19 +1740,30 @@ 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
- const coerced = isBreakpointMap(value) ? value : coerce(value);
1705
- raw.set(coerced);
1706
- return isBreakpointMap(coerced) ? resolveFromMap(coerced, currentBp(), defaultValue) : coerced;
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
1758
  const instance = injector.get(BREAKPOINT_INSTANCE_TOKEN, null);
1713
- if (!instance)
1759
+ if (!instance) {
1760
+ if (ngDevMode && r !== undefined && !warnedMissingInstance) {
1761
+ warnedMissingInstance = true;
1762
+ console.warn('[ethlete] A breakpoint map was bound to an input, but its component does not call ' +
1763
+ 'provideBreakpointInstance(), so the value will not follow the breakpoint after the first render.');
1764
+ }
1714
1765
  return;
1766
+ }
1715
1767
  for (const key of Object.keys(instance)) {
1716
1768
  const val = instance[key];
1717
1769
  if (val && typeof val === 'function' && val[SIGNAL]?.transformFn === transformFn) {
@@ -1720,7 +1772,7 @@ const breakpointTransformBase = (coerce, defaultValue) => {
1720
1772
  }
1721
1773
  }
1722
1774
  }
1723
- if (!cachedSig || r === undefined || !isBreakpointMap(r))
1775
+ if (!cachedSig || r === undefined)
1724
1776
  return;
1725
1777
  const resolved = resolveFromMap(r, bp, defaultValue);
1726
1778
  untracked(() => setInputSignal(cachedSig, resolved));
@@ -1966,14 +2018,13 @@ const signalElementChildren = (el, options) => {
1966
2018
  const buildSignalEffects = (el, config) => {
1967
2019
  const elements = buildElementSignal(el);
1968
2020
  const injector = inject(Injector);
2021
+ const liveTokens = new Map(Object.entries(config.tokenMap));
1969
2022
  effect(() => {
1970
2023
  const { currentElements, previousElements } = elements();
1971
2024
  for (const previousEl of previousElements) {
1972
2025
  if (currentElements.includes(previousEl))
1973
2026
  continue;
1974
- const tokens = Object.keys(config.tokenMap)
1975
- .map((key) => key.split(' '))
1976
- .flat();
2027
+ const tokens = [...liveTokens.keys()].flatMap((key) => key.split(' '));
1977
2028
  if (!tokens.length)
1978
2029
  continue;
1979
2030
  config.cleanupFn(previousEl, tokens);
@@ -1981,7 +2032,7 @@ const buildSignalEffects = (el, config) => {
1981
2032
  for (const currentEl of currentElements) {
1982
2033
  if (previousElements.includes(currentEl))
1983
2034
  continue;
1984
- for (const [tokens, condition] of Object.entries(config.tokenMap)) {
2035
+ for (const [tokens, condition] of liveTokens) {
1985
2036
  untracked(() => {
1986
2037
  const tokenArray = tokens.split(' ');
1987
2038
  if (!tokenArray.length)
@@ -1996,6 +2047,7 @@ const buildSignalEffects = (el, config) => {
1996
2047
  const push = (tokens, signal) => {
1997
2048
  if (has(tokens))
1998
2049
  return;
2050
+ liveTokens.set(tokens, signal);
1999
2051
  runInInjectionContext(injector, () => {
2000
2052
  effects[tokens] = effect(() => {
2001
2053
  const { currentElements } = untracked(() => elements());
@@ -2017,6 +2069,7 @@ const buildSignalEffects = (el, config) => {
2017
2069
  const remove = (tokens) => {
2018
2070
  effects[tokens]?.destroy();
2019
2071
  delete effects[tokens];
2072
+ liveTokens.delete(tokens);
2020
2073
  for (const el of elements().currentElements) {
2021
2074
  const tokenArray = tokens.split(' ');
2022
2075
  if (!tokenArray.length)
@@ -2163,9 +2216,22 @@ const isElementVisible = (options) => {
2163
2216
  intersectionRatio: Math.round(Math.min(inlineIntersectionPercentage, blockIntersectionPercentage) * 100) / 100,
2164
2217
  };
2165
2218
  };
2219
+ const nearestAlignment = (elStart, elEnd, conStart, conEnd) => {
2220
+ const startOutside = elStart < conStart;
2221
+ const endOutside = elEnd > conEnd;
2222
+ const fits = elEnd - elStart <= conEnd - conStart;
2223
+ if (startOutside === endOutside)
2224
+ return null;
2225
+ if (startOutside === fits)
2226
+ return 'start';
2227
+ return 'end';
2228
+ };
2166
2229
  const getElementScrollCoordinates = (options) => {
2167
2230
  const { container, element, direction, behavior = 'smooth', origin = 'nearest', scrollBlockMargin = 0, scrollInlineMargin = 0, } = options;
2168
- if (!element || !container || !elementCanScroll(container)) {
2231
+ if (!element ||
2232
+ !container ||
2233
+ (container.isConnected && !container.contains(element)) ||
2234
+ !elementCanScroll(container)) {
2169
2235
  return {
2170
2236
  behavior,
2171
2237
  left: undefined,
@@ -2193,33 +2259,33 @@ const getElementScrollCoordinates = (options) => {
2193
2259
  let scrollTopTo = scrollTop;
2194
2260
  const relativeTop = elTop - conTop;
2195
2261
  const relativeLeft = elLeft - conLeft;
2262
+ const calculateInlineStart = () => (scrollLeftTo = scrollLeft + relativeLeft - scrollInlineMargin);
2263
+ const calculateBlockStart = () => (scrollTopTo = scrollTop + relativeTop - scrollBlockMargin);
2264
+ const calculateInlineEnd = () => (scrollLeftTo = scrollLeft + relativeLeft - conWidth + elWidth + scrollInlineMargin);
2265
+ const calculateBlockEnd = () => (scrollTopTo = scrollTop + relativeTop - conHeight + elHeight + scrollBlockMargin);
2196
2266
  const calculateScrollToStart = () => {
2197
- scrollLeftTo = scrollLeft + relativeLeft - scrollInlineMargin;
2198
- scrollTopTo = scrollTop + relativeTop - scrollBlockMargin;
2267
+ calculateInlineStart();
2268
+ calculateBlockStart();
2199
2269
  };
2200
2270
  const calculateScrollToEnd = () => {
2201
- scrollLeftTo = scrollLeft + relativeLeft - conWidth + elWidth + scrollInlineMargin;
2202
- scrollTopTo = scrollTop + relativeTop - conHeight + elHeight + scrollBlockMargin;
2271
+ calculateInlineEnd();
2272
+ calculateBlockEnd();
2203
2273
  };
2204
2274
  const calculateScrollToCenter = () => {
2205
2275
  scrollLeftTo = scrollLeft + relativeLeft - conWidth / 2 + elWidth / 2;
2206
2276
  scrollTopTo = scrollTop + relativeTop - conHeight / 2 + elHeight / 2;
2207
2277
  };
2208
2278
  const calculateScrollToNearest = () => {
2209
- const isAbove = elBottom <= conTop;
2210
- const isPartialAbove = elTop < conTop && elBottom > conTop;
2211
- const isBelow = elTop >= conBottom;
2212
- const isPartialBelow = elTop < conBottom && elBottom > conBottom;
2213
- const isLeft = elRight <= conLeft;
2214
- const isPartialLeft = elLeft < conLeft && elRight > conLeft;
2215
- const isRight = elLeft >= conRight;
2216
- const isPartialRight = elLeft < conRight && elRight > conRight;
2217
- if (isAbove || isPartialAbove || isLeft || isPartialLeft) {
2218
- calculateScrollToStart();
2219
- }
2220
- else if (isBelow || isPartialBelow || isRight || isPartialRight) {
2221
- calculateScrollToEnd();
2222
- }
2279
+ const inline = nearestAlignment(elLeft, elRight, conLeft, conRight);
2280
+ const block = nearestAlignment(elTop, elBottom, conTop, conBottom);
2281
+ if (inline === 'start')
2282
+ calculateInlineStart();
2283
+ else if (inline === 'end')
2284
+ calculateInlineEnd();
2285
+ if (block === 'start')
2286
+ calculateBlockStart();
2287
+ else if (block === 'end')
2288
+ calculateBlockEnd();
2223
2289
  };
2224
2290
  switch (origin) {
2225
2291
  case 'start':
@@ -2403,8 +2469,10 @@ const createPositionObject = (entry) => {
2403
2469
  isVisible,
2404
2470
  };
2405
2471
  };
2472
+ const unwrap = (value) => (isSignal(value) ? value() : value);
2473
+ const isZeroMargin = (margin) => !margin || /^(0(px|%)?\s*)+$/.test(margin.trim());
2406
2474
  const signalElementIntersection = (el, options) => {
2407
- const { root: rootBinding, enabled, ...observerOptions } = options ?? {};
2475
+ const { root: rootBinding, enabled, rootMargin, threshold, ...observerOptions } = options ?? {};
2408
2476
  const destroyRef = inject(DestroyRef);
2409
2477
  const elements = buildElementSignal(el);
2410
2478
  const root = firstElementSignal(rootBinding ? buildElementSignal(rootBinding) : createEmptyElementSignal());
@@ -2416,6 +2484,7 @@ const signalElementIntersection = (el, options) => {
2416
2484
  const observer = signal(null, /* @ts-ignore */
2417
2485
  ...(ngDevMode ? [{ debugName: "observer" }] : /* istanbul ignore next */ []));
2418
2486
  const currentlyObservedElements = new Set();
2487
+ let observedRootMargin;
2419
2488
  const updateIntersections = (entries) => {
2420
2489
  let currentValues = [...elementIntersectionSignal()];
2421
2490
  for (const entry of entries) {
@@ -2445,7 +2514,7 @@ const signalElementIntersection = (el, options) => {
2445
2514
  }
2446
2515
  zone.run(() => elementIntersectionSignal.set(currentValues));
2447
2516
  };
2448
- const updateIntersectionObserver = (rendered, enabled, rootEl) => {
2517
+ const updateIntersectionObserver = (rendered, enabled, rootEl, margin, thresholdValue) => {
2449
2518
  observer()?.disconnect();
2450
2519
  currentlyObservedElements.clear();
2451
2520
  if (!rendered || !enabled) {
@@ -2455,8 +2524,11 @@ const signalElementIntersection = (el, options) => {
2455
2524
  }
2456
2525
  const newObserver = new IntersectionObserver((entries) => updateIntersections(entries), {
2457
2526
  ...observerOptions,
2527
+ ...(margin !== undefined && { rootMargin: margin }),
2528
+ ...(thresholdValue !== undefined && { threshold: thresholdValue }),
2458
2529
  root: rootEl,
2459
2530
  });
2531
+ observedRootMargin = margin;
2460
2532
  observer.set(newObserver);
2461
2533
  };
2462
2534
  const updateObservedElements = (observer, elements) => {
@@ -2469,15 +2541,16 @@ const signalElementIntersection = (el, options) => {
2469
2541
  const newIntersectionValue = [];
2470
2542
  for (const el of elements.currentElements) {
2471
2543
  if (currentlyObservedElements.has(el)) {
2472
- const existingEntryIndex = currIntersectionValue.findIndex((v) => v.target === el);
2473
- const existingEntry = currIntersectionValue[existingEntryIndex];
2474
- if (!existingEntry) {
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);
2544
+ const existingEntry = currIntersectionValue.find((v) => v.target === el);
2545
+ if (existingEntry)
2546
+ newIntersectionValue.push(existingEntry);
2479
2547
  continue;
2480
2548
  }
2549
+ currentlyObservedElements.add(el);
2550
+ observer.observe(el);
2551
+ // isElementVisible ignores rootMargin, so with one set the observer's own first entry is the only correct one.
2552
+ if (!isZeroMargin(observedRootMargin))
2553
+ continue;
2481
2554
  const initialElementVisibility = isElementVisible({
2482
2555
  container: rootEl,
2483
2556
  element: el,
@@ -2497,8 +2570,6 @@ const signalElementIntersection = (el, options) => {
2497
2570
  ...intersectionEntry,
2498
2571
  ...createPositionObject(intersectionEntry),
2499
2572
  });
2500
- currentlyObservedElements.add(el);
2501
- observer.observe(el);
2502
2573
  }
2503
2574
  for (const el of elements.previousElements) {
2504
2575
  if (elements.currentElements.includes(el))
@@ -2512,9 +2583,13 @@ const signalElementIntersection = (el, options) => {
2512
2583
  const rootEl = root().currentElement;
2513
2584
  const rendered = isRendered();
2514
2585
  const enabled = isEnabled();
2515
- untracked(() => updateIntersectionObserver(rendered, enabled, rootEl));
2586
+ const margin = rootMargin === undefined ? undefined : unwrap(rootMargin);
2587
+ const thresholdValue = threshold === undefined ? undefined : unwrap(threshold);
2588
+ untracked(() => updateIntersectionObserver(rendered, enabled, rootEl, margin, thresholdValue));
2516
2589
  });
2517
- effect(() => {
2590
+ // The initial entries are measured here, not in an effect: an effect runs during change detection,
2591
+ // before elements rendered in the same pass have their layout.
2592
+ afterRenderEffect(() => {
2518
2593
  const els = elements();
2519
2594
  const obs = observer();
2520
2595
  untracked(() => updateObservedElements(obs, els));
@@ -2540,6 +2615,8 @@ const signalElementLastScrollDirection = (el) => {
2540
2615
  lastScrollLeft = 0;
2541
2616
  return of(null);
2542
2617
  }
2618
+ lastScrollTop = currentElement.scrollTop;
2619
+ lastScrollLeft = currentElement.scrollLeft;
2543
2620
  return fromEvent(currentElement, 'scroll').pipe(tap(() => {
2544
2621
  const { scrollTop, scrollLeft } = currentElement;
2545
2622
  const time = Date.now();
@@ -2644,11 +2721,12 @@ const writeScrollbarSizeToCssVariables = () => {
2644
2721
  if (!isPlatformBrowser(inject(PLATFORM_ID))) {
2645
2722
  return;
2646
2723
  }
2647
- const environmentInjector = inject(EnvironmentInjector);
2648
- if (scrollbarSizeWriters.has(environmentInjector)) {
2724
+ // The app's injector, not the caller's: a writer started from a component must outlive it.
2725
+ const appInjector = inject(ApplicationRef).injector;
2726
+ if (scrollbarSizeWriters.has(appInjector)) {
2649
2727
  return;
2650
2728
  }
2651
- scrollbarSizeWriters.add(environmentInjector);
2729
+ scrollbarSizeWriters.add(appInjector);
2652
2730
  const document = inject(DOCUMENT);
2653
2731
  const renderer = injectRenderer();
2654
2732
  const scrollbarDimensions = injectScrollbarDimensions();
@@ -2660,7 +2738,7 @@ const writeScrollbarSizeToCssVariables = () => {
2660
2738
  '--et-sw': `${dimensions.width}px`,
2661
2739
  '--et-sh': `${dimensions.height}px`,
2662
2740
  });
2663
- });
2741
+ }, { injector: appInjector });
2664
2742
  };
2665
2743
  const viewportSizeWriters = /* @__PURE__ */ new WeakSet();
2666
2744
  /**
@@ -2672,11 +2750,11 @@ const writeViewportSizeToCssVariables = () => {
2672
2750
  if (!isPlatformBrowser(inject(PLATFORM_ID))) {
2673
2751
  return;
2674
2752
  }
2675
- const environmentInjector = inject(EnvironmentInjector);
2676
- if (viewportSizeWriters.has(environmentInjector)) {
2753
+ const appInjector = inject(ApplicationRef).injector;
2754
+ if (viewportSizeWriters.has(appInjector)) {
2677
2755
  return;
2678
2756
  }
2679
- viewportSizeWriters.add(environmentInjector);
2757
+ viewportSizeWriters.add(appInjector);
2680
2758
  const document = inject(DOCUMENT);
2681
2759
  const renderer = injectRenderer();
2682
2760
  const htmlElementDimensions = injectViewportDimensions();
@@ -2688,7 +2766,7 @@ const writeViewportSizeToCssVariables = () => {
2688
2766
  '--et-vw': `${dimensions.width}px`,
2689
2767
  '--et-vh': `${dimensions.height}px`,
2690
2768
  });
2691
- });
2769
+ }, { injector: appInjector });
2692
2770
  };
2693
2771
 
2694
2772
  /** The deadzone in pixels after which the cursor drag scroll will take effect. */
@@ -2876,7 +2954,8 @@ const createInitialRoute = () => {
2876
2954
  if (!isBrowser) {
2877
2955
  return router.url;
2878
2956
  }
2879
- return window.location.pathname + window.location.search + window.location.hash;
2957
+ const path = inject(Location).path(true);
2958
+ return path.startsWith('/') ? path : `/${path}`;
2880
2959
  };
2881
2960
  /** Inject the current router event */
2882
2961
  const injectRouterEvent = /* @__PURE__ */ memoizeSignal(() => {
@@ -2943,6 +3022,7 @@ const injectRoute = /* @__PURE__ */ memoizeSignal(() => {
2943
3022
  return withoutFragment;
2944
3023
  });
2945
3024
  });
3025
+ const withInjector = (config, fn) => config?.injector ? runInInjectionContext(config.injector, fn) : fn();
2946
3026
  const createRouterState = (router) => {
2947
3027
  let route = router.routerState.snapshot.root;
2948
3028
  while (route.firstChild) {
@@ -2982,37 +3062,35 @@ const injectRouterState = /* @__PURE__ */ memoizeSignal(() => {
2982
3062
  });
2983
3063
  /** Inject a signal containing the current route fragment (the part after the # inside the url if present) */
2984
3064
  const injectFragment = (config) => {
2985
- const routerState = config?.injector
2986
- ? runInInjectionContext(config.injector, injectRouterState)
2987
- : injectRouterState();
3065
+ const routerState = withInjector(config, injectRouterState);
2988
3066
  const fragment = computed(() => routerState().fragment, /* @ts-ignore */
2989
3067
  ...(ngDevMode ? [{ debugName: "fragment" }] : /* istanbul ignore next */ []));
2990
3068
  return transformOrReturn(fragment, config);
2991
3069
  };
2992
3070
  /** Inject all currently available query parameters as a signal */
2993
- const injectQueryParams = () => {
2994
- const routerState = injectRouterState();
3071
+ const injectQueryParams = (config) => {
3072
+ const routerState = withInjector(config, injectRouterState);
2995
3073
  const queryParams = computed(() => routerState().queryParams, /* @ts-ignore */
2996
3074
  ...(ngDevMode ? [{ debugName: "queryParams" }] : /* istanbul ignore next */ []));
2997
3075
  return queryParams;
2998
3076
  };
2999
3077
  /** Inject all currently available route data as a signal */
3000
- const injectRouteData = () => {
3001
- const routerState = injectRouterState();
3078
+ const injectRouteData = (config) => {
3079
+ const routerState = withInjector(config, injectRouterState);
3002
3080
  const data = computed(() => routerState().data, /* @ts-ignore */
3003
3081
  ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
3004
3082
  return data;
3005
3083
  };
3006
3084
  /** Inject the current route title as a signal */
3007
3085
  const injectRouteTitle = (config) => {
3008
- const routerState = injectRouterState();
3086
+ const routerState = withInjector(config, injectRouterState);
3009
3087
  const title = computed(() => routerState().title, /* @ts-ignore */
3010
3088
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
3011
3089
  return transformOrReturn(title, config);
3012
3090
  };
3013
3091
  /** Inject all currently available path parameters as a signal */
3014
- const injectPathParams = () => {
3015
- const routerState = injectRouterState();
3092
+ const injectPathParams = (config) => {
3093
+ const routerState = withInjector(config, injectRouterState);
3016
3094
  const pathParams = computed(() => routerState().pathParams, /* @ts-ignore */
3017
3095
  ...(ngDevMode ? [{ debugName: "pathParams" }] : /* istanbul ignore next */ []));
3018
3096
  return pathParams;
@@ -3023,21 +3101,48 @@ const getQueryParamFromUrl = (key) => {
3023
3101
  const urlParams = new URLSearchParams(window.location.search);
3024
3102
  return urlParams.get(key);
3025
3103
  };
3026
- /** Inject a specific query parameter as a signal */
3104
+ const getAllQueryParamsFromUrl = (key) => {
3105
+ if (typeof window === 'undefined')
3106
+ return [];
3107
+ return new URLSearchParams(window.location.search).getAll(key);
3108
+ };
3109
+ const toValues = (value) => {
3110
+ if (value === undefined || value === null)
3111
+ return [];
3112
+ return Array.isArray(value) ? value : [value];
3113
+ };
3114
+ /**
3115
+ * Inject a specific query parameter as a signal. A repeated parameter (`?tag=a&tag=b`) resolves to its first value;
3116
+ * use `injectQueryParamAll` to read every value.
3117
+ */
3027
3118
  const injectQueryParam = (key, config) => {
3028
- const queryParams = injectQueryParams();
3029
- const src = computed(() => queryParams()[key] ?? (config?.requireSync ? getQueryParamFromUrl(key) : null));
3119
+ const queryParams = injectQueryParams(config);
3120
+ const src = computed(() => {
3121
+ const first = toValues(queryParams()[key])[0];
3122
+ return first ?? (config?.requireSync ? getQueryParamFromUrl(key) : null);
3123
+ }, /* @ts-ignore */
3124
+ ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
3125
+ return transformOrReturn(src, config);
3126
+ };
3127
+ /** Inject every value of a specific query parameter as a signal. Resolves to an empty array when it is absent. */
3128
+ const injectQueryParamAll = (key, config) => {
3129
+ const queryParams = injectQueryParams(config);
3130
+ const src = computed(() => {
3131
+ const values = toValues(queryParams()[key]);
3132
+ return values.length || !config?.requireSync ? values : getAllQueryParamsFromUrl(key);
3133
+ }, /* @ts-ignore */
3134
+ ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
3030
3135
  return transformOrReturn(src, config);
3031
3136
  };
3032
3137
  /** Inject a specific route data item as a signal */
3033
3138
  const injectRouteDataItem = (key, config) => {
3034
- const data = injectRouteData();
3139
+ const data = injectRouteData(config);
3035
3140
  const src = computed(() => data()[key] ?? null);
3036
3141
  return transformOrReturn(src, config);
3037
3142
  };
3038
3143
  /** Inject a specific path parameter as a signal */
3039
3144
  const injectPathParam = (key, config) => {
3040
- const pathParams = injectPathParams();
3145
+ const pathParams = injectPathParams(config);
3041
3146
  const src = computed(() => pathParams()[key] ?? null);
3042
3147
  return transformOrReturn(src, config);
3043
3148
  };
@@ -3110,9 +3215,9 @@ const injectPathParamChanges = /* @__PURE__ */ memoizeSignal(() => {
3110
3215
  * protected cameFromCart = this.navState?.fromCart ?? false;
3111
3216
  * }
3112
3217
  */
3113
- const injectRouterNavigationState = () => {
3114
- const router = inject(Router);
3115
- return router.getCurrentNavigation()?.extras.state ?? null;
3218
+ const injectRouterNavigationState = (config) => {
3219
+ const router = withInjector(config, () => inject(Router));
3220
+ return router.currentNavigation()?.extras.state ?? null;
3116
3221
  };
3117
3222
 
3118
3223
  const ET_DISABLE_SCROLL_TOP = /* @__PURE__ */ Symbol('ET_DISABLE_SCROLL_TOP');
@@ -3341,7 +3446,7 @@ const setupScrollRestoration = (config = {}) => {
3341
3446
  // Read off the navigation rather than `history.state`: a symbol key does not survive being
3342
3447
  // structured-cloned into the history entry, which is what keeps the mark from outliving this
3343
3448
  // navigation and re-firing when the user later pops back to the entry it created.
3344
- const navigationState = router.getCurrentNavigation()?.extras.state;
3449
+ const navigationState = router.currentNavigation()?.extras.state;
3345
3450
  wantsRestore = !isPopstate && navigationState?.[ET_RESTORE_SCROLL] === true;
3346
3451
  };
3347
3452
  const onNavigationEnd = (event) => {
@@ -3434,7 +3539,7 @@ const setupScrollRestoration = (config = {}) => {
3434
3539
  const DISABLE_LOGGER_PARAM = 'et-logger-quiet';
3435
3540
  const createLogger = (config) => {
3436
3541
  const { scope, feature } = config;
3437
- const disableLogging = injectQueryParam(DISABLE_LOGGER_PARAM);
3542
+ const disableLogging = injectQueryParam(DISABLE_LOGGER_PARAM, { transform: (value) => value !== null });
3438
3543
  return {
3439
3544
  log: (...args) => {
3440
3545
  if (!disableLogging())
@@ -3465,9 +3570,10 @@ const escapeHtmlPreservingEntities = (str) => str
3465
3570
  * text only. */
3466
3571
  const MARKDOWN_VERBATIM_ATTR = 'data-markdown-verbatim';
3467
3572
  const VERBATIM_RE = /* @__PURE__ */ new RegExp(`<span\\b[^>]*\\b${MARKDOWN_VERBATIM_ATTR}\\b[^>]*>([^<]*)<\\/span>`, 'gi');
3573
+ const codePointToString = (codePoint) => (codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : '\ufffd');
3468
3574
  const decodeUrlCharacterReferences = (url) => url
3469
- .replace(/&#(\d+);?/g, (_, value) => String.fromCodePoint(Number(value)))
3470
- .replace(/&#x([0-9a-f]+);?/gi, (_, value) => String.fromCodePoint(Number.parseInt(value, 16)))
3575
+ .replace(/&#(\d+);?/g, (_, value) => codePointToString(Number(value)))
3576
+ .replace(/&#x([0-9a-f]+);?/gi, (_, value) => codePointToString(Number.parseInt(value, 16)))
3471
3577
  .replace(/&(colon|tab|newline);/gi, (_, entity) => entity.toLowerCase() === 'colon' ? ':' : entity.toLowerCase() === 'tab' ? '\t' : '\n');
3472
3578
  /** Whether `url` may be embedded as an image `src`: everything except `javascript:` and `vbscript:`
3473
3579
  * URLs and non-image `data:` URLs, however they are cased, spaced or entity-encoded. */
@@ -3584,6 +3690,10 @@ const processInline = (text) => {
3584
3690
  .replace(placeholderRe('IC'), (match, i) => inlineCodes[+i] ?? match)
3585
3691
  .replace(placeholderRe('ESC'), (match, i) => escapes[+i] ?? match);
3586
3692
  };
3693
+ const parseStartNumber = (openTag) => {
3694
+ const value = /\bstart\s*=\s*["']?(-?\d+)/i.exec(openTag)?.[1];
3695
+ return value === undefined ? 1 : Number(value);
3696
+ };
3587
3697
  /** The first top-level `<ul>`/`<ol>` in `html`, matched with balanced nesting. */
3588
3698
  const findList = (html) => {
3589
3699
  const open = /<(ul|ol)\b[^>]*>/i.exec(html);
@@ -3602,6 +3712,7 @@ const findList = (html) => {
3602
3712
  start: open.index,
3603
3713
  end: tag.lastIndex,
3604
3714
  ordered: openTagName.toLowerCase() === 'ol',
3715
+ startNumber: parseStartNumber(open[0]),
3605
3716
  inner: html.slice(innerStart, match.index),
3606
3717
  };
3607
3718
  }
@@ -3701,15 +3812,15 @@ const buildBlockquoteHtml = (lines) => {
3701
3812
  return `<blockquote>${html || '<br>'}</blockquote>`;
3702
3813
  };
3703
3814
  /** Serializes a list's inner HTML to Markdown, indenting nested lists two spaces per level. */
3704
- const listToMarkdown = (inner, ordered, depth) => {
3815
+ const listToMarkdown = (inner, ordered, startNumber, depth) => {
3705
3816
  const indent = ' '.repeat(depth);
3706
- let n = 1;
3817
+ let n = startNumber;
3707
3818
  return findListItems(inner)
3708
3819
  .map((itemInner) => {
3709
3820
  let content = itemInner;
3710
3821
  let nestedMarkdown = '';
3711
3822
  for (let nested = findList(content); nested; nested = findList(content)) {
3712
- nestedMarkdown += `\n${listToMarkdown(nested.inner, nested.ordered, depth + 1)}`;
3823
+ nestedMarkdown += `\n${listToMarkdown(nested.inner, nested.ordered, nested.startNumber, depth + 1)}`;
3713
3824
  content = content.slice(0, nested.start) + content.slice(nested.end);
3714
3825
  }
3715
3826
  const marker = ordered ? `${n++}. ` : '- ';
@@ -3723,7 +3834,12 @@ const parseListLines = (lines) => lines
3723
3834
  .map((line) => {
3724
3835
  const match = /^(\s*)([-*+]|\d+\.)\s+(.*)$/.exec(line);
3725
3836
  return match
3726
- ? { indent: Math.floor((match[1] ?? '').length / 2), ordered: /\d/.test(match[2] ?? ''), text: match[3] ?? '' }
3837
+ ? {
3838
+ indent: Math.floor((match[1] ?? '').length / 2),
3839
+ ordered: /\d/.test(match[2] ?? ''),
3840
+ number: Number.parseInt(match[2] ?? '', 10),
3841
+ text: match[3] ?? '',
3842
+ }
3727
3843
  : null;
3728
3844
  })
3729
3845
  .filter((line) => line !== null);
@@ -3744,7 +3860,9 @@ const buildListHtml = (lines, start, baseIndent) => {
3744
3860
  items += `<li>${item}</li>`;
3745
3861
  }
3746
3862
  const tag = ordered ? 'ol' : 'ul';
3747
- return { html: `<${tag}>${items}</${tag}>`, next: i };
3863
+ const startNumber = ordered ? (lines[start]?.number ?? 1) : 1;
3864
+ const startAttr = startNumber === 1 ? '' : ` start="${startNumber}"`;
3865
+ return { html: `<${tag}${startAttr}>${items}</${tag}>`, next: i };
3748
3866
  };
3749
3867
  const escapeMarkdownText = (text) => text
3750
3868
  .replace(/[\\`[\]]/g, '\\$&')
@@ -3938,7 +4056,7 @@ const htmlToMarkdown = (html) => {
3938
4056
  }
3939
4057
  // Lists - replace each top-level list with its recursively-serialized Markdown (handles nesting).
3940
4058
  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)}`;
4059
+ md = `${md.slice(0, list.start)}\n${listToMarkdown(list.inner, list.ordered, list.startNumber, 0)}\n${md.slice(list.end)}`;
3942
4060
  }
3943
4061
  // Tables
3944
4062
  md = md.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, tableContent) => {
@@ -4536,6 +4654,22 @@ const createUserConsentProvider = (options) => ({
4536
4654
  }),
4537
4655
  });
4538
4656
 
4657
+ /**
4658
+ * Declares the `et-visually-hidden` class as a styles-only component: content that stays in the
4659
+ * accessibility tree but is clipped out of the layout. Mount it from any component whose template
4660
+ * uses the class.
4661
+ */
4662
+ class VisuallyHiddenStylesComponent {
4663
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: VisuallyHiddenStylesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4664
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.6", type: VisuallyHiddenStylesComponent, isStandalone: true, selector: "et-visually-hidden-styles", ngImport: i0, template: '', isInline: true, styles: ["@layer components{.et-visually-hidden{position:absolute;inline-size:1px;block-size:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}}\n"], encapsulation: i0.ViewEncapsulation.None }); }
4665
+ }
4666
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: VisuallyHiddenStylesComponent, decorators: [{
4667
+ type: Component,
4668
+ args: [{ selector: 'et-visually-hidden-styles', template: '', encapsulation: ViewEncapsulation.None, styles: ["@layer components{.et-visually-hidden{position:absolute;inline-size:1px;block-size:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}}\n"] }]
4669
+ }] });
4670
+ /** Mounts {@link VisuallyHiddenStylesComponent}. Call from an injection context. */
4671
+ const mountVisuallyHidden = () => injectStyleManager().mount(VisuallyHiddenStylesComponent);
4672
+
4539
4673
  /**
4540
4674
  * Declares the `--ease-*` custom properties that every animated stylesheet in the SDK
4541
4675
  * transitions with, as a styles-only component. Mount it from any component or directive whose
@@ -4670,6 +4804,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
4670
4804
  }]
4671
4805
  }], ctorParameters: () => [] });
4672
4806
 
4807
+ // codes 9000-9099
4808
+ const ANIMATION_ERROR_CODES = {
4809
+ /** `*etAnimatedIf` was used without an `[etAnimatedLifecycle]` element around it. */
4810
+ MISSING_ANIMATED_LIFECYCLE: 9000,
4811
+ };
4812
+
4673
4813
  /** Runs `cb` two frames from now. Returns a function that cancels it if it has not run yet. */
4674
4814
  const nextFrame = (cb) => {
4675
4815
  if (typeof requestAnimationFrame === 'undefined') {
@@ -4729,7 +4869,12 @@ class AnimatedLifecycleDirective {
4729
4869
  this.transitionIdCounter = 0;
4730
4870
  this.forcedThisFrame = false;
4731
4871
  this.forcedFrameResetId = null;
4732
- this.state$ = new BehaviorSubject('init');
4872
+ this.stateSubject$ = new BehaviorSubject('init');
4873
+ this._state = signal('init', /* @ts-ignore */
4874
+ ...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
4875
+ this.state = this._state.asReadonly();
4876
+ /** Emits the current lifecycle state synchronously on subscribe, then every change. */
4877
+ this.state$ = this.stateSubject$.asObservable();
4733
4878
  this.stateChange = outputFromObservable(this.state$);
4734
4879
  this.skipNextEnter = model(false, /* @ts-ignore */
4735
4880
  ...(ngDevMode ? [{ debugName: "skipNextEnter" }] : /* istanbul ignore next */ []));
@@ -4752,7 +4897,7 @@ class AnimatedLifecycleDirective {
4752
4897
  this.isConstructed = true;
4753
4898
  }
4754
4899
  enter() {
4755
- const currentState = this.state$.value;
4900
+ const currentState = this.state();
4756
4901
  if (currentState === 'entering')
4757
4902
  return;
4758
4903
  if ((currentState === 'init' && !this.isConstructed) || this.skipNextEnter()) {
@@ -4822,7 +4967,7 @@ class AnimatedLifecycleDirective {
4822
4967
  previousCancel$.complete();
4823
4968
  }
4824
4969
  leave() {
4825
- const currentState = this.state$.value;
4970
+ const currentState = this.state();
4826
4971
  if (currentState === 'leaving')
4827
4972
  return;
4828
4973
  if (currentState === 'init') {
@@ -4923,11 +5068,11 @@ class AnimatedLifecycleDirective {
4923
5068
  this.addClass(activeClass);
4924
5069
  fromNextFrame()
4925
5070
  .pipe(tap(() => {
4926
- if (this.state$.value === expectedState) {
5071
+ if (this.state() === expectedState) {
4927
5072
  this.removeClass(fromClass);
4928
5073
  this.addClass(toClass);
4929
5074
  }
4930
- }), switchMap(() => this.whenAnimationsSettled()), tap(() => this.debugLog(`${transitionId}: settled`)), filter(() => this.state$.value === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
5075
+ }), switchMap(() => this.whenAnimationsSettled()), tap(() => this.debugLog(`${transitionId}: settled`)), filter(() => this.state() === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
4931
5076
  .subscribe();
4932
5077
  }
4933
5078
  handleInterruptedTransition(config) {
@@ -4935,7 +5080,7 @@ class AnimatedLifecycleDirective {
4935
5080
  this.removeClasses(...removeClasses);
4936
5081
  addClasses.forEach((cls) => this.addClass(cls));
4937
5082
  this.whenAnimationsSettled()
4938
- .pipe(tap(() => this.debugLog(`${transitionId}: interrupt settled`)), filter(() => this.state$.value === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
5083
+ .pipe(tap(() => this.debugLog(`${transitionId}: interrupt settled`)), filter(() => this.state() === expectedState), tap(onComplete), take(1), takeUntil(cancelSignal), takeUntilDestroyed(this.destroyRef))
4939
5084
  .subscribe();
4940
5085
  }
4941
5086
  /**
@@ -4970,8 +5115,9 @@ class AnimatedLifecycleDirective {
4970
5115
  this.renderer.removeClass(this.element, ...classNames);
4971
5116
  }
4972
5117
  updateState(newState) {
4973
- this.debugLog(`state: ${this.state$.value} → ${newState}`);
4974
- this.state$.next(newState);
5118
+ this.debugLog(`state: ${this.state()} → ${newState}`);
5119
+ this._state.set(newState);
5120
+ this.stateSubject$.next(newState);
4975
5121
  }
4976
5122
  debugLog(message, data) {
4977
5123
  const el = this.element;
@@ -5011,27 +5157,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
5011
5157
 
5012
5158
  const ANIMATED_IF_TOKEN = new InjectionToken('ANIMATED_IF_TOKEN');
5013
5159
  class AnimatedIfDirective {
5160
+ static ngTemplateContextGuard(_dir, _ctx) {
5161
+ return true;
5162
+ }
5014
5163
  constructor() {
5015
5164
  this.templateRef = inject(TemplateRef);
5016
5165
  this.viewContainerRef = inject(ViewContainerRef);
5017
- this.animatedLifecycle = inject(ANIMATED_LIFECYCLE_TOKEN);
5166
+ this.animatedLifecycle = inject(ANIMATED_LIFECYCLE_TOKEN, { optional: true });
5018
5167
  this.hasView = false;
5168
+ this.context = { $implicit: null, etAnimatedIf: null };
5019
5169
  this.ifValue = input(null, { ...(ngDevMode ? { debugName: "ifValue" } : /* istanbul ignore next */ {}), alias: 'etAnimatedIf' });
5170
+ const animatedLifecycle = this.animatedLifecycle;
5171
+ if (!animatedLifecycle) {
5172
+ throw new RuntimeError(ANIMATION_ERROR_CODES.MISSING_ANIMATED_LIFECYCLE, '*etAnimatedIf needs an element with [etAnimatedLifecycle] around it to animate against.');
5173
+ }
5020
5174
  toObservable(this.ifValue)
5021
5175
  .pipe(switchMap((value) => {
5022
5176
  if (value) {
5177
+ this.context.$implicit = value;
5178
+ this.context.etAnimatedIf = value;
5023
5179
  if (!this.hasView) {
5024
- this.viewContainerRef.createEmbeddedView(this.templateRef);
5180
+ this.viewContainerRef.createEmbeddedView(this.templateRef, this.context);
5025
5181
  this.hasView = true;
5026
5182
  }
5027
- this.animatedLifecycle.enter();
5183
+ if (animatedLifecycle.state() !== 'entered')
5184
+ animatedLifecycle.enter();
5028
5185
  return of(null);
5029
5186
  }
5030
5187
  if (!this.hasView) {
5031
5188
  return of(null);
5032
5189
  }
5033
- this.animatedLifecycle.leave();
5034
- return this.animatedLifecycle.state$.pipe(filter((state) => state === 'left'), take(1), tap(() => {
5190
+ animatedLifecycle.leave();
5191
+ return animatedLifecycle.state$.pipe(filter((state) => state === 'left'), take(1), tap(() => {
5035
5192
  this.viewContainerRef.clear();
5036
5193
  this.hasView = false;
5037
5194
  }));
@@ -5207,6 +5364,23 @@ const createFlipAnimation = (config) => {
5207
5364
  };
5208
5365
  };
5209
5366
 
5367
+ /** The confirm an unsaved-changes tracker or guard runs when its own config has none. */
5368
+ const UNSAVED_CHANGES_CONFIRM = /* @__PURE__ */ new InjectionToken('UNSAVED_CHANGES_CONFIRM');
5369
+ /**
5370
+ * Sets the confirm every unsaved-changes tracker and guard below this injector runs when its config has no
5371
+ * `confirm`. `factory` runs once, in an injection context, and returns the confirm.
5372
+ *
5373
+ * @example
5374
+ * provideUnsavedChangesConfirm(() => {
5375
+ * const dialogs = inject(MyDialogs);
5376
+ * return () => dialogs.confirmDiscard();
5377
+ * });
5378
+ */
5379
+ const provideUnsavedChangesConfirm = (factory) => ({
5380
+ provide: UNSAVED_CHANGES_CONFIRM,
5381
+ useFactory: factory,
5382
+ });
5383
+
5210
5384
  const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() => {
5211
5385
  const _registrations = signal([], /* @ts-ignore */
5212
5386
  ...(ngDevMode ? [{ debugName: "_registrations" }] : /* istanbul ignore next */ []));
@@ -5290,6 +5464,11 @@ const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() =>
5290
5464
  const provideUnsavedChangesCoordinator = /* @__PURE__ */ toProvideFn(UNSAVED_CHANGES_COORDINATOR_DEF);
5291
5465
  const injectUnsavedChangesCoordinator = /* @__PURE__ */ toInjectFn(UNSAVED_CHANGES_COORDINATOR_DEF);
5292
5466
 
5467
+ const UNSAVED_CHANGES_ERROR_CODES = {
5468
+ /** An unsaved-changes tracker or guard has no `confirm`, and no `provideUnsavedChangesConfirm()` is in scope. */
5469
+ MISSING_CONFIRM: 9100,
5470
+ };
5471
+
5293
5472
  const isAbstractControl = (value) => !!value &&
5294
5473
  typeof value === 'object' &&
5295
5474
  typeof value.getRawValue === 'function' &&
@@ -5313,22 +5492,25 @@ const normalizeUnsavedChangesSource = (source) => {
5313
5492
  const peek = untracked(source);
5314
5493
  // A signal that currently holds a FieldTree - or nothing yet (the late/async form case).
5315
5494
  if (peek === null || peek === undefined || isFieldTree(peek)) {
5316
- const fieldSignal = source;
5495
+ const lateSource = source;
5317
5496
  return {
5318
5497
  value: computed(() => {
5319
- const field = fieldSignal();
5320
- return isFieldTree(field) ? field().value() : null;
5498
+ const current = lateSource();
5499
+ if (isFieldTree(current))
5500
+ return current().value();
5501
+ return (current ?? null);
5321
5502
  }),
5322
5503
  setValue: (next) => {
5323
- const field = untracked(fieldSignal);
5324
- if (isFieldTree(field)) {
5325
- field().value.set(next);
5504
+ const current = untracked(lateSource);
5505
+ if (isFieldTree(current)) {
5506
+ current().value.set(next);
5507
+ }
5508
+ else if (current !== null && current !== undefined && 'set' in source) {
5509
+ source.set(next);
5326
5510
  }
5327
5511
  },
5328
5512
  };
5329
5513
  }
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
5514
  const writable = source;
5333
5515
  return { value: writable, setValue: (next) => writable.set(next) };
5334
5516
  }
@@ -5497,6 +5679,7 @@ const FAVICON_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
5497
5679
  }
5498
5680
  return new Promise((resolve) => {
5499
5681
  const image = new Image();
5682
+ image.crossOrigin = 'anonymous';
5500
5683
  image.onload = () => {
5501
5684
  baseImage = image;
5502
5685
  resolve(image);
@@ -6488,12 +6671,12 @@ const createUnsavedChangesTabLock = (config) => {
6488
6671
  return { destroy };
6489
6672
  };
6490
6673
 
6491
- const toBooleanPromise = (result) => {
6674
+ const toBooleanPromise = (result, signal) => {
6492
6675
  if (result instanceof Promise) {
6493
6676
  return result.then(Boolean);
6494
6677
  }
6495
6678
  if (isObservable(result)) {
6496
- return firstValueFrom(result.pipe(take(1), map(Boolean), defaultIfEmpty(false), catchError(() => of(false))));
6679
+ return firstValueFrom(result.pipe(take(1), map(Boolean), takeUntil(fromEvent(signal, 'abort')), defaultIfEmpty(false), catchError(() => of(false))));
6497
6680
  }
6498
6681
  return Promise.resolve(Boolean(result));
6499
6682
  };
@@ -6514,7 +6697,11 @@ const toBooleanPromise = (result) => {
6514
6697
  */
6515
6698
  const createUnsavedChangesTracker = (config) => {
6516
6699
  assertInInjectionContext(createUnsavedChangesTracker);
6517
- const { compareFn, confirm } = config;
6700
+ const { compareFn } = config;
6701
+ const confirm = config.confirm ?? inject(UNSAVED_CHANGES_CONFIRM, { optional: true });
6702
+ if (!confirm && isDevMode()) {
6703
+ throw new RuntimeError(UNSAVED_CHANGES_ERROR_CODES.MISSING_CONFIRM, '[createUnsavedChangesTracker] No confirm: pass `confirm` or call `provideUnsavedChangesConfirm()` in a parent injector.');
6704
+ }
6518
6705
  const normalized = normalizeUnsavedChangesSource(config.source);
6519
6706
  const hasExplicitDefault = config.defaultValue !== undefined;
6520
6707
  const initialDefault = hasExplicitDefault
@@ -6565,7 +6752,10 @@ const createUnsavedChangesTracker = (config) => {
6565
6752
  if (untracked(_isAbandoned) || !untracked(hasChanges)) {
6566
6753
  return Promise.resolve(true);
6567
6754
  }
6568
- return coordinator.runCheck((context) => toBooleanPromise(confirm(untracked(normalized.value), context)));
6755
+ if (!confirm) {
6756
+ return Promise.resolve(true);
6757
+ }
6758
+ return coordinator.runCheck((context) => toBooleanPromise(confirm(untracked(normalized.value), context), context.signal));
6569
6759
  };
6570
6760
  const refreshDefaultValue = () => _defaultValue.set(untracked(normalized.value));
6571
6761
  const restoreDefaultValue = () => {
@@ -6877,12 +7067,19 @@ class ClickOutsideDirective {
6877
7067
  this.elementRef = inject(ElementRef);
6878
7068
  this.document = inject(DOCUMENT);
6879
7069
  this.didClickOutside = output({ alias: 'etClickOutside' });
7070
+ this.pressStartedInside = false;
7071
+ fromEvent(this.document.documentElement, 'pointerdown', { capture: true })
7072
+ .pipe(tap((event) => {
7073
+ this.pressStartedInside = event.composedPath().includes(this.elementRef.nativeElement);
7074
+ }), takeUntilDestroyed())
7075
+ .subscribe();
6880
7076
  fromEvent(this.document.documentElement, 'click')
6881
7077
  .pipe(tap((event) => {
6882
7078
  const hostElement = this.elementRef.nativeElement;
6883
7079
  const activeElement = event.target;
6884
- const isInside = hostElement.contains(activeElement);
6885
- if (isInside)
7080
+ const startedInside = this.pressStartedInside;
7081
+ this.pressStartedInside = false;
7082
+ if (startedInside || event.composedPath().includes(hostElement))
6886
7083
  return;
6887
7084
  if (isOnHigherOverlayLayer(activeElement, resolveOverlayLayer(hostElement)))
6888
7085
  return;
@@ -6906,7 +7103,7 @@ class RepeatDirective {
6906
7103
  this.viewContainerRef = inject(ViewContainerRef);
6907
7104
  this.repeatCount = input(2, { ...(ngDevMode ? { debugName: "repeatCount" } : /* istanbul ignore next */ {}), alias: 'etRepeat', transform: numberAttribute });
6908
7105
  effect(() => {
6909
- const count = Math.max(0, this.repeatCount());
7106
+ const count = Math.max(0, this.repeatCount()) || 0;
6910
7107
  while (this.viewContainerRef.length < count) {
6911
7108
  this.viewContainerRef.createEmbeddedView(this.mainTemplateRef);
6912
7109
  }
@@ -7036,7 +7233,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
7036
7233
 
7037
7234
  // jsdom and other non-browser DOMs ship no pointer capture; the document listeners track the
7038
7235
  // gesture either way, so a failure here only costs events over a cross-origin frame.
7039
- const capturePointer = (el, pointerId) => {
7236
+ const capturePointer$1 = (el, pointerId) => {
7040
7237
  try {
7041
7238
  el.setPointerCapture(pointerId);
7042
7239
  }
@@ -7066,7 +7263,7 @@ const setupDragObservable = (startEvent, el, commitThreshold) => {
7066
7263
  return EMPTY;
7067
7264
  }
7068
7265
  committed = true;
7069
- capturePointer(el, pointerId);
7266
+ capturePointer$1(el, pointerId);
7070
7267
  lastX = e.clientX;
7071
7268
  lastY = e.clientY;
7072
7269
  // Emit the catch-up move together with start so consumers snap to the pointer
@@ -8065,10 +8262,10 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8065
8262
  ...(ngDevMode ? [{ debugName: "_componentInstance" }] : /* istanbul ignore next */ []));
8066
8263
  let positionUpdater = null;
8067
8264
  let backdropUpdater = null;
8068
- const beforeOpenedSubject = new Subject();
8069
- const afterOpenedSubject = new Subject();
8070
- const beforeClosedSubject = new Subject();
8071
- const afterClosedSubject = new Subject();
8265
+ const beforeOpenedSubject = new ReplaySubject(1);
8266
+ const afterOpenedSubject = new ReplaySubject(1);
8267
+ const beforeClosedSubject = new ReplaySubject(1);
8268
+ const afterClosedSubject = new ReplaySubject(1);
8072
8269
  const closeGuards = new Set();
8073
8270
  return {
8074
8271
  id,
@@ -8076,7 +8273,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8076
8273
  elements,
8077
8274
  state: _state.asReadonly(),
8078
8275
  componentInstance: _componentInstance.asReadonly(),
8079
- beforeOpenedSubject,
8080
8276
  close(result, source = 'api') {
8081
8277
  if (_state() === 'closing' || _state() === 'closed') {
8082
8278
  return;
@@ -8119,6 +8315,10 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8119
8315
  attachComponentRef(componentRef) {
8120
8316
  _componentInstance.set(componentRef.instance);
8121
8317
  },
8318
+ markBeforeOpened() {
8319
+ beforeOpenedSubject.next();
8320
+ beforeOpenedSubject.complete();
8321
+ },
8122
8322
  attachPositionUpdater(updater) {
8123
8323
  positionUpdater = updater;
8124
8324
  },
@@ -8187,6 +8387,14 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8187
8387
  // Synchronous teardown for each currently-mounted overlay, run when the runtime's injector is
8188
8388
  // destroyed (app teardown). Registered on mount, removed once the overlay is destroyed normally.
8189
8389
  const mountedTeardowns = new Set();
8390
+ const armedReopenSwallows = new Set();
8391
+ const focusRestoreChains = new WeakMap();
8392
+ const resolveFocusRestoreChain = (focused) => {
8393
+ if (!focused)
8394
+ return [];
8395
+ const owner = openEntriesState().find((entry) => entry.elements.hostElement.contains(focused));
8396
+ return [focused, ...((owner && focusRestoreChains.get(owner)) ?? [])];
8397
+ };
8190
8398
  const getRootElement = (targetDocument, zIndex) => {
8191
8399
  let documentRoots = rootElements.get(targetDocument);
8192
8400
  if (!documentRoots) {
@@ -8259,9 +8467,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8259
8467
  const paneElement = renderer.createElement('div');
8260
8468
  const backdropElement = signal(null, /* @ts-ignore */
8261
8469
  ...(ngDevMode ? [{ debugName: "backdropElement" }] : /* istanbul ignore next */ []));
8262
- const previousFocusedElement = isHTMLOrSVGElement(targetDocument.activeElement)
8263
- ? targetDocument.activeElement
8264
- : null;
8470
+ const focusRestoreChain = resolveFocusRestoreChain(isHTMLOrSVGElement(targetDocument.activeElement) ? targetDocument.activeElement : null);
8265
8471
  const autoFocus = config.autoFocus ?? 'first-tabbable';
8266
8472
  let currentHasBackdrop = config.hasBackdrop !== false;
8267
8473
  let currentPositionStrategy = config.positionStrategy;
@@ -8338,8 +8544,8 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8338
8544
  renderer.addClass(paneElement, 'et-overlay-runtime-pane', ...(config.paneClass ?? []));
8339
8545
  appRef.attachView(componentRef.hostView);
8340
8546
  overlayRef.attachComponentRef(componentRef);
8341
- overlayRef.beforeOpenedSubject.next();
8342
- overlayRef.beforeOpenedSubject.complete();
8547
+ overlayRef.markBeforeOpened();
8548
+ focusRestoreChains.set(overlayRef, focusRestoreChain);
8343
8549
  openEntriesState.update((entries) => [...entries, overlayRef]);
8344
8550
  let positionCleanup = setupPositioning(activeConfig(), hostElement, paneElement, overlayRef, renderer);
8345
8551
  cleanupFns.push(() => positionCleanup());
@@ -8394,8 +8600,8 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8394
8600
  }
8395
8601
  openEntriesState.update((entries) => entries.filter((entry) => entry !== overlayRef));
8396
8602
  maybeDestroyRootElements(targetDocument);
8397
- if (config.restoreFocus !== false && overlayStillOwnsFocus && previousFocusedElement?.isConnected) {
8398
- previousFocusedElement.focus({ preventScroll: true });
8603
+ if (config.restoreFocus !== false && overlayStillOwnsFocus) {
8604
+ focusRestoreChain.find((element) => element.isConnected)?.focus({ preventScroll: true });
8399
8605
  }
8400
8606
  overlayRef.finishClose(closeEvent);
8401
8607
  };
@@ -8413,7 +8619,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8413
8619
  return;
8414
8620
  }
8415
8621
  const lifecycle = getAnimatedLifecycle(componentRef);
8416
- animationDebugLog(`runtime ${config.id}`, `close requested (source "${closeEvent.source}", lifecycle state "${lifecycle?.state$.value ?? 'none'}", delegate ${config.animationDelegate?.leave ? 'yes' : 'no'})`);
8622
+ animationDebugLog(`runtime ${config.id}`, `close requested (source "${closeEvent.source}", lifecycle state "${lifecycle?.state() ?? 'none'}", delegate ${config.animationDelegate?.leave ? 'yes' : 'no'})`);
8417
8623
  // The reference element is gone - there is nothing left to animate away from, so tear the
8418
8624
  // overlay down synchronously instead of playing a leave transition from a stale position.
8419
8625
  if (!lifecycle || closeEvent.source === 'reference-detached') {
@@ -8470,12 +8676,22 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8470
8676
  const originElement = getOriginElement();
8471
8677
  if (originElement && originElement.contains(target)) {
8472
8678
  const swallowReopenClick = (clickEvent) => {
8679
+ disarmReopenSwallow();
8473
8680
  if (isElement(clickEvent.target) && originElement.contains(clickEvent.target)) {
8474
8681
  clickEvent.stopImmediatePropagation();
8475
8682
  clickEvent.preventDefault();
8476
8683
  }
8477
8684
  };
8478
- targetDocument.addEventListener('click', swallowReopenClick, { capture: true, once: true });
8685
+ // A press that ends without a click (cancelled, dragged off) must not leave the swallow
8686
+ // armed for the next, unrelated click on the origin.
8687
+ const disarmReopenSwallow = () => {
8688
+ targetDocument.removeEventListener('click', swallowReopenClick, true);
8689
+ targetDocument.removeEventListener('pointerdown', disarmReopenSwallow, true);
8690
+ armedReopenSwallows.delete(disarmReopenSwallow);
8691
+ };
8692
+ targetDocument.addEventListener('click', swallowReopenClick, true);
8693
+ targetDocument.addEventListener('pointerdown', disarmReopenSwallow, true);
8694
+ armedReopenSwallows.add(disarmReopenSwallow);
8479
8695
  }
8480
8696
  };
8481
8697
  targetDocument.addEventListener('pointerdown', onPointerDown, true);
@@ -8494,7 +8710,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8494
8710
  overlayRef.markOpened();
8495
8711
  return;
8496
8712
  }
8497
- animationDebugLog(`runtime ${config.id}`, `enter (delegate ${config.animationDelegate?.enter ? 'yes' : 'no'}, lifecycle state "${lifecycle.state$.value}")`);
8713
+ animationDebugLog(`runtime ${config.id}`, `enter (delegate ${config.animationDelegate?.enter ? 'yes' : 'no'}, lifecycle state "${lifecycle.state()}")`);
8498
8714
  const readySubscription = lifecycle.state$
8499
8715
  .pipe(filter((state) => state === 'entering' || state === 'entered'), take(1), tap(() => {
8500
8716
  nextFrame(() => {
@@ -8520,6 +8736,7 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8520
8736
  };
8521
8737
  destroyRef.onDestroy(() => {
8522
8738
  [...mountedTeardowns].forEach((teardown) => teardown());
8739
+ [...armedReopenSwallows].forEach((disarm) => disarm());
8523
8740
  [...rootElements.keys()].forEach((targetDocument) => maybeDestroyRootElements(targetDocument));
8524
8741
  });
8525
8742
  return {
@@ -9110,7 +9327,7 @@ const initials = (value, maxLength = 2) => {
9110
9327
  return '';
9111
9328
  return words
9112
9329
  .slice(0, maxLength)
9113
- .map((word) => word[0]?.toUpperCase() ?? '')
9330
+ .map((word) => [...word.normalize()][0]?.toUpperCase() ?? '')
9114
9331
  .join('');
9115
9332
  };
9116
9333
 
@@ -9189,20 +9406,38 @@ const EDGE_CURSORS = {
9189
9406
  nw: 'nwse-resize',
9190
9407
  se: 'nwse-resize',
9191
9408
  };
9192
- const setupResizeObservable = (startEvent, edge, doc) => {
9409
+ // jsdom and other non-browser DOMs ship no pointer capture; the document listeners track the
9410
+ // gesture either way, so a failure here only costs events over a cross-origin frame.
9411
+ const capturePointer = (el, pointerId) => {
9412
+ try {
9413
+ el.setPointerCapture(pointerId);
9414
+ }
9415
+ catch {
9416
+ return;
9417
+ }
9418
+ };
9419
+ const setupResizeObservable = (startEvent, handle, edge, doc) => {
9193
9420
  const pointerId = startEvent.pointerId;
9194
9421
  const startX = startEvent.clientX;
9195
9422
  const startY = startEvent.clientY;
9196
9423
  let cancelled = false;
9424
+ let last = { edge, totalDx: 0, totalDy: 0, clientX: startX, clientY: startY };
9197
9425
  const end$ = merge(fromEvent(doc, 'pointerup'), fromEvent(doc, 'pointercancel')).pipe(filter((e) => e.pointerId === pointerId), take(1),
9198
9426
  // Read below by the terminating `defer`, which only runs once this has completed the moves.
9199
9427
  tap((e) => (cancelled = e.type === 'pointercancel')));
9200
9428
  return defer(() => {
9201
9429
  const releaseSelection = suppressTextSelection(doc);
9202
- return concat(of({ type: 'start', edge }), fromEvent(doc, 'pointermove').pipe(filter((e) => e.pointerId === pointerId), map((e) => ({
9203
- type: 'move',
9204
- data: { edge, dx: e.clientX - startX, dy: e.clientY - startY, clientX: e.clientX, clientY: e.clientY },
9205
- })), takeUntil(end$)), defer(() => of({ type: cancelled ? 'cancelled' : 'end' }))).pipe(finalize(releaseSelection));
9430
+ capturePointer(handle, pointerId);
9431
+ return concat(of({ type: 'start', edge }), fromEvent(doc, 'pointermove').pipe(filter((e) => e.pointerId === pointerId), map((e) => {
9432
+ last = {
9433
+ edge,
9434
+ totalDx: e.clientX - startX,
9435
+ totalDy: e.clientY - startY,
9436
+ clientX: e.clientX,
9437
+ clientY: e.clientY,
9438
+ };
9439
+ return { type: 'move', data: last };
9440
+ }), takeUntil(end$)), defer(() => of(cancelled ? { type: 'cancelled' } : { type: 'end', data: last }))).pipe(finalize(releaseSelection));
9206
9441
  });
9207
9442
  };
9208
9443
  class ResizeHandlesComponent {
@@ -9212,10 +9447,10 @@ class ResizeHandlesComponent {
9212
9447
  ...(ngDevMode ? [{ debugName: "edges" }] : /* istanbul ignore next */ []));
9213
9448
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9214
9449
  this.gestureStart$ = new Subject();
9215
- this.gesture$ = this.gestureStart$.pipe(exhaustMap(({ event, edge }) => setupResizeObservable(event, edge, this.el.nativeElement.ownerDocument)), share(), takeUntilDestroyed());
9450
+ this.gesture$ = this.gestureStart$.pipe(exhaustMap(({ event, handle, edge }) => setupResizeObservable(event, handle, edge, this.el.nativeElement.ownerDocument)), share(), takeUntilDestroyed());
9216
9451
  this.resizeStarted = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'start'), map((e) => e.edge)));
9217
9452
  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(() => undefined)));
9453
+ this.resizeEnded = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'end'), map((e) => e.data)));
9219
9454
  /**
9220
9455
  * The browser took the gesture away mid-resize (a system gesture, an incoming call, the tab going
9221
9456
  * to the background). Revert to the size the resize started from - the user never let go, so there
@@ -9236,7 +9471,7 @@ class ResizeHandlesComponent {
9236
9471
  if (event.button !== 0 || this.disabled())
9237
9472
  return;
9238
9473
  event.stopPropagation();
9239
- this.gestureStart$.next({ event, edge });
9474
+ this.gestureStart$.next({ event, handle: event.currentTarget, edge });
9240
9475
  }
9241
9476
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ResizeHandlesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9242
9477
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ResizeHandlesComponent, isStandalone: true, selector: "et-resize-handles", inputs: { edges: { classPropertyName: "edges", publicName: "edges", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resizeStarted: "resizeStarted", resizeMoved: "resizeMoved", resizeEnded: "resizeEnded", resizeCancelled: "resizeCancelled" }, host: { properties: { "attr.inert": "disabled() ? \"\" : null", "attr.data-active-edge": "activeEdge()" }, classAttribute: "et-resize-handles" }, ngImport: i0, template: `
@@ -9498,6 +9733,226 @@ const injectDefaultSurfaceTheme = (type) => {
9498
9733
  return defaults.length === 1 ? (defaults[0] ?? null) : null;
9499
9734
  };
9500
9735
 
9736
+ /**
9737
+ * Reserved color name. Resolves the color tokens from the ambient surface's neutral swatch
9738
+ * (`SurfaceTheme['interactionColor']`) instead of a registered accent, so a secondary action can
9739
+ * read as chrome without the app registering a neutral color theme. Apps must not register a color
9740
+ * theme under this name.
9741
+ */
9742
+ const SURFACE_COLOR = 'surface';
9743
+ const createCssColorThemeName = (name) => name.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
9744
+ const COLOR_THEMES_DEF = /* @__PURE__ */ defineStaticProvider(undefined, {
9745
+ name: 'Color Themes',
9746
+ });
9747
+ const ɵProvideColorThemes = /* @__PURE__ */ toProvideFn(COLOR_THEMES_DEF);
9748
+ const injectColorThemes = /* @__PURE__ */ toInjectFn(COLOR_THEMES_DEF);
9749
+ const COLOR_THEMES_PREFIX_DEF = /* @__PURE__ */ defineStaticProvider('et', {
9750
+ name: 'Themes Prefix',
9751
+ });
9752
+ const ɵProvideColorThemesPrefix = /* @__PURE__ */ toProvideFn(COLOR_THEMES_PREFIX_DEF);
9753
+ const injectColorThemesPrefix = /* @__PURE__ */ toInjectFn(COLOR_THEMES_PREFIX_DEF);
9754
+ const provideColorThemesWithTailwind4 = (themes, prefix = 'et') => {
9755
+ if (isDevMode() && themes.some((theme) => theme.name === SURFACE_COLOR)) {
9756
+ console.error(`[provideColorThemesWithTailwind4] "${SURFACE_COLOR}" is a reserved color name that resolves to the ambient surface's neutral swatch. Rename your theme.`);
9757
+ }
9758
+ return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix(prefix)];
9759
+ };
9760
+ const findColorThemeByType = (themes, type, caller) => {
9761
+ if (!themes) {
9762
+ throw new Error(`[${caller}] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
9763
+ }
9764
+ const theme = themes.find((t) => t.type === type);
9765
+ if (!theme) {
9766
+ throw new Error(`[${caller}] No color theme with type "${type}" found. Add a theme with type: "${type}" to provideColorThemesWithTailwind4().`);
9767
+ }
9768
+ if (isDevMode()) {
9769
+ const duplicates = themes.filter((t) => t.type === type);
9770
+ if (duplicates.length > 1) {
9771
+ console.error(`[${caller}] Multiple themes with type "${type}" found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
9772
+ }
9773
+ }
9774
+ return theme;
9775
+ };
9776
+ const injectColorThemeByType = (type) => findColorThemeByType(injectColorThemes({ optional: true }), type, 'injectColorThemeByType');
9777
+ /**
9778
+ * A signal of the color theme for `type` on the surface this element renders on: the theme the nearest
9779
+ * surface names in `semanticColorThemes`, else the first registered theme of that `type`. Reading it throws
9780
+ * when the app registered no theme of that `type`, so read it only for a state that renders.
9781
+ *
9782
+ * @example
9783
+ * protected successTheme = injectSemanticColorTheme('success');
9784
+ * // <span [etProvideColor]="successTheme()">+12%</span>
9785
+ */
9786
+ const injectSemanticColorTheme = (type) => {
9787
+ const themes = injectColorThemes({ optional: true });
9788
+ const surfaceProvider = inject(SURFACE_PROVIDER, { optional: true });
9789
+ const defaultSurface = injectDefaultSurfaceTheme();
9790
+ return computed(() => {
9791
+ const surface = surfaceProvider?.activeTheme() ?? defaultSurface;
9792
+ const mappedName = surface?.semanticColorThemes?.[type];
9793
+ const mapped = mappedName ? themes?.find((t) => t.name === mappedName) : undefined;
9794
+ if (mapped)
9795
+ return mapped;
9796
+ if (mappedName && isDevMode()) {
9797
+ console.error(`[injectSemanticColorTheme] Surface theme "${surface?.name}" maps "${type}" to the color theme "${mappedName}", which is not registered. Add it to provideColorThemesWithTailwind4().`);
9798
+ }
9799
+ return findColorThemeByType(themes, type, 'injectSemanticColorTheme');
9800
+ });
9801
+ };
9802
+ /** @deprecated Use `injectSemanticColorTheme('error')`, which follows the surface the element renders on. */
9803
+ const injectErrorTheme = () => injectColorThemeByType('error');
9804
+ /** @deprecated Use `injectSemanticColorTheme('warning')`, which follows the surface the element renders on. */
9805
+ const injectWarningTheme = () => injectColorThemeByType('warning');
9806
+ /** @deprecated Use `injectSemanticColorTheme('success')`, which follows the surface the element renders on. */
9807
+ const injectSuccessTheme = () => injectColorThemeByType('success');
9808
+ const injectDefaultColorTheme = () => {
9809
+ const themes = injectColorThemes({ optional: true });
9810
+ if (!themes) {
9811
+ throw new Error(`[injectDefaultColorTheme] No color themes provided. Call provideColorThemesWithTailwind4() in your app config.`);
9812
+ }
9813
+ const theme = themes.find((t) => t.isDefault);
9814
+ if (!theme) {
9815
+ throw new Error(`[injectDefaultColorTheme] No color theme with isDefault: true found. Mark one theme as the default in provideColorThemesWithTailwind4().`);
9816
+ }
9817
+ if (isDevMode()) {
9818
+ const duplicates = themes.filter((t) => t.isDefault);
9819
+ if (duplicates.length > 1) {
9820
+ console.error(`[injectDefaultColorTheme] Multiple themes with isDefault: true found: ${duplicates.map((t) => t.name).join(', ')}. Only the first one will be used.`);
9821
+ }
9822
+ }
9823
+ return theme;
9824
+ };
9825
+
9826
+ const COLOR_PROVIDER = new InjectionToken('ColorProvider');
9827
+ /**
9828
+ * The color provider attached to a bootstrapped root component (e.g. `ProvideColorDirective` in
9829
+ * the app component's `hostDirectives`), if any. Detached DOM such as overlay panes can't reach
9830
+ * it through element DI, so overlay containers fall back to this when resolving the provider to
9831
+ * `syncWithProvider()` - resolve at sync time, not at injection time, since root components only
9832
+ * register with `ApplicationRef` once bootstrap completes.
9833
+ */
9834
+ const resolveAppRootColorProvider = (appRef) => {
9835
+ for (const componentRef of appRef.components) {
9836
+ const provider = componentRef.injector.get(COLOR_PROVIDER, null);
9837
+ if (provider) {
9838
+ return provider;
9839
+ }
9840
+ }
9841
+ return null;
9842
+ };
9843
+ const FORCED_COLOR_UNSET = /* @__PURE__ */ Symbol('FORCED_COLOR_UNSET');
9844
+ const appsWarnedAboutMissingThemes = /* @__PURE__ */ new WeakSet();
9845
+ class ProvideColorDirective {
9846
+ constructor() {
9847
+ this.themes = injectColorThemes({ optional: true });
9848
+ this.prefix = injectColorThemesPrefix({ optional: true });
9849
+ this.injector = inject(Injector);
9850
+ this.parentProvider = inject(COLOR_PROVIDER, { optional: true, skipSelf: true });
9851
+ this.appRef = inject(ApplicationRef);
9852
+ this.currentProviderSync = null;
9853
+ this.forcedColor = signal(FORCED_COLOR_UNSET, /* @ts-ignore */
9854
+ ...(ngDevMode ? [{ debugName: "forcedColor" }] : /* istanbul ignore next */ []));
9855
+ this.color = input(undefined, { ...(ngDevMode ? { debugName: "color" } : /* istanbul ignore next */ {}), alias: 'etProvideColor' });
9856
+ this.effectiveColor = computed(() => {
9857
+ const forcedColor = this.forcedColor();
9858
+ if (forcedColor !== FORCED_COLOR_UNSET) {
9859
+ return forcedColor;
9860
+ }
9861
+ return this.color();
9862
+ }, /* @ts-ignore */
9863
+ ...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
9864
+ /**
9865
+ * The color that actually applies at this provider's location: its own effective color, or -
9866
+ * when this provider is passive (no input, nothing forced) - the nearest ancestor provider's.
9867
+ * Mirrors the CSS cascade, which falls through a provider's `-color--inherited` scope class;
9868
+ * consumers that re-apply context across detached DOM (overlay panes) must use this instead
9869
+ * of `effectiveColor` or a passive in-between provider (e.g. a form field's) erases the theme.
9870
+ */
9871
+ this.resolvedColor = computed(() => {
9872
+ const own = this.effectiveColor();
9873
+ const ownName = typeof own === 'object' && own !== null ? own.name : own;
9874
+ if (ownName) {
9875
+ return own;
9876
+ }
9877
+ return this.parentProvider?.resolvedColor();
9878
+ }, /* @ts-ignore */
9879
+ ...(ngDevMode ? [{ debugName: "resolvedColor" }] : /* istanbul ignore next */ []));
9880
+ this.colorName = computed(() => {
9881
+ const raw = this.effectiveColor();
9882
+ const value = typeof raw === 'object' && raw !== null ? raw.name : raw;
9883
+ if (value === SURFACE_COLOR)
9884
+ return SURFACE_COLOR;
9885
+ if (!value)
9886
+ return;
9887
+ if (!this.themes) {
9888
+ if (isDevMode() && !appsWarnedAboutMissingThemes.has(this.appRef)) {
9889
+ appsWarnedAboutMissingThemes.add(this.appRef);
9890
+ console.warn(`etProvideColor="${value}" has no effect because no color themes are registered. Please make sure to add provideColorThemesWithTailwind4()`);
9891
+ }
9892
+ return;
9893
+ }
9894
+ if (isDevMode() && !this.themes.some((theme) => theme.name === value)) {
9895
+ console.error(`Theme ${value} does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
9896
+ }
9897
+ return createCssColorThemeName(value);
9898
+ }, /* @ts-ignore */
9899
+ ...(ngDevMode ? [{ debugName: "colorName" }] : /* istanbul ignore next */ []));
9900
+ this.themeClass = computed(() => {
9901
+ const prefix = this.prefix || 'et';
9902
+ if (this.colorName()) {
9903
+ return `${prefix}-color--${this.colorName()}`;
9904
+ }
9905
+ return `${prefix}-color--inherited`;
9906
+ }, /* @ts-ignore */
9907
+ ...(ngDevMode ? [{ debugName: "themeClass" }] : /* istanbul ignore next */ []));
9908
+ }
9909
+ syncWithProvider(provider) {
9910
+ this.currentProviderSync?.destroy();
9911
+ runInInjectionContext(this.injector, () => {
9912
+ this.currentProviderSync = effect(() => {
9913
+ const provideColor = provider.resolvedColor();
9914
+ untracked(() => {
9915
+ if (provideColor === undefined) {
9916
+ this.clearForcedColor();
9917
+ return;
9918
+ }
9919
+ this.forceColor(provideColor);
9920
+ });
9921
+ }, /* @ts-ignore */
9922
+ ...(ngDevMode ? [{ debugName: "currentProviderSync" }] : /* istanbul ignore next */ []));
9923
+ });
9924
+ }
9925
+ stopSyncWithProvider() {
9926
+ const hadProviderSync = !!this.currentProviderSync;
9927
+ this.currentProviderSync?.destroy();
9928
+ this.currentProviderSync = null;
9929
+ if (hadProviderSync) {
9930
+ this.clearForcedColor();
9931
+ }
9932
+ }
9933
+ forceColor(color) {
9934
+ this.forcedColor.set(color);
9935
+ }
9936
+ clearForcedColor() {
9937
+ if (this.forcedColor() === FORCED_COLOR_UNSET) {
9938
+ return;
9939
+ }
9940
+ this.forcedColor.set(FORCED_COLOR_UNSET);
9941
+ }
9942
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
9943
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: ProvideColorDirective, isStandalone: true, selector: "[etProvideColor]", inputs: { color: { classPropertyName: "color", publicName: "etProvideColor", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "themeClass()" } }, providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }], ngImport: i0 }); }
9944
+ }
9945
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ProvideColorDirective, decorators: [{
9946
+ type: Directive,
9947
+ args: [{
9948
+ selector: '[etProvideColor]',
9949
+ providers: [{ provide: COLOR_PROVIDER, useExisting: ProvideColorDirective }],
9950
+ host: {
9951
+ '[class]': 'themeClass()',
9952
+ },
9953
+ }]
9954
+ }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "etProvideColor", required: false }] }] } });
9955
+
9501
9956
  const SURFACE_PROVIDER = new InjectionToken('SurfaceProvider');
9502
9957
  const FORCED_SURFACE_UNSET = /* @__PURE__ */ Symbol('FORCED_SURFACE_UNSET');
9503
9958
  class ProvideSurfaceDirective {
@@ -9507,6 +9962,9 @@ class ProvideSurfaceDirective {
9507
9962
  this.injector = inject(Injector);
9508
9963
  this.parentProvider = inject(SURFACE_PROVIDER, { optional: true, skipSelf: true });
9509
9964
  this.defaultTheme = injectDefaultSurfaceTheme();
9965
+ this.colorThemes = injectColorThemes({ optional: true });
9966
+ this.colorPrefix = injectColorThemesPrefix({ optional: true });
9967
+ this.colorProvider = inject(COLOR_PROVIDER, { optional: true });
9510
9968
  this.currentProviderSync = null;
9511
9969
  this.forcedSurface = signal(FORCED_SURFACE_UNSET, /* @ts-ignore */
9512
9970
  ...(ngDevMode ? [{ debugName: "forcedSurface" }] : /* istanbul ignore next */ []));
@@ -9552,12 +10010,23 @@ class ProvideSurfaceDirective {
9552
10010
  return createCssSurfaceName(value);
9553
10011
  }, /* @ts-ignore */
9554
10012
  ...(ngDevMode ? [{ debugName: "surfaceName" }] : /* istanbul ignore next */ []));
10013
+ this.colorThemeClass = computed(() => {
10014
+ const colorTheme = this.resolvedTheme()?.colorTheme;
10015
+ if (!colorTheme || this.colorProvider?.resolvedColor())
10016
+ return null;
10017
+ if (isDevMode() && !this.colorThemes?.some((theme) => theme.name === colorTheme)) {
10018
+ console.error(`Surface theme ${this.resolvedTheme()?.name} names the color theme ${colorTheme}, which does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
10019
+ }
10020
+ return `${this.colorPrefix || 'et'}-color--${createCssColorThemeName(colorTheme)}`;
10021
+ }, /* @ts-ignore */
10022
+ ...(ngDevMode ? [{ debugName: "colorThemeClass" }] : /* istanbul ignore next */ []));
9555
10023
  this.surfaceClass = computed(() => {
9556
10024
  const prefix = this.prefix || 'et';
9557
- if (this.surfaceName()) {
9558
- return `${prefix}-surface--${this.surfaceName()}`;
9559
- }
9560
- return `${prefix}-surface--inherited`;
10025
+ const surfaceClass = this.surfaceName()
10026
+ ? `${prefix}-surface--${this.surfaceName()}`
10027
+ : `${prefix}-surface--inherited`;
10028
+ const colorThemeClass = this.colorThemeClass();
10029
+ return colorThemeClass ? `${surfaceClass} ${colorThemeClass}` : surfaceClass;
9561
10030
  }, /* @ts-ignore */
9562
10031
  ...(ngDevMode ? [{ debugName: "surfaceClass" }] : /* istanbul ignore next */ []));
9563
10032
  }
@@ -9766,87 +10235,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
9766
10235
  const COLOR_PALETTE_DEF = /* @__PURE__ */ defineStaticProvider(undefined, {
9767
10236
  name: 'Color Palette',
9768
10237
  });
10238
+ const injectColorPaletteConfig = /* @__PURE__ */ toInjectFn(COLOR_PALETTE_DEF);
10239
+ const selectPalette = (config, surface) => {
10240
+ if (Array.isArray(config))
10241
+ return config;
10242
+ return (surface === null ? undefined : config[surface]) ?? config.default;
10243
+ };
9769
10244
  /**
9770
10245
  * The color themes a subtree may offer a user as pickable choices, in the order they should appear.
9771
10246
  * A curated slice of what `provideColorThemesWithTailwind4` registered: an app registers every theme
9772
10247
  * it renders with, including ones nobody should pick by hand (`error`, `warning`), and adds the
9773
10248
  * user-facing label a swatch needs.
9774
10249
  *
9775
- * Components that let a user choose a color read it optionally and fall back to accepting a raw
9776
- * theme name when it is absent - the scheduler's `colorToken` field is the reference consumer.
10250
+ * Pass one list, or one list per registered surface theme name plus a `default`. The charts draw with
10251
+ * the list of the surface they sit on; a color picker always offers the `default` list.
9777
10252
  *
9778
10253
  * @example
9779
10254
  * provideColorPalette([
9780
10255
  * { token: 'brand', label: 'Team green' },
9781
10256
  * { token: 'ocean', label: 'Training blue' },
9782
10257
  * ]);
10258
+ *
10259
+ * @example
10260
+ * provideColorPalette({
10261
+ * default: [{ token: 'ocean', label: 'Training' }],
10262
+ * 'dark-card': [{ token: 'ocean-bright', label: 'Training' }],
10263
+ * });
9783
10264
  */
9784
- const provideColorPalette = /* @__PURE__ */ toProvideFn(COLOR_PALETTE_DEF);
9785
- /** The palette from {@link provideColorPalette}. Pass `{ optional: true }` - most apps provide none. */
9786
- const injectColorPalette = /* @__PURE__ */ toInjectFn(COLOR_PALETTE_DEF);
9787
- const COLOR_PALETTE = /* @__PURE__ */ toToken(COLOR_PALETTE_DEF);
9788
-
10265
+ const provideColorPalette = (palette) => COLOR_PALETTE_DEF.provide(palette);
10266
+ function injectColorPalette(options) {
10267
+ const config = options ? injectColorPaletteConfig(options) : injectColorPaletteConfig();
10268
+ return config ? selectPalette(config, null) : null;
10269
+ }
9789
10270
  /**
9790
- * Reserved color name. Resolves the color tokens from the ambient surface's neutral swatch
9791
- * (`SurfaceTheme['interactionColor']`) instead of a registered accent, so a secondary action can
9792
- * read as chrome without the app registering a neutral color theme. Apps must not register a color
9793
- * theme under this name.
10271
+ * The palette from {@link provideColorPalette} for the surface the current subtree renders on: that
10272
+ * surface theme's list, else the `default` list. `null` when no palette is provided.
9794
10273
  */
9795
- const SURFACE_COLOR = 'surface';
9796
- const createCssColorThemeName = (name) => name.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
9797
- const COLOR_THEMES_DEF = /* @__PURE__ */ defineStaticProvider(undefined, {
9798
- name: 'Color Themes',
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;
10274
+ const injectSurfaceColorPalette = () => {
10275
+ const config = injectColorPaletteConfig({ optional: true });
10276
+ const surface = injectParentSurface();
10277
+ return computed(() => (config ? selectPalette(config, surface()?.name ?? null) : null));
9849
10278
  };
10279
+ const COLOR_PALETTE = /* @__PURE__ */ toToken(COLOR_PALETTE_DEF);
9850
10280
 
9851
10281
  /** Reads the nonce where Angular's default `CSP_NONCE` does, since these run outside any injector. */
9852
10282
  const createNoncedStyle = () => {
@@ -10049,127 +10479,6 @@ const provideColorThemes = (themes) => {
10049
10479
  return [ɵProvideColorThemes(themes), ɵProvideColorThemesPrefix('et')];
10050
10480
  };
10051
10481
 
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
10482
  class SurfaceInteractiveStylesComponent {
10174
10483
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: SurfaceInteractiveStylesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10175
10484
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.6", type: SurfaceInteractiveStylesComponent, isStandalone: true, selector: "et-surface-interactive-styles", ngImport: i0, template: '', isInline: true, styles: ["@layer components{:where(.et-surface-interactive){--et-surface-interaction-rgb: var(--et-surface-interaction);--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}@media(hover:hover){:where(.et-surface-interactive:hover){--et-surface-interaction-rgb: var(--et-surface-interaction-hover, var(--et-surface-interaction));--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}}:where(.et-surface-interactive:focus-visible){--et-surface-interaction-rgb: var( --et-surface-interaction-focus, var(--et-surface-interaction-hover, var(--et-surface-interaction)) );--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}:where(.et-surface-interactive:active){--et-surface-interaction-rgb: var( --et-surface-interaction-active, var(--et-surface-interaction-focus, var(--et-surface-interaction-hover, var(--et-surface-interaction))) );--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}:where(.et-surface-interactive[disabled]),:where(.et-surface-interactive[aria-disabled=true]){--et-surface-interaction-rgb: var( --et-surface-interaction-disabled, var( --et-surface-interaction-active, var(--et-surface-interaction-focus, var(--et-surface-interaction-hover, var(--et-surface-interaction))) ) );--et-surface-interaction-solid: rgb(var(--et-surface-interaction-rgb))}}\n"], encapsulation: i0.ViewEncapsulation.None }); }
@@ -10210,11 +10519,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
10210
10519
 
10211
10520
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
10212
10521
  /** The version of `@ethlete/core` this build was cut from. */
10213
- const CORE_VERSION = '5.0.0-next.61';
10522
+ const CORE_VERSION = '5.0.0-next.62';
10214
10523
 
10215
10524
  /**
10216
10525
  * Generated bundle index. Do not edit.
10217
10526
  */
10218
10527
 
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, createOverlayRuntimeRef, 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, injectQueryParamChanges, injectQueryParams, injectRenderer, injectRoute, injectRouteData, injectRouteDataItem, injectRouteTitle, injectRouterEvent, injectRouterNavigationState, injectRouterState, injectScrollbarDimensions, injectStructuredDataConfig, injectStructuredDataStore, injectStyleManager, injectSuccessTheme, 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, nextFrame, normalizeGameResultType, normalizeMatchParticipant, normalizeMatchParticipants, normalizeMatchScore, normalizeMatchState, normalizeMatchType, normalizeUnsavedChangesSource, numberBreakpointTransform, onOverlayViewportInsetsChange, 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, provideUnsavedChangesCoordinator, provideViewportConfig, randomId, readBuildFingerprint, readFromClipboard, registerAnchoredPositionMiddlewareExtras, registerAnchoredPositionSetup, reserveOverlayViewportSpace, resetPositioningStyles, resolveAppRootColorProvider, resolveOverlayLayer, resolveSurfaceByElevation, round, routerDisableScrollTop, routerRestoreScroll, scrollToElement, setBackdropStyles, setBaseElementStyles, 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 };
10528
+ export { ANIMATABLE_TOKEN, ANIMATED_IF_TOKEN, ANIMATED_LIFECYCLE_TOKEN, ANIMATION_DEBUG_STORAGE_KEY, ANIMATION_ERROR_CODES, AT_LEAST_ONE_REQUIRED, AnimatableDirective, AnimatedIfDirective, AnimatedLifecycleDirective, AutoSurfaceDirective, BOUNDARY_ELEMENT_TOKEN, BREAKPOINT_INSTANCE_TOKEN, BREAKPOINT_ORDER, COLOR_PALETTE, COLOR_PROVIDER, CORE_VERSION, ClickOutsideDirective, ColorInteractiveContainerDirective, ColorInteractiveDirective, ColorInteractiveExcludeDirective, ColorInteractiveHasFocusDirective, DEFAULT_MULTI_INSTANCE_RELS, DEFAULT_MULTI_INSTANCE_TAGS, DEFAULT_OVERLAY_LAYER, DEFAULT_VIEWPORT_CONFIG, DISABLE_LOGGER_PARAM, DragHandleDirective, ET_DISABLE_SCROLL_TOP, ET_DISABLE_SCROLL_TOP_AS_RETURN_ROUTE, ET_DISABLE_SCROLL_TOP_ON_PATH_PARAM_CHANGE, ET_PROPERTY_REMOVED, ET_RESTORE_SCROLL, EasingTokensStylesComponent, EthleteValidators, FOCUSABLE_SELECTOR, FOCUS_VISIBLE_TRACKER_TOKEN, HtmlToMarkdownPipe, IS_ARRAY_NOT_EMPTY, IS_EMAIL, InferMimeTypePipe, InitialsPipe, IsArrayNotEmpty, IsEmail, jsonLd as JsonLD, KeyPressManager, MARKDOWN_VERBATIM_ATTR, MUST_MATCH, MarkdownToHtmlPipe, MatchStateType, MustMatch, NOTIFICATION_CLICK_MESSAGE_TYPE, NormalizeGameResultTypePipe, NormalizeMatchParticipantsPipe, NormalizeMatchScorePipe, NormalizeMatchStatePipe, NormalizeMatchTypePipe, OVERLAY_LAYER_ATTRIBUTE, ProvideColorDirective, ProvideSurfaceDirective, RUNTIME_ERROR_NO_DATA, RepeatDirective, ResizeHandlesComponent, RestoreScrollDirective, RuntimeError, SURFACE_COLOR, SURFACE_PROVIDER, SURFACE_TYPE, SWIPE_VELOCITY_WINDOW_MS, ScrollObserverDirective, ScrollObserverEndDirective, ScrollObserverStartDirective, SlugifyPipe, StructuredDataComponent, SurfaceInteractiveDirective, ToArrayPipe, TypedQueryList, UNSAVED_CHANGES_CONFIRM, UNSAVED_CHANGES_ERROR_CODES, UNSAVED_CHANGES_TITLE_MARKER, ValidateAtLeastOneRequired, VisuallyHiddenStylesComponent, anchoredOverlayPosition, animationDebugLog, applyAlternateBinding, applyAlternateLanguagesBindings, applyArticleBindings, applyAuthorBinding, applyCanonicalBinding, applyCenteredPosition, applyDescriptionBinding, applyFaviconOverlay, applyGlobalPosition, applyHeadBinding, applyHeadTitleBinding, applyHeadTitleMarker, applyHostListener, applyHostListeners, applyInitialFocus, applyKeywordsBinding, applyLinkBinding, applyMetaBinding, applyNextBinding, applyOgBinding, applyOpenGraphBindings, applyPrevBinding, applyResourceHintsBindings, applyRobotsBinding, applySocialMediaBindings, applyStructuredDataBinding, applyTwitterCardBindings, boolBreakpointTransform, boundingClientRectToElementRect, breakpointTransformBase, buildElementSignal, buildSignalEffects, canUseSessionMemory, clamp, clone, cloneFormGroup, computedTillFalsy, computedTillTruthy, controlValueSignal, controlValueSignalWithPrevious, copyToClipboard, createAnchoredPositionCleanup, createArrayPropertyBinding, createAutoSessionMemoryKey, createCanAnimateSignal, createComponentId, createCssColorThemeName, createCssSurfaceName, createDestroy, createDocumentElementSignal, createElementDimensions, createEmptyElementSignal, createFlipAnimation, createFlipAnimationGroup, createIsRenderedSignal, createLogger, createObjectUrlHandle, createPropertyBinding, createRootThemeCss, createRoute, createRouterState, createRxHostListener, createSessionMemory, createSwatchCss, createSwipeTracker, createTailwindColorThemes, createTailwindCssVar, createTailwindRgbVar, createThemeStyle, createUnsavedChangesGuard, createUnsavedChangesTabLock, createUnsavedChangesTracker, createUserConsentProvider, deferredSignal, defineLabels, defineProvider, defineRootProvider, defineStaticProvider, defineStaticRootProvider, deleteCookie, dragGestureFrom, easeElastic, easeIn, easeInOut, easeLinear, easeOut, easeOutBack, easeOutBackStrong, elementCanScroll, enableAnchoredOverlayPositionExtras, equal, fetchDeployedBuildFingerprint$, firstElementSignal, focusElement, forceReflow, formatRuntimeError, fromNextFrame, getCookie, getDomain, getElementScrollCoordinates, getFocusableElements, getFormGroupValue, getGroupMatchPoints, getGroupMatchScore, getHeadingElement, getKnockoutMatchScore, getMatchScoreSubLine, getObjectProperty, getScrollContainerTarget, getScrollItemTarget, getScrollSnapTarget, hasCookie, holdScrollRestoration, htmlToMarkdown, inferMimeType, initials, injectAngularRootElement, injectAnimatedBlockSize, injectAppUpdates, injectAppUpdatesConfig, injectBoundaryElement, injectBreakpointInput, injectBreakpointIsMatched, injectBreakpointObserver, injectCanHover, injectColorPalette, injectColorThemes, injectColorThemesPrefix, injectCurrentBreakpoint, injectDefaultColorTheme, injectDefaultSurfaceTheme, injectDeviceInputType, injectDisplayOrientation, injectErrorTheme, injectFaviconStore, injectFileDownload, injectFocusVisibleTracker, injectFragment, injectHasPrecisionInput, injectHasTouchInput, injectHostElement, injectIs2Xl, injectIsDocumentVisible, injectIsLandscape, injectIsLg, injectIsMd, injectIsPortrait, injectIsRouterInitialized, injectIsSm, injectIsXl, injectIsXs, injectLinkStore, injectLinkStoreConfig, injectLocale, injectMediaQueryIsMatched, injectMetaConfig, injectMetaStore, injectNotifications, injectObserveBreakpoint, injectObserveMediaQuery, injectOverlayRuntime, injectParentSurface, injectPathParam, injectPathParamChanges, injectPathParams, injectPrefersReducedMotion, injectQueryParam, injectQueryParamAll, injectQueryParamChanges, injectQueryParams, injectRenderer, injectRoute, injectRouteData, injectRouteDataItem, injectRouteTitle, injectRouterEvent, injectRouterNavigationState, injectRouterState, injectScrollbarDimensions, injectSemanticColorTheme, injectStructuredDataConfig, injectStructuredDataStore, injectStyleManager, injectSuccessTheme, injectSurfaceColorPalette, injectSurfaceContextTracker, injectSurfaceThemes, injectSurfaceThemesPrefix, injectSurfaceType, injectTemplateRef, injectTitleConfig, injectTitleStore, injectUnsavedChangesCoordinator, injectUrl, injectViewportConfig, injectViewportDimensions, injectViewportSize, injectWarningTheme, isAnimationDebugEnabled, isArray, isElement, isElementSignal, isElementVisible, isFocusable, isGroupMatch, isHTMLElement, isHTMLOrSVGElement, isKnockoutMatch, isObject, isOnHigherOverlayLayer, isSafeLinkUrl, isSafeUrl, isStaleBuildError, markdownToHtml, matchesReducedMotion, maybeSignalValue, memoizeSignal, mountEasingTokens, mountVisuallyHidden, nextFrame, normalizeGameResultType, normalizeMatchParticipant, normalizeMatchParticipants, normalizeMatchScore, normalizeMatchState, normalizeMatchType, normalizeUnsavedChangesSource, numberBreakpointTransform, onOverlayViewportInsetsChange, optionalBooleanAttribute, overlayViewportInsets, overlayViewportInsetsFor, ownsActiveElement, previousSignalValue, provideAppUpdates, provideAppUpdatesConfig, provideAppUpdatesInstance, provideBoundaryElement, provideBreakpointInstance, provideBreakpointObserver, provideColorPalette, provideColorThemes, provideColorThemesWithTailwind4, provideFaviconStore, provideFocusVisibleTracker, provideLinkStore, provideLinkStoreConfig, provideLocale, provideMetaConfig, provideMetaStore, provideNotifications, provideOverlayRuntime, provideRenderer, provideStructuredDataConfig, provideStructuredDataStore, provideStyleManager, provideSurfaceContextTracker, provideSurfaceThemesWithTailwind4, provideTitleConfig, provideTitleStore, provideUnsavedChangesConfirm, provideUnsavedChangesCoordinator, provideViewportConfig, randomId, readBuildFingerprint, readFromClipboard, reserveOverlayViewportSpace, resolveAppRootColorProvider, resolveOverlayLayer, resolveSurfaceByElevation, round, routerDisableScrollTop, routerRestoreScroll, scrollToElement, setCookie, setInputSignal, setupFocusTrap, setupPositioning, setupScrollRestoration, signalAnimatedNumber, signalAttributes, signalClasses, signalCountdown, signalDeferredLoading, signalElementChildren, signalElementDimensions, signalElementIntersection, signalElementLastScrollDirection, signalElementMutations, signalElementScrollState, signalHostAttributes, signalHostClasses, signalHostElementDimensions, signalHostElementIntersection, signalHostElementLastScrollDirection, signalHostElementMutations, signalHostElementScrollState, signalHostStyles, signalIsRendered, signalStyles, slugify, suppressTextSelection, switchQueryListChanges, syncSignal, toArray, toArrayTrackByFn, toInjectFn, toProvideFn, toStringBinding, toToken, transformOrReturn, typedBreakpointTransform, useCursorDragScroll, writeScrollbarSizeToCssVariables, writeViewportSizeToCssVariables, ɵProvideColorThemes, ɵProvideColorThemesPrefix, ɵProvideSurfaceThemes, ɵProvideSurfaceThemesPrefix };
10220
10529
  //# sourceMappingURL=ethlete-core.mjs.map