@ethlete/core 5.0.0-next.62 → 5.0.0-next.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/fesm2022/ethlete-core.mjs +123 -63
- package/fesm2022/ethlete-core.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ethlete-core.d.ts +9 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @ethlete/core
|
|
2
2
|
|
|
3
|
+
## 5.0.0-next.63
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fix modal focus trap tab stops, overlay stacking order for Escape and outside clicks, focus restore to disabled openers, `afterOpened()` on early close, and scroll restoration under a base href.
|
|
8
|
+
- Fix edge cases in `AnimatedLifecycleDirective` classes, `getScrollSnapTarget`, `[etClickOutside]`, `signalElementIntersection` order, `isElementVisible().intersectionRatio` and `createUnsavedChangesTracker` with a `null` default.
|
|
9
|
+
- `htmlToMarkdown` drops empty headings and list items, and normalizes block alignment to `center`, `right` or `justify`; other values serialize as plain Markdown.
|
|
10
|
+
- The modal focus trap treats a native radio group as one Tab stop, so Tab no longer escapes a dialog through it.
|
|
11
|
+
|
|
3
12
|
## 5.0.0-next.62
|
|
4
13
|
|
|
5
14
|
### Major Changes
|
|
@@ -1755,8 +1755,17 @@ const breakpointTransformBase = (coerce, defaultValue) => {
|
|
|
1755
1755
|
const bp = currentBp();
|
|
1756
1756
|
const r = raw();
|
|
1757
1757
|
if (!cachedSig) {
|
|
1758
|
-
const instance = injector.get(BREAKPOINT_INSTANCE_TOKEN, null);
|
|
1759
|
-
if (
|
|
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) {
|
|
1760
1769
|
if (ngDevMode && r !== undefined && !warnedMissingInstance) {
|
|
1761
1770
|
warnedMissingInstance = true;
|
|
1762
1771
|
console.warn('[ethlete] A breakpoint map was bound to an input, but its component does not call ' +
|
|
@@ -1764,13 +1773,6 @@ const breakpointTransformBase = (coerce, defaultValue) => {
|
|
|
1764
1773
|
}
|
|
1765
1774
|
return;
|
|
1766
1775
|
}
|
|
1767
|
-
for (const key of Object.keys(instance)) {
|
|
1768
|
-
const val = instance[key];
|
|
1769
|
-
if (val && typeof val === 'function' && val[SIGNAL]?.transformFn === transformFn) {
|
|
1770
|
-
cachedSig = val;
|
|
1771
|
-
break;
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
1776
|
}
|
|
1775
1777
|
if (!cachedSig || r === undefined)
|
|
1776
1778
|
return;
|
|
@@ -2213,7 +2215,7 @@ const isElementVisible = (options) => {
|
|
|
2213
2215
|
containerRect,
|
|
2214
2216
|
elementRect,
|
|
2215
2217
|
// Round the intersection ratio to the nearest 0.01 to avoid floating point errors and system scaling issues.
|
|
2216
|
-
intersectionRatio: Math.round(
|
|
2218
|
+
intersectionRatio: Math.round(inlineIntersectionPercentage * blockIntersectionPercentage * 100) / 100,
|
|
2217
2219
|
};
|
|
2218
2220
|
};
|
|
2219
2221
|
const nearestAlignment = (elStart, elEnd, conStart, conEnd) => {
|
|
@@ -2331,6 +2333,8 @@ const getScrollSnapTarget = (items, container, direction, origin, margin = 0) =>
|
|
|
2331
2333
|
};
|
|
2332
2334
|
for (const item of items) {
|
|
2333
2335
|
const itemRect = item.getBoundingClientRect();
|
|
2336
|
+
if (!itemRect.width && !itemRect.height)
|
|
2337
|
+
continue;
|
|
2334
2338
|
const itemSize = direction === 'horizontal' ? itemRect.width : itemRect.height;
|
|
2335
2339
|
const relativeStart = direction === 'horizontal' ? itemRect.left - containerRect.left : itemRect.top - containerRect.top;
|
|
2336
2340
|
const relativeEnd = relativeStart + itemSize;
|
|
@@ -2512,6 +2516,12 @@ const signalElementIntersection = (el, options) => {
|
|
|
2512
2516
|
currentValues = [...currentValues, intersectionEntry];
|
|
2513
2517
|
}
|
|
2514
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));
|
|
2515
2525
|
zone.run(() => elementIntersectionSignal.set(currentValues));
|
|
2516
2526
|
};
|
|
2517
2527
|
const updateIntersectionObserver = (rendered, enabled, rootEl, margin, thresholdValue) => {
|
|
@@ -2954,7 +2964,10 @@ const createInitialRoute = () => {
|
|
|
2954
2964
|
if (!isBrowser) {
|
|
2955
2965
|
return router.url;
|
|
2956
2966
|
}
|
|
2957
|
-
|
|
2967
|
+
return readLocationPath(inject(Location));
|
|
2968
|
+
};
|
|
2969
|
+
const readLocationPath = (location) => {
|
|
2970
|
+
const path = location.path(true);
|
|
2958
2971
|
return path.startsWith('/') ? path : `/${path}`;
|
|
2959
2972
|
};
|
|
2960
2973
|
/** Inject the current router event */
|
|
@@ -3040,14 +3053,20 @@ const createRouterState = (router) => {
|
|
|
3040
3053
|
/**
|
|
3041
3054
|
* Read the current route (the committed url without query params and fragment) straight from the router.
|
|
3042
3055
|
* Unlike the `injectRoute` signal this reads synchronously, so it can be used inside a `router.events` handler
|
|
3043
|
-
* where `router.url` is already committed.
|
|
3056
|
+
* where `router.url` is already committed. Before the first navigation commits it falls back to `location`
|
|
3057
|
+
* (without the base href) when given, else to the browser location.
|
|
3044
3058
|
*/
|
|
3045
|
-
const createRoute = (router) => {
|
|
3046
|
-
const
|
|
3059
|
+
const createRoute = (router, location) => {
|
|
3060
|
+
const url = router.navigated ? router.url : readUncommittedUrl(router, location);
|
|
3061
|
+
return url.split('?')[0]?.split('#')[0] ?? '';
|
|
3062
|
+
};
|
|
3063
|
+
const readUncommittedUrl = (router, location) => {
|
|
3064
|
+
if (location) {
|
|
3065
|
+
return readLocationPath(location);
|
|
3066
|
+
}
|
|
3067
|
+
return typeof window === 'undefined'
|
|
3047
3068
|
? router.url
|
|
3048
3069
|
: window.location.pathname + window.location.search + window.location.hash;
|
|
3049
|
-
const url = router.navigated ? router.url : browserUrl;
|
|
3050
|
-
return url.split('?')[0]?.split('#')[0] ?? '';
|
|
3051
3070
|
};
|
|
3052
3071
|
/**
|
|
3053
3072
|
* Inject the complete router state. This includes the current route data, path params, query params, title and fragment.
|
|
@@ -3306,6 +3325,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3306
3325
|
return;
|
|
3307
3326
|
}
|
|
3308
3327
|
const router = inject(Router);
|
|
3328
|
+
const location = inject(Location);
|
|
3309
3329
|
const document = inject(DOCUMENT);
|
|
3310
3330
|
const injector = inject(Injector);
|
|
3311
3331
|
const destroyRef = inject(DestroyRef);
|
|
@@ -3334,7 +3354,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3334
3354
|
let wantsRestore = false;
|
|
3335
3355
|
let prev = {
|
|
3336
3356
|
state: createRouterState(router),
|
|
3337
|
-
route: createRoute(router),
|
|
3357
|
+
route: createRoute(router, location),
|
|
3338
3358
|
routeConfig: getDeepestRouteConfig(router),
|
|
3339
3359
|
};
|
|
3340
3360
|
let pendingRestore = null;
|
|
@@ -3452,7 +3472,7 @@ const setupScrollRestoration = (config = {}) => {
|
|
|
3452
3472
|
const onNavigationEnd = (event) => {
|
|
3453
3473
|
const curr = {
|
|
3454
3474
|
state: createRouterState(router),
|
|
3455
|
-
route: createRoute(router),
|
|
3475
|
+
route: createRoute(router, location),
|
|
3456
3476
|
routeConfig: getDeepestRouteConfig(router),
|
|
3457
3477
|
};
|
|
3458
3478
|
const prevState = prev.state;
|
|
@@ -3613,7 +3633,13 @@ const parseSeparatorAligns = (line) => parseTableRow(line).map((cell) => {
|
|
|
3613
3633
|
const separatorFor = (align) => align === 'center' ? ':---:' : align === 'right' ? '---:' : align === 'left' ? ':---' : '---';
|
|
3614
3634
|
const alignClass = (align) => (align ? ` class="et-rte-align-${align}"` : '');
|
|
3615
3635
|
const alignOf = (attrs) => (/\bclass\s*=\s*["'][^"']*\bet-rte-align-([a-z]+)/i.exec(attrs)?.[1] ??
|
|
3616
|
-
/\bstyle\s*=\s*["'][^"']*text-align:\s*([a-z]+)/i.exec(attrs)?.[1])?.toLowerCase() ?? null;
|
|
3636
|
+
/\bstyle\s*=\s*["'][^"']*text-align:\s*(?:-[a-z]+-)?([a-z]+)/i.exec(attrs)?.[1])?.toLowerCase() ?? null;
|
|
3637
|
+
const BLOCK_ALIGNS = /* @__PURE__ */ new Set(['center', 'right', 'justify']);
|
|
3638
|
+
const blockAlignOf = (attrs) => {
|
|
3639
|
+
const align = alignOf(attrs);
|
|
3640
|
+
return align && BLOCK_ALIGNS.has(align) ? align : null;
|
|
3641
|
+
};
|
|
3642
|
+
const isBlankHtml = (html) => !/<img\b/i.test(html) && !stripTags(html.replace(/ /gi, ' ')).trim();
|
|
3617
3643
|
/** Wraps `content` in an emphasis `marker`, hoisting boundary whitespace outside the delimiters -
|
|
3618
3644
|
* CommonMark emphasis must not face whitespace on the inside (`** fett**` doesn't parse), and
|
|
3619
3645
|
* ` ` counts as whitespace there too. Whitespace-only content stays unwrapped. */
|
|
@@ -3816,16 +3842,20 @@ const listToMarkdown = (inner, ordered, startNumber, depth) => {
|
|
|
3816
3842
|
const indent = ' '.repeat(depth);
|
|
3817
3843
|
let n = startNumber;
|
|
3818
3844
|
return findListItems(inner)
|
|
3819
|
-
.
|
|
3845
|
+
.flatMap((itemInner) => {
|
|
3820
3846
|
let content = itemInner;
|
|
3821
3847
|
let nestedMarkdown = '';
|
|
3822
3848
|
for (let nested = findList(content); nested; nested = findList(content)) {
|
|
3823
|
-
|
|
3849
|
+
const nestedItems = listToMarkdown(nested.inner, nested.ordered, nested.startNumber, depth + 1);
|
|
3850
|
+
if (nestedItems)
|
|
3851
|
+
nestedMarkdown += `\n${nestedItems}`;
|
|
3824
3852
|
content = content.slice(0, nested.start) + content.slice(nested.end);
|
|
3825
3853
|
}
|
|
3854
|
+
if (!nestedMarkdown && isBlankHtml(content))
|
|
3855
|
+
return [];
|
|
3826
3856
|
const marker = ordered ? `${n++}. ` : '- ';
|
|
3827
3857
|
// list items are single-line in this serializer, so a <br> degrades to a space
|
|
3828
|
-
return `${indent}${marker}${stripTags(content.replace(/<br\s*\/?>/gi, ' ')).trim()}${nestedMarkdown}
|
|
3858
|
+
return [`${indent}${marker}${stripTags(content.replace(/<br\s*\/?>/gi, ' ')).trim()}${nestedMarkdown}`];
|
|
3829
3859
|
})
|
|
3830
3860
|
.join('\n');
|
|
3831
3861
|
};
|
|
@@ -3934,7 +3964,7 @@ const markdownToHtml = (markdown, options = {}) => {
|
|
|
3934
3964
|
const align = aligned ? alignOf(aligned[2] ?? '') : null;
|
|
3935
3965
|
if (aligned && align) {
|
|
3936
3966
|
const tag = (aligned[1] ?? 'p').toLowerCase();
|
|
3937
|
-
return `<${tag}${alignClass(align)}>${sanitizeInlineHtml(aligned[3] ?? '')}</${tag}>`;
|
|
3967
|
+
return `<${tag}${alignClass(BLOCK_ALIGNS.has(align) ? align : null)}>${sanitizeInlineHtml(aligned[3] ?? '')}</${tag}>`;
|
|
3938
3968
|
}
|
|
3939
3969
|
// Heading
|
|
3940
3970
|
const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
|
|
@@ -4004,9 +4034,12 @@ const htmlToMarkdown = (html) => {
|
|
|
4004
4034
|
// stays HTML) and round-trip via a placeholder - extracted before the block passes below rewrite
|
|
4005
4035
|
// them, restored after the final tag-strip.
|
|
4006
4036
|
const alignedBlocks = [];
|
|
4007
|
-
md = md.replace(/<(p|h[1-6]|div)\b([^>]*\b(?:style="[^"]*text-align|class="[^"]*\bet-rte-align-)[^>]*)>([\s\S]*?)<\/\1>/gi, (
|
|
4037
|
+
md = md.replace(/<(p|h[1-6]|div)\b([^>]*\b(?:style="[^"]*text-align|class="[^"]*\bet-rte-align-)[^>]*)>([\s\S]*?)<\/\1>/gi, (block, tag, attrs, inner) => {
|
|
4038
|
+
const align = blockAlignOf(attrs);
|
|
4039
|
+
if (!align)
|
|
4040
|
+
return block.replace(/^<[a-z0-9]+\b[^>]*>/i, `<${tag}>`);
|
|
4008
4041
|
const name = tag.toLowerCase();
|
|
4009
|
-
return makePlaceholder('ALIGN', alignedBlocks.push(`<${name}${alignClass(
|
|
4042
|
+
return makePlaceholder('ALIGN', alignedBlocks.push(`<${name}${alignClass(align)}>${inner}</${name}>`) - 1);
|
|
4010
4043
|
});
|
|
4011
4044
|
md = escapeTextNodes(md);
|
|
4012
4045
|
// Code blocks - process before inline code
|
|
@@ -4016,7 +4049,7 @@ const htmlToMarkdown = (html) => {
|
|
|
4016
4049
|
// Headings - keep the inner markup so the inline passes below turn any nested
|
|
4017
4050
|
// bold/italic/link/code into markdown; leftover tags are stripped at the end.
|
|
4018
4051
|
for (let i = 6; i >= 1; i--) {
|
|
4019
|
-
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`);
|
|
4020
4053
|
}
|
|
4021
4054
|
// Bold + italic (combined before individual). Boundary whitespace is hoisted out of the
|
|
4022
4055
|
// delimiters (emphasize) - `<strong> x</strong>` must become ` **x**`, not the invalid `** x**`.
|
|
@@ -4856,6 +4889,7 @@ const ANIMATION_CLASSES = {
|
|
|
4856
4889
|
leaveDone: 'et-animation-leave-done',
|
|
4857
4890
|
leaveInterrupt: 'et-animation-leave-interrupt',
|
|
4858
4891
|
};
|
|
4892
|
+
const ALL_ANIMATION_CLASSES = /* @__PURE__ */ Object.values(ANIMATION_CLASSES);
|
|
4859
4893
|
const FORCE_INVISIBLE_CLASS = 'et-force-invisible';
|
|
4860
4894
|
class AnimatedLifecycleDirective {
|
|
4861
4895
|
constructor() {
|
|
@@ -4902,9 +4936,9 @@ class AnimatedLifecycleDirective {
|
|
|
4902
4936
|
return;
|
|
4903
4937
|
if ((currentState === 'init' && !this.isConstructed) || this.skipNextEnter()) {
|
|
4904
4938
|
this.debugLog('enter: instant (not constructed or skipNextEnter)', { currentState });
|
|
4905
|
-
this.
|
|
4939
|
+
this.cancelCurrentAnimation$.next();
|
|
4940
|
+
this.settleInstantly('entered');
|
|
4906
4941
|
this.skipNextEnter.set(false);
|
|
4907
|
-
this.addClass(ANIMATION_CLASSES.enterDone);
|
|
4908
4942
|
this.forcedThisFrame = false;
|
|
4909
4943
|
return;
|
|
4910
4944
|
}
|
|
@@ -4922,9 +4956,7 @@ class AnimatedLifecycleDirective {
|
|
|
4922
4956
|
transitionId,
|
|
4923
4957
|
});
|
|
4924
4958
|
if (skipAnimation) {
|
|
4925
|
-
this.
|
|
4926
|
-
this.updateState('entered');
|
|
4927
|
-
this.addClass(ANIMATION_CLASSES.enterDone);
|
|
4959
|
+
this.settleInstantly('entered');
|
|
4928
4960
|
previousCancel$.next();
|
|
4929
4961
|
previousCancel$.complete();
|
|
4930
4962
|
return;
|
|
@@ -4972,8 +5004,7 @@ class AnimatedLifecycleDirective {
|
|
|
4972
5004
|
return;
|
|
4973
5005
|
if (currentState === 'init') {
|
|
4974
5006
|
this.debugLog('leave: instant (state is init, enter never ran)');
|
|
4975
|
-
this.
|
|
4976
|
-
this.addClass(ANIMATION_CLASSES.leaveDone);
|
|
5007
|
+
this.settleInstantly('left');
|
|
4977
5008
|
this.forcedThisFrame = false;
|
|
4978
5009
|
return;
|
|
4979
5010
|
}
|
|
@@ -4991,9 +5022,7 @@ class AnimatedLifecycleDirective {
|
|
|
4991
5022
|
transitionId,
|
|
4992
5023
|
});
|
|
4993
5024
|
if (skipAnimation) {
|
|
4994
|
-
this.
|
|
4995
|
-
this.updateState('left');
|
|
4996
|
-
this.addClass(ANIMATION_CLASSES.leaveDone);
|
|
5025
|
+
this.settleInstantly('left');
|
|
4997
5026
|
previousCancel$.next();
|
|
4998
5027
|
previousCancel$.complete();
|
|
4999
5028
|
return;
|
|
@@ -5037,18 +5066,20 @@ class AnimatedLifecycleDirective {
|
|
|
5037
5066
|
}
|
|
5038
5067
|
forceEnteredState() {
|
|
5039
5068
|
this.cancelCurrentAnimation$.next();
|
|
5040
|
-
this.
|
|
5041
|
-
this.removeClasses(ANIMATION_CLASSES.enterFrom, ANIMATION_CLASSES.enterActive, ANIMATION_CLASSES.enterTo, ANIMATION_CLASSES.leaveFrom, ANIMATION_CLASSES.leaveActive, ANIMATION_CLASSES.leaveTo);
|
|
5042
|
-
this.addClass(ANIMATION_CLASSES.enterDone);
|
|
5069
|
+
this.settleInstantly('entered');
|
|
5043
5070
|
this.markForcedThisFrame();
|
|
5044
5071
|
}
|
|
5045
5072
|
forceLeftState() {
|
|
5046
5073
|
this.cancelCurrentAnimation$.next();
|
|
5047
|
-
this.
|
|
5048
|
-
this.removeClasses(ANIMATION_CLASSES.enterFrom, ANIMATION_CLASSES.enterActive, ANIMATION_CLASSES.enterTo, ANIMATION_CLASSES.leaveFrom, ANIMATION_CLASSES.leaveActive, ANIMATION_CLASSES.leaveTo);
|
|
5049
|
-
this.addClass(ANIMATION_CLASSES.leaveDone);
|
|
5074
|
+
this.settleInstantly('left');
|
|
5050
5075
|
this.markForcedThisFrame();
|
|
5051
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
|
+
}
|
|
5052
5083
|
markForcedThisFrame() {
|
|
5053
5084
|
this.forcedThisFrame = true;
|
|
5054
5085
|
if (typeof requestAnimationFrame === 'undefined')
|
|
@@ -6703,12 +6734,9 @@ const createUnsavedChangesTracker = (config) => {
|
|
|
6703
6734
|
throw new RuntimeError(UNSAVED_CHANGES_ERROR_CODES.MISSING_CONFIRM, '[createUnsavedChangesTracker] No confirm: pass `confirm` or call `provideUnsavedChangesConfirm()` in a parent injector.');
|
|
6704
6735
|
}
|
|
6705
6736
|
const normalized = normalizeUnsavedChangesSource(config.source);
|
|
6706
|
-
const
|
|
6707
|
-
const
|
|
6708
|
-
|
|
6709
|
-
? config.defaultValue()
|
|
6710
|
-
: config.defaultValue
|
|
6711
|
-
: 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);
|
|
6712
6740
|
const _defaultValue = signal(initialDefault, /* @ts-ignore */
|
|
6713
6741
|
...(ngDevMode ? [{ debugName: "_defaultValue" }] : /* istanbul ignore next */ []));
|
|
6714
6742
|
// No explicit default → capture the first non-null value the source produces as the baseline.
|
|
@@ -7073,6 +7101,9 @@ class ClickOutsideDirective {
|
|
|
7073
7101
|
this.pressStartedInside = event.composedPath().includes(this.elementRef.nativeElement);
|
|
7074
7102
|
}), takeUntilDestroyed())
|
|
7075
7103
|
.subscribe();
|
|
7104
|
+
fromEvent(this.document.documentElement, 'pointercancel', { capture: true })
|
|
7105
|
+
.pipe(tap(() => (this.pressStartedInside = false)), takeUntilDestroyed())
|
|
7106
|
+
.subscribe();
|
|
7076
7107
|
fromEvent(this.document.documentElement, 'click')
|
|
7077
7108
|
.pipe(tap((event) => {
|
|
7078
7109
|
const hostElement = this.elementRef.nativeElement;
|
|
@@ -7639,8 +7670,11 @@ const FOCUSABLE_SELECTOR = /* @__PURE__ */ [
|
|
|
7639
7670
|
'select:not([disabled])',
|
|
7640
7671
|
'textarea:not([disabled])',
|
|
7641
7672
|
'iframe',
|
|
7673
|
+
'audio[controls]',
|
|
7674
|
+
'video[controls]',
|
|
7675
|
+
'details > summary:first-of-type',
|
|
7642
7676
|
'[tabindex]:not([tabindex="-1"])',
|
|
7643
|
-
'[contenteditable="
|
|
7677
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
7644
7678
|
].join(',');
|
|
7645
7679
|
const isHTMLElement = (value) => {
|
|
7646
7680
|
// `instanceof` is realm-bound: an element adopted by a same-origin pop-up (the `document` mount
|
|
@@ -7670,10 +7704,24 @@ const isFocusable = (element, document) => {
|
|
|
7670
7704
|
const view = document.defaultView;
|
|
7671
7705
|
const style = view?.getComputedStyle(element);
|
|
7672
7706
|
const isVisible = style?.display !== 'none' && style?.visibility !== 'hidden' && element.getClientRects().length > 0;
|
|
7673
|
-
return isVisible &&
|
|
7674
|
-
|
|
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. */
|
|
7675
7716
|
const getFocusableElements = (container, document) => {
|
|
7676
|
-
|
|
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
|
+
});
|
|
7677
7725
|
};
|
|
7678
7726
|
const getHeadingElement = (container) => {
|
|
7679
7727
|
return container.querySelector('h1, h2, h3, h4, h5, h6, [role="heading"]');
|
|
@@ -7729,12 +7777,12 @@ const setupFocusTrap = (paneElement, overlayRef, enabled, isTopMost, document) =
|
|
|
7729
7777
|
focusElement(event.shiftKey ? (lastElement ?? null) : (firstElement ?? null));
|
|
7730
7778
|
return;
|
|
7731
7779
|
}
|
|
7732
|
-
if (event.shiftKey && activeElement
|
|
7780
|
+
if (event.shiftKey && firstElement && isSameTabStop(activeElement, firstElement)) {
|
|
7733
7781
|
event.preventDefault();
|
|
7734
7782
|
focusElement(lastElement ?? null);
|
|
7735
7783
|
return;
|
|
7736
7784
|
}
|
|
7737
|
-
if (!event.shiftKey && activeElement
|
|
7785
|
+
if (!event.shiftKey && lastElement && isSameTabStop(activeElement, lastElement)) {
|
|
7738
7786
|
event.preventDefault();
|
|
7739
7787
|
focusElement(firstElement ?? null);
|
|
7740
7788
|
}
|
|
@@ -8364,6 +8412,7 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
|
|
|
8364
8412
|
}
|
|
8365
8413
|
_state.set('closed');
|
|
8366
8414
|
_componentInstance.set(null);
|
|
8415
|
+
afterOpenedSubject.complete();
|
|
8367
8416
|
afterClosedSubject.next(closeEvent);
|
|
8368
8417
|
afterClosedSubject.complete();
|
|
8369
8418
|
},
|
|
@@ -8443,14 +8492,18 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8443
8492
|
}
|
|
8444
8493
|
};
|
|
8445
8494
|
const isTopMost = (overlayRef) => {
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
if (
|
|
8495
|
+
let topMost = null;
|
|
8496
|
+
let topMostLayer = -Infinity;
|
|
8497
|
+
for (const entry of openEntriesState()) {
|
|
8498
|
+
if (entry.config.passive || entry.state() === 'closing' || entry.state() === 'closed')
|
|
8450
8499
|
continue;
|
|
8451
|
-
|
|
8500
|
+
const entryLayer = entry.config.zIndex ?? DEFAULT_OVERLAY_LAYER;
|
|
8501
|
+
if (entryLayer >= topMostLayer) {
|
|
8502
|
+
topMost = entry;
|
|
8503
|
+
topMostLayer = entryLayer;
|
|
8504
|
+
}
|
|
8452
8505
|
}
|
|
8453
|
-
return
|
|
8506
|
+
return topMost === overlayRef;
|
|
8454
8507
|
};
|
|
8455
8508
|
const getAnimatedLifecycle = (componentRef) => {
|
|
8456
8509
|
const componentLifecycle = componentRef.instance.animatedLifecycle?.();
|
|
@@ -8601,7 +8654,13 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8601
8654
|
openEntriesState.update((entries) => entries.filter((entry) => entry !== overlayRef));
|
|
8602
8655
|
maybeDestroyRootElements(targetDocument);
|
|
8603
8656
|
if (config.restoreFocus !== false && overlayStillOwnsFocus) {
|
|
8604
|
-
|
|
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
|
+
}
|
|
8605
8664
|
}
|
|
8606
8665
|
overlayRef.finishClose(closeEvent);
|
|
8607
8666
|
};
|
|
@@ -8725,12 +8784,13 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
|
|
|
8725
8784
|
else {
|
|
8726
8785
|
lifecycle.enter();
|
|
8727
8786
|
}
|
|
8728
|
-
lifecycle.state$
|
|
8787
|
+
const enteredSubscription = lifecycle.state$
|
|
8729
8788
|
.pipe(filter((state) => state === 'entered'), take(1), tap(() => {
|
|
8730
8789
|
applyInitialFocus(paneElement, autoFocus, targetDocument);
|
|
8731
8790
|
overlayRef.markOpened();
|
|
8732
8791
|
}))
|
|
8733
8792
|
.subscribe();
|
|
8793
|
+
cleanupFns.push(() => enteredSubscription.unsubscribe());
|
|
8734
8794
|
});
|
|
8735
8795
|
return overlayRef;
|
|
8736
8796
|
};
|
|
@@ -10519,7 +10579,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
10519
10579
|
|
|
10520
10580
|
// Generated from package.json by tools/scripts/generate-version.js - do not edit.
|
|
10521
10581
|
/** The version of `@ethlete/core` this build was cut from. */
|
|
10522
|
-
const CORE_VERSION = '5.0.0-next.
|
|
10582
|
+
const CORE_VERSION = '5.0.0-next.63';
|
|
10523
10583
|
|
|
10524
10584
|
/**
|
|
10525
10585
|
* Generated bundle index. Do not edit.
|