@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/dev/jsenv_navi.js
CHANGED
|
@@ -6,8 +6,8 @@ import { installImportMetaCssBuild, coarsePointerSignal, windowHeightSignal, win
|
|
|
6
6
|
import { createPubSub, elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, createEventGroupLogger, 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, getVirtualKeyboardOverlayHeight, onAncestorReopen, isPressDisputedByDrag, canScroll, measureWidestChildRow, performTabNavigation, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, setVirtualKeyboardOverlaysContent, subscribeVirtualKeyboardGeometryChange } 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, prefixFirstAndIndentRemainingLines, 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.
|
|
@@ -457,7 +457,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
457
457
|
|
|
458
458
|
const URL_TARGET_ATTRIBUTE = "data-url-target";
|
|
459
459
|
|
|
460
|
-
const css$
|
|
460
|
+
const css$17 = /* css */ `@layer navi {
|
|
461
461
|
[data-url-target] {
|
|
462
462
|
animation: navi_url_target var(--navi-url-target-duration, 2s)
|
|
463
463
|
ease-out;
|
|
@@ -475,7 +475,7 @@ const css$16 = /* css */ `@layer navi {
|
|
|
475
475
|
}
|
|
476
476
|
}
|
|
477
477
|
`;
|
|
478
|
-
import.meta.css = [css$
|
|
478
|
+
import.meta.css = [css$17, "@jsenv/navi/src/nav/url_target/url_target.js"];
|
|
479
479
|
|
|
480
480
|
let urlTargetOptions = {
|
|
481
481
|
block: "start",
|
|
@@ -2080,7 +2080,7 @@ const useActionStatus = (action) => {
|
|
|
2080
2080
|
};
|
|
2081
2081
|
|
|
2082
2082
|
installImportMetaCssBuild(import.meta);
|
|
2083
|
-
const css$
|
|
2083
|
+
const css$16 = /* css */`.action_error {
|
|
2084
2084
|
background: #fdd;
|
|
2085
2085
|
border: 1px solid red;
|
|
2086
2086
|
margin-top: 0;
|
|
@@ -2104,7 +2104,7 @@ const ActionRenderer = ({
|
|
|
2104
2104
|
children,
|
|
2105
2105
|
disabled
|
|
2106
2106
|
}) => {
|
|
2107
|
-
import.meta.css = [css$
|
|
2107
|
+
import.meta.css = [css$16, "@jsenv/navi/src/action/action_renderer.jsx"];
|
|
2108
2108
|
if (action === undefined) {
|
|
2109
2109
|
throw new Error("ActionRenderer requires an action to render, but none was provided.");
|
|
2110
2110
|
}
|
|
@@ -14697,6 +14697,31 @@ const moveFocusTo = (
|
|
|
14697
14697
|
});
|
|
14698
14698
|
};
|
|
14699
14699
|
|
|
14700
|
+
/**
|
|
14701
|
+
* Gives the focus back to `target` once the commit a close is part of is over:
|
|
14702
|
+
* for a close caused by an unmount (UNMOUNT_EVENT_TYPE in
|
|
14703
|
+
* layout/open_controller.js), which runs from a cleanup while the tree leaving
|
|
14704
|
+
* with it is still in the document. Only its end tells a target leaving with
|
|
14705
|
+
* it — disconnected by then, and skipped — from one staying, and a focused
|
|
14706
|
+
* element that leaves makes the removal of its whole subtree restyle it.
|
|
14707
|
+
*
|
|
14708
|
+
* And only into a void: the close took the focus with it, so something holding
|
|
14709
|
+
* the focus by then — a page arriving that focused a field of its own — took it
|
|
14710
|
+
* after, and keeps it.
|
|
14711
|
+
*/
|
|
14712
|
+
const giveFocusBackAfterCommit = (target, giveFocus) => {
|
|
14713
|
+
queueMicrotask(() => {
|
|
14714
|
+
if (!target || !target.isConnected) {
|
|
14715
|
+
return;
|
|
14716
|
+
}
|
|
14717
|
+
const focusedNow = document.activeElement;
|
|
14718
|
+
if (focusedNow && focusedNow !== document.body) {
|
|
14719
|
+
return;
|
|
14720
|
+
}
|
|
14721
|
+
giveFocus();
|
|
14722
|
+
});
|
|
14723
|
+
};
|
|
14724
|
+
|
|
14700
14725
|
const focusTransferTarget = (target, focusVisible) => {
|
|
14701
14726
|
moveFocusTo(target, { focusVisible });
|
|
14702
14727
|
if (target.hasAttribute("navi-autofocus-select")) {
|
|
@@ -15251,6 +15276,12 @@ const createOpenController = (
|
|
|
15251
15276
|
closeEvent,
|
|
15252
15277
|
`closed by focusout -> let focus go away`,
|
|
15253
15278
|
);
|
|
15279
|
+
} else if (findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
15280
|
+
// Left the tree: what held the focus before the opening may be
|
|
15281
|
+
// leaving with it (see giveFocusBackAfterCommit).
|
|
15282
|
+
giveFocusBackAfterCommit(focusTransfer.focusedElement, () => {
|
|
15283
|
+
focusTransfer.restoreFocus(closeEvent);
|
|
15284
|
+
});
|
|
15254
15285
|
} else {
|
|
15255
15286
|
// Only the mousedown, deliberately: a popup with no backdrop is
|
|
15256
15287
|
// closed by a `pointerdown` that belongs to the page (see
|
|
@@ -15941,7 +15972,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
15941
15972
|
* (see calloutTemplate in callout.js).
|
|
15942
15973
|
*/
|
|
15943
15974
|
|
|
15944
|
-
const css$
|
|
15975
|
+
const css$15 = /* css */`.navi_callout_status_icon {
|
|
15945
15976
|
--x-callout-status-icon-color: var(--navi-callout-neutral-color);
|
|
15946
15977
|
box-sizing: border-box;
|
|
15947
15978
|
aspect-ratio: 1;
|
|
@@ -16012,7 +16043,7 @@ const CalloutStatusIcon = ({
|
|
|
16012
16043
|
status = "info",
|
|
16013
16044
|
shape = "square"
|
|
16014
16045
|
}) => {
|
|
16015
|
-
import.meta.css = [css$
|
|
16046
|
+
import.meta.css = [css$15, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
|
|
16016
16047
|
return jsx("span", {
|
|
16017
16048
|
className: "navi_callout_status_icon",
|
|
16018
16049
|
"data-status": status === "none" ? undefined : status,
|
|
@@ -16039,7 +16070,7 @@ let calloutCount = 0;
|
|
|
16039
16070
|
// large enough to survive the pixel or two a trackpad rests on.
|
|
16040
16071
|
const CLOSE_BY_SCROLL_DISTANCE = 10;
|
|
16041
16072
|
|
|
16042
|
-
const css$
|
|
16073
|
+
const css$14 = /* css */ `
|
|
16043
16074
|
/* jsenv-css-opaque: same as dialog.jsx, cascade order. */
|
|
16044
16075
|
@layer navi {
|
|
16045
16076
|
.navi_callout {
|
|
@@ -16400,7 +16431,7 @@ const openCallout = (
|
|
|
16400
16431
|
debug = () => {},
|
|
16401
16432
|
} = {},
|
|
16402
16433
|
) => {
|
|
16403
|
-
import.meta.css = [css$
|
|
16434
|
+
import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
16404
16435
|
if (debug === true) {
|
|
16405
16436
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
16406
16437
|
}
|
|
@@ -18119,14 +18150,8 @@ const createCalloutManager = (
|
|
|
18119
18150
|
};
|
|
18120
18151
|
if (findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
18121
18152
|
// Closed by a popup leaving the tree: the control may be leaving
|
|
18122
|
-
//
|
|
18123
|
-
|
|
18124
|
-
// restyles the whole subtree.
|
|
18125
|
-
queueMicrotask(() => {
|
|
18126
|
-
if (focusTarget.isConnected) {
|
|
18127
|
-
giveFocusBack();
|
|
18128
|
-
}
|
|
18129
|
-
});
|
|
18153
|
+
// with it (see giveFocusBackAfterCommit).
|
|
18154
|
+
giveFocusBackAfterCommit(focusTarget, giveFocusBack);
|
|
18130
18155
|
} else {
|
|
18131
18156
|
// Given before the callout leaves the document: removed while it
|
|
18132
18157
|
// holds the focus, it sends a focusout with nowhere to go up
|
|
@@ -21532,7 +21557,9 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
21532
21557
|
* meantime, nothing is picked up. The carry is the element's own, outcomes and
|
|
21533
21558
|
* refusal included, without the wait for intent (the hold was the proof), and
|
|
21534
21559
|
* the copy is brought to the finger rather than following it from wherever the
|
|
21535
|
-
* element stands.
|
|
21560
|
+
* element stands. Let go of there before it has travelled — the hold only meant
|
|
21561
|
+
* to open the popup — it goes back and nothing is answered, whatever it was
|
|
21562
|
+
* brought over. A copy or nothing: `move` and `moving` carry the thing itself,
|
|
21536
21563
|
* which would leave its place just for being handed a press. When it starts is
|
|
21537
21564
|
* navi's — after a popup still being lifted (see `--navi-grab` in commands.js).
|
|
21538
21565
|
*
|
|
@@ -31796,6 +31823,12 @@ const useUrlSearchParam = (paramName, defaultValue) => {
|
|
|
31796
31823
|
return [value, setSearchParamValue];
|
|
31797
31824
|
};
|
|
31798
31825
|
|
|
31826
|
+
// The title belongs to the last Head to arrive, whatever order they leave in:
|
|
31827
|
+
// the page being left by a route transition is taken down AFTER the page
|
|
31828
|
+
// arriving is up (see keepLeavingPages in route.jsx), and putting back the
|
|
31829
|
+
// title it found would put its own page's title over the new one.
|
|
31830
|
+
const titleEntries = [];
|
|
31831
|
+
let titleWithoutHead = null;
|
|
31799
31832
|
const Head = ({
|
|
31800
31833
|
children
|
|
31801
31834
|
}) => {
|
|
@@ -31804,15 +31837,17 @@ const Head = ({
|
|
|
31804
31837
|
return undefined;
|
|
31805
31838
|
}
|
|
31806
31839
|
const childArray = Array.isArray(children) ? children : [children];
|
|
31807
|
-
const previousTitle = document.title;
|
|
31808
31840
|
const appendedElements = [];
|
|
31841
|
+
let titleEntry = null;
|
|
31809
31842
|
for (const child of childArray) {
|
|
31810
31843
|
if (!child) {
|
|
31811
31844
|
continue;
|
|
31812
31845
|
}
|
|
31813
31846
|
if (child.type === "title") {
|
|
31814
31847
|
const titleChildren = child.props.children;
|
|
31815
|
-
|
|
31848
|
+
titleEntry = {
|
|
31849
|
+
title: Array.isArray(titleChildren) ? titleChildren.join("") : titleChildren ?? ""
|
|
31850
|
+
};
|
|
31816
31851
|
continue;
|
|
31817
31852
|
}
|
|
31818
31853
|
const el = document.createElement(child.type);
|
|
@@ -31823,8 +31858,19 @@ const Head = ({
|
|
|
31823
31858
|
document.head.appendChild(el);
|
|
31824
31859
|
appendedElements.push(el);
|
|
31825
31860
|
}
|
|
31861
|
+
if (titleEntry) {
|
|
31862
|
+
if (titleEntries.length === 0) {
|
|
31863
|
+
titleWithoutHead = document.title;
|
|
31864
|
+
}
|
|
31865
|
+
titleEntries.push(titleEntry);
|
|
31866
|
+
document.title = titleEntry.title;
|
|
31867
|
+
}
|
|
31826
31868
|
return () => {
|
|
31827
|
-
|
|
31869
|
+
if (titleEntry) {
|
|
31870
|
+
titleEntries.splice(titleEntries.indexOf(titleEntry), 1);
|
|
31871
|
+
const lastEntry = titleEntries[titleEntries.length - 1];
|
|
31872
|
+
document.title = lastEntry ? lastEntry.title : titleWithoutHead;
|
|
31873
|
+
}
|
|
31828
31874
|
for (const el of appendedElements) {
|
|
31829
31875
|
el.remove();
|
|
31830
31876
|
}
|
|
@@ -31987,75 +32033,7 @@ const samePages = (pages, otherPages) => {
|
|
|
31987
32033
|
return true;
|
|
31988
32034
|
};
|
|
31989
32035
|
|
|
31990
|
-
|
|
31991
|
-
* Route is the single primitive for URL-based rendering.
|
|
31992
|
-
*
|
|
31993
|
-
* ## Layout pattern
|
|
31994
|
-
* Use this when multiple routes share a common layout but have no shared URL prefix,
|
|
31995
|
-
* making it impossible to set a guard route on the parent container.
|
|
31996
|
-
* For example, `/profile` and `/settings` both live inside `AuthLayout` but there
|
|
31997
|
-
* is no `/auth/` prefix to match on. A container Route wraps them: the active
|
|
31998
|
-
* child's element is injected as `children` into the layout element.
|
|
31999
|
-
* If a page needs state owned by the layout, the layout must expose it via context.
|
|
32000
|
-
*
|
|
32001
|
-
* ```jsx
|
|
32002
|
-
* const PROFILE_ROUTE = route("/profile");
|
|
32003
|
-
* const SETTINGS_ROUTE = route("/settings");
|
|
32004
|
-
*
|
|
32005
|
-
* <Route element={AuthLayout}>
|
|
32006
|
-
* <Route route={PROFILE_ROUTE} element={ProfilePage} />
|
|
32007
|
-
* <Route route={SETTINGS_ROUTE} element={SettingsPage} />
|
|
32008
|
-
* </Route>
|
|
32009
|
-
* ```
|
|
32010
|
-
*
|
|
32011
|
-
* Such a container holds no fallback, and cannot: what makes this one the right
|
|
32012
|
-
* layout is a child matching, and having no prefix is the very reason it was
|
|
32013
|
-
* reached for — so there is no address that belongs to it and to none of its
|
|
32014
|
-
* children. An address nothing claims is the router's to answer; write the
|
|
32015
|
-
* fallback there.
|
|
32016
|
-
*
|
|
32017
|
-
* ## Self-contained section pattern
|
|
32018
|
-
* Use this when routes share a common URL prefix (e.g. `/dashboard/`).
|
|
32019
|
-
* A single leaf Route in the top-level router matches the prefix; the component
|
|
32020
|
-
* it renders owns its sub-router and all related routes internally.
|
|
32021
|
-
* Everything about the section — routes, structure, sub-pages — is co-located.
|
|
32022
|
-
* The component is not a reusable layout; it is the section.
|
|
32023
|
-
*
|
|
32024
|
-
* Compared to the layout pattern, a dedicated section component is more powerful:
|
|
32025
|
-
* - Wrapper elements (chrome, nav, containers) are part of the component's render,
|
|
32026
|
-
* not injected via `children`, so a layout is not needed.
|
|
32027
|
-
* - Local state can be passed directly via `elementProps` to sub-pages. With the
|
|
32028
|
-
* layout pattern, sub-pages receive state only through `children` or context.
|
|
32029
|
-
*
|
|
32030
|
-
* The layout pattern remains necessary when a shared prefix does not exist
|
|
32031
|
-
* (see "Layout pattern" above).
|
|
32032
|
-
*
|
|
32033
|
-
* ```jsx
|
|
32034
|
-
* const DASHBOARD_SECTION_ROUTE = route("/dashboard/");
|
|
32035
|
-
* const DASHBOARD_HOME_ROUTE = route("/dashboard");
|
|
32036
|
-
* const DASHBOARD_POSTS_ROUTE = route("/dashboard/posts");
|
|
32037
|
-
*
|
|
32038
|
-
* // top-level router — only knows about the prefix
|
|
32039
|
-
* <Route route={DASHBOARD_SECTION_ROUTE} element={DashboardSection} />
|
|
32040
|
-
*
|
|
32041
|
-
* // Dashboard owns the rest
|
|
32042
|
-
* const DashboardSection = () => {
|
|
32043
|
-
* const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
32044
|
-
*
|
|
32045
|
-
* return <div
|
|
32046
|
-
* style="background: lightblue; padding: 10px;"
|
|
32047
|
-
* onClick={() => setSidebarOpen(o => !o)}
|
|
32048
|
-
* >
|
|
32049
|
-
* <Route>
|
|
32050
|
-
* <Route route={DASHBOARD_HOME_ROUTE} element={DashboardHomePage} elementProps={{ sidebarOpen}} />
|
|
32051
|
-
* <Route route={DASHBOARD_POSTS_ROUTE} element={DashboardPostsPage} elementProps={{ sidebarOpen }} />
|
|
32052
|
-
* <Route fallback element={DashboardNotFound} />
|
|
32053
|
-
* </Route>
|
|
32054
|
-
* </div>;
|
|
32055
|
-
* }
|
|
32056
|
-
* ```
|
|
32057
|
-
*/
|
|
32058
|
-
|
|
32036
|
+
installImportMetaCssBuild(import.meta);
|
|
32059
32037
|
|
|
32060
32038
|
/**
|
|
32061
32039
|
* Keep every container showing the page it is showing, whatever the routes say.
|
|
@@ -32079,6 +32057,56 @@ const freezeRouteRender = () => {
|
|
|
32079
32057
|
routeRenderFrozenSignal.value++;
|
|
32080
32058
|
};
|
|
32081
32059
|
};
|
|
32060
|
+
|
|
32061
|
+
/**
|
|
32062
|
+
* Keep the page a container leaves, hidden, until its route transition is over.
|
|
32063
|
+
*
|
|
32064
|
+
* The browser photographs the page being left before the change, and the page
|
|
32065
|
+
* arriving inside the transition's update callback — where everything done
|
|
32066
|
+
* delays the movement. Taking the page being left down is most of that: every
|
|
32067
|
+
* hook of every component in it is cleaned up, and a page of thirty cards
|
|
32068
|
+
* takes longer to take down than the page arriving takes to build. Its picture
|
|
32069
|
+
* is already taken, so it only has to stay out of the second one: a container
|
|
32070
|
+
* that changes page while this is held keeps the page it was showing, hidden,
|
|
32071
|
+
* and drops it once the movement is over (see route_transition.jsx).
|
|
32072
|
+
*
|
|
32073
|
+
* Kept only where Preact would have unmounted it: a page rendered by the same
|
|
32074
|
+
* element on both sides is the same component instance carried over, exactly
|
|
32075
|
+
* as without a movement.
|
|
32076
|
+
*/
|
|
32077
|
+
let leavingPagesHold = null;
|
|
32078
|
+
const keepLeavingPages = () => {
|
|
32079
|
+
const hold = {
|
|
32080
|
+
released: false,
|
|
32081
|
+
containers: new Set()
|
|
32082
|
+
};
|
|
32083
|
+
leavingPagesHold = hold;
|
|
32084
|
+
return () => {
|
|
32085
|
+
if (hold.released) {
|
|
32086
|
+
return;
|
|
32087
|
+
}
|
|
32088
|
+
hold.released = true;
|
|
32089
|
+
if (leavingPagesHold === hold) {
|
|
32090
|
+
leavingPagesHold = null;
|
|
32091
|
+
}
|
|
32092
|
+
for (const rerender of hold.containers) {
|
|
32093
|
+
rerender();
|
|
32094
|
+
}
|
|
32095
|
+
hold.containers.clear();
|
|
32096
|
+
};
|
|
32097
|
+
};
|
|
32098
|
+
|
|
32099
|
+
// What a page can learn about its place from the container rendering it:
|
|
32100
|
+
// whether it is a page kept while leaving (see keepLeavingPages). Such a page
|
|
32101
|
+
// is still mounted, and whatever it fills OUTSIDE its own nodes — a slot in a
|
|
32102
|
+
// bar, say — would otherwise go on showing with the page arriving (see
|
|
32103
|
+
// layout/slot.jsx).
|
|
32104
|
+
const RoutePageContext = createContext(null);
|
|
32105
|
+
const LEAVING_ATTRIBUTE = "data-navi-route-leaving";
|
|
32106
|
+
const css$13 = /* css */`[data-navi-route-leaving] {
|
|
32107
|
+
display: none !important;
|
|
32108
|
+
}
|
|
32109
|
+
`;
|
|
32082
32110
|
const debug$1 = (...args) => {
|
|
32083
32111
|
{
|
|
32084
32112
|
return;
|
|
@@ -32198,6 +32226,7 @@ const RouteContainer = ({
|
|
|
32198
32226
|
shownBranchRef.current = activeBranch;
|
|
32199
32227
|
}
|
|
32200
32228
|
const branch = shownBranchRef.current || activeBranch;
|
|
32229
|
+
const content = useContentKeepingLeavingPages(branch);
|
|
32201
32230
|
|
|
32202
32231
|
// The two things this component knows that nobody outside can find out: the
|
|
32203
32232
|
// pages a named fallback is the absence of — the children it was written
|
|
@@ -32219,7 +32248,6 @@ const RouteContainer = ({
|
|
|
32219
32248
|
};
|
|
32220
32249
|
}, [fallbackPage]);
|
|
32221
32250
|
debug$1(`[container "${id}"] RENDER, active=${branch ? branch.type : "none"}`);
|
|
32222
|
-
const content = branch ? branch.node : null;
|
|
32223
32251
|
if (!content) {
|
|
32224
32252
|
return null;
|
|
32225
32253
|
}
|
|
@@ -32228,12 +32256,152 @@ const RouteContainer = ({
|
|
|
32228
32256
|
}
|
|
32229
32257
|
return content;
|
|
32230
32258
|
};
|
|
32259
|
+
// What a container renders: the page it shows and, while a route transition
|
|
32260
|
+
// keeps them (see keepLeavingPages), the pages it has left — each the very
|
|
32261
|
+
// vnode rendered last time, which Preact does not look into, between two
|
|
32262
|
+
// markers that say which nodes to hide. Always an array, even of one page: the
|
|
32263
|
+
// page being left must stay the child it was when a page is added beside it,
|
|
32264
|
+
// or Preact takes it down and builds it again.
|
|
32265
|
+
let leavingKeyCount = 0;
|
|
32266
|
+
const useContentKeepingLeavingPages = branch => {
|
|
32267
|
+
const [, rerender] = useReducer(countRenders, 0);
|
|
32268
|
+
const shownRef = useRef(null);
|
|
32269
|
+
const leavingRef = useRef([]);
|
|
32270
|
+
const shown = shownRef.current;
|
|
32271
|
+
const element = branchElement(branch);
|
|
32272
|
+
let key;
|
|
32273
|
+
let page = null;
|
|
32274
|
+
if (shown && branch && (shown.id === branch.id || shown.element === element)) {
|
|
32275
|
+
// The same page, or one Preact carries over from it: the key it was
|
|
32276
|
+
// mounted with, or it would be mounted again.
|
|
32277
|
+
key = shown.key;
|
|
32278
|
+
page = shown.page;
|
|
32279
|
+
} else if (shown && leavingPagesHold && !document.querySelector(":modal")) {
|
|
32280
|
+
// Never while a modal dialog is open: it keeps everything but itself out
|
|
32281
|
+
// of reach — the page arriving could not even take the focus — for as long
|
|
32282
|
+
// as it is open, and one in a page kept while leaving stays open for the
|
|
32283
|
+
// whole movement. The page is then taken down at once, as without one.
|
|
32284
|
+
leavingRef.current.push({
|
|
32285
|
+
vnode: shown.vnode,
|
|
32286
|
+
page: shown.page,
|
|
32287
|
+
key: `navi-route-leaving-${++leavingKeyCount}`,
|
|
32288
|
+
hold: leavingPagesHold,
|
|
32289
|
+
startRef: createRef(),
|
|
32290
|
+
endRef: createRef(),
|
|
32291
|
+
hidden: false
|
|
32292
|
+
});
|
|
32293
|
+
leavingPagesHold.containers.add(rerender);
|
|
32294
|
+
// Keyed apart from the page it takes the place of, which is still there.
|
|
32295
|
+
key = `navi-route-${++leavingKeyCount}`;
|
|
32296
|
+
}
|
|
32297
|
+
let vnode = null;
|
|
32298
|
+
if (branch) {
|
|
32299
|
+
if (!page) {
|
|
32300
|
+
page = {
|
|
32301
|
+
leavingSignal: signal(false)
|
|
32302
|
+
};
|
|
32303
|
+
}
|
|
32304
|
+
vnode = h(RoutePageContext.Provider, {
|
|
32305
|
+
key,
|
|
32306
|
+
value: page
|
|
32307
|
+
}, branch.node);
|
|
32308
|
+
}
|
|
32309
|
+
shownRef.current = branch ? {
|
|
32310
|
+
id: branch.id,
|
|
32311
|
+
element,
|
|
32312
|
+
key,
|
|
32313
|
+
vnode,
|
|
32314
|
+
page
|
|
32315
|
+
} : null;
|
|
32316
|
+
const leaving = leavingRef.current.filter(entry => !entry.hold.released);
|
|
32317
|
+
leavingRef.current = leaving;
|
|
32318
|
+
|
|
32319
|
+
// Hidden as soon as it is in the DOM next to the page arriving: this runs in
|
|
32320
|
+
// the update callback's render, before the second picture is taken.
|
|
32321
|
+
useLayoutEffect(() => {
|
|
32322
|
+
for (const entry of leavingRef.current) {
|
|
32323
|
+
const start = entry.startRef.current;
|
|
32324
|
+
const end = entry.endRef.current;
|
|
32325
|
+
if (entry.hidden || !start || !end) {
|
|
32326
|
+
continue;
|
|
32327
|
+
}
|
|
32328
|
+
entry.hidden = true;
|
|
32329
|
+
entry.page.leavingSignal.value = true;
|
|
32330
|
+
const focusedElement = document.activeElement;
|
|
32331
|
+
let node = start.nextSibling;
|
|
32332
|
+
while (node && node !== end) {
|
|
32333
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
32334
|
+
node.setAttribute(LEAVING_ATTRIBUTE, "");
|
|
32335
|
+
// What the keyboard held in it goes where the page's removal would
|
|
32336
|
+
// have sent it: a hidden element keeps the focus, and would go on
|
|
32337
|
+
// receiving the keys pressed during the movement.
|
|
32338
|
+
if (node.contains(focusedElement)) {
|
|
32339
|
+
focusedElement.blur();
|
|
32340
|
+
}
|
|
32341
|
+
}
|
|
32342
|
+
node = node.nextSibling;
|
|
32343
|
+
}
|
|
32344
|
+
}
|
|
32345
|
+
});
|
|
32346
|
+
useLayoutEffect(() => {
|
|
32347
|
+
return () => {
|
|
32348
|
+
for (const entry of leavingRef.current) {
|
|
32349
|
+
entry.hold.containers.delete(rerender);
|
|
32350
|
+
}
|
|
32351
|
+
};
|
|
32352
|
+
}, []);
|
|
32353
|
+
if (leaving.length === 0) {
|
|
32354
|
+
return vnode ? [vnode] : null;
|
|
32355
|
+
}
|
|
32356
|
+
import.meta.css = [css$13, "@jsenv/navi/src/nav/route.jsx"];
|
|
32357
|
+
const children = vnode ? [vnode] : [];
|
|
32358
|
+
for (const entry of leaving) {
|
|
32359
|
+
children.push(h("template", {
|
|
32360
|
+
key: `${entry.key}-start`,
|
|
32361
|
+
ref: entry.startRef
|
|
32362
|
+
}), entry.vnode, h("template", {
|
|
32363
|
+
key: `${entry.key}-end`,
|
|
32364
|
+
ref: entry.endRef
|
|
32365
|
+
}));
|
|
32366
|
+
}
|
|
32367
|
+
return children;
|
|
32368
|
+
};
|
|
32369
|
+
const countRenders = count => count + 1;
|
|
32370
|
+
// What Preact compares when a container changes page, below the <Route> it
|
|
32371
|
+
// renders either way: the element the page is rendered with — a layout, for a
|
|
32372
|
+
// container branch.
|
|
32373
|
+
const branchElement = branch => {
|
|
32374
|
+
if (!branch) {
|
|
32375
|
+
return null;
|
|
32376
|
+
}
|
|
32377
|
+
let node = branch.node;
|
|
32378
|
+
while (node && node.type !== Route) {
|
|
32379
|
+
// A wrapper around the branch (see wrapBranch).
|
|
32380
|
+
node = node.props.children;
|
|
32381
|
+
}
|
|
32382
|
+
if (!node) {
|
|
32383
|
+
return null;
|
|
32384
|
+
}
|
|
32385
|
+
const {
|
|
32386
|
+
element
|
|
32387
|
+
} = node.props;
|
|
32388
|
+
if (element === undefined || typeof element === "function") {
|
|
32389
|
+
return element;
|
|
32390
|
+
}
|
|
32391
|
+
return element.type;
|
|
32392
|
+
};
|
|
32393
|
+
|
|
32231
32394
|
// Walk JSX children vnodes (without rendering) to build a branch list and
|
|
32232
32395
|
// find the active one in the same pass. Anything that is not a <Route> is read
|
|
32233
32396
|
// through and kept around the branch it holds (see below).
|
|
32234
32397
|
// Returns { matchingBranch, fallbackBranch, activeBranch, pages }.
|
|
32235
|
-
|
|
32398
|
+
//
|
|
32399
|
+
// Each branch is given an id from where it is written among the children,
|
|
32400
|
+
// which is what tells a container it changed page: the vnodes themselves are
|
|
32401
|
+
// new at every render of the parent.
|
|
32402
|
+
const collectBranches = (children, path = "") => {
|
|
32236
32403
|
let matchingBranch = null;
|
|
32404
|
+
let childIndex = 0;
|
|
32237
32405
|
let fallbackBranch = null;
|
|
32238
32406
|
// Every page a branch can be on, at any depth: what a named fallback is the
|
|
32239
32407
|
// ABSENCE of (see route_fallback.js). A container with a guard route counts
|
|
@@ -32251,6 +32419,7 @@ const collectBranches = children => {
|
|
|
32251
32419
|
}
|
|
32252
32420
|
return;
|
|
32253
32421
|
}
|
|
32422
|
+
const id = `${path}${childIndex++}`;
|
|
32254
32423
|
if (child.type !== Route) {
|
|
32255
32424
|
// Anything else is a wrapper around branches, and the two that matter are
|
|
32256
32425
|
// navi's own: a page says what it renders and delegates what it cannot —
|
|
@@ -32270,7 +32439,7 @@ const collectBranches = children => {
|
|
|
32270
32439
|
matchingBranch: matchingInside,
|
|
32271
32440
|
fallbackBranch: fallbackInside,
|
|
32272
32441
|
pages: pagesInside
|
|
32273
|
-
} = collectBranches(wrapperChildren);
|
|
32442
|
+
} = collectBranches(wrapperChildren, `${id}.`);
|
|
32274
32443
|
for (const pageInside of pagesInside) {
|
|
32275
32444
|
pages.push(pageInside);
|
|
32276
32445
|
}
|
|
@@ -32293,7 +32462,7 @@ const collectBranches = children => {
|
|
|
32293
32462
|
matchingBranch: matchingChild,
|
|
32294
32463
|
fallbackBranch: fallbackChild,
|
|
32295
32464
|
pages: pagesChild
|
|
32296
|
-
} = collectBranches(nodeChildren);
|
|
32465
|
+
} = collectBranches(nodeChildren, `${id}.`);
|
|
32297
32466
|
if (fallbackChild && !route) {
|
|
32298
32467
|
// A fallback is the branch taken for an address none of its siblings
|
|
32299
32468
|
// claim — so something has to make that address BELONG to this
|
|
@@ -32317,6 +32486,7 @@ const collectBranches = children => {
|
|
|
32317
32486
|
}
|
|
32318
32487
|
const branch = {
|
|
32319
32488
|
type: "container",
|
|
32489
|
+
id,
|
|
32320
32490
|
node: child
|
|
32321
32491
|
};
|
|
32322
32492
|
const guardMatching = route ? route.matchingSignal.value : false;
|
|
@@ -32334,6 +32504,7 @@ const collectBranches = children => {
|
|
|
32334
32504
|
if (!fallbackBranch) {
|
|
32335
32505
|
fallbackBranch = {
|
|
32336
32506
|
type: "fallback",
|
|
32507
|
+
id,
|
|
32337
32508
|
node: child,
|
|
32338
32509
|
page: fallback.isRouteFallback ? fallback : null
|
|
32339
32510
|
};
|
|
@@ -32341,6 +32512,7 @@ const collectBranches = children => {
|
|
|
32341
32512
|
} else {
|
|
32342
32513
|
const branch = {
|
|
32343
32514
|
type: "leaf",
|
|
32515
|
+
id,
|
|
32344
32516
|
node: child
|
|
32345
32517
|
};
|
|
32346
32518
|
pages.push({
|
|
@@ -32828,7 +33000,7 @@ const holdTransitionFurniture = (owner, areaElement) => {
|
|
|
32828
33000
|
}
|
|
32829
33001
|
const namesLeaving = [];
|
|
32830
33002
|
for (const element of namedElements) {
|
|
32831
|
-
if (!element
|
|
33003
|
+
if (!isRendered(element)) {
|
|
32832
33004
|
namesLeaving.push(nameByElement.get(element));
|
|
32833
33005
|
} else if (element.matches(FIXED_BAR_SELECTOR$1)) {
|
|
32834
33006
|
element.setAttribute(FURNITURE_ATTRIBUTE, "shared");
|
|
@@ -32947,11 +33119,16 @@ const travelRule = (side, names, movementProperty) => {
|
|
|
32947
33119
|
* The pictures are up and about to move: what no picture covers heads to the
|
|
32948
33120
|
* state arriving, on the same clock.
|
|
32949
33121
|
*/
|
|
32950
|
-
const startTransitionFurniture = (owner) => {
|
|
33122
|
+
const startTransitionFurniture = (owner, { duration } = {}) => {
|
|
32951
33123
|
if (owner !== furnitureOwner) {
|
|
32952
33124
|
return;
|
|
32953
33125
|
}
|
|
32954
33126
|
for (const restWall of restWallElements) {
|
|
33127
|
+
// A duration of the movement's own is written for its pictures alone (see
|
|
33128
|
+
// transition_values.js); the stand-in is an element, and is told.
|
|
33129
|
+
if (duration !== undefined) {
|
|
33130
|
+
restWall.style.setProperty("--navi-route-transition-duration", duration);
|
|
33131
|
+
}
|
|
32955
33132
|
restWall.style.opacity = restWallOpacity;
|
|
32956
33133
|
}
|
|
32957
33134
|
};
|
|
@@ -32982,7 +33159,9 @@ const releaseTransitionFurniture = (owner) => {
|
|
|
32982
33159
|
// area's stand-in answers for it.
|
|
32983
33160
|
const paintTransitionWalls = (areaElement) => {
|
|
32984
33161
|
removeTransitionWalls();
|
|
32985
|
-
const sources =
|
|
33162
|
+
const sources = Array.from(
|
|
33163
|
+
document.querySelectorAll(TOP_LAYER_WALL_SELECTOR),
|
|
33164
|
+
).filter(isRendered);
|
|
32986
33165
|
if (sources.length === 0) {
|
|
32987
33166
|
return sources;
|
|
32988
33167
|
}
|
|
@@ -33184,6 +33363,14 @@ const subtractRect = (rect, hole) => {
|
|
|
33184
33363
|
return pieces;
|
|
33185
33364
|
};
|
|
33186
33365
|
|
|
33366
|
+
// Out of the state arriving: taken out of the document, or in a page kept
|
|
33367
|
+
// hidden until its movement is over (see keepLeavingPages in route.jsx) —
|
|
33368
|
+
// where a dialog is still :modal and a popover still :popover-open, and
|
|
33369
|
+
// neither is painted or photographed.
|
|
33370
|
+
const isRendered = (element) => {
|
|
33371
|
+
return element.isConnected && element.checkVisibility();
|
|
33372
|
+
};
|
|
33373
|
+
|
|
33187
33374
|
const removeRestWalls = () => {
|
|
33188
33375
|
for (const restWall of restWallElements) {
|
|
33189
33376
|
restWall.remove();
|
|
@@ -33193,6 +33380,52 @@ const removeRestWalls = () => {
|
|
|
33193
33380
|
leavingRects = new Map();
|
|
33194
33381
|
};
|
|
33195
33382
|
|
|
33383
|
+
/**
|
|
33384
|
+
* The values a movement hands to its pictures, written where only the pictures
|
|
33385
|
+
* read them: on ::view-transition, the root of the pseudo-element tree, rather
|
|
33386
|
+
* than on the document's root.
|
|
33387
|
+
*
|
|
33388
|
+
* A custom property written on :root is inherited by every element of the
|
|
33389
|
+
* document, and writing one restyles all of them. A movement writes its
|
|
33390
|
+
* numbers as its pages are held, again whenever a scroll moves the window, and
|
|
33391
|
+
* takes them off as it ends — each time a whole-document restyle of a page that
|
|
33392
|
+
* can be thousands of elements long. On ::view-transition they reach every
|
|
33393
|
+
* picture by plain inheritance, and no element at all.
|
|
33394
|
+
*
|
|
33395
|
+
* An inline style cannot target a pseudo-element, so they are declarations of
|
|
33396
|
+
* one rule, in a sheet of their own. Written only: what a pseudo-element that
|
|
33397
|
+
* is not on screen computes is not reliably readable, so nothing reads them
|
|
33398
|
+
* back from there.
|
|
33399
|
+
*/
|
|
33400
|
+
|
|
33401
|
+
let valuesStyle = null;
|
|
33402
|
+
const getValuesStyle = () => {
|
|
33403
|
+
if (valuesStyle) {
|
|
33404
|
+
return valuesStyle;
|
|
33405
|
+
}
|
|
33406
|
+
const sheet = new CSSStyleSheet();
|
|
33407
|
+
sheet.replaceSync(":root::view-transition {}");
|
|
33408
|
+
const [rule] = sheet.cssRules;
|
|
33409
|
+
if (rule) {
|
|
33410
|
+
document.adoptedStyleSheets.push(sheet);
|
|
33411
|
+
valuesStyle = rule.style;
|
|
33412
|
+
} else {
|
|
33413
|
+
// A browser without view transitions drops the rule: the movement runs its
|
|
33414
|
+
// steps all the same (see start_view_transition_polyfill.js), with no
|
|
33415
|
+
// pictures to hand anything to.
|
|
33416
|
+
valuesStyle = document.createElement("div").style;
|
|
33417
|
+
}
|
|
33418
|
+
return valuesStyle;
|
|
33419
|
+
};
|
|
33420
|
+
|
|
33421
|
+
const setTransitionValue = (name, value) => {
|
|
33422
|
+
getValuesStyle().setProperty(name, value);
|
|
33423
|
+
};
|
|
33424
|
+
|
|
33425
|
+
const removeTransitionValue = (name) => {
|
|
33426
|
+
getValuesStyle().removeProperty(name);
|
|
33427
|
+
};
|
|
33428
|
+
|
|
33196
33429
|
installImportMetaCssBuild(import.meta);
|
|
33197
33430
|
|
|
33198
33431
|
const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
|
|
@@ -33227,96 +33460,6 @@ const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
|
|
|
33227
33460
|
--navi-transition-cover-left: 0px;
|
|
33228
33461
|
}
|
|
33229
33462
|
}
|
|
33230
|
-
|
|
33231
|
-
@property --navi-transition-window-top {
|
|
33232
|
-
syntax: "*";
|
|
33233
|
-
inherits: false
|
|
33234
|
-
}
|
|
33235
|
-
|
|
33236
|
-
@property --navi-transition-window-left {
|
|
33237
|
-
syntax: "*";
|
|
33238
|
-
inherits: false
|
|
33239
|
-
}
|
|
33240
|
-
|
|
33241
|
-
@property --navi-transition-window-width {
|
|
33242
|
-
syntax: "*";
|
|
33243
|
-
inherits: false
|
|
33244
|
-
}
|
|
33245
|
-
|
|
33246
|
-
@property --navi-transition-window-height {
|
|
33247
|
-
syntax: "*";
|
|
33248
|
-
inherits: false
|
|
33249
|
-
}
|
|
33250
|
-
|
|
33251
|
-
@property --navi-transition-window-old-top {
|
|
33252
|
-
syntax: "*";
|
|
33253
|
-
inherits: false
|
|
33254
|
-
}
|
|
33255
|
-
|
|
33256
|
-
@property --navi-transition-window-old-left {
|
|
33257
|
-
syntax: "*";
|
|
33258
|
-
inherits: false
|
|
33259
|
-
}
|
|
33260
|
-
|
|
33261
|
-
@property --navi-transition-window-new-top {
|
|
33262
|
-
syntax: "*";
|
|
33263
|
-
inherits: false
|
|
33264
|
-
}
|
|
33265
|
-
|
|
33266
|
-
@property --navi-transition-window-new-left {
|
|
33267
|
-
syntax: "*";
|
|
33268
|
-
inherits: false
|
|
33269
|
-
}
|
|
33270
|
-
|
|
33271
|
-
@property --navi-transition-old-band-top {
|
|
33272
|
-
syntax: "*";
|
|
33273
|
-
inherits: false
|
|
33274
|
-
}
|
|
33275
|
-
|
|
33276
|
-
@property --navi-transition-old-band-right {
|
|
33277
|
-
syntax: "*";
|
|
33278
|
-
inherits: false
|
|
33279
|
-
}
|
|
33280
|
-
|
|
33281
|
-
@property --navi-transition-old-band-bottom {
|
|
33282
|
-
syntax: "*";
|
|
33283
|
-
inherits: false
|
|
33284
|
-
}
|
|
33285
|
-
|
|
33286
|
-
@property --navi-transition-old-band-left {
|
|
33287
|
-
syntax: "*";
|
|
33288
|
-
inherits: false
|
|
33289
|
-
}
|
|
33290
|
-
|
|
33291
|
-
:root::view-transition, :root::view-transition-group(*), :root::view-transition-image-pair(*), :root::view-transition-old(*), :root::view-transition-new(*) {
|
|
33292
|
-
--navi-transition-window-top: inherit;
|
|
33293
|
-
--navi-transition-window-left: inherit;
|
|
33294
|
-
--navi-transition-window-width: inherit;
|
|
33295
|
-
--navi-transition-window-height: inherit;
|
|
33296
|
-
--navi-transition-window-old-top: inherit;
|
|
33297
|
-
--navi-transition-window-old-left: inherit;
|
|
33298
|
-
--navi-transition-window-new-top: inherit;
|
|
33299
|
-
--navi-transition-window-new-left: inherit;
|
|
33300
|
-
--navi-transition-old-band-top: inherit;
|
|
33301
|
-
--navi-transition-old-band-right: inherit;
|
|
33302
|
-
--navi-transition-old-band-bottom: inherit;
|
|
33303
|
-
--navi-transition-old-band-left: inherit;
|
|
33304
|
-
}
|
|
33305
|
-
|
|
33306
|
-
:root::view-transition-group-children(*) {
|
|
33307
|
-
--navi-transition-window-top: inherit;
|
|
33308
|
-
--navi-transition-window-left: inherit;
|
|
33309
|
-
--navi-transition-window-width: inherit;
|
|
33310
|
-
--navi-transition-window-height: inherit;
|
|
33311
|
-
--navi-transition-window-old-top: inherit;
|
|
33312
|
-
--navi-transition-window-old-left: inherit;
|
|
33313
|
-
--navi-transition-window-new-top: inherit;
|
|
33314
|
-
--navi-transition-window-new-left: inherit;
|
|
33315
|
-
--navi-transition-old-band-top: inherit;
|
|
33316
|
-
--navi-transition-old-band-right: inherit;
|
|
33317
|
-
--navi-transition-old-band-bottom: inherit;
|
|
33318
|
-
--navi-transition-old-band-left: inherit;
|
|
33319
|
-
}
|
|
33320
33463
|
`;
|
|
33321
33464
|
|
|
33322
33465
|
// Called by whatever needs the window — a render, a movement declared or
|
|
@@ -33414,22 +33557,21 @@ const holdTransitionWindow = (
|
|
|
33414
33557
|
const right =
|
|
33415
33558
|
rectBefore.right > rectAfter.right ? rectBefore.right : rectAfter.right;
|
|
33416
33559
|
windowOwner = owner;
|
|
33417
|
-
|
|
33418
|
-
|
|
33419
|
-
|
|
33420
|
-
|
|
33421
|
-
|
|
33422
|
-
|
|
33423
|
-
|
|
33424
|
-
|
|
33425
|
-
style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
33560
|
+
setTransitionValue(WINDOW_TOP_PROPERTY, `${top}px`);
|
|
33561
|
+
setTransitionValue(WINDOW_LEFT_PROPERTY, `${left}px`);
|
|
33562
|
+
setTransitionValue(WINDOW_WIDTH_PROPERTY, `${right - left}px`);
|
|
33563
|
+
setTransitionValue(WINDOW_HEIGHT_PROPERTY, `${bottom - top}px`);
|
|
33564
|
+
setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
|
|
33565
|
+
setTransitionValue(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
|
|
33566
|
+
setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${rectAfter.top}px`);
|
|
33567
|
+
setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
33426
33568
|
const oldBand = furnitureLive
|
|
33427
33569
|
? bandTheCutMayBeRelaxedTo(bandBefore, bandAfter, rectAfter)
|
|
33428
33570
|
: bandBefore;
|
|
33429
|
-
|
|
33430
|
-
|
|
33431
|
-
|
|
33432
|
-
|
|
33571
|
+
setTransitionValue(OLD_BAND_TOP_PROPERTY, `${oldBand.top}px`);
|
|
33572
|
+
setTransitionValue(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
|
|
33573
|
+
setTransitionValue(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
|
|
33574
|
+
setTransitionValue(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
|
|
33433
33575
|
followArrivingState(element, rectAfter, {
|
|
33434
33576
|
outerScroll,
|
|
33435
33577
|
windowTop: top,
|
|
@@ -33451,9 +33593,8 @@ const releaseTransitionWindow = (owner) => {
|
|
|
33451
33593
|
if (unfollowArrivingState) {
|
|
33452
33594
|
unfollowArrivingState();
|
|
33453
33595
|
}
|
|
33454
|
-
const { style } = document.documentElement;
|
|
33455
33596
|
for (const property of WINDOW_PROPERTIES) {
|
|
33456
|
-
|
|
33597
|
+
removeTransitionValue(property);
|
|
33457
33598
|
}
|
|
33458
33599
|
};
|
|
33459
33600
|
|
|
@@ -33462,12 +33603,11 @@ const releaseTransitionWindow = (owner) => {
|
|
|
33462
33603
|
// file) — and with it the offset outside the box, which carries the window
|
|
33463
33604
|
// and the corner of the state being left along. Requested, never awaited: the
|
|
33464
33605
|
// first call runs inside the update callback, where a frame cannot come.
|
|
33465
|
-
// Written only when it moved: every write restyles the
|
|
33606
|
+
// Written only when it moved: every write restyles the pictures.
|
|
33466
33607
|
const followArrivingState = (element, rectAtHold, heldAt) => {
|
|
33467
33608
|
if (unfollowArrivingState) {
|
|
33468
33609
|
unfollowArrivingState();
|
|
33469
33610
|
}
|
|
33470
|
-
const { style } = document.documentElement;
|
|
33471
33611
|
let top = rectAtHold.top;
|
|
33472
33612
|
let left = rectAtHold.left;
|
|
33473
33613
|
let outerTop = heldAt.outerScroll.top;
|
|
@@ -33476,24 +33616,27 @@ const followArrivingState = (element, rectAtHold, heldAt) => {
|
|
|
33476
33616
|
const rect = element.getBoundingClientRect();
|
|
33477
33617
|
if (rect.top !== top) {
|
|
33478
33618
|
top = rect.top;
|
|
33479
|
-
|
|
33619
|
+
setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${top}px`);
|
|
33480
33620
|
}
|
|
33481
33621
|
if (rect.left !== left) {
|
|
33482
33622
|
left = rect.left;
|
|
33483
|
-
|
|
33623
|
+
setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${left}px`);
|
|
33484
33624
|
}
|
|
33485
33625
|
const outerScroll = readOuterScroll(element);
|
|
33486
33626
|
if (outerScroll.top !== outerTop) {
|
|
33487
33627
|
outerTop = outerScroll.top;
|
|
33488
33628
|
const shift = heldAt.outerScroll.top - outerTop;
|
|
33489
|
-
|
|
33490
|
-
|
|
33629
|
+
setTransitionValue(WINDOW_TOP_PROPERTY, `${heldAt.windowTop + shift}px`);
|
|
33630
|
+
setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${heldAt.oldTop + shift}px`);
|
|
33491
33631
|
}
|
|
33492
33632
|
if (outerScroll.left !== outerLeft) {
|
|
33493
33633
|
outerLeft = outerScroll.left;
|
|
33494
33634
|
const shift = heldAt.outerScroll.left - outerLeft;
|
|
33495
|
-
|
|
33496
|
-
|
|
33635
|
+
setTransitionValue(
|
|
33636
|
+
WINDOW_LEFT_PROPERTY,
|
|
33637
|
+
`${heldAt.windowLeft + shift}px`,
|
|
33638
|
+
);
|
|
33639
|
+
setTransitionValue(
|
|
33497
33640
|
WINDOW_OLD_LEFT_PROPERTY,
|
|
33498
33641
|
`${heldAt.oldLeft + shift}px`,
|
|
33499
33642
|
);
|
|
@@ -33856,58 +33999,9 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
|
|
|
33856
33999
|
inherits: false
|
|
33857
34000
|
}
|
|
33858
34001
|
|
|
33859
|
-
@property --navi-route-transition-clip-top {
|
|
33860
|
-
syntax: "*";
|
|
33861
|
-
inherits: false
|
|
33862
|
-
}
|
|
33863
|
-
|
|
33864
|
-
@property --navi-route-transition-clip-right {
|
|
33865
|
-
syntax: "*";
|
|
33866
|
-
inherits: false
|
|
33867
|
-
}
|
|
33868
|
-
|
|
33869
|
-
@property --navi-route-transition-clip-bottom {
|
|
33870
|
-
syntax: "*";
|
|
33871
|
-
inherits: false
|
|
33872
|
-
}
|
|
33873
|
-
|
|
33874
|
-
@property --navi-route-transition-clip-left {
|
|
33875
|
-
syntax: "*";
|
|
33876
|
-
inherits: false
|
|
33877
|
-
}
|
|
33878
|
-
|
|
33879
|
-
@property --navi-route-transition-travel-x {
|
|
33880
|
-
syntax: "*";
|
|
33881
|
-
inherits: false
|
|
33882
|
-
}
|
|
33883
|
-
|
|
33884
|
-
@property --navi-route-transition-travel-y {
|
|
33885
|
-
syntax: "*";
|
|
33886
|
-
inherits: false
|
|
33887
|
-
}
|
|
33888
|
-
|
|
33889
34002
|
:root::view-transition, :root::view-transition-group(*), :root::view-transition-image-pair(*), :root::view-transition-old(*), :root::view-transition-new(*) {
|
|
33890
34003
|
--navi-route-transition-leave: inherit;
|
|
33891
34004
|
--navi-route-transition-enter: inherit;
|
|
33892
|
-
--navi-route-transition-covered: inherit;
|
|
33893
|
-
--navi-route-transition-clip-top: inherit;
|
|
33894
|
-
--navi-route-transition-clip-right: inherit;
|
|
33895
|
-
--navi-route-transition-clip-bottom: inherit;
|
|
33896
|
-
--navi-route-transition-clip-left: inherit;
|
|
33897
|
-
--navi-route-transition-travel-x: inherit;
|
|
33898
|
-
--navi-route-transition-travel-y: inherit;
|
|
33899
|
-
}
|
|
33900
|
-
|
|
33901
|
-
:root::view-transition-group-children(*) {
|
|
33902
|
-
--navi-route-transition-leave: inherit;
|
|
33903
|
-
--navi-route-transition-enter: inherit;
|
|
33904
|
-
--navi-route-transition-covered: inherit;
|
|
33905
|
-
--navi-route-transition-clip-top: inherit;
|
|
33906
|
-
--navi-route-transition-clip-right: inherit;
|
|
33907
|
-
--navi-route-transition-clip-bottom: inherit;
|
|
33908
|
-
--navi-route-transition-clip-left: inherit;
|
|
33909
|
-
--navi-route-transition-travel-x: inherit;
|
|
33910
|
-
--navi-route-transition-travel-y: inherit;
|
|
33911
34005
|
}
|
|
33912
34006
|
|
|
33913
34007
|
:root[data-navi-route-transition] [data-navi-route-transition-area] {
|
|
@@ -33928,28 +34022,32 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
|
|
|
33928
34022
|
|
|
33929
34023
|
&[data-navi-route-transition-target="area"] {
|
|
33930
34024
|
view-transition-name: none;
|
|
33931
|
-
|
|
33932
|
-
|
|
33933
|
-
|
|
33934
|
-
|
|
33935
|
-
|
|
33936
|
-
|
|
33937
|
-
|
|
33938
|
-
|
|
33939
|
-
|
|
33940
|
-
|
|
33941
|
-
|
|
33942
|
-
var(--navi-transition-
|
|
33943
|
-
|
|
33944
|
-
|
|
33945
|
-
|
|
33946
|
-
|
|
33947
|
-
|
|
33948
|
-
|
|
33949
|
-
var(--navi-transition-
|
|
33950
|
-
|
|
33951
|
-
|
|
33952
|
-
|
|
34025
|
+
|
|
34026
|
+
&::view-transition {
|
|
34027
|
+
--navi-route-transition-clip-top: max(0px,
|
|
34028
|
+
min(var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
|
|
34029
|
+
var(--navi-transition-old-band-top)) - var(--navi-transition-window-top));
|
|
34030
|
+
--navi-route-transition-clip-left: max(0px,
|
|
34031
|
+
min(var(--navi-safe-area-inset-left) +
|
|
34032
|
+
var(--navi-transition-cover-left),
|
|
34033
|
+
var(--navi-transition-old-band-left)) - var(--navi-transition-window-left));
|
|
34034
|
+
--navi-route-transition-clip-bottom: max(0px,
|
|
34035
|
+
var(--navi-transition-window-top) +
|
|
34036
|
+
var(--navi-transition-window-height) +
|
|
34037
|
+
min(var(--navi-safe-area-inset-bottom) +
|
|
34038
|
+
var(--navi-transition-cover-bottom),
|
|
34039
|
+
var(--navi-transition-old-band-bottom)) -
|
|
34040
|
+
100dvh);
|
|
34041
|
+
--navi-route-transition-clip-right: max(0px,
|
|
34042
|
+
var(--navi-transition-window-left) +
|
|
34043
|
+
var(--navi-transition-window-width) +
|
|
34044
|
+
min(var(--navi-safe-area-inset-right) +
|
|
34045
|
+
var(--navi-transition-cover-right),
|
|
34046
|
+
var(--navi-transition-old-band-right)) -
|
|
34047
|
+
100dvw);
|
|
34048
|
+
--navi-route-transition-travel-x: calc(var(--navi-transition-window-width) - var(--navi-route-transition-clip-left) - var(--navi-route-transition-clip-right));
|
|
34049
|
+
--navi-route-transition-travel-y: calc(var(--navi-transition-window-height) - var(--navi-route-transition-clip-top) - var(--navi-route-transition-clip-bottom));
|
|
34050
|
+
}
|
|
33953
34051
|
|
|
33954
34052
|
&::view-transition-group(*) {
|
|
33955
34053
|
z-index: 0;
|
|
@@ -34977,7 +35075,9 @@ const beginTransition = ({
|
|
|
34977
35075
|
decision: navigationDecision,
|
|
34978
35076
|
walkHome: null,
|
|
34979
35077
|
releaseReverting: null,
|
|
34980
|
-
|
|
35078
|
+
duration: undefined,
|
|
35079
|
+
restoreDuration: null,
|
|
35080
|
+
releaseLeavingPages: null
|
|
34981
35081
|
};
|
|
34982
35082
|
// The document wears one transition at a time, and the one this interrupts
|
|
34983
35083
|
// ends only after this one has begun — too late to take off what it wears
|
|
@@ -35030,18 +35130,13 @@ const beginTransition = ({
|
|
|
35030
35130
|
if (areaElement) {
|
|
35031
35131
|
documentElement.setAttribute(TRANSITION_TARGET_ATTRIBUTE, "area");
|
|
35032
35132
|
}
|
|
35033
|
-
// A duration of this relation's own, worn
|
|
35034
|
-
//
|
|
35035
|
-
// erased.
|
|
35133
|
+
// A duration of this relation's own, worn by the pictures for the length of
|
|
35134
|
+
// the transition, over whatever the application says on the root.
|
|
35036
35135
|
if (duration !== undefined) {
|
|
35037
|
-
|
|
35038
|
-
|
|
35136
|
+
transition.duration = typeof duration === "number" ? `${duration}ms` : duration;
|
|
35137
|
+
setTransitionValue(TRANSITION_DURATION_PROPERTY, transition.duration);
|
|
35039
35138
|
transition.restoreDuration = () => {
|
|
35040
|
-
|
|
35041
|
-
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, durationBefore);
|
|
35042
|
-
} else {
|
|
35043
|
-
documentElement.style.removeProperty(TRANSITION_DURATION_PROPERTY);
|
|
35044
|
-
}
|
|
35139
|
+
removeTransitionValue(TRANSITION_DURATION_PROPERTY);
|
|
35045
35140
|
};
|
|
35046
35141
|
}
|
|
35047
35142
|
const releaseRendering = takeoverRoutingRenderingHold();
|
|
@@ -35054,7 +35149,9 @@ const beginTransition = ({
|
|
|
35054
35149
|
// the only place the silent misconfigurations show. They are all about the
|
|
35055
35150
|
// same thing — a movement playing on pictures that are not the pages.
|
|
35056
35151
|
const viewTransitionReady = () => {
|
|
35057
|
-
startTransitionFurniture(transition
|
|
35152
|
+
startTransitionFurniture(transition, {
|
|
35153
|
+
duration: transition.duration
|
|
35154
|
+
});
|
|
35058
35155
|
const capturedNames = capturedViewTransitionNames();
|
|
35059
35156
|
if (areaElements.length > 0) {
|
|
35060
35157
|
if (!capturedNames.has(AREA_NAME)) {
|
|
@@ -35080,6 +35177,10 @@ const beginTransition = ({
|
|
|
35080
35177
|
// is awaited here must be able to resolve without a frame: the document
|
|
35081
35178
|
// is frozen for the whole of this callback.
|
|
35082
35179
|
try {
|
|
35180
|
+
// The page being left is photographed already: it only has to stay out
|
|
35181
|
+
// of the second picture, not be taken down before it (see
|
|
35182
|
+
// keepLeavingPages in route.jsx).
|
|
35183
|
+
transition.releaseLeavingPages = keepLeavingPages();
|
|
35083
35184
|
// Releasing flushes the held render synchronously, so a route that
|
|
35084
35185
|
// rendered has already resolved the wait by the next line.
|
|
35085
35186
|
releaseRendering();
|
|
@@ -35132,6 +35233,11 @@ const beginTransition = ({
|
|
|
35132
35233
|
// must not strip what that one is wearing.
|
|
35133
35234
|
renderWait.stop();
|
|
35134
35235
|
releaseRendering();
|
|
35236
|
+
// Taken down once nobody sees it any more.
|
|
35237
|
+
if (transition.releaseLeavingPages) {
|
|
35238
|
+
transition.releaseLeavingPages();
|
|
35239
|
+
transition.releaseLeavingPages = null;
|
|
35240
|
+
}
|
|
35135
35241
|
// The hold a way back took, when it is still standing: the pictures were
|
|
35136
35242
|
// turned round a second time and played out, or something else took the
|
|
35137
35243
|
// document over mid-walk. A hold nobody gives back freezes the page.
|
|
@@ -45068,8 +45174,12 @@ const useInteractiveProps = (props, {
|
|
|
45068
45174
|
}
|
|
45069
45175
|
// Read by READONLY_CONSTRAINT to say how many runs are already out, and by
|
|
45070
45176
|
// the action handlers below, which are where a run joins and leaves the
|
|
45071
|
-
// count (a labelled block away, hence through the controller).
|
|
45072
|
-
|
|
45177
|
+
// count (a labelled block away, hence through the controller). None for a
|
|
45178
|
+
// control without an action of its own, by the same rule as being held
|
|
45179
|
+
// back: what it runs (the noop every such control has, a link's
|
|
45180
|
+
// navigation) is nothing the guard counts, and would hold a slot for its
|
|
45181
|
+
// length.
|
|
45182
|
+
uiStateController.parallelGuard = props.action ? parallelGuard : null;
|
|
45073
45183
|
if (heldByParallelGuard) {
|
|
45074
45184
|
controlHostProps["data-readonly-reason"] = "parallel-guard";
|
|
45075
45185
|
}
|
|
@@ -45344,7 +45454,11 @@ const useInteractiveProps = (props, {
|
|
|
45344
45454
|
// carried by every navi_action_* event of that run).
|
|
45345
45455
|
uiStateController.pendingActionEvent = e.detail.event;
|
|
45346
45456
|
uiStateController.actionInFlight = true;
|
|
45347
|
-
|
|
45457
|
+
// Given back to the guard it was counted in, whatever the control
|
|
45458
|
+
// holds by then: one that lost its action mid-run has no guard left
|
|
45459
|
+
// to name, and the slot would stay taken.
|
|
45460
|
+
const countedIn = uiStateController.parallelGuard;
|
|
45461
|
+
countedIn?.claim(uiStateController);
|
|
45348
45462
|
if (optimistic) {
|
|
45349
45463
|
syncOptimisticAttribute(true);
|
|
45350
45464
|
}
|
|
@@ -45362,7 +45476,7 @@ const useInteractiveProps = (props, {
|
|
|
45362
45476
|
e.detail.addSideEffect((outcome, outcomeEvent) => {
|
|
45363
45477
|
uiStateController.actionInFlight = false;
|
|
45364
45478
|
uiStateController.runningActionSignal.value = null;
|
|
45365
|
-
|
|
45479
|
+
countedIn?.release(uiStateController);
|
|
45366
45480
|
// The outcome callbacks are told from here, the run's side effect,
|
|
45367
45481
|
// and not from the onnavi_action_* handlers below: the run can
|
|
45368
45482
|
// outlive this control (its own answer, written to a store the
|
|
@@ -48895,6 +49009,90 @@ const BinderItemContext = createContext(null);
|
|
|
48895
49009
|
*/
|
|
48896
49010
|
const NavContext = createContext(null);
|
|
48897
49011
|
|
|
49012
|
+
/**
|
|
49013
|
+
* Whether the page one is on is `route` with `params` — what a link or a button
|
|
49014
|
+
* pointing at a route shows as current.
|
|
49015
|
+
*
|
|
49016
|
+
* Read through a computed of that one answer, so the component reading it
|
|
49017
|
+
* re-renders when the answer flips, and not whenever the route's status moves:
|
|
49018
|
+
* a page arriving on the route with other params — one profile opening, under a
|
|
49019
|
+
* list of links to a hundred other profiles — starts the route matching and
|
|
49020
|
+
* changes its params, and leaves every one of those links exactly as it was.
|
|
49021
|
+
*
|
|
49022
|
+
* `except`: routes whose matching vetoes the answer — a route named there is
|
|
49023
|
+
* somewhere else, whatever the rest of the reading says. `also`: routes whose
|
|
49024
|
+
* matching grants it. Both take a route or a list of routes.
|
|
49025
|
+
*/
|
|
49026
|
+
const useRouteCurrent = (route, params, { except, also } = {}) => {
|
|
49027
|
+
const exceptRoutes = toRouteList(except);
|
|
49028
|
+
const alsoRoutes = toRouteList(also);
|
|
49029
|
+
const paramsKey = getParamsCacheKey(params);
|
|
49030
|
+
const exceptKey = routeListKey(exceptRoutes);
|
|
49031
|
+
const alsoKey = routeListKey(alsoRoutes);
|
|
49032
|
+
const currentSignal = useMemo(() => {
|
|
49033
|
+
if (paramsKey === null) {
|
|
49034
|
+
return null;
|
|
49035
|
+
}
|
|
49036
|
+
return computed(() =>
|
|
49037
|
+
readRouteCurrent(route, params, exceptRoutes, alsoRoutes),
|
|
49038
|
+
);
|
|
49039
|
+
}, [route, paramsKey, exceptKey, alsoKey]);
|
|
49040
|
+
if (!currentSignal) {
|
|
49041
|
+
// Params no key can be made of (a date, an object): read on the spot, the
|
|
49042
|
+
// component then re-renders with the status of every route it reads.
|
|
49043
|
+
return readRouteCurrent(route, params, exceptRoutes, alsoRoutes);
|
|
49044
|
+
}
|
|
49045
|
+
return currentSignal.value;
|
|
49046
|
+
};
|
|
49047
|
+
|
|
49048
|
+
// Every signal is read even once the answer is known: reading is what
|
|
49049
|
+
// subscribes, and a route skipped today is the one whose change has to be heard
|
|
49050
|
+
// tomorrow.
|
|
49051
|
+
const readRouteCurrent = (route, params, exceptRoutes, alsoRoutes) => {
|
|
49052
|
+
const matching = route.matchingSignal.value;
|
|
49053
|
+
const paramsMatching = route.matchesParams(params);
|
|
49054
|
+
let someExceptMatching = false;
|
|
49055
|
+
for (const exceptRoute of exceptRoutes) {
|
|
49056
|
+
if (exceptRoute.matchingSignal.value) {
|
|
49057
|
+
someExceptMatching = true;
|
|
49058
|
+
}
|
|
49059
|
+
}
|
|
49060
|
+
let someAlsoMatching = false;
|
|
49061
|
+
for (const alsoRoute of alsoRoutes) {
|
|
49062
|
+
if (alsoRoute.matchingSignal.value) {
|
|
49063
|
+
someAlsoMatching = true;
|
|
49064
|
+
}
|
|
49065
|
+
}
|
|
49066
|
+
if (someExceptMatching) {
|
|
49067
|
+
return false;
|
|
49068
|
+
}
|
|
49069
|
+
return (matching && paramsMatching) || someAlsoMatching;
|
|
49070
|
+
};
|
|
49071
|
+
|
|
49072
|
+
const toRouteList = (routes) => {
|
|
49073
|
+
if (!routes) {
|
|
49074
|
+
return [];
|
|
49075
|
+
}
|
|
49076
|
+
return Array.isArray(routes) ? routes : [routes];
|
|
49077
|
+
};
|
|
49078
|
+
|
|
49079
|
+
// A list of routes as a key, since an inline `currentAlso={[A, B]}` is a new
|
|
49080
|
+
// array at every render of the parent.
|
|
49081
|
+
const routeIdByRoute = new WeakMap();
|
|
49082
|
+
let routeIdCount = 0;
|
|
49083
|
+
const routeListKey = (routes) => {
|
|
49084
|
+
let key = "";
|
|
49085
|
+
for (const route of routes) {
|
|
49086
|
+
let id = routeIdByRoute.get(route);
|
|
49087
|
+
if (id === undefined) {
|
|
49088
|
+
id = ++routeIdCount;
|
|
49089
|
+
routeIdByRoute.set(route, id);
|
|
49090
|
+
}
|
|
49091
|
+
key += `${id},`;
|
|
49092
|
+
}
|
|
49093
|
+
return key;
|
|
49094
|
+
};
|
|
49095
|
+
|
|
48898
49096
|
/*
|
|
48899
49097
|
* Custom hook to apply semi-transparent color when an element should be dimmed.
|
|
48900
49098
|
*
|
|
@@ -49444,15 +49642,17 @@ const LinkWithRoute = ({
|
|
|
49444
49642
|
assertRoute(route);
|
|
49445
49643
|
}
|
|
49446
49644
|
const url = route.buildUrl(routeParams);
|
|
49447
|
-
|
|
49448
|
-
|
|
49449
|
-
|
|
49450
|
-
|
|
49451
|
-
|
|
49452
|
-
|
|
49453
|
-
|
|
49454
|
-
|
|
49455
|
-
|
|
49645
|
+
{
|
|
49646
|
+
for (const amendingRoute of [currentExcept, currentAlso].flat()) {
|
|
49647
|
+
if (amendingRoute) {
|
|
49648
|
+
assertRoute(amendingRoute);
|
|
49649
|
+
}
|
|
49650
|
+
}
|
|
49651
|
+
}
|
|
49652
|
+
const linkMatching = useRouteCurrent(route, routeParams, {
|
|
49653
|
+
except: currentExcept,
|
|
49654
|
+
also: currentAlso
|
|
49655
|
+
});
|
|
49456
49656
|
const innerCurrent = current || linkMatching;
|
|
49457
49657
|
return jsx(Link, {
|
|
49458
49658
|
href: url,
|
|
@@ -49461,25 +49661,6 @@ const LinkWithRoute = ({
|
|
|
49461
49661
|
children: children || route.buildRelativeUrl(routeParams)
|
|
49462
49662
|
});
|
|
49463
49663
|
};
|
|
49464
|
-
// A route, or a list of them, read as one answer: does any of them match.
|
|
49465
|
-
// Reading matchingSignal during the render is what subscribes the component to
|
|
49466
|
-
// it, so every route is read even once one has answered yes — stopping early
|
|
49467
|
-
// would leave the component deaf to the ones it skipped.
|
|
49468
|
-
const useSomeRouteMatching = routes => {
|
|
49469
|
-
if (!routes) {
|
|
49470
|
-
return false;
|
|
49471
|
-
}
|
|
49472
|
-
let someMatching = false;
|
|
49473
|
-
for (const route of Array.isArray(routes) ? routes : [routes]) {
|
|
49474
|
-
{
|
|
49475
|
-
assertRoute(route);
|
|
49476
|
-
}
|
|
49477
|
-
if (route.matchingSignal.value) {
|
|
49478
|
-
someMatching = true;
|
|
49479
|
-
}
|
|
49480
|
-
}
|
|
49481
|
-
return someMatching;
|
|
49482
|
-
};
|
|
49483
49664
|
const LinkPlain = props => {
|
|
49484
49665
|
const defaultRef = useRef();
|
|
49485
49666
|
props.ref = props.ref || defaultRef;
|
|
@@ -54566,11 +54747,7 @@ const ButtonWithRoute = props => {
|
|
|
54566
54747
|
assertRoute(route);
|
|
54567
54748
|
}
|
|
54568
54749
|
const url = route.buildUrl(routeParams);
|
|
54569
|
-
const
|
|
54570
|
-
matching
|
|
54571
|
-
} = useRouteStatus(route);
|
|
54572
|
-
const paramsAreMatching = route.matchesParams(routeParams);
|
|
54573
|
-
const linkMatching = matching && paramsAreMatching;
|
|
54750
|
+
const linkMatching = useRouteCurrent(route, routeParams);
|
|
54574
54751
|
|
|
54575
54752
|
// Merged into whatever the caller already holds: a button can be forced into
|
|
54576
54753
|
// a state for a demo and still learn its own current-ness from its route.
|
|
@@ -66003,10 +66180,24 @@ const useDialogProps = props => {
|
|
|
66003
66180
|
// whichever show call ran, not just when a stray authored display
|
|
66004
66181
|
// property is actually present — harmless the rest of the time.
|
|
66005
66182
|
dialogEl.setAttribute("navi-hidden", "");
|
|
66006
|
-
|
|
66007
|
-
|
|
66183
|
+
const hideNatively = () => {
|
|
66184
|
+
if (isTopLayerPopover) {
|
|
66185
|
+
dialogEl.hidePopover();
|
|
66186
|
+
} else {
|
|
66187
|
+
dialogEl.close();
|
|
66188
|
+
}
|
|
66189
|
+
};
|
|
66190
|
+
if (findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
66191
|
+
// Leaving the tree: hidden natively once the commit is over. Its
|
|
66192
|
+
// removal takes it out of the top layer and hands the focus to
|
|
66193
|
+
// nobody, where closing it now hands the focus to whatever held it
|
|
66194
|
+
// before the opening — often a trigger leaving in the same commit
|
|
66195
|
+
// (see giveFocusBackAfterCommit). What is left to close then is its
|
|
66196
|
+
// state: a <dialog> removed while open keeps its `open` attribute, and
|
|
66197
|
+
// a subtree <Suspense> parks and puts back would show it again.
|
|
66198
|
+
queueMicrotask(hideNatively);
|
|
66008
66199
|
} else {
|
|
66009
|
-
|
|
66200
|
+
hideNatively();
|
|
66010
66201
|
}
|
|
66011
66202
|
// Held at the size it has right now, for the whole way out. cleanup()
|
|
66012
66203
|
// below already stops the JS repositioning, but the size is CSS-driven
|
|
@@ -67711,7 +67902,13 @@ const usePopoverProps = props => {
|
|
|
67711
67902
|
// reliably sufficient once a consumer's own CSS also sets display.
|
|
67712
67903
|
popoverEl.setAttribute("navi-hidden", "");
|
|
67713
67904
|
if (isTopLayer) {
|
|
67714
|
-
|
|
67905
|
+
// Left to its removal when the popover leaves the tree: that hides it
|
|
67906
|
+
// and hands the focus to nobody, where hidePopover() hands the focus
|
|
67907
|
+
// to whatever held it before the opening — often a trigger leaving in
|
|
67908
|
+
// the same commit (see giveFocusBackAfterCommit).
|
|
67909
|
+
if (!findEvent(closeEvent, UNMOUNT_EVENT_TYPE)) {
|
|
67910
|
+
popoverEl.hidePopover();
|
|
67911
|
+
}
|
|
67715
67912
|
} else {
|
|
67716
67913
|
openLocalPopoverCount = Math.max(0, openLocalPopoverCount - 1);
|
|
67717
67914
|
}
|
|
@@ -92004,8 +92201,7 @@ const createSlot = (SlotRenderer = Box) => {
|
|
|
92004
92201
|
};
|
|
92005
92202
|
const SlotFill = props => {
|
|
92006
92203
|
const fillerRef = useRef();
|
|
92007
|
-
|
|
92008
|
-
slotPropsSignal.value = props;
|
|
92204
|
+
const routePage = useContext(RoutePageContext);
|
|
92009
92205
|
useLayoutEffect(() => {
|
|
92010
92206
|
return () => {
|
|
92011
92207
|
// Within one diff preact mounts the newcomer before unmounting the
|
|
@@ -92018,6 +92214,26 @@ const createSlot = (SlotRenderer = Box) => {
|
|
|
92018
92214
|
slotPropsSignal.value = null;
|
|
92019
92215
|
};
|
|
92020
92216
|
}, []);
|
|
92217
|
+
// In a page kept while leaving: out of the slot as if unmounted, since
|
|
92218
|
+
// the page arriving is what the slot must show — or nothing, when it
|
|
92219
|
+
// fills none.
|
|
92220
|
+
if (routePage && routePage.leavingSignal.value) {
|
|
92221
|
+
if (filler === fillerRef) {
|
|
92222
|
+
filler = null;
|
|
92223
|
+
slotPropsSignal.value = null;
|
|
92224
|
+
}
|
|
92225
|
+
return null;
|
|
92226
|
+
}
|
|
92227
|
+
if (!fillerRef.current) {
|
|
92228
|
+
fillerRef.current = true;
|
|
92229
|
+
filler = fillerRef;
|
|
92230
|
+
}
|
|
92231
|
+
if (filler === null) {
|
|
92232
|
+
filler = fillerRef;
|
|
92233
|
+
}
|
|
92234
|
+
if (filler === fillerRef) {
|
|
92235
|
+
slotPropsSignal.value = props;
|
|
92236
|
+
}
|
|
92021
92237
|
return null;
|
|
92022
92238
|
};
|
|
92023
92239
|
return [Slot, SlotFill];
|