@ethlete/core 5.0.0-next.34 → 5.0.0-next.36

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.
@@ -7,7 +7,7 @@ import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';
7
7
  import { coerceElement } from '@angular/cdk/coercion';
8
8
  import { DOCUMENT as DOCUMENT$1, isPlatformBrowser } from '@angular/common';
9
9
  import { BreakpointObserver } from '@angular/cdk/layout';
10
- import { Router, NavigationEnd, NavigationSkipped } from '@angular/router';
10
+ import { Router, NavigationEnd, NavigationSkipped, NavigationStart } from '@angular/router';
11
11
  import { Overlay } from '@angular/cdk/overlay';
12
12
  import { ComponentPortal } from '@angular/cdk/portal';
13
13
  import { autoUpdate, computePosition, offset, flip, size, shift, limitShift, arrow, hide } from '@floating-ui/dom';
@@ -1972,7 +1972,12 @@ const signalStyles = (el, styleMap) => {
1972
1972
  const signalHostStyles = (styleMap) => signalStyles(inject(ElementRef), styleMap);
1973
1973
 
1974
1974
  const elementCanScroll = (element, direction) => {
1975
- const el = element || document.documentElement;
1975
+ // Omitting the element means "the document scroller". Resolved lazily and guarded so a call on the
1976
+ // server answers "cannot scroll" instead of throwing on a missing `document`.
1977
+ const el = element || (typeof document === 'undefined' ? null : document.documentElement);
1978
+ if (!el) {
1979
+ return false;
1980
+ }
1976
1981
  const { scrollHeight, clientHeight, scrollWidth, clientWidth } = el;
1977
1982
  if (direction === 'x') {
1978
1983
  return scrollWidth > clientWidth;
@@ -1982,17 +1987,25 @@ const elementCanScroll = (element, direction) => {
1982
1987
  }
1983
1988
  return scrollHeight > clientHeight || scrollWidth > clientWidth;
1984
1989
  };
1985
- const createViewportRect = () => ({
1986
- left: 0,
1987
- top: 0,
1988
- right: window.innerWidth,
1989
- bottom: window.innerHeight,
1990
- width: window.innerWidth,
1991
- height: window.innerHeight,
1992
- x: 0,
1993
- y: 0,
1994
- toJSON: () => ({}),
1995
- });
1990
+ const createViewportRect = () => {
1991
+ // Collapses to a zero rect where there is no window (server). `isElementVisible` never reaches the
1992
+ // intersection math there anyway — `elementCanScroll` already reports the document as unscrollable
1993
+ // — so the rect only has to be a valid shape.
1994
+ const hasWindow = typeof window !== 'undefined';
1995
+ const width = hasWindow ? window.innerWidth : 0;
1996
+ const height = hasWindow ? window.innerHeight : 0;
1997
+ return {
1998
+ left: 0,
1999
+ top: 0,
2000
+ right: width,
2001
+ bottom: height,
2002
+ width,
2003
+ height,
2004
+ x: 0,
2005
+ y: 0,
2006
+ toJSON: () => ({}),
2007
+ };
2008
+ };
1996
2009
  const isElementVisible = (options) => {
1997
2010
  const { container, element } = options;
1998
2011
  if (!element) {
@@ -2444,6 +2457,21 @@ const signalElementLastScrollDirection = (el) => {
2444
2457
  };
2445
2458
  const signalHostElementLastScrollDirection = () => signalElementLastScrollDirection(inject(ElementRef));
2446
2459
 
2460
+ /**
2461
+ * A signal for whether this page is the one the reader is actually looking at — `false` for a backgrounded
2462
+ * tab or a minimised window.
2463
+ *
2464
+ * Not the same question as whether an element is on screen: an IntersectionObserver reports a fully visible
2465
+ * element in a background tab, because the tab's own visibility is no part of what it measures. Anything
2466
+ * that moves on its own needs to ask both, and a timer is not a substitute — browsers throttle timers in a
2467
+ * hidden tab rather than stopping them, so work keeps piling up out of sight.
2468
+ */
2469
+ const injectIsDocumentVisible = memoizeSignal(() => {
2470
+ const document = inject(DOCUMENT);
2471
+ const isVisible = () => document.visibilityState === 'visible';
2472
+ return toSignal(fromEvent(document, 'visibilitychange').pipe(map(isVisible)), { initialValue: isVisible() });
2473
+ });
2474
+
2447
2475
  const areScrollStatesEqual = (a, b) => {
2448
2476
  return (a.canScroll === b.canScroll &&
2449
2477
  a.canScrollHorizontally === b.canScrollHorizontally &&
@@ -2557,6 +2585,8 @@ const writeViewportSizeToCssVariables = () => {
2557
2585
 
2558
2586
  /** The deadzone in pixels after which the cursor drag scroll will take effect. */
2559
2587
  const CURSOR_DRAG_SCROLL_DEADZONE = 5;
2588
+ /** `MouseEvent.button` for the primary button. A secondary click opens a menu; it does not drag. */
2589
+ const PRIMARY_MOUSE_BUTTON = 0;
2560
2590
  /** The class that is added to the element when the cursor is being dragged. */
2561
2591
  const CURSOR_DRAG_SCROLLING_CLASS = 'et-cursor-drag-scroll--scrolling';
2562
2592
  const CURSOR_DRAG_INIT_CLASS = 'et-cursor-drag-scroll--init';
@@ -2692,15 +2722,18 @@ const useCursorDragScroll = (el, options) => {
2692
2722
  dragAmount.set({ x: 0, y: 0 });
2693
2723
  };
2694
2724
  const setupDragging = (e) => {
2695
- const mouseUp = fromEvent(document, 'mouseup');
2725
+ // A context menu takes the pointer away without ever delivering a `mouseup` — so without it in here, a
2726
+ // right click (or a ctrl-click on a Mac, which is the primary button) leaves the drag latched on and
2727
+ // every later mouse move scrolls the container.
2728
+ const dragEnd = merge(fromEvent(document, 'mouseup'), fromEvent(document, 'contextmenu'));
2696
2729
  const mouseMove = fromEvent(document, 'mousemove');
2697
2730
  const el = element().currentElement;
2698
2731
  if (!el)
2699
2732
  return;
2700
2733
  mouseMove
2701
- .pipe(takeUntilDestroyed(destroyRef), takeUntil(mouseUp), tap((e) => updateDragging(e)))
2734
+ .pipe(takeUntilDestroyed(destroyRef), takeUntil(dragEnd), tap((e) => updateDragging(e)))
2702
2735
  .subscribe();
2703
- mouseUp
2736
+ dragEnd
2704
2737
  .pipe(take(1), takeUntilDestroyed(destroyRef), tap(() => updateDraggingEnd()))
2705
2738
  .subscribe();
2706
2739
  initialDragPosition.set({ x: e.clientX, y: e.clientY });
@@ -2708,7 +2741,7 @@ const useCursorDragScroll = (el, options) => {
2708
2741
  isInitDragging.set(true);
2709
2742
  };
2710
2743
  toObservable(element)
2711
- .pipe(map((e) => e?.currentElement), switchMap((el) => (el ? fromEvent(el, 'mousedown') : of(null))), filter((e) => !!e), filter(() => enabled()), tap((e) => setupDragging(e)), takeUntilDestroyed())
2744
+ .pipe(map((e) => e?.currentElement), switchMap((el) => (el ? fromEvent(el, 'mousedown') : of(null))), filter((e) => !!e), filter((e) => e.button === PRIMARY_MOUSE_BUTTON), filter(() => enabled()), tap((e) => setupDragging(e)), takeUntilDestroyed())
2712
2745
  .subscribe();
2713
2746
  return {
2714
2747
  isDragging: isDragging.asReadonly(),
@@ -2941,6 +2974,31 @@ const injectPathParamChanges = memoizeSignal(() => {
2941
2974
  return changes;
2942
2975
  });
2943
2976
  });
2977
+ /**
2978
+ * The state handed to the navigation that is currently in progress, i.e. what
2979
+ * `router.navigate(…, { state })` passed — typed to whatever you expect.
2980
+ *
2981
+ * Read **synchronously**, in a constructor or a resolver, and deliberately not a signal: navigation state exists
2982
+ * only for the duration of the navigation that carries it. By the time an effect flushed, the navigation would be
2983
+ * over and the answer always `null`. Returns `null` when there is no navigation in flight or it carried no state —
2984
+ * which is the normal case for a page arrived at by typing its URL, so always handle it.
2985
+ *
2986
+ * Use it for the things a URL should not carry: a "you were redirected because…" reason, a pre-fetched object the
2987
+ * previous page already had, a scroll intent. Anything the page needs to survive a reload belongs in the URL.
2988
+ *
2989
+ * @example
2990
+ * type CheckoutNavState = { fromCart: boolean };
2991
+ *
2992
+ * export class CheckoutComponent {
2993
+ * private navState = injectRouterNavigationState<CheckoutNavState>();
2994
+ *
2995
+ * protected cameFromCart = this.navState?.fromCart ?? false;
2996
+ * }
2997
+ */
2998
+ const injectRouterNavigationState = () => {
2999
+ const router = inject(Router);
3000
+ return router.getCurrentNavigation()?.extras.state ?? null;
3001
+ };
2944
3002
 
2945
3003
  const ET_DISABLE_SCROLL_TOP = Symbol('ET_DISABLE_SCROLL_TOP');
2946
3004
  const ET_DISABLE_SCROLL_TOP_AS_RETURN_ROUTE = Symbol('ET_DISABLE_SCROLL_TOP_AS_RETURN_ROUTE');
@@ -2951,22 +3009,182 @@ const routerDisableScrollTop = (config = {}) => {
2951
3009
  ...(config.onPathParamChange ? { [ET_DISABLE_SCROLL_TOP_ON_PATH_PARAM_CHANGE]: true } : {}),
2952
3010
  };
2953
3011
  };
3012
+ /**
3013
+ * Registry of "not settled yet" reporters. Lives in the root injector so a route component can
3014
+ * reach the registration created by the app level `setupScrollRestoration` call.
3015
+ * @internal
3016
+ */
3017
+ const [, injectScrollRestorationHolds] = createRootProvider(() => {
3018
+ const holds = new Set();
3019
+ return {
3020
+ add: (isPending) => {
3021
+ holds.add(isPending);
3022
+ return () => holds.delete(isPending);
3023
+ },
3024
+ isHeld: () => {
3025
+ for (const isPending of holds) {
3026
+ if (isPending())
3027
+ return true;
3028
+ }
3029
+ return false;
3030
+ },
3031
+ };
3032
+ }, { name: 'ScrollRestorationHolds' });
3033
+ /**
3034
+ * Suspend scroll restoration while `isPending` reads `true`.
3035
+ *
3036
+ * Restoration already waits for the scroll container to grow tall enough to reach the saved offset,
3037
+ * so this is only needed when the content may take longer than `restore.timeout` to get there —
3038
+ * a slow list request, for instance. Registers for the lifetime of the current injection context.
3039
+ *
3040
+ * @example
3041
+ * // inside a route component
3042
+ * holdScrollRestoration(() => query.isLoading());
3043
+ */
3044
+ const holdScrollRestoration = (isPending) => {
3045
+ const remove = injectScrollRestorationHolds().add(isPending);
3046
+ inject(DestroyRef).onDestroy(remove);
3047
+ };
3048
+ /** Upper bound on remembered history entries. Each entry is a single number, so this is generous. */
3049
+ const MAX_STORED_POSITIONS = 200;
3050
+ const DEFAULT_RESTORE_TIMEOUT = 1000;
3051
+ const DEFAULT_RESTORE_MAX_TIMEOUT = 10_000;
3052
+ /** Events that mean the user took over scrolling, so a pending restoration must be abandoned. */
3053
+ const INTERACTION_EVENTS = ['wheel', 'touchmove', 'keydown'];
2954
3054
  const setupScrollRestoration = (config = {}) => {
2955
3055
  if (!isPlatformBrowser(inject(PLATFORM_ID))) {
2956
3056
  return;
2957
3057
  }
2958
3058
  const router = inject(Router);
2959
3059
  const document = inject(DOCUMENT);
3060
+ const injector = inject(Injector);
3061
+ const holds = injectScrollRestorationHolds();
3062
+ const restoreEnabled = config.restore?.enabled === true;
3063
+ const restoreTimeout = config.restore?.timeout ?? DEFAULT_RESTORE_TIMEOUT;
3064
+ const restoreMaxTimeout = config.restore?.maxTimeout ?? DEFAULT_RESTORE_MAX_TIMEOUT;
3065
+ const clampOnTimeout = config.restore?.clampOnTimeout ?? true;
3066
+ const resolveScrollElement = () => {
3067
+ const el = typeof config.scrollElement === 'function' ? config.scrollElement() : config.scrollElement;
3068
+ return el ?? document.documentElement;
3069
+ };
3070
+ // The browser restores the offset against the still-loading (much shorter) document, which is the
3071
+ // exact problem we are solving — take it over completely.
3072
+ if (restoreEnabled && 'scrollRestoration' in history) {
3073
+ history.scrollRestoration = 'manual';
3074
+ }
3075
+ /** Saved offsets keyed by the router's per-history-entry `navigationId`. */
3076
+ const positions = new Map();
3077
+ // Mirrors Angular's own `RouterScroller`: the offset of the currently displayed page belongs to
3078
+ // the navigation that produced it, and a popstate reports the id of the entry it moves to.
3079
+ let lastNavigationId = 0;
3080
+ let restoredNavigationId = 0;
3081
+ let isPopstate = false;
2960
3082
  let prev = { state: createRouterState(router), route: createRoute(router) };
2961
- router.events
2962
- .pipe(filter((e) => e instanceof NavigationEnd || e instanceof NavigationSkipped), takeUntilDestroyed())
2963
- .subscribe(() => {
3083
+ let pendingRestore = null;
3084
+ const cancelPendingRestore = () => {
3085
+ pendingRestore?.();
3086
+ pendingRestore = null;
3087
+ };
3088
+ const scheduleRestore = (target) => {
3089
+ cancelPendingRestore();
3090
+ let frame = 0;
3091
+ let started = false;
3092
+ let cancelled = false;
3093
+ // The user scrolling always wins over a restoration we have not committed yet.
3094
+ const abortByUser = () => cancelPendingRestore();
3095
+ const hardDeadline = Date.now() + Math.max(restoreTimeout, restoreMaxTimeout);
3096
+ let deadline = Date.now() + restoreTimeout;
3097
+ const step = () => {
3098
+ frame = 0;
3099
+ if (cancelled)
3100
+ return;
3101
+ const el = resolveScrollElement();
3102
+ const maxOffset = Math.max(0, el.scrollHeight - el.clientHeight);
3103
+ if (maxOffset >= target) {
3104
+ el.scrollTop = target;
3105
+ cancelPendingRestore();
3106
+ return;
3107
+ }
3108
+ const now = Date.now();
3109
+ // A registered hold means the page knows more data is coming, so the content is expected to
3110
+ // grow — suspend the clock instead of giving up on a still loading page.
3111
+ if (holds.isHeld() && now < hardDeadline) {
3112
+ deadline = now + restoreTimeout;
3113
+ }
3114
+ if (now < deadline && now < hardDeadline) {
3115
+ frame = requestAnimationFrame(step);
3116
+ return;
3117
+ }
3118
+ if (clampOnTimeout && maxOffset > 0) {
3119
+ el.scrollTop = Math.min(target, maxOffset);
3120
+ }
3121
+ cancelPendingRestore();
3122
+ };
3123
+ // Wait for the new route to actually render before reading any geometry — on `NavigationEnd` the
3124
+ // components exist but the DOM still shows the previous page, whose height would be measured
3125
+ // instead. The timeout is a safety net in case no render pass follows.
3126
+ const start = () => {
3127
+ if (started || cancelled)
3128
+ return;
3129
+ started = true;
3130
+ frame = requestAnimationFrame(step);
3131
+ };
3132
+ const renderRef = afterNextRender({ read: start }, { injector });
3133
+ const fallback = setTimeout(start);
3134
+ for (const eventName of INTERACTION_EVENTS) {
3135
+ document.addEventListener(eventName, abortByUser, { capture: true, passive: true });
3136
+ }
3137
+ pendingRestore = () => {
3138
+ // `cancelled` is not redundant with destroying the render hook: a superseded restoration must
3139
+ // not apply its (now stale) offset even if its hook had already been queued to run.
3140
+ cancelled = true;
3141
+ renderRef.destroy();
3142
+ if (frame)
3143
+ cancelAnimationFrame(frame);
3144
+ clearTimeout(fallback);
3145
+ for (const eventName of INTERACTION_EVENTS) {
3146
+ document.removeEventListener(eventName, abortByUser, { capture: true });
3147
+ }
3148
+ };
3149
+ };
3150
+ const onNavigationStart = (event) => {
3151
+ // A new navigation supersedes a restoration that has not committed yet.
3152
+ cancelPendingRestore();
3153
+ if (!restoreEnabled)
3154
+ return;
3155
+ positions.set(lastNavigationId, resolveScrollElement().scrollTop);
3156
+ if (positions.size > MAX_STORED_POSITIONS) {
3157
+ const oldest = positions.keys().next();
3158
+ if (!oldest.done)
3159
+ positions.delete(oldest.value);
3160
+ }
3161
+ isPopstate = event.navigationTrigger === 'popstate';
3162
+ restoredNavigationId = event.restoredState?.navigationId ?? 0;
3163
+ };
3164
+ const onNavigationEnd = (event) => {
2964
3165
  const curr = { state: createRouterState(router), route: createRoute(router) };
2965
3166
  const prevState = prev.state;
2966
3167
  const currState = curr.state;
2967
3168
  const sameUrlNavigation = prev.route === curr.route;
2968
3169
  const didFragmentChange = prevState.fragment !== currState.fragment;
2969
3170
  prev = curr;
3171
+ if (event instanceof NavigationEnd) {
3172
+ lastNavigationId = event.id;
3173
+ }
3174
+ else {
3175
+ // A skipped same url navigation is not a history move, so there is nothing to restore.
3176
+ isPopstate = false;
3177
+ restoredNavigationId = 0;
3178
+ }
3179
+ if (restoreEnabled && isPopstate) {
3180
+ const target = positions.get(restoredNavigationId);
3181
+ // The saved offset wins over both scroll to top and fragment scrolling: the user may well have
3182
+ // scrolled away from the anchor before leaving the page.
3183
+ if (target !== undefined) {
3184
+ scheduleRestore(target);
3185
+ return;
3186
+ }
3187
+ }
2970
3188
  if (sameUrlNavigation) {
2971
3189
  const allQueryParams = [
2972
3190
  ...new Set(Object.keys(prevState.queryParams).concat(Object.keys(currState.queryParams))),
@@ -2978,7 +3196,7 @@ const setupScrollRestoration = (config = {}) => {
2978
3196
  const caseQueryParams = changedQueryParams.some((key) => config.queryParamTriggerList?.includes(key));
2979
3197
  const caseFragment = didFragmentChange && config.fragment?.enabled;
2980
3198
  if (caseQueryParams) {
2981
- (config.scrollElement ?? document.documentElement).scrollTop = 0;
3199
+ resolveScrollElement().scrollTop = 0;
2982
3200
  }
2983
3201
  else if (caseFragment) {
2984
3202
  const fragmentElement = document.getElementById(currState.fragment ?? '');
@@ -2994,9 +3212,17 @@ const setupScrollRestoration = (config = {}) => {
2994
3212
  if (viaReturnRoute || explicitly || pathParamsChange) {
2995
3213
  return;
2996
3214
  }
2997
- const el = config.scrollElement ?? document.documentElement;
2998
- el.scrollTop = 0;
3215
+ resolveScrollElement().scrollTop = 0;
2999
3216
  }
3217
+ };
3218
+ router.events
3219
+ .pipe(filter((e) => e instanceof NavigationStart || e instanceof NavigationEnd || e instanceof NavigationSkipped), takeUntilDestroyed())
3220
+ .subscribe((event) => {
3221
+ if (event instanceof NavigationStart) {
3222
+ onNavigationStart(event);
3223
+ return;
3224
+ }
3225
+ onNavigationEnd(event);
3000
3226
  });
3001
3227
  };
3002
3228
 
@@ -3575,6 +3801,8 @@ const getClientXY = (event) => {
3575
3801
  }
3576
3802
  return { clientX: event.clientX, clientY: event.clientY };
3577
3803
  };
3804
+ /** Trailing window over which {@link SwipeTracker.end} measures the release velocity. */
3805
+ const SWIPE_VELOCITY_WINDOW_MS = 100;
3578
3806
  const createSwipeTracker = (startEvent) => {
3579
3807
  const { clientX: originClientX, clientY: originClientY } = getClientXY(startEvent);
3580
3808
  const startTime = Date.now();
@@ -3582,10 +3810,25 @@ const createSwipeTracker = (startEvent) => {
3582
3810
  let movementY = 0;
3583
3811
  let isSwiping = false;
3584
3812
  let isScrolling = false;
3813
+ /**
3814
+ * Recent positions, oldest first, used to derive the release velocity. Trimmed to the trailing
3815
+ * window plus the one sample just before it, which serves as that window's baseline.
3816
+ */
3817
+ let samples = [{ t: startTime, movementX: 0, movementY: 0 }];
3585
3818
  const update = (event) => {
3586
3819
  const { clientX, clientY } = getClientXY(event);
3587
3820
  movementX = clientX - originClientX;
3588
3821
  movementY = clientY - originClientY;
3822
+ const now = Date.now();
3823
+ samples.push({ t: now, movementX, movementY });
3824
+ // Trim against the newest sample rather than `now + window`: `end()` measures from its own
3825
+ // timestamp, which is never earlier, so this always keeps a superset of what it needs.
3826
+ const cutoff = now - SWIPE_VELOCITY_WINDOW_MS;
3827
+ let dropCount = 0;
3828
+ while (dropCount + 1 < samples.length && samples[dropCount + 1].t < cutoff)
3829
+ dropCount++;
3830
+ if (dropCount)
3831
+ samples = samples.slice(dropCount);
3589
3832
  const positiveMovementX = Math.abs(movementX);
3590
3833
  const positiveMovementY = Math.abs(movementY);
3591
3834
  if (!isSwiping && !isScrolling) {
@@ -3608,12 +3851,31 @@ const createSwipeTracker = (startEvent) => {
3608
3851
  isSwiping,
3609
3852
  };
3610
3853
  };
3854
+ const measureReleaseVelocity = (endTime) => {
3855
+ const windowStart = endTime - SWIPE_VELOCITY_WINDOW_MS;
3856
+ const firstInWindow = samples.findIndex((sample) => sample.t >= windowStart);
3857
+ // Nothing moved during the window: the pointer was held still before release, so the gesture
3858
+ // carries no momentum no matter how fast it started out.
3859
+ if (firstInWindow === -1)
3860
+ return { x: 0, y: 0 };
3861
+ const newest = samples[samples.length - 1];
3862
+ // Measure across the window when it holds a span of its own. With a single sample in it — a
3863
+ // pointer emitting moves slower than the window — reach back one sample for a baseline instead
3864
+ // of reporting nothing.
3865
+ const hasSpanInWindow = samples.length - firstInWindow >= 2;
3866
+ const baseline = samples[hasSpanInWindow ? firstInWindow : Math.max(0, firstInWindow - 1)];
3867
+ const elapsed = newest.t - baseline.t;
3868
+ if (elapsed <= 0)
3869
+ return { x: 0, y: 0 };
3870
+ return {
3871
+ x: ((newest.movementX - baseline.movementX) / elapsed) * 1000,
3872
+ y: ((newest.movementY - baseline.movementY) / elapsed) * 1000,
3873
+ };
3874
+ };
3611
3875
  const end = () => {
3612
- const swipeTime = Math.max(1, Date.now() - startTime);
3613
3876
  const positiveMovementX = Math.abs(movementX);
3614
3877
  const positiveMovementY = Math.abs(movementY);
3615
- const pixelPerSecondX = (movementX / swipeTime) * 1000;
3616
- const pixelPerSecondY = (movementY / swipeTime) * 1000;
3878
+ const { x: pixelPerSecondX, y: pixelPerSecondY } = measureReleaseVelocity(Date.now());
3617
3879
  return {
3618
3880
  movementX,
3619
3881
  movementY,
@@ -3630,6 +3892,7 @@ const createSwipeTracker = (startEvent) => {
3630
3892
  const cancel = () => {
3631
3893
  movementX = 0;
3632
3894
  movementY = 0;
3895
+ samples = [{ t: Date.now(), movementX: 0, movementY: 0 }];
3633
3896
  };
3634
3897
  return { update, end, cancel };
3635
3898
  };
@@ -3717,6 +3980,56 @@ const [provideLocale, injectLocale] = createRootProvider(() => {
3717
3980
  name: 'Locale',
3718
3981
  });
3719
3982
 
3983
+ const resolveSource = (source, locale) => typeof source === 'function' ? source(locale) : source;
3984
+ /**
3985
+ * The one mechanism for the strings a component renders or announces itself. Every domain in the UI
3986
+ * library that has such strings exposes exactly this pair — `provide<Domain>Labels` to localize a
3987
+ * subtree, `inject<Domain>Labels()` to read the result — so localizing an app is the same move
3988
+ * repeated per domain rather than a different one each time.
3989
+ *
3990
+ * `defaults` may itself be locale-derived, for a domain that ships more than one language (the
3991
+ * `@ethlete/query` error tables, say). Overrides are partial and layer on top of whatever the locale
3992
+ * resolved to, so translating into a third language while keeping a shipped one as the base is the
3993
+ * same call.
3994
+ *
3995
+ * The injector returns a **signal**, because both halves can change at runtime — read it in a
3996
+ * template or computed, never destructure it once.
3997
+ *
3998
+ * @example
3999
+ * export type BreadcrumbLabels = { navigation: string; overflow: string };
4000
+ *
4001
+ * export const DEFAULT_BREADCRUMB_LABELS: BreadcrumbLabels = {
4002
+ * navigation: 'Breadcrumb',
4003
+ * overflow: 'Show hidden levels',
4004
+ * };
4005
+ *
4006
+ * export const [provideBreadcrumbLabels, injectBreadcrumbLabels, BREADCRUMB_LABELS] =
4007
+ * createLabels<BreadcrumbLabels>('BREADCRUMB_LABELS', DEFAULT_BREADCRUMB_LABELS);
4008
+ *
4009
+ * @example
4010
+ * // consumer, fixed wording
4011
+ * provideBreadcrumbLabels({ navigation: 'Brotkrumen' });
4012
+ *
4013
+ * // consumer, driven by the app's i18n and re-resolved on a locale change
4014
+ * provideBreadcrumbLabels((locale) => ({ navigation: translate('breadcrumb.nav', locale) }));
4015
+ */
4016
+ const createLabels = (name, defaults) => {
4017
+ const token = new InjectionToken(name, {
4018
+ providedIn: 'root',
4019
+ factory: () => ({}),
4020
+ });
4021
+ const provide = (labels) => ({ provide: token, useValue: labels });
4022
+ const injectFn = () => {
4023
+ const source = inject(token);
4024
+ const { currentLocale } = injectLocale();
4025
+ return computed(() => {
4026
+ const locale = currentLocale();
4027
+ return { ...resolveSource(defaults, locale), ...resolveSource(source, locale) };
4028
+ });
4029
+ };
4030
+ return [provide, injectFn, token];
4031
+ };
4032
+
3720
4033
  const [provideStyleManager, injectStyleManager] = createRootProvider(() => {
3721
4034
  const appRef = inject(ApplicationRef);
3722
4035
  const destroyRef = inject(DestroyRef);
@@ -3799,8 +4112,23 @@ const fromNextFrame = () => {
3799
4112
  });
3800
4113
  });
3801
4114
  };
3802
- const forceReflow = (element = document.body) => {
3803
- return element.offsetHeight;
4115
+ const forceReflow = (element) => {
4116
+ // The `document.body` fallback is resolved lazily and guarded: there is nothing to reflow on the
4117
+ // server, so this becomes a no-op instead of throwing on a missing `document`.
4118
+ const el = element ?? (typeof document === 'undefined' ? null : document.body);
4119
+ return el?.offsetHeight ?? 0;
4120
+ };
4121
+ /**
4122
+ * Whether the user asked for reduced motion, read straight from `matchMedia`.
4123
+ *
4124
+ * Inside a component, prefer the reactive `injectPrefersReducedMotion()`. This is for the animation
4125
+ * helpers that run from effects, event handlers and imperative APIs, where there is no injection
4126
+ * context. Pass the element being animated so the query resolves against its own window — otherwise
4127
+ * an animation inside an iframe would read the top-level document's preference.
4128
+ */
4129
+ const matchesReducedMotion = (element) => {
4130
+ const view = element?.ownerDocument.defaultView ?? (typeof window === 'undefined' ? null : window);
4131
+ return view?.matchMedia('(prefers-reduced-motion: reduce)').matches ?? false;
3804
4132
  };
3805
4133
 
3806
4134
  const ANIMATED_LIFECYCLE_TOKEN = new InjectionToken('ANIMATED_LIFECYCLE_DIRECTIVE_TOKEN');
@@ -4489,11 +4817,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
4489
4817
  }], ctorParameters: () => [], propDecorators: { placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], fallbackPlacements: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackPlacements", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], arrowPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "arrowPadding", required: false }] }], viewportPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportPadding", required: false }] }], autoResize: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoResize", required: false }] }], shift: [{ type: i0.Input, args: [{ isSignal: true, alias: "shift", required: false }] }], autoHide: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoHide", required: false }] }], autoCloseIfReferenceHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCloseIfReferenceHidden", required: false }] }], referenceElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "referenceElement", required: false }] }], mirrorWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "mirrorWidth", required: false }] }] } });
4490
4818
 
4491
4819
  const createFlipAnimationGroup = (config) => {
4492
- const { elements, duration = 250, easing = 'cubic-bezier(0.4, 0, 0.2, 1)' } = config;
4820
+ const { elements, duration = 250, easing = 'cubic-bezier(0.4, 0, 0.2, 1)', ignoreReducedMotion } = config;
4493
4821
  const flips = elements.map((el) => {
4494
4822
  const element = 'element' in el ? el.element : el;
4495
4823
  const originElement = 'originElement' in el ? el.originElement : undefined;
4496
- return createFlipAnimation({ element, originElement, duration, easing });
4824
+ return createFlipAnimation({ element, originElement, duration, easing, ignoreReducedMotion });
4497
4825
  });
4498
4826
  const onStart$ = combineLatest(flips.map((animation) => animation.onStart$));
4499
4827
  const onFinish$ = combineLatest(flips.map((animation) => animation.onFinish$));
@@ -4517,7 +4845,7 @@ const createFlipAnimationGroup = (config) => {
4517
4845
  };
4518
4846
  };
4519
4847
  const createFlipAnimation = (config) => {
4520
- const { element: el, originElement = el, duration = 250, easing = 'cubic-bezier(0.4, 0, 0.2, 1)' } = config;
4848
+ const { element: el, originElement = el, duration = 250, easing = 'cubic-bezier(0.4, 0, 0.2, 1)', ignoreReducedMotion = false, } = config;
4521
4849
  let initialRect = originElement.getBoundingClientRect();
4522
4850
  let animation = null;
4523
4851
  const onStart$ = new Subject();
@@ -4549,6 +4877,10 @@ const createFlipAnimation = (config) => {
4549
4877
  scaleX: initialRect.width / lastRect.width,
4550
4878
  scaleY: initialRect.height / lastRect.height,
4551
4879
  };
4880
+ // Under reduced motion the element jumps straight to its final position. Collapsing the duration
4881
+ // instead of bailing out keeps the `Animation` lifecycle intact, so callers that wait on
4882
+ // `onFinish$` to clean up still get their event.
4883
+ const effectiveDuration = !ignoreReducedMotion && matchesReducedMotion(el) ? 0 : duration;
4552
4884
  animation = el.animate([
4553
4885
  {
4554
4886
  transformOrigin: 'top left',
@@ -4562,7 +4894,7 @@ const createFlipAnimation = (config) => {
4562
4894
  transform: 'none',
4563
4895
  },
4564
4896
  ], {
4565
- duration,
4897
+ duration: effectiveDuration,
4566
4898
  easing,
4567
4899
  fill: 'both',
4568
4900
  });
@@ -4776,6 +5108,20 @@ const setupDragObservable = (startEvent, el, commitThreshold) => {
4776
5108
  }), takeUntil(end$));
4777
5109
  return concat(moves$, defer(() => of(committed ? { type: 'end' } : { type: 'tapped' })));
4778
5110
  };
5111
+ /**
5112
+ * Observe one drag gesture, starting from a `pointerdown` on `element`.
5113
+ *
5114
+ * Emits `start` once the pointer has travelled `commitThreshold` px (so a click is not a drag),
5115
+ * immediately followed by a catch-up `move`, then a `move` per pointer move, and finally `end`. A
5116
+ * pointer released before crossing the threshold emits a single `tapped` instead. The stream
5117
+ * completes with the gesture, so there is nothing to unsubscribe on the happy path.
5118
+ *
5119
+ * This is the primitive behind {@link DragHandleDirective}. Prefer the directive; reach for this when
5120
+ * the draggable element belongs to someone else's template (e.g. a table feature attaching a drag to
5121
+ * header cells the table itself renders) and you can only work from a delegated `pointerdown`.
5122
+ */
5123
+ const dragGestureFrom = (event, element, options) => setupDragObservable(event, element, options?.commitThreshold ?? 8);
5124
+
4779
5125
  class DragHandleDirective {
4780
5126
  constructor() {
4781
5127
  this.el = inject(ElementRef);
@@ -4783,7 +5129,7 @@ class DragHandleDirective {
4783
5129
  ...(ngDevMode ? [{ debugName: "commitThreshold" }] : /* istanbul ignore next */ []));
4784
5130
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4785
5131
  this.gestureStart$ = new Subject();
4786
- this.gesture$ = this.gestureStart$.pipe(exhaustMap((event) => setupDragObservable(event, this.el.nativeElement, this.commitThreshold())), takeUntilDestroyed(), share());
5132
+ this.gesture$ = this.gestureStart$.pipe(exhaustMap((event) => dragGestureFrom(event, this.el.nativeElement, { commitThreshold: this.commitThreshold() })), takeUntilDestroyed(), share());
4787
5133
  this.dragTapped = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'tapped'), map(() => undefined)));
4788
5134
  this.dragStarted = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'start'), map((e) => e.data)));
