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

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