@jsenv/navi 0.29.413 → 0.29.414

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.
@@ -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$17 = /* css */ `@layer navi {
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$17, "@jsenv/navi/src/navi_z_indexes.js"];
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$16 = /* css */ `@layer navi {
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$16, "@jsenv/navi/src/nav/url_target/url_target.js"];
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$15 = /* css */`.action_error {
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$15, "@jsenv/navi/src/action/action_renderer.jsx"];
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
  }
@@ -15336,7 +15336,7 @@ installImportMetaCssBuild(import.meta);
15336
15336
  * (see calloutTemplate in callout.js).
15337
15337
  */
15338
15338
 
15339
- const css$14 = /* css */`.navi_callout_status_icon {
15339
+ const css$15 = /* css */`.navi_callout_status_icon {
15340
15340
  --x-callout-status-icon-color: var(--navi-callout-neutral-color);
15341
15341
  box-sizing: border-box;
15342
15342
  aspect-ratio: 1;
@@ -15407,7 +15407,7 @@ const CalloutStatusIcon = ({
15407
15407
  status = "info",
15408
15408
  shape = "square"
15409
15409
  }) => {
15410
- import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
15410
+ import.meta.css = [css$15, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
15411
15411
  return jsx("span", {
15412
15412
  className: "navi_callout_status_icon",
15413
15413
  "data-status": status === "none" ? undefined : status,
@@ -15434,7 +15434,7 @@ let calloutCount = 0;
15434
15434
  // large enough to survive the pixel or two a trackpad rests on.
15435
15435
  const CLOSE_BY_SCROLL_DISTANCE = 10;
15436
15436
 
15437
- const css$13 = /* css */ `
15437
+ const css$14 = /* css */ `
15438
15438
  /* jsenv-css-opaque: same as dialog.jsx, cascade order. */
15439
15439
  @layer navi {
15440
15440
  .navi_callout {
@@ -15795,7 +15795,7 @@ const openCallout = (
15795
15795
  debug = () => {},
15796
15796
  } = {},
15797
15797
  ) => {
15798
- import.meta.css = [css$13, "@jsenv/navi/src/control/rules/callout/callout.js"];
15798
+ import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout.js"];
15799
15799
  if (debug === true) {
15800
15800
  debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
15801
15801
  }
@@ -20787,7 +20787,9 @@ const swipeTypeOf = (axis, pulled) => {
20787
20787
  * meantime, nothing is picked up. The carry is the element's own, outcomes and
20788
20788
  * refusal included, without the wait for intent (the hold was the proof), and
20789
20789
  * the copy is brought to the finger rather than following it from wherever the
20790
- * element stands. A copy or nothing: `move` and `moving` carry the thing itself,
20790
+ * element stands. Let go of there before it has travelled — the hold only meant
20791
+ * to open the popup — it goes back and nothing is answered, whatever it was
20792
+ * brought over. A copy or nothing: `move` and `moving` carry the thing itself,
20791
20793
  * which would leave its place just for being handed a press. When it starts is
20792
20794
  * navi's — after a popup still being lifted (see `--navi-grab` in commands.js).
20793
20795
  *
@@ -30886,6 +30888,12 @@ const useUrlSearchParam = (paramName, defaultValue) => {
30886
30888
  return [value, setSearchParamValue];
30887
30889
  };
30888
30890
 
30891
+ // The title belongs to the last Head to arrive, whatever order they leave in:
30892
+ // the page being left by a route transition is taken down AFTER the page
30893
+ // arriving is up (see keepLeavingPages in route.jsx), and putting back the
30894
+ // title it found would put its own page's title over the new one.
30895
+ const titleEntries = [];
30896
+ let titleWithoutHead = null;
30889
30897
  const Head = ({
30890
30898
  children
30891
30899
  }) => {
@@ -30894,15 +30902,17 @@ const Head = ({
30894
30902
  return undefined;
30895
30903
  }
30896
30904
  const childArray = Array.isArray(children) ? children : [children];
30897
- const previousTitle = document.title;
30898
30905
  const appendedElements = [];
30906
+ let titleEntry = null;
30899
30907
  for (const child of childArray) {
30900
30908
  if (!child) {
30901
30909
  continue;
30902
30910
  }
30903
30911
  if (child.type === "title") {
30904
30912
  const titleChildren = child.props.children;
30905
- document.title = Array.isArray(titleChildren) ? titleChildren.join("") : titleChildren ?? "";
30913
+ titleEntry = {
30914
+ title: Array.isArray(titleChildren) ? titleChildren.join("") : titleChildren ?? ""
30915
+ };
30906
30916
  continue;
30907
30917
  }
30908
30918
  const el = document.createElement(child.type);
@@ -30913,8 +30923,19 @@ const Head = ({
30913
30923
  document.head.appendChild(el);
30914
30924
  appendedElements.push(el);
30915
30925
  }
30926
+ if (titleEntry) {
30927
+ if (titleEntries.length === 0) {
30928
+ titleWithoutHead = document.title;
30929
+ }
30930
+ titleEntries.push(titleEntry);
30931
+ document.title = titleEntry.title;
30932
+ }
30916
30933
  return () => {
30917
- document.title = previousTitle;
30934
+ if (titleEntry) {
30935
+ titleEntries.splice(titleEntries.indexOf(titleEntry), 1);
30936
+ const lastEntry = titleEntries[titleEntries.length - 1];
30937
+ document.title = lastEntry ? lastEntry.title : titleWithoutHead;
30938
+ }
30918
30939
  for (const el of appendedElements) {
30919
30940
  el.remove();
30920
30941
  }
@@ -31077,75 +31098,7 @@ const samePages = (pages, otherPages) => {
31077
31098
  return true;
31078
31099
  };
31079
31100
 
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
-
31101
+ installImportMetaCssBuild(import.meta);
31149
31102
 
31150
31103
  /**
31151
31104
  * Keep every container showing the page it is showing, whatever the routes say.
@@ -31169,6 +31122,56 @@ const freezeRouteRender = () => {
31169
31122
  routeRenderFrozenSignal.value++;
31170
31123
  };
31171
31124
  };
31125
+
31126
+ /**
31127
+ * Keep the page a container leaves, hidden, until its route transition is over.
31128
+ *
31129
+ * The browser photographs the page being left before the change, and the page
31130
+ * arriving inside the transition's update callback — where everything done
31131
+ * delays the movement. Taking the page being left down is most of that: every
31132
+ * hook of every component in it is cleaned up, and a page of thirty cards
31133
+ * takes longer to take down than the page arriving takes to build. Its picture
31134
+ * is already taken, so it only has to stay out of the second one: a container
31135
+ * that changes page while this is held keeps the page it was showing, hidden,
31136
+ * and drops it once the movement is over (see route_transition.jsx).
31137
+ *
31138
+ * Kept only where Preact would have unmounted it: a page rendered by the same
31139
+ * element on both sides is the same component instance carried over, exactly
31140
+ * as without a movement.
31141
+ */
31142
+ let leavingPagesHold = null;
31143
+ const keepLeavingPages = () => {
31144
+ const hold = {
31145
+ released: false,
31146
+ containers: new Set()
31147
+ };
31148
+ leavingPagesHold = hold;
31149
+ return () => {
31150
+ if (hold.released) {
31151
+ return;
31152
+ }
31153
+ hold.released = true;
31154
+ if (leavingPagesHold === hold) {
31155
+ leavingPagesHold = null;
31156
+ }
31157
+ for (const rerender of hold.containers) {
31158
+ rerender();
31159
+ }
31160
+ hold.containers.clear();
31161
+ };
31162
+ };
31163
+
31164
+ // What a page can learn about its place from the container rendering it:
31165
+ // whether it is a page kept while leaving (see keepLeavingPages). Such a page
31166
+ // is still mounted, and whatever it fills OUTSIDE its own nodes — a slot in a
31167
+ // bar, say — would otherwise go on showing with the page arriving (see
31168
+ // layout/slot.jsx).
31169
+ const RoutePageContext = createContext(null);
31170
+ const LEAVING_ATTRIBUTE = "data-navi-route-leaving";
31171
+ const css$13 = /* css */`[data-navi-route-leaving] {
31172
+ display: none !important;
31173
+ }
31174
+ `;
31172
31175
  const debug$1 = (...args) => {
31173
31176
  {
31174
31177
  return;
@@ -31288,6 +31291,7 @@ const RouteContainer = ({
31288
31291
  shownBranchRef.current = activeBranch;
31289
31292
  }
31290
31293
  const branch = shownBranchRef.current || activeBranch;
31294
+ const content = useContentKeepingLeavingPages(branch);
31291
31295
 
31292
31296
  // The two things this component knows that nobody outside can find out: the
31293
31297
  // pages a named fallback is the absence of — the children it was written
@@ -31309,7 +31313,6 @@ const RouteContainer = ({
31309
31313
  };
31310
31314
  }, [fallbackPage]);
31311
31315
  debug$1(`[container "${id}"] RENDER, active=${branch ? branch.type : "none"}`);
31312
- const content = branch ? branch.node : null;
31313
31316
  if (!content) {
31314
31317
  return null;
31315
31318
  }
@@ -31318,12 +31321,141 @@ const RouteContainer = ({
31318
31321
  }
31319
31322
  return content;
31320
31323
  };
31324
+ // What a container renders: the page it shows and, while a route transition
31325
+ // keeps them (see keepLeavingPages), the pages it has left — each the very
31326
+ // vnode rendered last time, which Preact does not look into, between two
31327
+ // markers that say which nodes to hide. Always an array, even of one page: the
31328
+ // page being left must stay the child it was when a page is added beside it,
31329
+ // or Preact takes it down and builds it again.
31330
+ let leavingKeyCount = 0;
31331
+ const useContentKeepingLeavingPages = branch => {
31332
+ const [, rerender] = useReducer(countRenders, 0);
31333
+ const shownRef = useRef(null);
31334
+ const leavingRef = useRef([]);
31335
+ const shown = shownRef.current;
31336
+ const element = branchElement(branch);
31337
+ let key;
31338
+ let page = null;
31339
+ if (shown && branch && (shown.id === branch.id || shown.element === element)) {
31340
+ // The same page, or one Preact carries over from it: the key it was
31341
+ // mounted with, or it would be mounted again.
31342
+ key = shown.key;
31343
+ page = shown.page;
31344
+ } else if (shown && leavingPagesHold) {
31345
+ leavingRef.current.push({
31346
+ vnode: shown.vnode,
31347
+ page: shown.page,
31348
+ key: `navi-route-leaving-${++leavingKeyCount}`,
31349
+ hold: leavingPagesHold,
31350
+ startRef: createRef(),
31351
+ endRef: createRef(),
31352
+ hidden: false
31353
+ });
31354
+ leavingPagesHold.containers.add(rerender);
31355
+ // Keyed apart from the page it takes the place of, which is still there.
31356
+ key = `navi-route-${++leavingKeyCount}`;
31357
+ }
31358
+ let vnode = null;
31359
+ if (branch) {
31360
+ if (!page) {
31361
+ page = {
31362
+ leavingSignal: signal(false)
31363
+ };
31364
+ }
31365
+ vnode = h(RoutePageContext.Provider, {
31366
+ key,
31367
+ value: page
31368
+ }, branch.node);
31369
+ }
31370
+ shownRef.current = branch ? {
31371
+ id: branch.id,
31372
+ element,
31373
+ key,
31374
+ vnode,
31375
+ page
31376
+ } : null;
31377
+ const leaving = leavingRef.current.filter(entry => !entry.hold.released);
31378
+ leavingRef.current = leaving;
31379
+
31380
+ // Hidden as soon as it is in the DOM next to the page arriving: this runs in
31381
+ // the update callback's render, before the second picture is taken.
31382
+ useLayoutEffect(() => {
31383
+ for (const entry of leavingRef.current) {
31384
+ const start = entry.startRef.current;
31385
+ const end = entry.endRef.current;
31386
+ if (entry.hidden || !start || !end) {
31387
+ continue;
31388
+ }
31389
+ entry.hidden = true;
31390
+ entry.page.leavingSignal.value = true;
31391
+ let node = start.nextSibling;
31392
+ while (node && node !== end) {
31393
+ if (node.nodeType === Node.ELEMENT_NODE) {
31394
+ node.setAttribute(LEAVING_ATTRIBUTE, "");
31395
+ }
31396
+ node = node.nextSibling;
31397
+ }
31398
+ }
31399
+ });
31400
+ useLayoutEffect(() => {
31401
+ return () => {
31402
+ for (const entry of leavingRef.current) {
31403
+ entry.hold.containers.delete(rerender);
31404
+ }
31405
+ };
31406
+ }, []);
31407
+ if (leaving.length === 0) {
31408
+ return vnode ? [vnode] : null;
31409
+ }
31410
+ import.meta.css = [css$13, "@jsenv/navi/src/nav/route.jsx"];
31411
+ const children = vnode ? [vnode] : [];
31412
+ for (const entry of leaving) {
31413
+ children.push(h("template", {
31414
+ key: `${entry.key}-start`,
31415
+ ref: entry.startRef
31416
+ }), entry.vnode, h("template", {
31417
+ key: `${entry.key}-end`,
31418
+ ref: entry.endRef
31419
+ }));
31420
+ }
31421
+ return children;
31422
+ };
31423
+ const countRenders = count => count + 1;
31424
+ // What Preact compares when a container changes page, below the <Route> it
31425
+ // renders either way: the element the page is rendered with — a layout, for a
31426
+ // container branch.
31427
+ const branchElement = branch => {
31428
+ if (!branch) {
31429
+ return null;
31430
+ }
31431
+ let node = branch.node;
31432
+ while (node && node.type !== Route) {
31433
+ // A wrapper around the branch (see wrapBranch).
31434
+ node = node.props.children;
31435
+ }
31436
+ if (!node) {
31437
+ return null;
31438
+ }
31439
+ const {
31440
+ element
31441
+ } = node.props;
31442
+ if (element === undefined || typeof element === "function") {
31443
+ return element;
31444
+ }
31445
+ return element.type;
31446
+ };
31447
+
31321
31448
  // Walk JSX children vnodes (without rendering) to build a branch list and
31322
31449
  // find the active one in the same pass. Anything that is not a <Route> is read
31323
31450
  // through and kept around the branch it holds (see below).
31324
31451
  // Returns { matchingBranch, fallbackBranch, activeBranch, pages }.
31325
- const collectBranches = children => {
31452
+ //
31453
+ // Each branch is given an id from where it is written among the children,
31454
+ // which is what tells a container it changed page: the vnodes themselves are
31455
+ // new at every render of the parent.
31456
+ const collectBranches = (children, path = "") => {
31326
31457
  let matchingBranch = null;
31458
+ let childIndex = 0;
31327
31459
  let fallbackBranch = null;
31328
31460
  // Every page a branch can be on, at any depth: what a named fallback is the
31329
31461
  // ABSENCE of (see route_fallback.js). A container with a guard route counts
@@ -31341,6 +31473,7 @@ const collectBranches = children => {
31341
31473
  }
31342
31474
  return;
31343
31475
  }
31476
+ const id = `${path}${childIndex++}`;
31344
31477
  if (child.type !== Route) {
31345
31478
  // Anything else is a wrapper around branches, and the two that matter are
31346
31479
  // navi's own: a page says what it renders and delegates what it cannot —
@@ -31360,7 +31493,7 @@ const collectBranches = children => {
31360
31493
  matchingBranch: matchingInside,
31361
31494
  fallbackBranch: fallbackInside,
31362
31495
  pages: pagesInside
31363
- } = collectBranches(wrapperChildren);
31496
+ } = collectBranches(wrapperChildren, `${id}.`);
31364
31497
  for (const pageInside of pagesInside) {
31365
31498
  pages.push(pageInside);
31366
31499
  }
@@ -31383,7 +31516,7 @@ const collectBranches = children => {
31383
31516
  matchingBranch: matchingChild,
31384
31517
  fallbackBranch: fallbackChild,
31385
31518
  pages: pagesChild
31386
- } = collectBranches(nodeChildren);
31519
+ } = collectBranches(nodeChildren, `${id}.`);
31387
31520
  if (fallbackChild && !route) {
31388
31521
  // A fallback is the branch taken for an address none of its siblings
31389
31522
  // claim — so something has to make that address BELONG to this
@@ -31407,6 +31540,7 @@ const collectBranches = children => {
31407
31540
  }
31408
31541
  const branch = {
31409
31542
  type: "container",
31543
+ id,
31410
31544
  node: child
31411
31545
  };
31412
31546
  const guardMatching = route ? route.matchingSignal.value : false;
@@ -31424,6 +31558,7 @@ const collectBranches = children => {
31424
31558
  if (!fallbackBranch) {
31425
31559
  fallbackBranch = {
31426
31560
  type: "fallback",
31561
+ id,
31427
31562
  node: child,
31428
31563
  page: fallback.isRouteFallback ? fallback : null
31429
31564
  };
@@ -31431,6 +31566,7 @@ const collectBranches = children => {
31431
31566
  } else {
31432
31567
  const branch = {
31433
31568
  type: "leaf",
31569
+ id,
31434
31570
  node: child
31435
31571
  };
31436
31572
  pages.push({
@@ -31878,7 +32014,7 @@ const holdTransitionFurniture = (owner, areaElement) => {
31878
32014
  }
31879
32015
  const namesLeaving = [];
31880
32016
  for (const element of namedElements) {
31881
- if (!element.isConnected) {
32017
+ if (!isRendered(element)) {
31882
32018
  namesLeaving.push(nameByElement.get(element));
31883
32019
  } else if (element.matches(FIXED_BAR_SELECTOR$1)) {
31884
32020
  element.setAttribute(FURNITURE_ATTRIBUTE, "shared");
@@ -31997,11 +32133,16 @@ const travelRule = (side, names, movementProperty) => {
31997
32133
  * The pictures are up and about to move: what no picture covers heads to the
31998
32134
  * state arriving, on the same clock.
31999
32135
  */
32000
- const startTransitionFurniture = (owner) => {
32136
+ const startTransitionFurniture = (owner, { duration } = {}) => {
32001
32137
  if (owner !== furnitureOwner) {
32002
32138
  return;
32003
32139
  }
32004
32140
  for (const restWall of restWallElements) {
32141
+ // A duration of the movement's own is written for its pictures alone (see
32142
+ // transition_values.js); the stand-in is an element, and is told.
32143
+ if (duration !== undefined) {
32144
+ restWall.style.setProperty("--navi-route-transition-duration", duration);
32145
+ }
32005
32146
  restWall.style.opacity = restWallOpacity;
32006
32147
  }
32007
32148
  };
@@ -32032,7 +32173,9 @@ const releaseTransitionFurniture = (owner) => {
32032
32173
  // area's stand-in answers for it.
32033
32174
  const paintTransitionWalls = (areaElement) => {
32034
32175
  removeTransitionWalls();
32035
- const sources = document.querySelectorAll(TOP_LAYER_WALL_SELECTOR);
32176
+ const sources = Array.from(
32177
+ document.querySelectorAll(TOP_LAYER_WALL_SELECTOR),
32178
+ ).filter(isRendered);
32036
32179
  if (sources.length === 0) {
32037
32180
  return sources;
32038
32181
  }
@@ -32234,6 +32377,14 @@ const subtractRect = (rect, hole) => {
32234
32377
  return pieces;
32235
32378
  };
32236
32379
 
32380
+ // Out of the state arriving: taken out of the document, or in a page kept
32381
+ // hidden until its movement is over (see keepLeavingPages in route.jsx) —
32382
+ // where a dialog is still :modal and a popover still :popover-open, and
32383
+ // neither is painted or photographed.
32384
+ const isRendered = (element) => {
32385
+ return element.isConnected && element.checkVisibility();
32386
+ };
32387
+
32237
32388
  const removeRestWalls = () => {
32238
32389
  for (const restWall of restWallElements) {
32239
32390
  restWall.remove();
@@ -32243,6 +32394,52 @@ const removeRestWalls = () => {
32243
32394
  leavingRects = new Map();
32244
32395
  };
32245
32396
 
32397
+ /**
32398
+ * The values a movement hands to its pictures, written where only the pictures
32399
+ * read them: on ::view-transition, the root of the pseudo-element tree, rather
32400
+ * than on the document's root.
32401
+ *
32402
+ * A custom property written on :root is inherited by every element of the
32403
+ * document, and writing one restyles all of them. A movement writes its
32404
+ * numbers as its pages are held, again whenever a scroll moves the window, and
32405
+ * takes them off as it ends — each time a whole-document restyle of a page that
32406
+ * can be thousands of elements long. On ::view-transition they reach every
32407
+ * picture by plain inheritance, and no element at all.
32408
+ *
32409
+ * An inline style cannot target a pseudo-element, so they are declarations of
32410
+ * one rule, in a sheet of their own. Written only: what a pseudo-element that
32411
+ * is not on screen computes is not reliably readable, so nothing reads them
32412
+ * back from there.
32413
+ */
32414
+
32415
+ let valuesStyle = null;
32416
+ const getValuesStyle = () => {
32417
+ if (valuesStyle) {
32418
+ return valuesStyle;
32419
+ }
32420
+ const sheet = new CSSStyleSheet();
32421
+ sheet.replaceSync(":root::view-transition {}");
32422
+ const [rule] = sheet.cssRules;
32423
+ if (rule) {
32424
+ document.adoptedStyleSheets.push(sheet);
32425
+ valuesStyle = rule.style;
32426
+ } else {
32427
+ // A browser without view transitions drops the rule: the movement runs its
32428
+ // steps all the same (see start_view_transition_polyfill.js), with no
32429
+ // pictures to hand anything to.
32430
+ valuesStyle = document.createElement("div").style;
32431
+ }
32432
+ return valuesStyle;
32433
+ };
32434
+
32435
+ const setTransitionValue = (name, value) => {
32436
+ getValuesStyle().setProperty(name, value);
32437
+ };
32438
+
32439
+ const removeTransitionValue = (name) => {
32440
+ getValuesStyle().removeProperty(name);
32441
+ };
32442
+
32246
32443
  installImportMetaCssBuild(import.meta);
32247
32444
 
32248
32445
  const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
@@ -32277,96 +32474,6 @@ const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
32277
32474
  --navi-transition-cover-left: 0px;
32278
32475
  }
32279
32476
  }
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
32477
  `;
32371
32478
 
32372
32479
  // Called by whatever needs the window — a render, a movement declared or
@@ -32464,22 +32571,21 @@ const holdTransitionWindow = (
32464
32571
  const right =
32465
32572
  rectBefore.right > rectAfter.right ? rectBefore.right : rectAfter.right;
32466
32573
  windowOwner = owner;
32467
- const { style } = document.documentElement;
32468
- style.setProperty(WINDOW_TOP_PROPERTY, `${top}px`);
32469
- style.setProperty(WINDOW_LEFT_PROPERTY, `${left}px`);
32470
- style.setProperty(WINDOW_WIDTH_PROPERTY, `${right - left}px`);
32471
- style.setProperty(WINDOW_HEIGHT_PROPERTY, `${bottom - top}px`);
32472
- style.setProperty(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
32473
- style.setProperty(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
32474
- style.setProperty(WINDOW_NEW_TOP_PROPERTY, `${rectAfter.top}px`);
32475
- style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
32574
+ setTransitionValue(WINDOW_TOP_PROPERTY, `${top}px`);
32575
+ setTransitionValue(WINDOW_LEFT_PROPERTY, `${left}px`);
32576
+ setTransitionValue(WINDOW_WIDTH_PROPERTY, `${right - left}px`);
32577
+ setTransitionValue(WINDOW_HEIGHT_PROPERTY, `${bottom - top}px`);
32578
+ setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
32579
+ setTransitionValue(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
32580
+ setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${rectAfter.top}px`);
32581
+ setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
32476
32582
  const oldBand = furnitureLive
32477
32583
  ? bandTheCutMayBeRelaxedTo(bandBefore, bandAfter, rectAfter)
32478
32584
  : bandBefore;
32479
- style.setProperty(OLD_BAND_TOP_PROPERTY, `${oldBand.top}px`);
32480
- style.setProperty(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
32481
- style.setProperty(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
32482
- style.setProperty(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
32585
+ setTransitionValue(OLD_BAND_TOP_PROPERTY, `${oldBand.top}px`);
32586
+ setTransitionValue(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
32587
+ setTransitionValue(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
32588
+ setTransitionValue(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
32483
32589
  followArrivingState(element, rectAfter, {
32484
32590
  outerScroll,
32485
32591
  windowTop: top,
@@ -32501,9 +32607,8 @@ const releaseTransitionWindow = (owner) => {
32501
32607
  if (unfollowArrivingState) {
32502
32608
  unfollowArrivingState();
32503
32609
  }
32504
- const { style } = document.documentElement;
32505
32610
  for (const property of WINDOW_PROPERTIES) {
32506
- style.removeProperty(property);
32611
+ removeTransitionValue(property);
32507
32612
  }
32508
32613
  };
32509
32614
 
@@ -32512,12 +32617,11 @@ const releaseTransitionWindow = (owner) => {
32512
32617
  // file) — and with it the offset outside the box, which carries the window
32513
32618
  // and the corner of the state being left along. Requested, never awaited: the
32514
32619
  // first call runs inside the update callback, where a frame cannot come.
32515
- // Written only when it moved: every write restyles the root and the pictures.
32620
+ // Written only when it moved: every write restyles the pictures.
32516
32621
  const followArrivingState = (element, rectAtHold, heldAt) => {
32517
32622
  if (unfollowArrivingState) {
32518
32623
  unfollowArrivingState();
32519
32624
  }
32520
- const { style } = document.documentElement;
32521
32625
  let top = rectAtHold.top;
32522
32626
  let left = rectAtHold.left;
32523
32627
  let outerTop = heldAt.outerScroll.top;
@@ -32526,24 +32630,27 @@ const followArrivingState = (element, rectAtHold, heldAt) => {
32526
32630
  const rect = element.getBoundingClientRect();
32527
32631
  if (rect.top !== top) {
32528
32632
  top = rect.top;
32529
- style.setProperty(WINDOW_NEW_TOP_PROPERTY, `${top}px`);
32633
+ setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${top}px`);
32530
32634
  }
32531
32635
  if (rect.left !== left) {
32532
32636
  left = rect.left;
32533
- style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${left}px`);
32637
+ setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${left}px`);
32534
32638
  }
32535
32639
  const outerScroll = readOuterScroll(element);
32536
32640
  if (outerScroll.top !== outerTop) {
32537
32641
  outerTop = outerScroll.top;
32538
32642
  const shift = heldAt.outerScroll.top - outerTop;
32539
- style.setProperty(WINDOW_TOP_PROPERTY, `${heldAt.windowTop + shift}px`);
32540
- style.setProperty(WINDOW_OLD_TOP_PROPERTY, `${heldAt.oldTop + shift}px`);
32643
+ setTransitionValue(WINDOW_TOP_PROPERTY, `${heldAt.windowTop + shift}px`);
32644
+ setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${heldAt.oldTop + shift}px`);
32541
32645
  }
32542
32646
  if (outerScroll.left !== outerLeft) {
32543
32647
  outerLeft = outerScroll.left;
32544
32648
  const shift = heldAt.outerScroll.left - outerLeft;
32545
- style.setProperty(WINDOW_LEFT_PROPERTY, `${heldAt.windowLeft + shift}px`);
32546
- style.setProperty(
32649
+ setTransitionValue(
32650
+ WINDOW_LEFT_PROPERTY,
32651
+ `${heldAt.windowLeft + shift}px`,
32652
+ );
32653
+ setTransitionValue(
32547
32654
  WINDOW_OLD_LEFT_PROPERTY,
32548
32655
  `${heldAt.oldLeft + shift}px`,
32549
32656
  );
@@ -32906,58 +33013,9 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
32906
33013
  inherits: false
32907
33014
  }
32908
33015
 
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
33016
  :root::view-transition, :root::view-transition-group(*), :root::view-transition-image-pair(*), :root::view-transition-old(*), :root::view-transition-new(*) {
32940
33017
  --navi-route-transition-leave: inherit;
32941
33018
  --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
33019
  }
32962
33020
 
32963
33021
  :root[data-navi-route-transition] [data-navi-route-transition-area] {
@@ -32978,28 +33036,32 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
32978
33036
 
32979
33037
  &[data-navi-route-transition-target="area"] {
32980
33038
  view-transition-name: none;
32981
- --navi-route-transition-clip-top: max(0px,
32982
- min(var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
32983
- var(--navi-transition-old-band-top)) - var(--navi-transition-window-top));
32984
- --navi-route-transition-clip-left: max(0px,
32985
- min(var(--navi-safe-area-inset-left) + var(--navi-transition-cover-left),
32986
- var(--navi-transition-old-band-left)) - var(--navi-transition-window-left));
32987
- --navi-route-transition-clip-bottom: max(0px,
32988
- var(--navi-transition-window-top) +
32989
- var(--navi-transition-window-height) +
32990
- min(var(--navi-safe-area-inset-bottom) +
32991
- var(--navi-transition-cover-bottom),
32992
- var(--navi-transition-old-band-bottom)) -
32993
- 100dvh);
32994
- --navi-route-transition-clip-right: max(0px,
32995
- var(--navi-transition-window-left) +
32996
- var(--navi-transition-window-width) +
32997
- min(var(--navi-safe-area-inset-right) +
32998
- var(--navi-transition-cover-right),
32999
- var(--navi-transition-old-band-right)) -
33000
- 100dvw);
33001
- --navi-route-transition-travel-x: calc(var(--navi-transition-window-width) - var(--navi-route-transition-clip-left) - var(--navi-route-transition-clip-right));
33002
- --navi-route-transition-travel-y: calc(var(--navi-transition-window-height) - var(--navi-route-transition-clip-top) - var(--navi-route-transition-clip-bottom));
33039
+
33040
+ &::view-transition {
33041
+ --navi-route-transition-clip-top: max(0px,
33042
+ min(var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
33043
+ var(--navi-transition-old-band-top)) - var(--navi-transition-window-top));
33044
+ --navi-route-transition-clip-left: max(0px,
33045
+ min(var(--navi-safe-area-inset-left) +
33046
+ var(--navi-transition-cover-left),
33047
+ var(--navi-transition-old-band-left)) - var(--navi-transition-window-left));
33048
+ --navi-route-transition-clip-bottom: max(0px,
33049
+ var(--navi-transition-window-top) +
33050
+ var(--navi-transition-window-height) +
33051
+ min(var(--navi-safe-area-inset-bottom) +
33052
+ var(--navi-transition-cover-bottom),
33053
+ var(--navi-transition-old-band-bottom)) -
33054
+ 100dvh);
33055
+ --navi-route-transition-clip-right: max(0px,
33056
+ var(--navi-transition-window-left) +
33057
+ var(--navi-transition-window-width) +
33058
+ min(var(--navi-safe-area-inset-right) +
33059
+ var(--navi-transition-cover-right),
33060
+ var(--navi-transition-old-band-right)) -
33061
+ 100dvw);
33062
+ --navi-route-transition-travel-x: calc(var(--navi-transition-window-width) - var(--navi-route-transition-clip-left) - var(--navi-route-transition-clip-right));
33063
+ --navi-route-transition-travel-y: calc(var(--navi-transition-window-height) - var(--navi-route-transition-clip-top) - var(--navi-route-transition-clip-bottom));
33064
+ }
33003
33065
 
33004
33066
  &::view-transition-group(*) {
33005
33067
  z-index: 0;
@@ -34027,7 +34089,9 @@ const beginTransition = ({
34027
34089
  decision: navigationDecision,
34028
34090
  walkHome: null,
34029
34091
  releaseReverting: null,
34030
- restoreDuration: null
34092
+ duration: undefined,
34093
+ restoreDuration: null,
34094
+ releaseLeavingPages: null
34031
34095
  };
34032
34096
  // The document wears one transition at a time, and the one this interrupts
34033
34097
  // ends only after this one has begun — too late to take off what it wears
@@ -34080,18 +34144,13 @@ const beginTransition = ({
34080
34144
  if (areaElement) {
34081
34145
  documentElement.setAttribute(TRANSITION_TARGET_ATTRIBUTE, "area");
34082
34146
  }
34083
- // A duration of this relation's own, worn for the length of the transition —
34084
- // and whatever the application had written inline put back afterwards, not
34085
- // erased.
34147
+ // A duration of this relation's own, worn by the pictures for the length of
34148
+ // the transition, over whatever the application says on the root.
34086
34149
  if (duration !== undefined) {
34087
- const durationBefore = documentElement.style.getPropertyValue(TRANSITION_DURATION_PROPERTY);
34088
- documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, typeof duration === "number" ? `${duration}ms` : duration);
34150
+ transition.duration = typeof duration === "number" ? `${duration}ms` : duration;
34151
+ setTransitionValue(TRANSITION_DURATION_PROPERTY, transition.duration);
34089
34152
  transition.restoreDuration = () => {
34090
- if (durationBefore) {
34091
- documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, durationBefore);
34092
- } else {
34093
- documentElement.style.removeProperty(TRANSITION_DURATION_PROPERTY);
34094
- }
34153
+ removeTransitionValue(TRANSITION_DURATION_PROPERTY);
34095
34154
  };
34096
34155
  }
34097
34156
  const releaseRendering = takeoverRoutingRenderingHold();
@@ -34104,7 +34163,9 @@ const beginTransition = ({
34104
34163
  // the only place the silent misconfigurations show. They are all about the
34105
34164
  // same thing — a movement playing on pictures that are not the pages.
34106
34165
  const viewTransitionReady = () => {
34107
- startTransitionFurniture(transition);
34166
+ startTransitionFurniture(transition, {
34167
+ duration: transition.duration
34168
+ });
34108
34169
  const capturedNames = capturedViewTransitionNames();
34109
34170
  if (areaElements.length > 0) {
34110
34171
  if (!capturedNames.has(AREA_NAME)) {
@@ -34130,6 +34191,10 @@ const beginTransition = ({
34130
34191
  // is awaited here must be able to resolve without a frame: the document
34131
34192
  // is frozen for the whole of this callback.
34132
34193
  try {
34194
+ // The page being left is photographed already: it only has to stay out
34195
+ // of the second picture, not be taken down before it (see
34196
+ // keepLeavingPages in route.jsx).
34197
+ transition.releaseLeavingPages = keepLeavingPages();
34133
34198
  // Releasing flushes the held render synchronously, so a route that
34134
34199
  // rendered has already resolved the wait by the next line.
34135
34200
  releaseRendering();
@@ -34182,6 +34247,11 @@ const beginTransition = ({
34182
34247
  // must not strip what that one is wearing.
34183
34248
  renderWait.stop();
34184
34249
  releaseRendering();
34250
+ // Taken down once nobody sees it any more.
34251
+ if (transition.releaseLeavingPages) {
34252
+ transition.releaseLeavingPages();
34253
+ transition.releaseLeavingPages = null;
34254
+ }
34185
34255
  // The hold a way back took, when it is still standing: the pictures were
34186
34256
  // turned round a second time and played out, or something else took the
34187
34257
  // document over mid-walk. A hold nobody gives back freezes the page.
@@ -89869,8 +89939,7 @@ const createSlot = (SlotRenderer = Box) => {
89869
89939
  };
89870
89940
  const SlotFill = props => {
89871
89941
  const fillerRef = useRef();
89872
- filler = fillerRef;
89873
- slotPropsSignal.value = props;
89942
+ const routePage = useContext(RoutePageContext);
89874
89943
  useLayoutEffect(() => {
89875
89944
  return () => {
89876
89945
  // Within one diff preact mounts the newcomer before unmounting the
@@ -89883,6 +89952,26 @@ const createSlot = (SlotRenderer = Box) => {
89883
89952
  slotPropsSignal.value = null;
89884
89953
  };
89885
89954
  }, []);
89955
+ // In a page kept while leaving: out of the slot as if unmounted, since
89956
+ // the page arriving is what the slot must show — or nothing, when it
89957
+ // fills none.
89958
+ if (routePage && routePage.leavingSignal.value) {
89959
+ if (filler === fillerRef) {
89960
+ filler = null;
89961
+ slotPropsSignal.value = null;
89962
+ }
89963
+ return null;
89964
+ }
89965
+ if (!fillerRef.current) {
89966
+ fillerRef.current = true;
89967
+ filler = fillerRef;
89968
+ }
89969
+ if (filler === null) {
89970
+ filler = fillerRef;
89971
+ }
89972
+ if (filler === fillerRef) {
89973
+ slotPropsSignal.value = props;
89974
+ }
89886
89975
  return null;
89887
89976
  };
89888
89977
  return [Slot, SlotFill];