4789
5135
  this.dragMoved = outputFromObservable(this.gesture$.pipe(filter((e) => e.type === 'move'), map((e) => e.data)));
@@ -5016,7 +5362,10 @@ const createAnchoredPositionCleanup = (strategy, paneElement, overlayRef, render
5016
5362
  if (arrowElement) {
5017
5363
  middleware.push(arrow({
5018
5364
  element: arrowElement,
5019
- padding: strategy.arrowPadding ?? 4,
5365
+ // keeps the arrow off a rounded pane corner by default: 12px covers the radius of the
5366
+ // built-in boxed panes (--et-overlay-radius). Panes with a larger radius have to pass their
5367
+ // own — an arrow whose base sits on the corner arc looks detached from the pane.
5368
+ padding: strategy.arrowPadding ?? 12,
5020
5369
  }));
5021
5370
  }
5022
5371
  if (strategy.autoHide || strategy.autoCloseIfReferenceHidden) {
@@ -8257,5 +8606,5 @@ const createUnsavedChangesGuard = (config) => {
8257
8606
  * Generated bundle index. Do not edit.
8258
8607
  */
8259
8608
 
8260
- export { ANIMATABLE_TOKEN, ANIMATED_IF_TOKEN, ANIMATED_LIFECYCLE_TOKEN, ANIMATION_DEBUG_STORAGE_KEY, AT_LEAST_ONE_REQUIRED, AnimatableDirective, AnimatedIfDirective, AnimatedLifecycleDirective, AnimatedOverlayDirective, AutoSurfaceDirective, BOUNDARY_ELEMENT_TOKEN, BREAKPOINT_INSTANCE_TOKEN, BREAKPOINT_ORDER, COLOR_PROVIDER, ClickOutsideDirective, ColorInteractiveContainerDirective, ColorInteractiveDirective, ColorInteractiveExcludeDirective, ColorInteractiveHasFocusDirective, DEFAULT_MULTI_INSTANCE_RELS, DEFAULT_MULTI_INSTANCE_TAGS, 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, FOCUSABLE_SELECTOR, FOCUS_VISIBLE_TRACKER_TOKEN, HtmlToMarkdownPipe, IS_ARRAY_NOT_EMPTY, IS_EMAIL, InferMimeTypePipe, InitialsPipe, IsArrayNotEmpty, IsEmail, jsonLd as JsonLD, KeyPressManager, MUST_MATCH, MarkdownToHtmlPipe, MustMatch, NormalizeGameResultTypePipe, NormalizeMatchParticipantsPipe, NormalizeMatchScorePipe, NormalizeMatchStatePipe, NormalizeMatchTypePipe, PropsDirective, ProvideColorDirective, ProvideSurfaceDirective, RUNTIME_ERROR_NO_DATA, RepeatDirective, ResizeHandlesComponent, RuntimeError, SEO_DIRECTIVE_TOKEN, SURFACE_PROVIDER, SURFACE_TYPE, ScrollObserverDirective, ScrollObserverEndDirective, ScrollObserverStartDirective, SeoDirective, SlugifyPipe, StructuredDataComponent, SurfaceInteractiveDirective, ToArrayPipe, TypedQueryList, ValidateAtLeastOneRequired, Validators, animationDebugLog, applyAlternateBinding, applyAlternateLanguagesBindings, applyArticleBindings, applyAuthorBinding, applyCanonicalBinding, applyCenteredPosition, applyDescriptionBinding, applyGlobalPosition, applyHeadBinding, applyHeadTitleBinding, applyHostListener, applyHostListeners, applyInitialFocus, applyKeywordsBinding, applyLinkBinding, applyMetaBinding, applyNextBinding, applyOgBinding, applyOpenGraphBindings, applyPrevBinding, applyResourceHintsBindings, applyRobotsBinding, applySocialMediaBindings, applyStructuredDataBinding, applyTwitterCardBindings, areScrollStatesEqual, bindProps, boolBreakpointTransform, booleanBreakpointAttribute, boundingClientRectToElementRect, breakpointTransformBase, buildElementSignal, buildSignalEffects, canUseSessionMemory, clamp, clone, cloneFormGroup, computedTillFalsy, computedTillTruthy, controlValueSignal, controlValueSignalWithPrevious, copyToClipboard, createAnchoredPositionCleanup, createArrayPropertyBinding, createAutoSessionMemoryKey, createBulkPropertyBinding, createCanAnimateSignal, createComponentId, createCssColorThemeName, createCssSurfaceName, createDependencyStash, createDestroy, createDocumentElementSignal, createElementDictionary, createElementDimensions, createEmptyElementSignal, createFlipAnimation, createFlipAnimationGroup, createHostProps, createIsRenderedSignal, createLogger, createOverlayRuntimeRef, createPropHandlers, createPropertyBinding, createProps, createProvider, createRootProvider, createRootThemeCss, createRoute, createRouterState, createRxHostListener, createSessionMemory, createSetup, createStaticProvider, createStaticRootProvider, createSwatchCss, createSwipeTracker, createTailwindColorThemes, createTailwindCssVar, createTailwindRgbVar, createThemeStyle, createUnsavedChangesGuard, createUnsavedChangesTracker, createUserConsentProvider, deferredSignal, deleteCookie, easeElastic, easeIn, easeInOut, easeLinear, easeOut, easeOutBack, easeOutBackStrong, elementCanScroll, equal, firstElementSignal, focusElement, forceReflow, formatRuntimeError, fromNextFrame, getCookie, getDomain, getElementScrollCoordinates, getFocusableElements, getFormGroupValue, getGroupMatchPoints, getGroupMatchScore, getHeadingElement, getKnockoutMatchScore, getMatchScoreSubLine, getObjectProperty, getScrollContainerTarget, getScrollItemTarget, getScrollSnapTarget, hasCookie, htmlToMarkdown, inferMimeType, initials, injectAngularRootElement, injectAnimatedBlockSize, injectBoundaryElement, injectBreakpointInput, injectBreakpointIsMatched, injectBreakpointObserver, injectCanHover, injectColorThemes, injectColorThemesPrefix, injectCurrentBreakpoint, injectDeviceInputType, injectDisplayOrientation, injectErrorTheme, injectFocusVisibleTracker, injectFragment, injectHasPrecisionInput, injectHasTouchInput, injectHostElement, injectIs2Xl, injectIsLandscape, injectIsLg, injectIsMd, injectIsPortrait, injectIsRouterInitialized, injectIsSm, injectIsXl, injectIsXs, injectLinkStore, injectLinkStoreConfig, injectLocale, injectMediaQueryIsMatched, injectMetaConfig, injectMetaStore, injectObserveBreakpoint, injectObserveMediaQuery, injectOverlayRuntime, injectPathParam, injectPathParamChanges, injectPathParams, injectPrefersReducedMotion, injectQueryParam, injectQueryParamChanges, injectQueryParams, injectRenderer, injectRoute, injectRouteData, injectRouteDataItem, injectRouteTitle, injectRouterEvent, injectRouterState, injectScrollbarDimensions, injectStructuredDataConfig, injectStructuredDataStore, injectStyleManager, injectSuccessTheme, injectSurfaceContextTracker, injectSurfaceThemes, injectSurfaceThemesPrefix, injectTemplateRef, injectTitleConfig, injectTitleStore, injectUrl, injectViewportConfig, injectViewportDimensions, injectViewportSize, injectWarningTheme, isAnimationDebugEnabled, isArray, isElementSignal, isElementVisible, isFocusable, isGroupMatch, isHTMLElement, isKnockoutMatch, isObject, markdownToHtml, maybeSignalValue, memoizeSignal, nextFrame, normalizeGameResultType, normalizeMatchParticipant, normalizeMatchParticipants, normalizeMatchScore, normalizeMatchState, normalizeMatchType, normalizeUnsavedChangesSource, numberBreakpointAttribute, numberBreakpointTransform, previousSignalValue, provideBoundaryElement, provideBreakpointInstance, provideBreakpointObserver, provideColorThemes, provideColorThemesWithTailwind4, provideFocusVisibleTracker, provideLinkStore, provideLinkStoreConfig, provideLocale, provideMetaConfig, provideMetaStore, provideOverlayRuntime, provideRenderer, provideStructuredDataConfig, provideStructuredDataStore, provideStyleManager, provideSurfaceContextTracker, provideSurfaceThemesWithTailwind4, provideTitleConfig, provideTitleStore, provideViewportConfig, randomId, readFromClipboard, resetPositioningStyles, resolveAppRootColorProvider, resolveSurfaceByElevation, round, routerDisableScrollTop, scrollToElement, setBackdropStyles, setBaseElementStyles, setCookie, setInputSignal, setupFocusTrap, setupPositioning, setupScrollRestoration, signalAnimatedNumber, signalAttributes, signalClasses, signalElementChildren, signalElementDimensions, signalElementIntersection, signalElementLastScrollDirection, signalElementMutations, signalElementScrollState, signalHostAttributes, signalHostClasses, signalHostElementDimensions, signalHostElementIntersection, signalHostElementLastScrollDirection, signalHostElementMutations, signalHostElementScrollState, signalHostStyles, signalIsRendered, signalStyles, slugify, switchQueryListChanges, syncSignal, templateComputed, toArray, toArrayTrackByFn, toStringBinding, transformOrReturn, typedBreakpointTransform, unbindProps, useCursorDragScroll, writeScrollbarSizeToCssVariables, writeViewportSizeToCssVariables, ɵProvideColorThemes, ɵProvideColorThemesPrefix, ɵProvideSurfaceThemes, ɵProvideSurfaceThemesPrefix };
8609
+ export { ANIMATABLE_TOKEN, ANIMATED_IF_TOKEN, ANIMATED_LIFECYCLE_TOKEN, ANIMATION_DEBUG_STORAGE_KEY, AT_LEAST_ONE_REQUIRED, AnimatableDirective, AnimatedIfDirective, AnimatedLifecycleDirective, AnimatedOverlayDirective, AutoSurfaceDirective, BOUNDARY_ELEMENT_TOKEN, BREAKPOINT_INSTANCE_TOKEN, BREAKPOINT_ORDER, COLOR_PROVIDER, ClickOutsideDirective, ColorInteractiveContainerDirective, ColorInteractiveDirective, ColorInteractiveExcludeDirective, ColorInteractiveHasFocusDirective, DEFAULT_MULTI_INSTANCE_RELS, DEFAULT_MULTI_INSTANCE_TAGS, 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, FOCUSABLE_SELECTOR, FOCUS_VISIBLE_TRACKER_TOKEN, HtmlToMarkdownPipe, IS_ARRAY_NOT_EMPTY, IS_EMAIL, InferMimeTypePipe, InitialsPipe, IsArrayNotEmpty, IsEmail, jsonLd as JsonLD, KeyPressManager, MUST_MATCH, MarkdownToHtmlPipe, MustMatch, NormalizeGameResultTypePipe, NormalizeMatchParticipantsPipe, NormalizeMatchScorePipe, NormalizeMatchStatePipe, NormalizeMatchTypePipe, PropsDirective, ProvideColorDirective, ProvideSurfaceDirective, RUNTIME_ERROR_NO_DATA, RepeatDirective, ResizeHandlesComponent, RuntimeError, SEO_DIRECTIVE_TOKEN, SURFACE_PROVIDER, SURFACE_TYPE, SWIPE_VELOCITY_WINDOW_MS, ScrollObserverDirective, ScrollObserverEndDirective, ScrollObserverStartDirective, SeoDirective, SlugifyPipe, StructuredDataComponent, SurfaceInteractiveDirective, ToArrayPipe, TypedQueryList, ValidateAtLeastOneRequired, Validators, animationDebugLog, applyAlternateBinding, applyAlternateLanguagesBindings, applyArticleBindings, applyAuthorBinding, applyCanonicalBinding, applyCenteredPosition, applyDescriptionBinding, applyGlobalPosition, applyHeadBinding, applyHeadTitleBinding, applyHostListener, applyHostListeners, applyInitialFocus, applyKeywordsBinding, applyLinkBinding, applyMetaBinding, applyNextBinding, applyOgBinding, applyOpenGraphBindings, applyPrevBinding, applyResourceHintsBindings, applyRobotsBinding, applySocialMediaBindings, applyStructuredDataBinding, applyTwitterCardBindings, areScrollStatesEqual, bindProps, boolBreakpointTransform, booleanBreakpointAttribute, boundingClientRectToElementRect, breakpointTransformBase, buildElementSignal, buildSignalEffects, canUseSessionMemory, clamp, clone, cloneFormGroup, computedTillFalsy, computedTillTruthy, controlValueSignal, controlValueSignalWithPrevious, copyToClipboard, createAnchoredPositionCleanup, createArrayPropertyBinding, createAutoSessionMemoryKey, createBulkPropertyBinding, createCanAnimateSignal, createComponentId, createCssColorThemeName, createCssSurfaceName, createDependencyStash, createDestroy, createDocumentElementSignal, createElementDictionary, createElementDimensions, createEmptyElementSignal, createFlipAnimation, createFlipAnimationGroup, createHostProps, createIsRenderedSignal, createLabels, createLogger, createOverlayRuntimeRef, createPropHandlers, createPropertyBinding, createProps, createProvider, createRootProvider, createRootThemeCss, createRoute, createRouterState, createRxHostListener, createSessionMemory, createSetup, createStaticProvider, createStaticRootProvider, createSwatchCss, createSwipeTracker, createTailwindColorThemes, createTailwindCssVar, createTailwindRgbVar, createThemeStyle, createUnsavedChangesGuard, createUnsavedChangesTracker, createUserConsentProvider, deferredSignal, deleteCookie, dragGestureFrom, easeElastic, easeIn, easeInOut, easeLinear, easeOut, easeOutBack, easeOutBackStrong, elementCanScroll, equal, 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, injectBoundaryElement, injectBreakpointInput, injectBreakpointIsMatched, injectBreakpointObserver, injectCanHover, injectColorThemes, injectColorThemesPrefix, injectCurrentBreakpoint, injectDeviceInputType, injectDisplayOrientation, injectErrorTheme, injectFocusVisibleTracker, injectFragment, injectHasPrecisionInput, injectHasTouchInput, injectHostElement, injectIs2Xl, injectIsDocumentVisible, injectIsLandscape, injectIsLg, injectIsMd, injectIsPortrait, injectIsRouterInitialized, injectIsSm, injectIsXl, injectIsXs, injectLinkStore, injectLinkStoreConfig, injectLocale, injectMediaQueryIsMatched, injectMetaConfig, injectMetaStore, injectObserveBreakpoint, injectObserveMediaQuery, injectOverlayRuntime, injectPathParam, injectPathParamChanges, injectPathParams, injectPrefersReducedMotion, injectQueryParam, injectQueryParamChanges, injectQueryParams, injectRenderer, injectRoute, injectRouteData, injectRouteDataItem, injectRouteTitle, injectRouterEvent, injectRouterNavigationState, injectRouterState, injectScrollbarDimensions, injectStructuredDataConfig, injectStructuredDataStore, injectStyleManager, injectSuccessTheme, injectSurfaceContextTracker, injectSurfaceThemes, injectSurfaceThemesPrefix, injectTemplateRef, injectTitleConfig, injectTitleStore, injectUrl, injectViewportConfig, injectViewportDimensions, injectViewportSize, injectWarningTheme, isAnimationDebugEnabled, isArray, isElementSignal, isElementVisible, isFocusable, isGroupMatch, isHTMLElement, isKnockoutMatch, isObject, markdownToHtml, matchesReducedMotion, maybeSignalValue, memoizeSignal, nextFrame, normalizeGameResultType, normalizeMatchParticipant, normalizeMatchParticipants, normalizeMatchScore, normalizeMatchState, normalizeMatchType, normalizeUnsavedChangesSource, numberBreakpointAttribute, numberBreakpointTransform, previousSignalValue, provideBoundaryElement, provideBreakpointInstance, provideBreakpointObserver, provideColorThemes, provideColorThemesWithTailwind4, provideFocusVisibleTracker, provideLinkStore, provideLinkStoreConfig, provideLocale, provideMetaConfig, provideMetaStore, provideOverlayRuntime, provideRenderer, provideStructuredDataConfig, provideStructuredDataStore, provideStyleManager, provideSurfaceContextTracker, provideSurfaceThemesWithTailwind4, provideTitleConfig, provideTitleStore, provideViewportConfig, randomId, readFromClipboard, resetPositioningStyles, resolveAppRootColorProvider, resolveSurfaceByElevation, round, routerDisableScrollTop, scrollToElement, setBackdropStyles, setBaseElementStyles, setCookie, setInputSignal, setupFocusTrap, setupPositioning, setupScrollRestoration, signalAnimatedNumber, signalAttributes, signalClasses, signalElementChildren, signalElementDimensions, signalElementIntersection, signalElementLastScrollDirection, signalElementMutations, signalElementScrollState, signalHostAttributes, signalHostClasses, signalHostElementDimensions, signalHostElementIntersection, signalHostElementLastScrollDirection, signalHostElementMutations, signalHostElementScrollState, signalHostStyles, signalIsRendered, signalStyles, slugify, switchQueryListChanges, syncSignal, templateComputed, toArray, toArrayTrackByFn, toStringBinding, transformOrReturn, typedBreakpointTransform, unbindProps, useCursorDragScroll, writeScrollbarSizeToCssVariables, writeViewportSizeToCssVariables, ɵProvideColorThemes, ɵProvideColorThemesPrefix, ɵProvideSurfaceThemes, ɵProvideSurfaceThemesPrefix };
8261
8610
  //# sourceMappingURL=ethlete-core.mjs.map