@jsenv/navi 0.29.413 → 0.29.415
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/dist/dev/jsenv_navi.js +555 -339
- package/dist/dev/jsenv_navi.js.map +21 -17
- package/dist/jsenv_navi.js +548 -336
- package/dist/jsenv_navi.js.map +21 -17
- package/docs/AI_INSTRUCTIONS.md +10 -5
- package/docs/autofocus.md +19 -0
- package/docs/route_transitions.md +101 -1
- package/docs/view_transitions.md +61 -0
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -6,8 +6,8 @@ import { installImportMetaCssBuild, coarsePointerSignal, windowHeightSignal, win
|
|
|
6
6
|
import { createPubSub, elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, findEvent, mergeOneStyle, findFocusable, getElementSignature, chainEvent, isTouchDrivenEvent, getKeyboardEventDefaultAction, isPressDrivenClick, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, createValueEffect, findFocusDelegateTarget, scrollIntoViewThroughScrollables, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, getScrollContainerSet, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, keepTouchRefusable, waitForTap, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, dragSourceThatStoodDown, markDragSource, refuseDragTo, startDragTo, installPanZoom, createInternalCustomEvent, activeElementSignal, normalizeStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, getScrollContainer, watchWheelTravel, scrollRoomTowards, trapScrollInside, scrollIntoViewScoped, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, releaseWheelGesture, getScrollIntoViewScopedOffsets, wheelGestureIsTakenFrom, claimWheelGesture, initFocusGroup, stringifyStyle as stringifyStyle$1, isScrollable, resolveOklchLightness, contrastColor, parsePositionArea, snapToPixel, trapFocusInside, onAncestorReopen, isPressDisputedByDrag, canScroll, measureWidestChildRow, performTabNavigation, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, setVirtualKeyboardOverlaysContent, subscribeVirtualKeyboardGeometryChange, getVirtualKeyboardOverlayHeight } from "@jsenv/dom";
|
|
7
7
|
export { chainEvent, clickIsSuppressed, contrastColor, createDragGestureController, dragAfterIntent, findEvent, markDragSource, startDragTo } from "@jsenv/dom";
|
|
8
8
|
import { signal, computed, effect, untracked, batch, useComputed, useSignal } from "@preact/signals";
|
|
9
|
-
import { options, isValidElement, createContext, render, h, toChildArray, cloneElement, createElement, Fragment as Fragment$1 } from "preact";
|
|
10
|
-
import { useErrorBoundary, useLayoutEffect, useContext, useMemo, useRef, useEffect, useCallback, useState, useId } from "preact/hooks";
|
|
9
|
+
import { options, isValidElement, createContext, render, h, toChildArray, createRef, cloneElement, createElement, Fragment as Fragment$1 } from "preact";
|
|
10
|
+
import { useErrorBoundary, useLayoutEffect, useContext, useMemo, useRef, useEffect, useCallback, useState, useReducer, useId } from "preact/hooks";
|
|
11
11
|
import { humanizeI18n, setRuntimeLangSource, formatDuration, formatMonth, formatDay, resolveTimeRangePrecision, formatDatePlaceholder, toDate, getRelativeDay, formatDayRelative, formatMonthPlaceholder, formatWeekPlaceholder, formatDatetimePlaceholder, formatDatetime, toTimeOfDay, formatTimeOfDay, formatTime, formatMinuteDuration, formatSecondDuration, formatHourDuration, formatTimeRelative, formatNumber, interpolateText, installInterpolateJsx } from "@jsenv/humanize";
|
|
12
12
|
export { createI18n, formatDatetime, formatDay, formatDayRelative, formatDuration, formatHourDuration, formatMinuteDuration, formatMonth, formatNumber, formatSecondDuration, formatTime, formatTimeOfDay, formatTimeRange, formatTimeRelative, interpolateText } from "@jsenv/humanize";
|
|
13
13
|
import { jsxs, jsx, Fragment } from "preact/jsx-runtime";
|
|
@@ -79,7 +79,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
79
79
|
* any of these, and a number is the last resort, not the first tool.
|
|
80
80
|
*/
|
|
81
81
|
|
|
82
|
-
const css$
|
|
82
|
+
const css$18 = /* css */ `@layer navi {
|
|
83
83
|
:root {
|
|
84
84
|
--navi-z-index-control-hovered: 1;
|
|
85
85
|
--navi-z-index-control-focused: 2;
|
|
@@ -92,7 +92,7 @@ const css$17 = /* css */ `@layer navi {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
`;
|
|
95
|
-
import.meta.css = [css$
|
|
95
|
+
import.meta.css = [css$18, "@jsenv/navi/src/navi_z_indexes.js"];
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* A navigation is ABOUT to be applied — said before its very first write.
|
|
@@ -430,7 +430,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
430
430
|
|
|
431
431
|
const URL_TARGET_ATTRIBUTE = "data-url-target";
|
|
432
432
|
|
|
433
|
-
const css$
|
|
433
|
+
const css$17 = /* css */ `@layer navi {
|
|
434
434
|
[data-url-target] {
|
|
435
435
|
animation: navi_url_target var(--navi-url-target-duration, 2s)
|
|
436
436
|
ease-out;
|
|
@@ -448,7 +448,7 @@ const css$16 = /* css */ `@layer navi {
|
|
|
448
448
|
}
|
|
449
449
|
}
|
|
450
450
|
`;
|
|
451
|
-
import.meta.css = [css$
|
|
451
|
+
import.meta.css = [css$17, "@jsenv/navi/src/nav/url_target/url_target.js"];
|
|
452
452
|
|
|
453
453
|
let urlTargetOptions = {
|
|
454
454
|
block: "start",
|
|
@@ -2048,7 +2048,7 @@ const useActionStatus = (action) => {
|
|
|
2048
2048
|
};
|
|
2049
2049
|
|
|
2050
2050
|
installImportMetaCssBuild(import.meta);
|
|
2051
|
-
const css$
|
|
2051
|
+
const css$16 = /* css */`.action_error {
|
|
2052
2052
|
background: #fdd;
|
|
2053
2053
|
border: 1px solid red;
|
|
2054
2054
|
margin-top: 0;
|
|
@@ -2072,7 +2072,7 @@ const ActionRenderer = ({
|
|
|
2072
2072
|
children,
|
|
2073
2073
|
disabled
|
|
2074
2074
|
}) => {
|
|
2075
|
-
import.meta.css = [css$
|
|
2075
|
+
import.meta.css = [css$16, "@jsenv/navi/src/action/action_renderer.jsx"];
|
|
2076
2076
|
if (action === undefined) {
|
|
2077
2077
|
throw new Error("ActionRenderer requires an action to render, but none was provided.");
|
|
2078
2078
|
}
|
|
@@ -14110,6 +14110,31 @@ const moveFocusTo = (
|
|
|
14110
14110
|
});
|
|
14111
14111
|
};
|
|
14112
14112
|
|
|
14113
|
+
/**
|
|
14114
|
+
* Gives the focus back to `target` once the commit a close is part of is over:
|
|
14115
|
+
* for a close caused by an unmount (UNMOUNT_EVENT_TYPE in
|
|
14116
|
+
* layout/open_controller.js), which runs from a cleanup while the tree leaving
|
|
14117
|
+
* with it is still in the document. Only its end tells a target leaving with
|
|
14118
|
+
* it — disconnected by then, and skipped — from one staying, and a focused
|
|
14119
|
+
* element that leaves makes the removal of its whole subtree restyle it.
|
|
14120
|
+
*
|
|
14121
|
+
* And only into a void: the close took the focus with it, so something holding
|
|
14122
|
+
* the focus by then — a page arriving that focused a field of its own — took it
|
|
14123
|
+
* after, and keeps it.
|
|
14124
|
+
*/
|
|
14125
|
+
const giveFocusBackAfterCommit = (target, giveFocus) => {
|
|
14126
|
+
queueMicrotask(() => {
|
|
14127
|
+
if (!target || !target.isConnected) {
|
|
14128
|
+
return;
|
|
14129
|
+
}
|
|
14130
|
+
const focusedNow = document.activeElement;
|
|
14131
|
+
if (focusedNow && focusedNow !== document.body) {
|
|
14132
|
+
return;
|
|
14133
|
+
}
|
|
14134
|
+
giveFocus();
|
|
14135
|
+
});
|
|
14136
|
+
};
|
|
14137
|
+
|
|
14113
14138
|
const focusTransferTarget = (target, focusVisible) => {
|
|
14114
14139
|
moveFocusTo(target, { focusVisible });
|
|
14115
14140
|
if (target.hasAttribute("navi-autofocus-select")) {
|
|
@@ -14664,6 +14689,12 @@ const createOpenController = (
|
|
|
14664
14689
|
closeEvent,
|
|
14665
14690
|
`closed by focusout -> let focus go away`,
|
|
14666
14691
|
);
|
|
14692
|
+
} else if (findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
14693
|
+
// Left the tree: what held the focus before the opening may be
|
|
14694
|
+
// leaving with it (see giveFocusBackAfterCommit).
|
|
14695
|
+
giveFocusBackAfterCommit(focusTransfer.focusedElement, () => {
|
|
14696
|
+
focusTransfer.restoreFocus(closeEvent);
|
|
14697
|
+
});
|
|
14667
14698
|
} else {
|
|
14668
14699
|
// Only the mousedown, deliberately: a popup with no backdrop is
|
|
14669
14700
|
// closed by a `pointerdown` that belongs to the page (see
|
|
@@ -15336,7 +15367,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
15336
15367
|
* (see calloutTemplate in callout.js).
|
|
15337
15368
|
*/
|
|
15338
15369
|
|
|
15339
|
-
const css$
|
|
15370
|
+
const css$15 = /* css */`.navi_callout_status_icon {
|
|
15340
15371
|
--x-callout-status-icon-color: var(--navi-callout-neutral-color);
|
|
15341
15372
|
box-sizing: border-box;
|
|
15342
15373
|
aspect-ratio: 1;
|
|
@@ -15407,7 +15438,7 @@ const CalloutStatusIcon = ({
|
|
|
15407
15438
|
status = "info",
|
|
15408
15439
|
shape = "square"
|
|
15409
15440
|
}) => {
|
|
15410
|
-
import.meta.css = [css$
|
|
15441
|
+
import.meta.css = [css$15, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
|
|
15411
15442
|
return jsx("span", {
|
|
15412
15443
|
className: "navi_callout_status_icon",
|
|
15413
15444
|
"data-status": status === "none" ? undefined : status,
|
|
@@ -15434,7 +15465,7 @@ let calloutCount = 0;
|
|
|
15434
15465
|
// large enough to survive the pixel or two a trackpad rests on.
|
|
15435
15466
|
const CLOSE_BY_SCROLL_DISTANCE = 10;
|
|
15436
15467
|
|
|
15437
|
-
const css$
|
|
15468
|
+
const css$14 = /* css */ `
|
|
15438
15469
|
/* jsenv-css-opaque: same as dialog.jsx, cascade order. */
|
|
15439
15470
|
@layer navi {
|
|
15440
15471
|
.navi_callout {
|
|
@@ -15795,7 +15826,7 @@ const openCallout = (
|
|
|
15795
15826
|
debug = () => {},
|
|
15796
15827
|
} = {},
|
|
15797
15828
|
) => {
|
|
15798
|
-
import.meta.css = [css$
|
|
15829
|
+
import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
15799
15830
|
if (debug === true) {
|
|
15800
15831
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
15801
15832
|
}
|
|
@@ -17425,14 +17456,8 @@ const createCalloutManager = (
|
|
|
17425
17456
|
};
|
|
17426
17457
|
if (findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
17427
17458
|
// Closed by a popup leaving the tree: the control may be leaving
|
|
17428
|
-
//
|
|
17429
|
-
|
|
17430
|
-
// restyles the whole subtree.
|
|
17431
|
-
queueMicrotask(() => {
|
|
17432
|
-
if (focusTarget.isConnected) {
|
|
17433
|
-
giveFocusBack();
|
|
17434
|
-
}
|
|
17435
|
-
});
|
|
17459
|
+
// with it (see giveFocusBackAfterCommit).
|
|
17460
|
+
giveFocusBackAfterCommit(focusTarget, giveFocusBack);
|
|
17436
17461
|
} else {
|
|
17437
17462
|
// Given before the callout leaves the document: removed while it
|
|
17438
17463
|
// holds the focus, it sends a focusout with nowhere to go up
|
|
@@ -20787,7 +20812,9 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
20787
20812
|
* meantime, nothing is picked up. The carry is the element's own, outcomes and
|
|
20788
20813
|
* refusal included, without the wait for intent (the hold was the proof), and
|
|
20789
20814
|
* the copy is brought to the finger rather than following it from wherever the
|
|
20790
|
-
* element stands.
|
|
20815
|
+
* element stands. Let go of there before it has travelled — the hold only meant
|
|
20816
|
+
* to open the popup — it goes back and nothing is answered, whatever it was
|
|
20817
|
+
* brought over. A copy or nothing: `move` and `moving` carry the thing itself,
|
|
20791
20818
|
* which would leave its place just for being handed a press. When it starts is
|
|
20792
20819
|
* navi's — after a popup still being lifted (see `--navi-grab` in commands.js).
|
|
20793
20820
|
*
|
|
@@ -30886,6 +30913,12 @@ const useUrlSearchParam = (paramName, defaultValue) => {
|
|
|
30886
30913
|
return [value, setSearchParamValue];
|
|
30887
30914
|
};
|
|
30888
30915
|
|
|
30916
|
+
// The title belongs to the last Head to arrive, whatever order they leave in:
|
|
30917
|
+
// the page being left by a route transition is taken down AFTER the page
|
|
30918
|
+
// arriving is up (see keepLeavingPages in route.jsx), and putting back the
|
|
30919
|
+
// title it found would put its own page's title over the new one.
|
|
30920
|
+
const titleEntries = [];
|
|
30921
|
+
let titleWithoutHead = null;
|
|
30889
30922
|
const Head = ({
|
|
30890
30923
|
children
|
|
30891
30924
|
}) => {
|
|
@@ -30894,15 +30927,17 @@ const Head = ({
|
|
|
30894
30927
|
return undefined;
|
|
30895
30928
|
}
|
|
30896
30929
|
const childArray = Array.isArray(children) ? children : [children];
|
|
30897
|
-
const previousTitle = document.title;
|
|
30898
30930
|
const appendedElements = [];
|
|
30931
|
+
let titleEntry = null;
|
|
30899
30932
|
for (const child of childArray) {
|
|
30900
30933
|
if (!child) {
|
|
30901
30934
|
continue;
|
|
30902
30935
|
}
|
|
30903
30936
|
if (child.type === "title") {
|
|
30904
30937
|
const titleChildren = child.props.children;
|
|
30905
|
-
|
|
30938
|
+
titleEntry = {
|
|
30939
|
+
title: Array.isArray(titleChildren) ? titleChildren.join("") : titleChildren ?? ""
|
|
30940
|
+
};
|
|
30906
30941
|
continue;
|
|
30907
30942
|
}
|
|
30908
30943
|
const el = document.createElement(child.type);
|
|
@@ -30913,8 +30948,19 @@ const Head = ({
|
|
|
30913
30948
|
document.head.appendChild(el);
|
|
30914
30949
|
appendedElements.push(el);
|
|
30915
30950
|
}
|
|
30951
|
+
if (titleEntry) {
|
|
30952
|
+
if (titleEntries.length === 0) {
|
|
30953
|
+
titleWithoutHead = document.title;
|
|
30954
|
+
}
|
|
30955
|
+
titleEntries.push(titleEntry);
|
|
30956
|
+
document.title = titleEntry.title;
|
|
30957
|
+
}
|
|
30916
30958
|
return () => {
|
|
30917
|
-
|
|
30959
|
+
if (titleEntry) {
|
|
30960
|
+
titleEntries.splice(titleEntries.indexOf(titleEntry), 1);
|
|
30961
|
+
const lastEntry = titleEntries[titleEntries.length - 1];
|
|
30962
|
+
document.title = lastEntry ? lastEntry.title : titleWithoutHead;
|
|
30963
|
+
}
|
|
30918
30964
|
for (const el of appendedElements) {
|
|
30919
30965
|
el.remove();
|
|
30920
30966
|
}
|
|
@@ -31077,75 +31123,7 @@ const samePages = (pages, otherPages) => {
|
|
|
31077
31123
|
return true;
|
|
31078
31124
|
};
|
|
31079
31125
|
|
|
31080
|
-
|
|
31081
|
-
* Route is the single primitive for URL-based rendering.
|
|
31082
|
-
*
|
|
31083
|
-
* ## Layout pattern
|
|
31084
|
-
* Use this when multiple routes share a common layout but have no shared URL prefix,
|
|
31085
|
-
* making it impossible to set a guard route on the parent container.
|
|
31086
|
-
* For example, `/profile` and `/settings` both live inside `AuthLayout` but there
|
|
31087
|
-
* is no `/auth/` prefix to match on. A container Route wraps them: the active
|
|
31088
|
-
* child's element is injected as `children` into the layout element.
|
|
31089
|
-
* If a page needs state owned by the layout, the layout must expose it via context.
|
|
31090
|
-
*
|
|
31091
|
-
* ```jsx
|
|
31092
|
-
* const PROFILE_ROUTE = route("/profile");
|
|
31093
|
-
* const SETTINGS_ROUTE = route("/settings");
|
|
31094
|
-
*
|
|
31095
|
-
* <Route element={AuthLayout}>
|
|
31096
|
-
* <Route route={PROFILE_ROUTE} element={ProfilePage} />
|
|
31097
|
-
* <Route route={SETTINGS_ROUTE} element={SettingsPage} />
|
|
31098
|
-
* </Route>
|
|
31099
|
-
* ```
|
|
31100
|
-
*
|
|
31101
|
-
* Such a container holds no fallback, and cannot: what makes this one the right
|
|
31102
|
-
* layout is a child matching, and having no prefix is the very reason it was
|
|
31103
|
-
* reached for — so there is no address that belongs to it and to none of its
|
|
31104
|
-
* children. An address nothing claims is the router's to answer; write the
|
|
31105
|
-
* fallback there.
|
|
31106
|
-
*
|
|
31107
|
-
* ## Self-contained section pattern
|
|
31108
|
-
* Use this when routes share a common URL prefix (e.g. `/dashboard/`).
|
|
31109
|
-
* A single leaf Route in the top-level router matches the prefix; the component
|
|
31110
|
-
* it renders owns its sub-router and all related routes internally.
|
|
31111
|
-
* Everything about the section — routes, structure, sub-pages — is co-located.
|
|
31112
|
-
* The component is not a reusable layout; it is the section.
|
|
31113
|
-
*
|
|
31114
|
-
* Compared to the layout pattern, a dedicated section component is more powerful:
|
|
31115
|
-
* - Wrapper elements (chrome, nav, containers) are part of the component's render,
|
|
31116
|
-
* not injected via `children`, so a layout is not needed.
|
|
31117
|
-
* - Local state can be passed directly via `elementProps` to sub-pages. With the
|
|
31118
|
-
* layout pattern, sub-pages receive state only through `children` or context.
|
|
31119
|
-
*
|
|
31120
|
-
* The layout pattern remains necessary when a shared prefix does not exist
|
|
31121
|
-
* (see "Layout pattern" above).
|
|
31122
|
-
*
|
|
31123
|
-
* ```jsx
|
|
31124
|
-
* const DASHBOARD_SECTION_ROUTE = route("/dashboard/");
|
|
31125
|
-
* const DASHBOARD_HOME_ROUTE = route("/dashboard");
|
|
31126
|
-
* const DASHBOARD_POSTS_ROUTE = route("/dashboard/posts");
|
|
31127
|
-
*
|
|
31128
|
-
* // top-level router — only knows about the prefix
|
|
31129
|
-
* <Route route={DASHBOARD_SECTION_ROUTE} element={DashboardSection} />
|
|
31130
|
-
*
|
|
31131
|
-
* // Dashboard owns the rest
|
|
31132
|
-
* const DashboardSection = () => {
|
|
31133
|
-
* const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
31134
|
-
*
|
|
31135
|
-
* return <div
|
|
31136
|
-
* style="background: lightblue; padding: 10px;"
|
|
31137
|
-
* onClick={() => setSidebarOpen(o => !o)}
|
|
31138
|
-
* >
|
|
31139
|
-
* <Route>
|
|
31140
|
-
* <Route route={DASHBOARD_HOME_ROUTE} element={DashboardHomePage} elementProps={{ sidebarOpen}} />
|
|
31141
|
-
* <Route route={DASHBOARD_POSTS_ROUTE} element={DashboardPostsPage} elementProps={{ sidebarOpen }} />
|
|
31142
|
-
* <Route fallback element={DashboardNotFound} />
|
|
31143
|
-
* </Route>
|
|
31144
|
-
* </div>;
|
|
31145
|
-
* }
|
|
31146
|
-
* ```
|
|
31147
|
-
*/
|
|
31148
|
-
|
|
31126
|
+
installImportMetaCssBuild(import.meta);
|
|
31149
31127
|
|
|
31150
31128
|
/**
|
|
31151
31129
|
* Keep every container showing the page it is showing, whatever the routes say.
|
|
@@ -31169,6 +31147,56 @@ const freezeRouteRender = () => {
|
|
|
31169
31147
|
routeRenderFrozenSignal.value++;
|
|
31170
31148
|
};
|
|
31171
31149
|
};
|
|
31150
|
+
|
|
31151
|
+
/**
|
|
31152
|
+
* Keep the page a container leaves, hidden, until its route transition is over.
|
|
31153
|
+
*
|
|
31154
|
+
* The browser photographs the page being left before the change, and the page
|
|
31155
|
+
* arriving inside the transition's update callback — where everything done
|
|
31156
|
+
* delays the movement. Taking the page being left down is most of that: every
|
|
31157
|
+
* hook of every component in it is cleaned up, and a page of thirty cards
|
|
31158
|
+
* takes longer to take down than the page arriving takes to build. Its picture
|
|
31159
|
+
* is already taken, so it only has to stay out of the second one: a container
|
|
31160
|
+
* that changes page while this is held keeps the page it was showing, hidden,
|
|
31161
|
+
* and drops it once the movement is over (see route_transition.jsx).
|
|
31162
|
+
*
|
|
31163
|
+
* Kept only where Preact would have unmounted it: a page rendered by the same
|
|
31164
|
+
* element on both sides is the same component instance carried over, exactly
|
|
31165
|
+
* as without a movement.
|
|
31166
|
+
*/
|
|
31167
|
+
let leavingPagesHold = null;
|
|
31168
|
+
const keepLeavingPages = () => {
|
|
31169
|
+
const hold = {
|
|
31170
|
+
released: false,
|
|
31171
|
+
containers: new Set()
|
|
31172
|
+
};
|
|
31173
|
+
leavingPagesHold = hold;
|
|
31174
|
+
return () => {
|
|
31175
|
+
if (hold.released) {
|
|
31176
|
+
return;
|
|
31177
|
+
}
|
|
31178
|
+
hold.released = true;
|
|
31179
|
+
if (leavingPagesHold === hold) {
|
|
31180
|
+
leavingPagesHold = null;
|
|
31181
|
+
}
|
|
31182
|
+
for (const rerender of hold.containers) {
|
|
31183
|
+
rerender();
|
|
31184
|
+
}
|
|
31185
|
+
hold.containers.clear();
|
|
31186
|
+
};
|
|
31187
|
+
};
|
|
31188
|
+
|
|
31189
|
+
// What a page can learn about its place from the container rendering it:
|
|
31190
|
+
// whether it is a page kept while leaving (see keepLeavingPages). Such a page
|
|
31191
|
+
// is still mounted, and whatever it fills OUTSIDE its own nodes — a slot in a
|
|
31192
|
+
// bar, say — would otherwise go on showing with the page arriving (see
|
|
31193
|
+
// layout/slot.jsx).
|
|
31194
|
+
const RoutePageContext = createContext(null);
|
|
31195
|
+
const LEAVING_ATTRIBUTE = "data-navi-route-leaving";
|
|
31196
|
+
const css$13 = /* css */`[data-navi-route-leaving] {
|
|
31197
|
+
display: none !important;
|
|
31198
|
+
}
|
|
31199
|
+
`;
|
|
31172
31200
|
const debug$1 = (...args) => {
|
|
31173
31201
|
{
|
|
31174
31202
|
return;
|
|
@@ -31288,6 +31316,7 @@ const RouteContainer = ({
|
|
|
31288
31316
|
shownBranchRef.current = activeBranch;
|
|
31289
31317
|
}
|
|
31290
31318
|
const branch = shownBranchRef.current || activeBranch;
|
|
31319
|
+
const content = useContentKeepingLeavingPages(branch);
|
|
31291
31320
|
|
|
31292
31321
|
// The two things this component knows that nobody outside can find out: the
|
|
31293
31322
|
// pages a named fallback is the absence of — the children it was written
|
|
@@ -31309,7 +31338,6 @@ const RouteContainer = ({
|
|
|
31309
31338
|
};
|
|
31310
31339
|
}, [fallbackPage]);
|
|
31311
31340
|
debug$1(`[container "${id}"] RENDER, active=${branch ? branch.type : "none"}`);
|
|
31312
|
-
const content = branch ? branch.node : null;
|
|
31313
31341
|
if (!content) {
|
|
31314
31342
|
return null;
|
|
31315
31343
|
}
|
|
@@ -31318,12 +31346,152 @@ const RouteContainer = ({
|
|
|
31318
31346
|
}
|
|
31319
31347
|
return content;
|
|
31320
31348
|
};
|
|
31349
|
+
// What a container renders: the page it shows and, while a route transition
|
|
31350
|
+
// keeps them (see keepLeavingPages), the pages it has left — each the very
|
|
31351
|
+
// vnode rendered last time, which Preact does not look into, between two
|
|
31352
|
+
// markers that say which nodes to hide. Always an array, even of one page: the
|
|
31353
|
+
// page being left must stay the child it was when a page is added beside it,
|
|
31354
|
+
// or Preact takes it down and builds it again.
|
|
31355
|
+
let leavingKeyCount = 0;
|
|
31356
|
+
const useContentKeepingLeavingPages = branch => {
|
|
31357
|
+
const [, rerender] = useReducer(countRenders, 0);
|
|
31358
|
+
const shownRef = useRef(null);
|
|
31359
|
+
const leavingRef = useRef([]);
|
|
31360
|
+
const shown = shownRef.current;
|
|
31361
|
+
const element = branchElement(branch);
|
|
31362
|
+
let key;
|
|
31363
|
+
let page = null;
|
|
31364
|
+
if (shown && branch && (shown.id === branch.id || shown.element === element)) {
|
|
31365
|
+
// The same page, or one Preact carries over from it: the key it was
|
|
31366
|
+
// mounted with, or it would be mounted again.
|
|
31367
|
+
key = shown.key;
|
|
31368
|
+
page = shown.page;
|
|
31369
|
+
} else if (shown && leavingPagesHold && !document.querySelector(":modal")) {
|
|
31370
|
+
// Never while a modal dialog is open: it keeps everything but itself out
|
|
31371
|
+
// of reach — the page arriving could not even take the focus — for as long
|
|
31372
|
+
// as it is open, and one in a page kept while leaving stays open for the
|
|
31373
|
+
// whole movement. The page is then taken down at once, as without one.
|
|
31374
|
+
leavingRef.current.push({
|
|
31375
|
+
vnode: shown.vnode,
|
|
31376
|
+
page: shown.page,
|
|
31377
|
+
key: `navi-route-leaving-${++leavingKeyCount}`,
|
|
31378
|
+
hold: leavingPagesHold,
|
|
31379
|
+
startRef: createRef(),
|
|
31380
|
+
endRef: createRef(),
|
|
31381
|
+
hidden: false
|
|
31382
|
+
});
|
|
31383
|
+
leavingPagesHold.containers.add(rerender);
|
|
31384
|
+
// Keyed apart from the page it takes the place of, which is still there.
|
|
31385
|
+
key = `navi-route-${++leavingKeyCount}`;
|
|
31386
|
+
}
|
|
31387
|
+
let vnode = null;
|
|
31388
|
+
if (branch) {
|
|
31389
|
+
if (!page) {
|
|
31390
|
+
page = {
|
|
31391
|
+
leavingSignal: signal(false)
|
|
31392
|
+
};
|
|
31393
|
+
}
|
|
31394
|
+
vnode = h(RoutePageContext.Provider, {
|
|
31395
|
+
key,
|
|
31396
|
+
value: page
|
|
31397
|
+
}, branch.node);
|
|
31398
|
+
}
|
|
31399
|
+
shownRef.current = branch ? {
|
|
31400
|
+
id: branch.id,
|
|
31401
|
+
element,
|
|
31402
|
+
key,
|
|
31403
|
+
vnode,
|
|
31404
|
+
page
|
|
31405
|
+
} : null;
|
|
31406
|
+
const leaving = leavingRef.current.filter(entry => !entry.hold.released);
|
|
31407
|
+
leavingRef.current = leaving;
|
|
31408
|
+
|
|
31409
|
+
// Hidden as soon as it is in the DOM next to the page arriving: this runs in
|
|
31410
|
+
// the update callback's render, before the second picture is taken.
|
|
31411
|
+
useLayoutEffect(() => {
|
|
31412
|
+
for (const entry of leavingRef.current) {
|
|
31413
|
+
const start = entry.startRef.current;
|
|
31414
|
+
const end = entry.endRef.current;
|
|
31415
|
+
if (entry.hidden || !start || !end) {
|
|
31416
|
+
continue;
|
|
31417
|
+
}
|
|
31418
|
+
entry.hidden = true;
|
|
31419
|
+
entry.page.leavingSignal.value = true;
|
|
31420
|
+
const focusedElement = document.activeElement;
|
|
31421
|
+
let node = start.nextSibling;
|
|
31422
|
+
while (node && node !== end) {
|
|
31423
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
31424
|
+
node.setAttribute(LEAVING_ATTRIBUTE, "");
|
|
31425
|
+
// What the keyboard held in it goes where the page's removal would
|
|
31426
|
+
// have sent it: a hidden element keeps the focus, and would go on
|
|
31427
|
+
// receiving the keys pressed during the movement.
|
|
31428
|
+
if (node.contains(focusedElement)) {
|
|
31429
|
+
focusedElement.blur();
|
|
31430
|
+
}
|
|
31431
|
+
}
|
|
31432
|
+
node = node.nextSibling;
|
|
31433
|
+
}
|
|
31434
|
+
}
|
|
31435
|
+
});
|
|
31436
|
+
useLayoutEffect(() => {
|
|
31437
|
+
return () => {
|
|
31438
|
+
for (const entry of leavingRef.current) {
|
|
31439
|
+
entry.hold.containers.delete(rerender);
|
|
31440
|
+
}
|
|
31441
|
+
};
|
|
31442
|
+
}, []);
|
|
31443
|
+
if (leaving.length === 0) {
|
|
31444
|
+
return vnode ? [vnode] : null;
|
|
31445
|
+
}
|
|
31446
|
+
import.meta.css = [css$13, "@jsenv/navi/src/nav/route.jsx"];
|
|
31447
|
+
const children = vnode ? [vnode] : [];
|
|
31448
|
+
for (const entry of leaving) {
|
|
31449
|
+
children.push(h("template", {
|
|
31450
|
+
key: `${entry.key}-start`,
|
|
31451
|
+
ref: entry.startRef
|
|
31452
|
+
}), entry.vnode, h("template", {
|
|
31453
|
+
key: `${entry.key}-end`,
|
|
31454
|
+
ref: entry.endRef
|
|
31455
|
+
}));
|
|
31456
|
+
}
|
|
31457
|
+
return children;
|
|
31458
|
+
};
|
|
31459
|
+
const countRenders = count => count + 1;
|
|
31460
|
+
// What Preact compares when a container changes page, below the <Route> it
|
|
31461
|
+
// renders either way: the element the page is rendered with — a layout, for a
|
|
31462
|
+
// container branch.
|
|
31463
|
+
const branchElement = branch => {
|
|
31464
|
+
if (!branch) {
|
|
31465
|
+
return null;
|
|
31466
|
+
}
|
|
31467
|
+
let node = branch.node;
|
|
31468
|
+
while (node && node.type !== Route) {
|
|
31469
|
+
// A wrapper around the branch (see wrapBranch).
|
|
31470
|
+
node = node.props.children;
|
|
31471
|
+
}
|
|
31472
|
+
if (!node) {
|
|
31473
|
+
return null;
|
|
31474
|
+
}
|
|
31475
|
+
const {
|
|
31476
|
+
element
|
|
31477
|
+
} = node.props;
|
|
31478
|
+
if (element === undefined || typeof element === "function") {
|
|
31479
|
+
return element;
|
|
31480
|
+
}
|
|
31481
|
+
return element.type;
|
|
31482
|
+
};
|
|
31483
|
+
|
|
31321
31484
|
// Walk JSX children vnodes (without rendering) to build a branch list and
|
|
31322
31485
|
// find the active one in the same pass. Anything that is not a <Route> is read
|
|
31323
31486
|
// through and kept around the branch it holds (see below).
|
|
31324
31487
|
// Returns { matchingBranch, fallbackBranch, activeBranch, pages }.
|
|
31325
|
-
|
|
31488
|
+
//
|
|
31489
|
+
// Each branch is given an id from where it is written among the children,
|
|
31490
|
+
// which is what tells a container it changed page: the vnodes themselves are
|
|
31491
|
+
// new at every render of the parent.
|
|
31492
|
+
const collectBranches = (children, path = "") => {
|
|
31326
31493
|
let matchingBranch = null;
|
|
31494
|
+
let childIndex = 0;
|
|
31327
31495
|
let fallbackBranch = null;
|
|
31328
31496
|
// Every page a branch can be on, at any depth: what a named fallback is the
|
|
31329
31497
|
// ABSENCE of (see route_fallback.js). A container with a guard route counts
|
|
@@ -31341,6 +31509,7 @@ const collectBranches = children => {
|
|
|
31341
31509
|
}
|
|
31342
31510
|
return;
|
|
31343
31511
|
}
|
|
31512
|
+
const id = `${path}${childIndex++}`;
|
|
31344
31513
|
if (child.type !== Route) {
|
|
31345
31514
|
// Anything else is a wrapper around branches, and the two that matter are
|
|
31346
31515
|
// navi's own: a page says what it renders and delegates what it cannot —
|
|
@@ -31360,7 +31529,7 @@ const collectBranches = children => {
|
|
|
31360
31529
|
matchingBranch: matchingInside,
|
|
31361
31530
|
fallbackBranch: fallbackInside,
|
|
31362
31531
|
pages: pagesInside
|
|
31363
|
-
} = collectBranches(wrapperChildren);
|
|
31532
|
+
} = collectBranches(wrapperChildren, `${id}.`);
|
|
31364
31533
|
for (const pageInside of pagesInside) {
|
|
31365
31534
|
pages.push(pageInside);
|
|
31366
31535
|
}
|
|
@@ -31383,7 +31552,7 @@ const collectBranches = children => {
|
|
|
31383
31552
|
matchingBranch: matchingChild,
|
|
31384
31553
|
fallbackBranch: fallbackChild,
|
|
31385
31554
|
pages: pagesChild
|
|
31386
|
-
} = collectBranches(nodeChildren);
|
|
31555
|
+
} = collectBranches(nodeChildren, `${id}.`);
|
|
31387
31556
|
if (fallbackChild && !route) {
|
|
31388
31557
|
// A fallback is the branch taken for an address none of its siblings
|
|
31389
31558
|
// claim — so something has to make that address BELONG to this
|
|
@@ -31407,6 +31576,7 @@ const collectBranches = children => {
|
|
|
31407
31576
|
}
|
|
31408
31577
|
const branch = {
|
|
31409
31578
|
type: "container",
|
|
31579
|
+
id,
|
|
31410
31580
|
node: child
|
|
31411
31581
|
};
|
|
31412
31582
|
const guardMatching = route ? route.matchingSignal.value : false;
|
|
@@ -31424,6 +31594,7 @@ const collectBranches = children => {
|
|
|
31424
31594
|
if (!fallbackBranch) {
|
|
31425
31595
|
fallbackBranch = {
|
|
31426
31596
|
type: "fallback",
|
|
31597
|
+
id,
|
|
31427
31598
|
node: child,
|
|
31428
31599
|
page: fallback.isRouteFallback ? fallback : null
|
|
31429
31600
|
};
|
|
@@ -31431,6 +31602,7 @@ const collectBranches = children => {
|
|
|
31431
31602
|
} else {
|
|
31432
31603
|
const branch = {
|
|
31433
31604
|
type: "leaf",
|
|
31605
|
+
id,
|
|
31434
31606
|
node: child
|
|
31435
31607
|
};
|
|
31436
31608
|
pages.push({
|
|
@@ -31878,7 +32050,7 @@ const holdTransitionFurniture = (owner, areaElement) => {
|
|
|
31878
32050
|
}
|
|
31879
32051
|
const namesLeaving = [];
|
|
31880
32052
|
for (const element of namedElements) {
|
|
31881
|
-
if (!element
|
|
32053
|
+
if (!isRendered(element)) {
|
|
31882
32054
|
namesLeaving.push(nameByElement.get(element));
|
|
31883
32055
|
} else if (element.matches(FIXED_BAR_SELECTOR$1)) {
|
|
31884
32056
|
element.setAttribute(FURNITURE_ATTRIBUTE, "shared");
|
|
@@ -31997,11 +32169,16 @@ const travelRule = (side, names, movementProperty) => {
|
|
|
31997
32169
|
* The pictures are up and about to move: what no picture covers heads to the
|
|
31998
32170
|
* state arriving, on the same clock.
|
|
31999
32171
|
*/
|
|
32000
|
-
const startTransitionFurniture = (owner) => {
|
|
32172
|
+
const startTransitionFurniture = (owner, { duration } = {}) => {
|
|
32001
32173
|
if (owner !== furnitureOwner) {
|
|
32002
32174
|
return;
|
|
32003
32175
|
}
|
|
32004
32176
|
for (const restWall of restWallElements) {
|
|
32177
|
+
// A duration of the movement's own is written for its pictures alone (see
|
|
32178
|
+
// transition_values.js); the stand-in is an element, and is told.
|
|
32179
|
+
if (duration !== undefined) {
|
|
32180
|
+
restWall.style.setProperty("--navi-route-transition-duration", duration);
|
|
32181
|
+
}
|
|
32005
32182
|
restWall.style.opacity = restWallOpacity;
|
|
32006
32183
|
}
|
|
32007
32184
|
};
|
|
@@ -32032,7 +32209,9 @@ const releaseTransitionFurniture = (owner) => {
|
|
|
32032
32209
|
// area's stand-in answers for it.
|
|
32033
32210
|
const paintTransitionWalls = (areaElement) => {
|
|
32034
32211
|
removeTransitionWalls();
|
|
32035
|
-
const sources =
|
|
32212
|
+
const sources = Array.from(
|
|
32213
|
+
document.querySelectorAll(TOP_LAYER_WALL_SELECTOR),
|
|
32214
|
+
).filter(isRendered);
|
|
32036
32215
|
if (sources.length === 0) {
|
|
32037
32216
|
return sources;
|
|
32038
32217
|
}
|
|
@@ -32234,6 +32413,14 @@ const subtractRect = (rect, hole) => {
|
|
|
32234
32413
|
return pieces;
|
|
32235
32414
|
};
|
|
32236
32415
|
|
|
32416
|
+
// Out of the state arriving: taken out of the document, or in a page kept
|
|
32417
|
+
// hidden until its movement is over (see keepLeavingPages in route.jsx) —
|
|
32418
|
+
// where a dialog is still :modal and a popover still :popover-open, and
|
|
32419
|
+
// neither is painted or photographed.
|
|
32420
|
+
const isRendered = (element) => {
|
|
32421
|
+
return element.isConnected && element.checkVisibility();
|
|
32422
|
+
};
|
|
32423
|
+
|
|
32237
32424
|
const removeRestWalls = () => {
|
|
32238
32425
|
for (const restWall of restWallElements) {
|
|
32239
32426
|
restWall.remove();
|
|
@@ -32243,6 +32430,52 @@ const removeRestWalls = () => {
|
|
|
32243
32430
|
leavingRects = new Map();
|
|
32244
32431
|
};
|
|
32245
32432
|
|
|
32433
|
+
/**
|
|
32434
|
+
* The values a movement hands to its pictures, written where only the pictures
|
|
32435
|
+
* read them: on ::view-transition, the root of the pseudo-element tree, rather
|
|
32436
|
+
* than on the document's root.
|
|
32437
|
+
*
|
|
32438
|
+
* A custom property written on :root is inherited by every element of the
|
|
32439
|
+
* document, and writing one restyles all of them. A movement writes its
|
|
32440
|
+
* numbers as its pages are held, again whenever a scroll moves the window, and
|
|
32441
|
+
* takes them off as it ends — each time a whole-document restyle of a page that
|
|
32442
|
+
* can be thousands of elements long. On ::view-transition they reach every
|
|
32443
|
+
* picture by plain inheritance, and no element at all.
|
|
32444
|
+
*
|
|
32445
|
+
* An inline style cannot target a pseudo-element, so they are declarations of
|
|
32446
|
+
* one rule, in a sheet of their own. Written only: what a pseudo-element that
|
|
32447
|
+
* is not on screen computes is not reliably readable, so nothing reads them
|
|
32448
|
+
* back from there.
|
|
32449
|
+
*/
|
|
32450
|
+
|
|
32451
|
+
let valuesStyle = null;
|
|
32452
|
+
const getValuesStyle = () => {
|
|
32453
|
+
if (valuesStyle) {
|
|
32454
|
+
return valuesStyle;
|
|
32455
|
+
}
|
|
32456
|
+
const sheet = new CSSStyleSheet();
|
|
32457
|
+
sheet.replaceSync(":root::view-transition {}");
|
|
32458
|
+
const [rule] = sheet.cssRules;
|
|
32459
|
+
if (rule) {
|
|
32460
|
+
document.adoptedStyleSheets.push(sheet);
|
|
32461
|
+
valuesStyle = rule.style;
|
|
32462
|
+
} else {
|
|
32463
|
+
// A browser without view transitions drops the rule: the movement runs its
|
|
32464
|
+
// steps all the same (see start_view_transition_polyfill.js), with no
|
|
32465
|
+
// pictures to hand anything to.
|
|
32466
|
+
valuesStyle = document.createElement("div").style;
|
|
32467
|
+
}
|
|
32468
|
+
return valuesStyle;
|
|
32469
|
+
};
|
|
32470
|
+
|
|
32471
|
+
const setTransitionValue = (name, value) => {
|
|
32472
|
+
getValuesStyle().setProperty(name, value);
|
|
32473
|
+
};
|
|
32474
|
+
|
|
32475
|
+
const removeTransitionValue = (name) => {
|
|
32476
|
+
getValuesStyle().removeProperty(name);
|
|
32477
|
+
};
|
|
32478
|
+
|
|
32246
32479
|
installImportMetaCssBuild(import.meta);
|
|
32247
32480
|
|
|
32248
32481
|
const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
|
|
@@ -32277,96 +32510,6 @@ const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
|
|
|
32277
32510
|
--navi-transition-cover-left: 0px;
|
|
32278
32511
|
}
|
|
32279
32512
|
}
|
|
32280
|
-
|
|
32281
|
-
@property --navi-transition-window-top {
|
|
32282
|
-
syntax: "*";
|
|
32283
|
-
inherits: false
|
|
32284
|
-
}
|
|
32285
|
-
|
|
32286
|
-
@property --navi-transition-window-left {
|
|
32287
|
-
syntax: "*";
|
|
32288
|
-
inherits: false
|
|
32289
|
-
}
|
|
32290
|
-
|
|
32291
|
-
@property --navi-transition-window-width {
|
|
32292
|
-
syntax: "*";
|
|
32293
|
-
inherits: false
|
|
32294
|
-
}
|
|
32295
|
-
|
|
32296
|
-
@property --navi-transition-window-height {
|
|
32297
|
-
syntax: "*";
|
|
32298
|
-
inherits: false
|
|
32299
|
-
}
|
|
32300
|
-
|
|
32301
|
-
@property --navi-transition-window-old-top {
|
|
32302
|
-
syntax: "*";
|
|
32303
|
-
inherits: false
|
|
32304
|
-
}
|
|
32305
|
-
|
|
32306
|
-
@property --navi-transition-window-old-left {
|
|
32307
|
-
syntax: "*";
|
|
32308
|
-
inherits: false
|
|
32309
|
-
}
|
|
32310
|
-
|
|
32311
|
-
@property --navi-transition-window-new-top {
|
|
32312
|
-
syntax: "*";
|
|
32313
|
-
inherits: false
|
|
32314
|
-
}
|
|
32315
|
-
|
|
32316
|
-
@property --navi-transition-window-new-left {
|
|
32317
|
-
syntax: "*";
|
|
32318
|
-
inherits: false
|
|
32319
|
-
}
|
|
32320
|
-
|
|
32321
|
-
@property --navi-transition-old-band-top {
|
|
32322
|
-
syntax: "*";
|
|
32323
|
-
inherits: false
|
|
32324
|
-
}
|
|
32325
|
-
|
|
32326
|
-
@property --navi-transition-old-band-right {
|
|
32327
|
-
syntax: "*";
|
|
32328
|
-
inherits: false
|
|
32329
|
-
}
|
|
32330
|
-
|
|
32331
|
-
@property --navi-transition-old-band-bottom {
|
|
32332
|
-
syntax: "*";
|
|
32333
|
-
inherits: false
|
|
32334
|
-
}
|
|
32335
|
-
|
|
32336
|
-
@property --navi-transition-old-band-left {
|
|
32337
|
-
syntax: "*";
|
|
32338
|
-
inherits: false
|
|
32339
|
-
}
|
|
32340
|
-
|
|
32341
|
-
:root::view-transition, :root::view-transition-group(*), :root::view-transition-image-pair(*), :root::view-transition-old(*), :root::view-transition-new(*) {
|
|
32342
|
-
--navi-transition-window-top: inherit;
|
|
32343
|
-
--navi-transition-window-left: inherit;
|
|
32344
|
-
--navi-transition-window-width: inherit;
|
|
32345
|
-
--navi-transition-window-height: inherit;
|
|
32346
|
-
--navi-transition-window-old-top: inherit;
|
|
32347
|
-
--navi-transition-window-old-left: inherit;
|
|
32348
|
-
--navi-transition-window-new-top: inherit;
|
|
32349
|
-
--navi-transition-window-new-left: inherit;
|
|
32350
|
-
--navi-transition-old-band-top: inherit;
|
|
32351
|
-
--navi-transition-old-band-right: inherit;
|
|
32352
|
-
--navi-transition-old-band-bottom: inherit;
|
|
32353
|
-
--navi-transition-old-band-left: inherit;
|
|
32354
|
-
}
|
|
32355
|
-
|
|
32356
|
-
:root::view-transition-group-children(*) {
|
|
32357
|
-
--navi-transition-window-top: inherit;
|
|
32358
|
-
--navi-transition-window-left: inherit;
|
|
32359
|
-
--navi-transition-window-width: inherit;
|
|
32360
|
-
--navi-transition-window-height: inherit;
|
|
32361
|
-
--navi-transition-window-old-top: inherit;
|
|
32362
|
-
--navi-transition-window-old-left: inherit;
|
|
32363
|
-
--navi-transition-window-new-top: inherit;
|
|
32364
|
-
--navi-transition-window-new-left: inherit;
|
|
32365
|
-
--navi-transition-old-band-top: inherit;
|
|
32366
|
-
--navi-transition-old-band-right: inherit;
|
|
32367
|
-
--navi-transition-old-band-bottom: inherit;
|
|
32368
|
-
--navi-transition-old-band-left: inherit;
|
|
32369
|
-
}
|
|
32370
32513
|
`;
|
|
32371
32514
|
|
|
32372
32515
|
// Called by whatever needs the window — a render, a movement declared or
|
|
@@ -32464,22 +32607,21 @@ const holdTransitionWindow = (
|
|
|
32464
32607
|
const right =
|
|
32465
32608
|
rectBefore.right > rectAfter.right ? rectBefore.right : rectAfter.right;
|
|
32466
32609
|
windowOwner = owner;
|
|
32467
|
-
|
|
32468
|
-
|
|
32469
|
-
|
|
32470
|
-
|
|
32471
|
-
|
|
32472
|
-
|
|
32473
|
-
|
|
32474
|
-
|
|
32475
|
-
style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
32610
|
+
setTransitionValue(WINDOW_TOP_PROPERTY, `${top}px`);
|
|
32611
|
+
setTransitionValue(WINDOW_LEFT_PROPERTY, `${left}px`);
|
|
32612
|
+
setTransitionValue(WINDOW_WIDTH_PROPERTY, `${right - left}px`);
|
|
32613
|
+
setTransitionValue(WINDOW_HEIGHT_PROPERTY, `${bottom - top}px`);
|
|
32614
|
+
setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
|
|
32615
|
+
setTransitionValue(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
|
|
32616
|
+
setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${rectAfter.top}px`);
|
|
32617
|
+
setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
32476
32618
|
const oldBand = furnitureLive
|
|
32477
32619
|
? bandTheCutMayBeRelaxedTo(bandBefore, bandAfter, rectAfter)
|
|
32478
32620
|
: bandBefore;
|
|
32479
|
-
|
|
32480
|
-
|
|
32481
|
-
|
|
32482
|
-
|
|
32621
|
+
setTransitionValue(OLD_BAND_TOP_PROPERTY, `${oldBand.top}px`);
|
|
32622
|
+
setTransitionValue(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
|
|
32623
|
+
setTransitionValue(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
|
|
32624
|
+
setTransitionValue(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
|
|
32483
32625
|
followArrivingState(element, rectAfter, {
|
|
32484
32626
|
outerScroll,
|
|
32485
32627
|
windowTop: top,
|
|
@@ -32501,9 +32643,8 @@ const releaseTransitionWindow = (owner) => {
|
|
|
32501
32643
|
if (unfollowArrivingState) {
|
|
32502
32644
|
unfollowArrivingState();
|
|
32503
32645
|
}
|
|
32504
|
-
const { style } = document.documentElement;
|
|
32505
32646
|
for (const property of WINDOW_PROPERTIES) {
|
|
32506
|
-
|
|
32647
|
+
removeTransitionValue(property);
|
|
32507
32648
|
}
|
|
32508
32649
|
};
|
|
32509
32650
|
|
|
@@ -32512,12 +32653,11 @@ const releaseTransitionWindow = (owner) => {
|
|
|
32512
32653
|
// file) — and with it the offset outside the box, which carries the window
|
|
32513
32654
|
// and the corner of the state being left along. Requested, never awaited: the
|
|
32514
32655
|
// first call runs inside the update callback, where a frame cannot come.
|
|
32515
|
-
// Written only when it moved: every write restyles the
|
|
32656
|
+
// Written only when it moved: every write restyles the pictures.
|
|
32516
32657
|
const followArrivingState = (element, rectAtHold, heldAt) => {
|
|
32517
32658
|
if (unfollowArrivingState) {
|
|
32518
32659
|
unfollowArrivingState();
|
|
32519
32660
|
}
|
|
32520
|
-
const { style } = document.documentElement;
|
|
32521
32661
|
let top = rectAtHold.top;
|
|
32522
32662
|
let left = rectAtHold.left;
|
|
32523
32663
|
let outerTop = heldAt.outerScroll.top;
|
|
@@ -32526,24 +32666,27 @@ const followArrivingState = (element, rectAtHold, heldAt) => {
|
|
|
32526
32666
|
const rect = element.getBoundingClientRect();
|
|
32527
32667
|
if (rect.top !== top) {
|
|
32528
32668
|
top = rect.top;
|
|
32529
|
-
|
|
32669
|
+
setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${top}px`);
|
|
32530
32670
|
}
|
|
32531
32671
|
if (rect.left !== left) {
|
|
32532
32672
|
left = rect.left;
|
|
32533
|
-
|
|
32673
|
+
setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${left}px`);
|
|
32534
32674
|
}
|
|
32535
32675
|
const outerScroll = readOuterScroll(element);
|
|
32536
32676
|
if (outerScroll.top !== outerTop) {
|
|
32537
32677
|
outerTop = outerScroll.top;
|
|
32538
32678
|
const shift = heldAt.outerScroll.top - outerTop;
|
|
32539
|
-
|
|
32540
|
-
|
|
32679
|
+
setTransitionValue(WINDOW_TOP_PROPERTY, `${heldAt.windowTop + shift}px`);
|
|
32680
|
+
setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${heldAt.oldTop + shift}px`);
|
|
32541
32681
|
}
|
|
32542
32682
|
if (outerScroll.left !== outerLeft) {
|
|
32543
32683
|
outerLeft = outerScroll.left;
|
|
32544
32684
|
const shift = heldAt.outerScroll.left - outerLeft;
|
|
32545
|
-
|
|
32546
|
-
|
|
32685
|
+
setTransitionValue(
|
|
32686
|
+
WINDOW_LEFT_PROPERTY,
|
|
32687
|
+
`${heldAt.windowLeft + shift}px`,
|
|
32688
|
+
);
|
|
32689
|
+
setTransitionValue(
|
|
32547
32690
|
WINDOW_OLD_LEFT_PROPERTY,
|
|
32548
32691
|
`${heldAt.oldLeft + shift}px`,
|
|
32549
32692
|
);
|
|
@@ -32906,58 +33049,9 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
|
|
|
32906
33049
|
inherits: false
|
|
32907
33050
|
}
|
|
32908
33051
|
|
|
32909
|
-
@property --navi-route-transition-clip-top {
|
|
32910
|
-
syntax: "*";
|
|
32911
|
-
inherits: false
|
|
32912
|
-
}
|
|
32913
|
-
|
|
32914
|
-
@property --navi-route-transition-clip-right {
|
|
32915
|
-
syntax: "*";
|
|
32916
|
-
inherits: false
|
|
32917
|
-
}
|
|
32918
|
-
|
|
32919
|
-
@property --navi-route-transition-clip-bottom {
|
|
32920
|
-
syntax: "*";
|
|
32921
|
-
inherits: false
|
|
32922
|
-
}
|
|
32923
|
-
|
|
32924
|
-
@property --navi-route-transition-clip-left {
|
|
32925
|
-
syntax: "*";
|
|
32926
|
-
inherits: false
|
|
32927
|
-
}
|
|
32928
|
-
|
|
32929
|
-
@property --navi-route-transition-travel-x {
|
|
32930
|
-
syntax: "*";
|
|
32931
|
-
inherits: false
|
|
32932
|
-
}
|
|
32933
|
-
|
|
32934
|
-
@property --navi-route-transition-travel-y {
|
|
32935
|
-
syntax: "*";
|
|
32936
|
-
inherits: false
|
|
32937
|
-
}
|
|
32938
|
-
|
|
32939
33052
|
:root::view-transition, :root::view-transition-group(*), :root::view-transition-image-pair(*), :root::view-transition-old(*), :root::view-transition-new(*) {
|
|
32940
33053
|
--navi-route-transition-leave: inherit;
|
|
32941
33054
|
--navi-route-transition-enter: inherit;
|
|
32942
|
-
--navi-route-transition-covered: inherit;
|
|
32943
|
-
--navi-route-transition-clip-top: inherit;
|
|
32944
|
-
--navi-route-transition-clip-right: inherit;
|
|
32945
|
-
--navi-route-transition-clip-bottom: inherit;
|
|
32946
|
-
--navi-route-transition-clip-left: inherit;
|
|
32947
|
-
--navi-route-transition-travel-x: inherit;
|
|
32948
|
-
--navi-route-transition-travel-y: inherit;
|
|
32949
|
-
}
|
|
32950
|
-
|
|
32951
|
-
:root::view-transition-group-children(*) {
|
|
32952
|
-
--navi-route-transition-leave: inherit;
|
|
32953
|
-
--navi-route-transition-enter: inherit;
|
|
32954
|
-
--navi-route-transition-covered: inherit;
|
|
32955
|
-
--navi-route-transition-clip-top: inherit;
|
|
32956
|
-
--navi-route-transition-clip-right: inherit;
|
|
32957
|
-
--navi-route-transition-clip-bottom: inherit;
|
|
32958
|
-
--navi-route-transition-clip-left: inherit;
|
|
32959
|
-
--navi-route-transition-travel-x: inherit;
|
|
32960
|
-
--navi-route-transition-travel-y: inherit;
|
|
32961
33055
|
}
|
|
32962
33056
|
|
|
32963
33057
|
:root[data-navi-route-transition] [data-navi-route-transition-area] {
|
|
@@ -32978,28 +33072,32 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
|
|
|
32978
33072
|
|
|
32979
33073
|
&[data-navi-route-transition-target="area"] {
|
|
32980
33074
|
view-transition-name: none;
|
|
32981
|
-
|
|
32982
|
-
|
|
32983
|
-
|
|
32984
|
-
|
|
32985
|
-
|
|
32986
|
-
|
|
32987
|
-
|
|
32988
|
-
|
|
32989
|
-
|
|
32990
|
-
|
|
32991
|
-
|
|
32992
|
-
var(--navi-transition-
|
|
32993
|
-
|
|
32994
|
-
|
|
32995
|
-
|
|
32996
|
-
|
|
32997
|
-
|
|
32998
|
-
|
|
32999
|
-
var(--navi-transition-
|
|
33000
|
-
|
|
33001
|
-
|
|
33002
|
-
|
|
33075
|
+
|
|
33076
|
+
&::view-transition {
|
|
33077
|
+
--navi-route-transition-clip-top: max(0px,
|
|
33078
|
+
min(var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
|
|
33079
|
+
var(--navi-transition-old-band-top)) - var(--navi-transition-window-top));
|
|
33080
|
+
--navi-route-transition-clip-left: max(0px,
|
|
33081
|
+
min(var(--navi-safe-area-inset-left) +
|
|
33082
|
+
var(--navi-transition-cover-left),
|
|
33083
|
+
var(--navi-transition-old-band-left)) - var(--navi-transition-window-left));
|
|
33084
|
+
--navi-route-transition-clip-bottom: max(0px,
|
|
33085
|
+
var(--navi-transition-window-top) +
|
|
33086
|
+
var(--navi-transition-window-height) +
|
|
33087
|
+
min(var(--navi-safe-area-inset-bottom) +
|
|
33088
|
+
var(--navi-transition-cover-bottom),
|
|
33089
|
+
var(--navi-transition-old-band-bottom)) -
|
|
33090
|
+
100dvh);
|
|
33091
|
+
--navi-route-transition-clip-right: max(0px,
|
|
33092
|
+
var(--navi-transition-window-left) +
|
|
33093
|
+
var(--navi-transition-window-width) +
|
|
33094
|
+
min(var(--navi-safe-area-inset-right) +
|
|
33095
|
+
var(--navi-transition-cover-right),
|
|
33096
|
+
var(--navi-transition-old-band-right)) -
|
|
33097
|
+
100dvw);
|
|
33098
|
+
--navi-route-transition-travel-x: calc(var(--navi-transition-window-width) - var(--navi-route-transition-clip-left) - var(--navi-route-transition-clip-right));
|
|
33099
|
+
--navi-route-transition-travel-y: calc(var(--navi-transition-window-height) - var(--navi-route-transition-clip-top) - var(--navi-route-transition-clip-bottom));
|
|
33100
|
+
}
|
|
33003
33101
|
|
|
33004
33102
|
&::view-transition-group(*) {
|
|
33005
33103
|
z-index: 0;
|
|
@@ -34027,7 +34125,9 @@ const beginTransition = ({
|
|
|
34027
34125
|
decision: navigationDecision,
|
|
34028
34126
|
walkHome: null,
|
|
34029
34127
|
releaseReverting: null,
|
|
34030
|
-
|
|
34128
|
+
duration: undefined,
|
|
34129
|
+
restoreDuration: null,
|
|
34130
|
+
releaseLeavingPages: null
|
|
34031
34131
|
};
|
|
34032
34132
|
// The document wears one transition at a time, and the one this interrupts
|
|
34033
34133
|
// ends only after this one has begun — too late to take off what it wears
|
|
@@ -34080,18 +34180,13 @@ const beginTransition = ({
|
|
|
34080
34180
|
if (areaElement) {
|
|
34081
34181
|
documentElement.setAttribute(TRANSITION_TARGET_ATTRIBUTE, "area");
|
|
34082
34182
|
}
|
|
34083
|
-
// A duration of this relation's own, worn
|
|
34084
|
-
//
|
|
34085
|
-
// erased.
|
|
34183
|
+
// A duration of this relation's own, worn by the pictures for the length of
|
|
34184
|
+
// the transition, over whatever the application says on the root.
|
|
34086
34185
|
if (duration !== undefined) {
|
|
34087
|
-
|
|
34088
|
-
|
|
34186
|
+
transition.duration = typeof duration === "number" ? `${duration}ms` : duration;
|
|
34187
|
+
setTransitionValue(TRANSITION_DURATION_PROPERTY, transition.duration);
|
|
34089
34188
|
transition.restoreDuration = () => {
|
|
34090
|
-
|
|
34091
|
-
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, durationBefore);
|
|
34092
|
-
} else {
|
|
34093
|
-
documentElement.style.removeProperty(TRANSITION_DURATION_PROPERTY);
|
|
34094
|
-
}
|
|
34189
|
+
removeTransitionValue(TRANSITION_DURATION_PROPERTY);
|
|
34095
34190
|
};
|
|
34096
34191
|
}
|
|
34097
34192
|
const releaseRendering = takeoverRoutingRenderingHold();
|
|
@@ -34104,7 +34199,9 @@ const beginTransition = ({
|
|
|
34104
34199
|
// the only place the silent misconfigurations show. They are all about the
|
|
34105
34200
|
// same thing — a movement playing on pictures that are not the pages.
|
|
34106
34201
|
const viewTransitionReady = () => {
|
|
34107
|
-
startTransitionFurniture(transition
|
|
34202
|
+
startTransitionFurniture(transition, {
|
|
34203
|
+
duration: transition.duration
|
|
34204
|
+
});
|
|
34108
34205
|
const capturedNames = capturedViewTransitionNames();
|
|
34109
34206
|
if (areaElements.length > 0) {
|
|
34110
34207
|
if (!capturedNames.has(AREA_NAME)) {
|
|
@@ -34130,6 +34227,10 @@ const beginTransition = ({
|
|
|
34130
34227
|
// is awaited here must be able to resolve without a frame: the document
|
|
34131
34228
|
// is frozen for the whole of this callback.
|
|
34132
34229
|
try {
|
|
34230
|
+
// The page being left is photographed already: it only has to stay out
|
|
34231
|
+
// of the second picture, not be taken down before it (see
|
|
34232
|
+
// keepLeavingPages in route.jsx).
|
|
34233
|
+
transition.releaseLeavingPages = keepLeavingPages();
|
|
34133
34234
|
// Releasing flushes the held render synchronously, so a route that
|
|
34134
34235
|
// rendered has already resolved the wait by the next line.
|
|
34135
34236
|
releaseRendering();
|
|
@@ -34182,6 +34283,11 @@ const beginTransition = ({
|
|
|
34182
34283
|
// must not strip what that one is wearing.
|
|
34183
34284
|
renderWait.stop();
|
|
34184
34285
|
releaseRendering();
|
|
34286
|
+
// Taken down once nobody sees it any more.
|
|
34287
|
+
if (transition.releaseLeavingPages) {
|
|
34288
|
+
transition.releaseLeavingPages();
|
|
34289
|
+
transition.releaseLeavingPages = null;
|
|
34290
|
+
}
|
|
34185
34291
|
// The hold a way back took, when it is still standing: the pictures were
|
|
34186
34292
|
// turned round a second time and played out, or something else took the
|
|
34187
34293
|
// document over mid-walk. A hold nobody gives back freezes the page.
|
|
@@ -43888,8 +43994,12 @@ const useInteractiveProps = (props, {
|
|
|
43888
43994
|
}
|
|
43889
43995
|
// Read by READONLY_CONSTRAINT to say how many runs are already out, and by
|
|
43890
43996
|
// the action handlers below, which are where a run joins and leaves the
|
|
43891
|
-
// count (a labelled block away, hence through the controller).
|
|
43892
|
-
|
|
43997
|
+
// count (a labelled block away, hence through the controller). None for a
|
|
43998
|
+
// control without an action of its own, by the same rule as being held
|
|
43999
|
+
// back: what it runs (the noop every such control has, a link's
|
|
44000
|
+
// navigation) is nothing the guard counts, and would hold a slot for its
|
|
44001
|
+
// length.
|
|
44002
|
+
uiStateController.parallelGuard = props.action ? parallelGuard : null;
|
|
43893
44003
|
if (heldByParallelGuard) {
|
|
43894
44004
|
controlHostProps["data-readonly-reason"] = "parallel-guard";
|
|
43895
44005
|
}
|
|
@@ -44163,7 +44273,11 @@ const useInteractiveProps = (props, {
|
|
|
44163
44273
|
// carried by every navi_action_* event of that run).
|
|
44164
44274
|
uiStateController.pendingActionEvent = e.detail.event;
|
|
44165
44275
|
uiStateController.actionInFlight = true;
|
|
44166
|
-
|
|
44276
|
+
// Given back to the guard it was counted in, whatever the control
|
|
44277
|
+
// holds by then: one that lost its action mid-run has no guard left
|
|
44278
|
+
// to name, and the slot would stay taken.
|
|
44279
|
+
const countedIn = uiStateController.parallelGuard;
|
|
44280
|
+
countedIn?.claim(uiStateController);
|
|
44167
44281
|
if (optimistic) {
|
|
44168
44282
|
syncOptimisticAttribute(true);
|
|
44169
44283
|
}
|
|
@@ -44181,7 +44295,7 @@ const useInteractiveProps = (props, {
|
|
|
44181
44295
|
e.detail.addSideEffect((outcome, outcomeEvent) => {
|
|
44182
44296
|
uiStateController.actionInFlight = false;
|
|
44183
44297
|
uiStateController.runningActionSignal.value = null;
|
|
44184
|
-
|
|
44298
|
+
countedIn?.release(uiStateController);
|
|
44185
44299
|
// The outcome callbacks are told from here, the run's side effect,
|
|
44186
44300
|
// and not from the onnavi_action_* handlers below: the run can
|
|
44187
44301
|
// outlive this control (its own answer, written to a store the
|
|
@@ -47713,6 +47827,90 @@ const BinderItemContext = createContext(null);
|
|
|
47713
47827
|
*/
|
|
47714
47828
|
const NavContext = createContext(null);
|
|
47715
47829
|
|
|
47830
|
+
/**
|
|
47831
|
+
* Whether the page one is on is `route` with `params` — what a link or a button
|
|
47832
|
+
* pointing at a route shows as current.
|
|
47833
|
+
*
|
|
47834
|
+
* Read through a computed of that one answer, so the component reading it
|
|
47835
|
+
* re-renders when the answer flips, and not whenever the route's status moves:
|
|
47836
|
+
* a page arriving on the route with other params — one profile opening, under a
|
|
47837
|
+
* list of links to a hundred other profiles — starts the route matching and
|
|
47838
|
+
* changes its params, and leaves every one of those links exactly as it was.
|
|
47839
|
+
*
|
|
47840
|
+
* `except`: routes whose matching vetoes the answer — a route named there is
|
|
47841
|
+
* somewhere else, whatever the rest of the reading says. `also`: routes whose
|
|
47842
|
+
* matching grants it. Both take a route or a list of routes.
|
|
47843
|
+
*/
|
|
47844
|
+
const useRouteCurrent = (route, params, { except, also } = {}) => {
|
|
47845
|
+
const exceptRoutes = toRouteList(except);
|
|
47846
|
+
const alsoRoutes = toRouteList(also);
|
|
47847
|
+
const paramsKey = getParamsCacheKey(params);
|
|
47848
|
+
const exceptKey = routeListKey(exceptRoutes);
|
|
47849
|
+
const alsoKey = routeListKey(alsoRoutes);
|
|
47850
|
+
const currentSignal = useMemo(() => {
|
|
47851
|
+
if (paramsKey === null) {
|
|
47852
|
+
return null;
|
|
47853
|
+
}
|
|
47854
|
+
return computed(() =>
|
|
47855
|
+
readRouteCurrent(route, params, exceptRoutes, alsoRoutes),
|
|
47856
|
+
);
|
|
47857
|
+
}, [route, paramsKey, exceptKey, alsoKey]);
|
|
47858
|
+
if (!currentSignal) {
|
|
47859
|
+
// Params no key can be made of (a date, an object): read on the spot, the
|
|
47860
|
+
// component then re-renders with the status of every route it reads.
|
|
47861
|
+
return readRouteCurrent(route, params, exceptRoutes, alsoRoutes);
|
|
47862
|
+
}
|
|
47863
|
+
return currentSignal.value;
|
|
47864
|
+
};
|
|
47865
|
+
|
|
47866
|
+
// Every signal is read even once the answer is known: reading is what
|
|
47867
|
+
// subscribes, and a route skipped today is the one whose change has to be heard
|
|
47868
|
+
// tomorrow.
|
|
47869
|
+
const readRouteCurrent = (route, params, exceptRoutes, alsoRoutes) => {
|
|
47870
|
+
const matching = route.matchingSignal.value;
|
|
47871
|
+
const paramsMatching = route.matchesParams(params);
|
|
47872
|
+
let someExceptMatching = false;
|
|
47873
|
+
for (const exceptRoute of exceptRoutes) {
|
|
47874
|
+
if (exceptRoute.matchingSignal.value) {
|
|
47875
|
+
someExceptMatching = true;
|
|
47876
|
+
}
|
|
47877
|
+
}
|
|
47878
|
+
let someAlsoMatching = false;
|
|
47879
|
+
for (const alsoRoute of alsoRoutes) {
|
|
47880
|
+
if (alsoRoute.matchingSignal.value) {
|
|
47881
|
+
someAlsoMatching = true;
|
|
47882
|
+
}
|
|
47883
|
+
}
|
|
47884
|
+
if (someExceptMatching) {
|
|
47885
|
+
return false;
|
|
47886
|
+
}
|
|
47887
|
+
return (matching && paramsMatching) || someAlsoMatching;
|
|
47888
|
+
};
|
|
47889
|
+
|
|
47890
|
+
const toRouteList = (routes) => {
|
|
47891
|
+
if (!routes) {
|
|
47892
|
+
return [];
|
|
47893
|
+
}
|
|
47894
|
+
return Array.isArray(routes) ? routes : [routes];
|
|
47895
|
+
};
|
|
47896
|
+
|
|
47897
|
+
// A list of routes as a key, since an inline `currentAlso={[A, B]}` is a new
|
|
47898
|
+
// array at every render of the parent.
|
|
47899
|
+
const routeIdByRoute = new WeakMap();
|
|
47900
|
+
let routeIdCount = 0;
|
|
47901
|
+
const routeListKey = (routes) => {
|
|
47902
|
+
let key = "";
|
|
47903
|
+
for (const route of routes) {
|
|
47904
|
+
let id = routeIdByRoute.get(route);
|
|
47905
|
+
if (id === undefined) {
|
|
47906
|
+
id = ++routeIdCount;
|
|
47907
|
+
routeIdByRoute.set(route, id);
|
|
47908
|
+
}
|
|
47909
|
+
key += `${id},`;
|
|
47910
|
+
}
|
|
47911
|
+
return key;
|
|
47912
|
+
};
|
|
47913
|
+
|
|
47716
47914
|
/*
|
|
47717
47915
|
* Custom hook to apply semi-transparent color when an element should be dimmed.
|
|
47718
47916
|
*
|
|
@@ -48256,15 +48454,10 @@ const LinkWithRoute = ({
|
|
|
48256
48454
|
...rest
|
|
48257
48455
|
}) => {
|
|
48258
48456
|
const url = route.buildUrl(routeParams);
|
|
48259
|
-
const {
|
|
48260
|
-
|
|
48261
|
-
|
|
48262
|
-
|
|
48263
|
-
const someExceptedRouteMatching = useSomeRouteMatching(currentExcept);
|
|
48264
|
-
const someAlsoRouteMatching = useSomeRouteMatching(currentAlso);
|
|
48265
|
-
// "Except" is a veto: a route named there is somewhere else, whatever the
|
|
48266
|
-
// rest of the reading says.
|
|
48267
|
-
const linkMatching = !someExceptedRouteMatching && (matching && paramsAreMatching || someAlsoRouteMatching);
|
|
48457
|
+
const linkMatching = useRouteCurrent(route, routeParams, {
|
|
48458
|
+
except: currentExcept,
|
|
48459
|
+
also: currentAlso
|
|
48460
|
+
});
|
|
48268
48461
|
const innerCurrent = current || linkMatching;
|
|
48269
48462
|
return jsx(Link, {
|
|
48270
48463
|
href: url,
|
|
@@ -48273,22 +48466,6 @@ const LinkWithRoute = ({
|
|
|
48273
48466
|
children: children || route.buildRelativeUrl(routeParams)
|
|
48274
48467
|
});
|
|
48275
48468
|
};
|
|
48276
|
-
// A route, or a list of them, read as one answer: does any of them match.
|
|
48277
|
-
// Reading matchingSignal during the render is what subscribes the component to
|
|
48278
|
-
// it, so every route is read even once one has answered yes — stopping early
|
|
48279
|
-
// would leave the component deaf to the ones it skipped.
|
|
48280
|
-
const useSomeRouteMatching = routes => {
|
|
48281
|
-
if (!routes) {
|
|
48282
|
-
return false;
|
|
48283
|
-
}
|
|
48284
|
-
let someMatching = false;
|
|
48285
|
-
for (const route of Array.isArray(routes) ? routes : [routes]) {
|
|
48286
|
-
if (route.matchingSignal.value) {
|
|
48287
|
-
someMatching = true;
|
|
48288
|
-
}
|
|
48289
|
-
}
|
|
48290
|
-
return someMatching;
|
|
48291
|
-
};
|
|
48292
48469
|
const LinkPlain = props => {
|
|
48293
48470
|
const defaultRef = useRef();
|
|
48294
48471
|
props.ref = props.ref || defaultRef;
|
|
@@ -53372,11 +53549,7 @@ const ButtonWithRoute = props => {
|
|
|
53372
53549
|
...rest
|
|
53373
53550
|
} = props;
|
|
53374
53551
|
const url = route.buildUrl(routeParams);
|
|
53375
|
-
const
|
|
53376
|
-
matching
|
|
53377
|
-
} = useRouteStatus(route);
|
|
53378
|
-
const paramsAreMatching = route.matchesParams(routeParams);
|
|
53379
|
-
const linkMatching = matching && paramsAreMatching;
|
|
53552
|
+
const linkMatching = useRouteCurrent(route, routeParams);
|
|
53380
53553
|
|
|
53381
53554
|
// Merged into whatever the caller already holds: a button can be forced into
|
|
53382
53555
|
// a state for a demo and still learn its own current-ness from its route.
|
|
@@ -64674,10 +64847,24 @@ const useDialogProps = props => {
|
|
|
64674
64847
|
// whichever show call ran, not just when a stray authored display
|
|
64675
64848
|
// property is actually present — harmless the rest of the time.
|
|
64676
64849
|
dialogEl.setAttribute("navi-hidden", "");
|
|
64677
|
-
|
|
64678
|
-
|
|
64850
|
+
const hideNatively = () => {
|
|
64851
|
+
if (isTopLayerPopover) {
|
|
64852
|
+
dialogEl.hidePopover();
|
|
64853
|
+
} else {
|
|
64854
|
+
dialogEl.close();
|
|
64855
|
+
}
|
|
64856
|
+
};
|
|
64857
|
+
if (findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
64858
|
+
// Leaving the tree: hidden natively once the commit is over. Its
|
|
64859
|
+
// removal takes it out of the top layer and hands the focus to
|
|
64860
|
+
// nobody, where closing it now hands the focus to whatever held it
|
|
64861
|
+
// before the opening — often a trigger leaving in the same commit
|
|
64862
|
+
// (see giveFocusBackAfterCommit). What is left to close then is its
|
|
64863
|
+
// state: a <dialog> removed while open keeps its `open` attribute, and
|
|
64864
|
+
// a subtree <Suspense> parks and puts back would show it again.
|
|
64865
|
+
queueMicrotask(hideNatively);
|
|
64679
64866
|
} else {
|
|
64680
|
-
|
|
64867
|
+
hideNatively();
|
|
64681
64868
|
}
|
|
64682
64869
|
// Held at the size it has right now, for the whole way out. cleanup()
|
|
64683
64870
|
// below already stops the JS repositioning, but the size is CSS-driven
|
|
@@ -66346,7 +66533,13 @@ const usePopoverProps = props => {
|
|
|
66346
66533
|
// reliably sufficient once a consumer's own CSS also sets display.
|
|
66347
66534
|
popoverEl.setAttribute("navi-hidden", "");
|
|
66348
66535
|
if (isTopLayer) {
|
|
66349
|
-
|
|
66536
|
+
// Left to its removal when the popover leaves the tree: that hides it
|
|
66537
|
+
// and hands the focus to nobody, where hidePopover() hands the focus
|
|
66538
|
+
// to whatever held it before the opening — often a trigger leaving in
|
|
66539
|
+
// the same commit (see giveFocusBackAfterCommit).
|
|
66540
|
+
if (!findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
66541
|
+
popoverEl.hidePopover();
|
|
66542
|
+
}
|
|
66350
66543
|
} else {
|
|
66351
66544
|
openLocalPopoverCount = Math.max(0, openLocalPopoverCount - 1);
|
|
66352
66545
|
}
|
|
@@ -89869,8 +90062,7 @@ const createSlot = (SlotRenderer = Box) => {
|
|
|
89869
90062
|
};
|
|
89870
90063
|
const SlotFill = props => {
|
|
89871
90064
|
const fillerRef = useRef();
|
|
89872
|
-
|
|
89873
|
-
slotPropsSignal.value = props;
|
|
90065
|
+
const routePage = useContext(RoutePageContext);
|
|
89874
90066
|
useLayoutEffect(() => {
|
|
89875
90067
|
return () => {
|
|
89876
90068
|
// Within one diff preact mounts the newcomer before unmounting the
|
|
@@ -89883,6 +90075,26 @@ const createSlot = (SlotRenderer = Box) => {
|
|
|
89883
90075
|
slotPropsSignal.value = null;
|
|
89884
90076
|
};
|
|
89885
90077
|
}, []);
|
|
90078
|
+
// In a page kept while leaving: out of the slot as if unmounted, since
|
|
90079
|
+
// the page arriving is what the slot must show — or nothing, when it
|
|
90080
|
+
// fills none.
|
|
90081
|
+
if (routePage && routePage.leavingSignal.value) {
|
|
90082
|
+
if (filler === fillerRef) {
|
|
90083
|
+
filler = null;
|
|
90084
|
+
slotPropsSignal.value = null;
|
|
90085
|
+
}
|
|
90086
|
+
return null;
|
|
90087
|
+
}
|
|
90088
|
+
if (!fillerRef.current) {
|
|
90089
|
+
fillerRef.current = true;
|
|
90090
|
+
filler = fillerRef;
|
|
90091
|
+
}
|
|
90092
|
+
if (filler === null) {
|
|
90093
|
+
filler = fillerRef;
|
|
90094
|
+
}
|
|
90095
|
+
if (filler === fillerRef) {
|
|
90096
|
+
slotPropsSignal.value = props;
|
|
90097
|
+
}
|
|
89886
90098
|
return null;
|
|
89887
90099
|
};
|
|
89888
90100
|
return [Slot, SlotFill];
|