@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, 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$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.
@@ -457,7 +457,7 @@ installImportMetaCssBuild(import.meta);
457
457
 
458
458
  const URL_TARGET_ATTRIBUTE = "data-url-target";
459
459
 
460
- const css$16 = /* css */ `@layer navi {
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$16, "@jsenv/navi/src/nav/url_target/url_target.js"];
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$15 = /* css */`.action_error {
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$15, "@jsenv/navi/src/action/action_renderer.jsx"];
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
  }
@@ -15941,7 +15941,7 @@ installImportMetaCssBuild(import.meta);
15941
15941
  * (see calloutTemplate in callout.js).
15942
15942
  */
15943
15943
 
15944
- const css$14 = /* css */`.navi_callout_status_icon {
15944
+ const css$15 = /* css */`.navi_callout_status_icon {
15945
15945
  --x-callout-status-icon-color: var(--navi-callout-neutral-color);
15946
15946
  box-sizing: border-box;
15947
15947
  aspect-ratio: 1;
@@ -16012,7 +16012,7 @@ const CalloutStatusIcon = ({
16012
16012
  status = "info",
16013
16013
  shape = "square"
16014
16014
  }) => {
16015
- import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
16015
+ import.meta.css = [css$15, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
16016
16016
  return jsx("span", {
16017
16017
  className: "navi_callout_status_icon",
16018
16018
  "data-status": status === "none" ? undefined : status,
@@ -16039,7 +16039,7 @@ let calloutCount = 0;
16039
16039
  // large enough to survive the pixel or two a trackpad rests on.
16040
16040
  const CLOSE_BY_SCROLL_DISTANCE = 10;
16041
16041
 
16042
- const css$13 = /* css */ `
16042
+ const css$14 = /* css */ `
16043
16043
  /* jsenv-css-opaque: same as dialog.jsx, cascade order. */
16044
16044
  @layer navi {
16045
16045
  .navi_callout {
@@ -16400,7 +16400,7 @@ const openCallout = (
16400
16400
  debug = () => {},
16401
16401
  } = {},
16402
16402
  ) => {
16403
- import.meta.css = [css$13, "@jsenv/navi/src/control/rules/callout/callout.js"];
16403
+ import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout.js"];
16404
16404
  if (debug === true) {
16405
16405
  debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
16406
16406
  }
@@ -21532,7 +21532,9 @@ const swipeTypeOf = (axis, pulled) => {
21532
21532
  * meantime, nothing is picked up. The carry is the element's own, outcomes and
21533
21533
  * refusal included, without the wait for intent (the hold was the proof), and
21534
21534
  * the copy is brought to the finger rather than following it from wherever the
21535
- * element stands. A copy or nothing: `move` and `moving` carry the thing itself,
21535
+ * element stands. Let go of there before it has travelled — the hold only meant
21536
+ * to open the popup — it goes back and nothing is answered, whatever it was
21537
+ * brought over. A copy or nothing: `move` and `moving` carry the thing itself,
21536
21538
  * which would leave its place just for being handed a press. When it starts is
21537
21539
  * navi's — after a popup still being lifted (see `--navi-grab` in commands.js).
21538
21540
  *
@@ -31796,6 +31798,12 @@ const useUrlSearchParam = (paramName, defaultValue) => {
31796
31798
  return [value, setSearchParamValue];
31797
31799
  };
31798
31800
 
31801
+ // The title belongs to the last Head to arrive, whatever order they leave in:
31802
+ // the page being left by a route transition is taken down AFTER the page
31803
+ // arriving is up (see keepLeavingPages in route.jsx), and putting back the
31804
+ // title it found would put its own page's title over the new one.
31805
+ const titleEntries = [];
31806
+ let titleWithoutHead = null;
31799
31807
  const Head = ({
31800
31808
  children
31801
31809
  }) => {
@@ -31804,15 +31812,17 @@ const Head = ({
31804
31812
  return undefined;
31805
31813
  }
31806
31814
  const childArray = Array.isArray(children) ? children : [children];
31807
- const previousTitle = document.title;
31808
31815
  const appendedElements = [];
31816
+ let titleEntry = null;
31809
31817
  for (const child of childArray) {
31810
31818
  if (!child) {
31811
31819
  continue;
31812
31820
  }
31813
31821
  if (child.type === "title") {
31814
31822
  const titleChildren = child.props.children;
31815
- document.title = Array.isArray(titleChildren) ? titleChildren.join("") : titleChildren ?? "";
31823
+ titleEntry = {
31824
+ title: Array.isArray(titleChildren) ? titleChildren.join("") : titleChildren ?? ""
31825
+ };
31816
31826
  continue;
31817
31827
  }
31818
31828
  const el = document.createElement(child.type);
@@ -31823,8 +31833,19 @@ const Head = ({
31823
31833
  document.head.appendChild(el);
31824
31834
  appendedElements.push(el);
31825
31835
  }
31836
+ if (titleEntry) {
31837
+ if (titleEntries.length === 0) {
31838
+ titleWithoutHead = document.title;
31839
+ }
31840
+ titleEntries.push(titleEntry);
31841
+ document.title = titleEntry.title;
31842
+ }
31826
31843
  return () => {
31827
- document.title = previousTitle;
31844
+ if (titleEntry) {
31845
+ titleEntries.splice(titleEntries.indexOf(titleEntry), 1);
31846
+ const lastEntry = titleEntries[titleEntries.length - 1];
31847
+ document.title = lastEntry ? lastEntry.title : titleWithoutHead;
31848
+ }
31828
31849
  for (const el of appendedElements) {
31829
31850
  el.remove();
31830
31851
  }
@@ -31987,75 +32008,7 @@ const samePages = (pages, otherPages) => {
31987
32008
  return true;
31988
32009
  };
31989
32010
 
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
-
32011
+ installImportMetaCssBuild(import.meta);
32059
32012
 
32060
32013
  /**
32061
32014
  * Keep every container showing the page it is showing, whatever the routes say.
@@ -32079,6 +32032,56 @@ const freezeRouteRender = () => {
32079
32032
  routeRenderFrozenSignal.value++;
32080
32033
  };
32081
32034
  };
32035
+
32036
+ /**
32037
+ * Keep the page a container leaves, hidden, until its route transition is over.
32038
+ *
32039
+ * The browser photographs the page being left before the change, and the page
32040
+ * arriving inside the transition's update callback — where everything done
32041
+ * delays the movement. Taking the page being left down is most of that: every
32042
+ * hook of every component in it is cleaned up, and a page of thirty cards
32043
+ * takes longer to take down than the page arriving takes to build. Its picture
32044
+ * is already taken, so it only has to stay out of the second one: a container
32045
+ * that changes page while this is held keeps the page it was showing, hidden,
32046
+ * and drops it once the movement is over (see route_transition.jsx).
32047
+ *
32048
+ * Kept only where Preact would have unmounted it: a page rendered by the same
32049
+ * element on both sides is the same component instance carried over, exactly
32050
+ * as without a movement.
32051
+ */
32052
+ let leavingPagesHold = null;
32053
+ const keepLeavingPages = () => {
32054
+ const hold = {
32055
+ released: false,
32056
+ containers: new Set()
32057
+ };
32058
+ leavingPagesHold = hold;
32059
+ return () => {
32060
+ if (hold.released) {
32061
+ return;
32062
+ }
32063
+ hold.released = true;
32064
+ if (leavingPagesHold === hold) {
32065
+ leavingPagesHold = null;
32066
+ }
32067
+ for (const rerender of hold.containers) {
32068
+ rerender();
32069
+ }
32070
+ hold.containers.clear();
32071
+ };
32072
+ };
32073
+
32074
+ // What a page can learn about its place from the container rendering it:
32075
+ // whether it is a page kept while leaving (see keepLeavingPages). Such a page
32076
+ // is still mounted, and whatever it fills OUTSIDE its own nodes — a slot in a
32077
+ // bar, say — would otherwise go on showing with the page arriving (see
32078
+ // layout/slot.jsx).
32079
+ const RoutePageContext = createContext(null);
32080
+ const LEAVING_ATTRIBUTE = "data-navi-route-leaving";
32081
+ const css$13 = /* css */`[data-navi-route-leaving] {
32082
+ display: none !important;
32083
+ }
32084
+ `;
32082
32085
  const debug$1 = (...args) => {
32083
32086
  {
32084
32087
  return;
@@ -32198,6 +32201,7 @@ const RouteContainer = ({
32198
32201
  shownBranchRef.current = activeBranch;
32199
32202
  }
32200
32203
  const branch = shownBranchRef.current || activeBranch;
32204
+ const content = useContentKeepingLeavingPages(branch);
32201
32205
 
32202
32206
  // The two things this component knows that nobody outside can find out: the
32203
32207
  // pages a named fallback is the absence of — the children it was written
@@ -32219,7 +32223,6 @@ const RouteContainer = ({
32219
32223
  };
32220
32224
  }, [fallbackPage]);
32221
32225
  debug$1(`[container "${id}"] RENDER, active=${branch ? branch.type : "none"}`);
32222
- const content = branch ? branch.node : null;
32223
32226
  if (!content) {
32224
32227
  return null;
32225
32228
  }
@@ -32228,12 +32231,141 @@ const RouteContainer = ({
32228
32231
  }
32229
32232
  return content;
32230
32233
  };
32234
+ // What a container renders: the page it shows and, while a route transition
32235
+ // keeps them (see keepLeavingPages), the pages it has left — each the very
32236
+ // vnode rendered last time, which Preact does not look into, between two
32237
+ // markers that say which nodes to hide. Always an array, even of one page: the
32238
+ // page being left must stay the child it was when a page is added beside it,
32239
+ // or Preact takes it down and builds it again.
32240
+ let leavingKeyCount = 0;
32241
+ const useContentKeepingLeavingPages = branch => {
32242
+ const [, rerender] = useReducer(countRenders, 0);
32243
+ const shownRef = useRef(null);
32244
+ const leavingRef = useRef([]);
32245
+ const shown = shownRef.current;
32246
+ const element = branchElement(branch);
32247
+ let key;
32248
+ let page = null;
32249
+ if (shown && branch && (shown.id === branch.id || shown.element === element)) {
32250
+ // The same page, or one Preact carries over from it: the key it was
32251
+ // mounted with, or it would be mounted again.
32252
+ key = shown.key;
32253
+ page = shown.page;
32254
+ } else if (shown && leavingPagesHold) {
32255
+ leavingRef.current.push({
32256
+ vnode: shown.vnode,
32257
+ page: shown.page,
32258
+ key: `navi-route-leaving-${++leavingKeyCount}`,
32259
+ hold: leavingPagesHold,
32260
+ startRef: createRef(),
32261
+ endRef: createRef(),
32262
+ hidden: false
32263
+ });
32264
+ leavingPagesHold.containers.add(rerender);
32265
+ // Keyed apart from the page it takes the place of, which is still there.
32266
+ key = `navi-route-${++leavingKeyCount}`;
32267
+ }
32268
+ let vnode = null;
32269
+ if (branch) {
32270
+ if (!page) {
32271
+ page = {
32272
+ leavingSignal: signal(false)
32273
+ };
32274
+ }
32275
+ vnode = h(RoutePageContext.Provider, {
32276
+ key,
32277
+ value: page
32278
+ }, branch.node);
32279
+ }
32280
+ shownRef.current = branch ? {
32281
+ id: branch.id,
32282
+ element,
32283
+ key,
32284
+ vnode,
32285
+ page
32286
+ } : null;
32287
+ const leaving = leavingRef.current.filter(entry => !entry.hold.released);
32288
+ leavingRef.current = leaving;
32289
+
32290
+ // Hidden as soon as it is in the DOM next to the page arriving: this runs in
32291
+ // the update callback's render, before the second picture is taken.
32292
+ useLayoutEffect(() => {
32293
+ for (const entry of leavingRef.current) {
32294
+ const start = entry.startRef.current;
32295
+ const end = entry.endRef.current;
32296
+ if (entry.hidden || !start || !end) {
32297
+ continue;
32298
+ }
32299
+ entry.hidden = true;
32300
+ entry.page.leavingSignal.value = true;
32301
+ let node = start.nextSibling;
32302
+ while (node && node !== end) {
32303
+ if (node.nodeType === Node.ELEMENT_NODE) {
32304
+ node.setAttribute(LEAVING_ATTRIBUTE, "");
32305
+ }
32306
+ node = node.nextSibling;
32307
+ }
32308
+ }
32309
+ });
32310
+ useLayoutEffect(() => {
32311
+ return () => {
32312
+ for (const entry of leavingRef.current) {
32313
+ entry.hold.containers.delete(rerender);
32314
+ }
32315
+ };
32316
+ }, []);
32317
+ if (leaving.length === 0) {
32318
+ return vnode ? [vnode] : null;
32319
+ }
32320
+ import.meta.css = [css$13, "@jsenv/navi/src/nav/route.jsx"];
32321
+ const children = vnode ? [vnode] : [];
32322
+ for (const entry of leaving) {
32323
+ children.push(h("template", {
32324
+ key: `${entry.key}-start`,
32325
+ ref: entry.startRef
32326
+ }), entry.vnode, h("template", {
32327
+ key: `${entry.key}-end`,
32328
+ ref: entry.endRef
32329
+ }));
32330
+ }
32331
+ return children;
32332
+ };
32333
+ const countRenders = count => count + 1;
32334
+ // What Preact compares when a container changes page, below the <Route> it
32335
+ // renders either way: the element the page is rendered with — a layout, for a
32336
+ // container branch.
32337
+ const branchElement = branch => {
32338
+ if (!branch) {
32339
+ return null;
32340
+ }
32341
+ let node = branch.node;
32342
+ while (node && node.type !== Route) {
32343
+ // A wrapper around the branch (see wrapBranch).
32344
+ node = node.props.children;
32345
+ }
32346
+ if (!node) {
32347
+ return null;
32348
+ }
32349
+ const {
32350
+ element
32351
+ } = node.props;
32352
+ if (element === undefined || typeof element === "function") {
32353
+ return element;
32354
+ }
32355
+ return element.type;
32356
+ };
32357
+
32231
32358
  // Walk JSX children vnodes (without rendering) to build a branch list and
32232
32359
  // find the active one in the same pass. Anything that is not a <Route> is read
32233
32360
  // through and kept around the branch it holds (see below).
32234
32361
  // Returns { matchingBranch, fallbackBranch, activeBranch, pages }.
32235
- const collectBranches = children => {
32362
+ //
32363
+ // Each branch is given an id from where it is written among the children,
32364
+ // which is what tells a container it changed page: the vnodes themselves are
32365
+ // new at every render of the parent.
32366
+ const collectBranches = (children, path = "") => {
32236
32367
  let matchingBranch = null;
32368
+ let childIndex = 0;
32237
32369
  let fallbackBranch = null;
32238
32370
  // Every page a branch can be on, at any depth: what a named fallback is the
32239
32371
  // ABSENCE of (see route_fallback.js). A container with a guard route counts
@@ -32251,6 +32383,7 @@ const collectBranches = children => {
32251
32383
  }
32252
32384
  return;
32253
32385
  }
32386
+ const id = `${path}${childIndex++}`;
32254
32387
  if (child.type !== Route) {
32255
32388
  // Anything else is a wrapper around branches, and the two that matter are
32256
32389
  // navi's own: a page says what it renders and delegates what it cannot —
@@ -32270,7 +32403,7 @@ const collectBranches = children => {
32270
32403
  matchingBranch: matchingInside,
32271
32404
  fallbackBranch: fallbackInside,
32272
32405
  pages: pagesInside
32273
- } = collectBranches(wrapperChildren);
32406
+ } = collectBranches(wrapperChildren, `${id}.`);
32274
32407
  for (const pageInside of pagesInside) {
32275
32408
  pages.push(pageInside);
32276
32409
  }
@@ -32293,7 +32426,7 @@ const collectBranches = children => {
32293
32426
  matchingBranch: matchingChild,
32294
32427
  fallbackBranch: fallbackChild,
32295
32428
  pages: pagesChild
32296
- } = collectBranches(nodeChildren);
32429
+ } = collectBranches(nodeChildren, `${id}.`);
32297
32430
  if (fallbackChild && !route) {
32298
32431
  // A fallback is the branch taken for an address none of its siblings
32299
32432
  // claim — so something has to make that address BELONG to this
@@ -32317,6 +32450,7 @@ const collectBranches = children => {
32317
32450
  }
32318
32451
  const branch = {
32319
32452
  type: "container",
32453
+ id,
32320
32454
  node: child
32321
32455
  };
32322
32456
  const guardMatching = route ? route.matchingSignal.value : false;
@@ -32334,6 +32468,7 @@ const collectBranches = children => {
32334
32468
  if (!fallbackBranch) {
32335
32469
  fallbackBranch = {
32336
32470
  type: "fallback",
32471
+ id,
32337
32472
  node: child,
32338
32473
  page: fallback.isRouteFallback ? fallback : null
32339
32474
  };
@@ -32341,6 +32476,7 @@ const collectBranches = children => {
32341
32476
  } else {
32342
32477
  const branch = {
32343
32478
  type: "leaf",
32479
+ id,
32344
32480
  node: child
32345
32481
  };
32346
32482
  pages.push({
@@ -32828,7 +32964,7 @@ const holdTransitionFurniture = (owner, areaElement) => {
32828
32964
  }
32829
32965
  const namesLeaving = [];
32830
32966
  for (const element of namedElements) {
32831
- if (!element.isConnected) {
32967
+ if (!isRendered(element)) {
32832
32968
  namesLeaving.push(nameByElement.get(element));
32833
32969
  } else if (element.matches(FIXED_BAR_SELECTOR$1)) {
32834
32970
  element.setAttribute(FURNITURE_ATTRIBUTE, "shared");
@@ -32947,11 +33083,16 @@ const travelRule = (side, names, movementProperty) => {
32947
33083
  * The pictures are up and about to move: what no picture covers heads to the
32948
33084
  * state arriving, on the same clock.
32949
33085
  */
32950
- const startTransitionFurniture = (owner) => {
33086
+ const startTransitionFurniture = (owner, { duration } = {}) => {
32951
33087
  if (owner !== furnitureOwner) {
32952
33088
  return;
32953
33089
  }
32954
33090
  for (const restWall of restWallElements) {
33091
+ // A duration of the movement's own is written for its pictures alone (see
33092
+ // transition_values.js); the stand-in is an element, and is told.
33093
+ if (duration !== undefined) {
33094
+ restWall.style.setProperty("--navi-route-transition-duration", duration);
33095
+ }
32955
33096
  restWall.style.opacity = restWallOpacity;
32956
33097
  }
32957
33098
  };
@@ -32982,7 +33123,9 @@ const releaseTransitionFurniture = (owner) => {
32982
33123
  // area's stand-in answers for it.
32983
33124
  const paintTransitionWalls = (areaElement) => {
32984
33125
  removeTransitionWalls();
32985
- const sources = document.querySelectorAll(TOP_LAYER_WALL_SELECTOR);
33126
+ const sources = Array.from(
33127
+ document.querySelectorAll(TOP_LAYER_WALL_SELECTOR),
33128
+ ).filter(isRendered);
32986
33129
  if (sources.length === 0) {
32987
33130
  return sources;
32988
33131
  }
@@ -33184,6 +33327,14 @@ const subtractRect = (rect, hole) => {
33184
33327
  return pieces;
33185
33328
  };
33186
33329
 
33330
+ // Out of the state arriving: taken out of the document, or in a page kept
33331
+ // hidden until its movement is over (see keepLeavingPages in route.jsx) —
33332
+ // where a dialog is still :modal and a popover still :popover-open, and
33333
+ // neither is painted or photographed.
33334
+ const isRendered = (element) => {
33335
+ return element.isConnected && element.checkVisibility();
33336
+ };
33337
+
33187
33338
  const removeRestWalls = () => {
33188
33339
  for (const restWall of restWallElements) {
33189
33340
  restWall.remove();
@@ -33193,6 +33344,52 @@ const removeRestWalls = () => {
33193
33344
  leavingRects = new Map();
33194
33345
  };
33195
33346
 
33347
+ /**
33348
+ * The values a movement hands to its pictures, written where only the pictures
33349
+ * read them: on ::view-transition, the root of the pseudo-element tree, rather
33350
+ * than on the document's root.
33351
+ *
33352
+ * A custom property written on :root is inherited by every element of the
33353
+ * document, and writing one restyles all of them. A movement writes its
33354
+ * numbers as its pages are held, again whenever a scroll moves the window, and
33355
+ * takes them off as it ends — each time a whole-document restyle of a page that
33356
+ * can be thousands of elements long. On ::view-transition they reach every
33357
+ * picture by plain inheritance, and no element at all.
33358
+ *
33359
+ * An inline style cannot target a pseudo-element, so they are declarations of
33360
+ * one rule, in a sheet of their own. Written only: what a pseudo-element that
33361
+ * is not on screen computes is not reliably readable, so nothing reads them
33362
+ * back from there.
33363
+ */
33364
+
33365
+ let valuesStyle = null;
33366
+ const getValuesStyle = () => {
33367
+ if (valuesStyle) {
33368
+ return valuesStyle;
33369
+ }
33370
+ const sheet = new CSSStyleSheet();
33371
+ sheet.replaceSync(":root::view-transition {}");
33372
+ const [rule] = sheet.cssRules;
33373
+ if (rule) {
33374
+ document.adoptedStyleSheets.push(sheet);
33375
+ valuesStyle = rule.style;
33376
+ } else {
33377
+ // A browser without view transitions drops the rule: the movement runs its
33378
+ // steps all the same (see start_view_transition_polyfill.js), with no
33379
+ // pictures to hand anything to.
33380
+ valuesStyle = document.createElement("div").style;
33381
+ }
33382
+ return valuesStyle;
33383
+ };
33384
+
33385
+ const setTransitionValue = (name, value) => {
33386
+ getValuesStyle().setProperty(name, value);
33387
+ };
33388
+
33389
+ const removeTransitionValue = (name) => {
33390
+ getValuesStyle().removeProperty(name);
33391
+ };
33392
+
33196
33393
  installImportMetaCssBuild(import.meta);
33197
33394
 
33198
33395
  const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
@@ -33227,96 +33424,6 @@ const TRANSITION_WINDOW_CSS = /* css */ `@property --navi-transition-cover-top {
33227
33424
  --navi-transition-cover-left: 0px;
33228
33425
  }
33229
33426
  }
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
33427
  `;
33321
33428
 
33322
33429
  // Called by whatever needs the window — a render, a movement declared or
@@ -33414,22 +33521,21 @@ const holdTransitionWindow = (
33414
33521
  const right =
33415
33522
  rectBefore.right > rectAfter.right ? rectBefore.right : rectAfter.right;
33416
33523
  windowOwner = owner;
33417
- const { style } = document.documentElement;
33418
- style.setProperty(WINDOW_TOP_PROPERTY, `${top}px`);
33419
- style.setProperty(WINDOW_LEFT_PROPERTY, `${left}px`);
33420
- style.setProperty(WINDOW_WIDTH_PROPERTY, `${right - left}px`);
33421
- style.setProperty(WINDOW_HEIGHT_PROPERTY, `${bottom - top}px`);
33422
- style.setProperty(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
33423
- style.setProperty(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
33424
- style.setProperty(WINDOW_NEW_TOP_PROPERTY, `${rectAfter.top}px`);
33425
- style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
33524
+ setTransitionValue(WINDOW_TOP_PROPERTY, `${top}px`);
33525
+ setTransitionValue(WINDOW_LEFT_PROPERTY, `${left}px`);
33526
+ setTransitionValue(WINDOW_WIDTH_PROPERTY, `${right - left}px`);
33527
+ setTransitionValue(WINDOW_HEIGHT_PROPERTY, `${bottom - top}px`);
33528
+ setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
33529
+ setTransitionValue(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
33530
+ setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${rectAfter.top}px`);
33531
+ setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
33426
33532
  const oldBand = furnitureLive
33427
33533
  ? bandTheCutMayBeRelaxedTo(bandBefore, bandAfter, rectAfter)
33428
33534
  : bandBefore;
33429
- style.setProperty(OLD_BAND_TOP_PROPERTY, `${oldBand.top}px`);
33430
- style.setProperty(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
33431
- style.setProperty(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
33432
- style.setProperty(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
33535
+ setTransitionValue(OLD_BAND_TOP_PROPERTY, `${oldBand.top}px`);
33536
+ setTransitionValue(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
33537
+ setTransitionValue(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
33538
+ setTransitionValue(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
33433
33539
  followArrivingState(element, rectAfter, {
33434
33540
  outerScroll,
33435
33541
  windowTop: top,
@@ -33451,9 +33557,8 @@ const releaseTransitionWindow = (owner) => {
33451
33557
  if (unfollowArrivingState) {
33452
33558
  unfollowArrivingState();
33453
33559
  }
33454
- const { style } = document.documentElement;
33455
33560
  for (const property of WINDOW_PROPERTIES) {
33456
- style.removeProperty(property);
33561
+ removeTransitionValue(property);
33457
33562
  }
33458
33563
  };
33459
33564
 
@@ -33462,12 +33567,11 @@ const releaseTransitionWindow = (owner) => {
33462
33567
  // file) — and with it the offset outside the box, which carries the window
33463
33568
  // and the corner of the state being left along. Requested, never awaited: the
33464
33569
  // first call runs inside the update callback, where a frame cannot come.
33465
- // Written only when it moved: every write restyles the root and the pictures.
33570
+ // Written only when it moved: every write restyles the pictures.
33466
33571
  const followArrivingState = (element, rectAtHold, heldAt) => {
33467
33572
  if (unfollowArrivingState) {
33468
33573
  unfollowArrivingState();
33469
33574
  }
33470
- const { style } = document.documentElement;
33471
33575
  let top = rectAtHold.top;
33472
33576
  let left = rectAtHold.left;
33473
33577
  let outerTop = heldAt.outerScroll.top;
@@ -33476,24 +33580,27 @@ const followArrivingState = (element, rectAtHold, heldAt) => {
33476
33580
  const rect = element.getBoundingClientRect();
33477
33581
  if (rect.top !== top) {
33478
33582
  top = rect.top;
33479
- style.setProperty(WINDOW_NEW_TOP_PROPERTY, `${top}px`);
33583
+ setTransitionValue(WINDOW_NEW_TOP_PROPERTY, `${top}px`);
33480
33584
  }
33481
33585
  if (rect.left !== left) {
33482
33586
  left = rect.left;
33483
- style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${left}px`);
33587
+ setTransitionValue(WINDOW_NEW_LEFT_PROPERTY, `${left}px`);
33484
33588
  }
33485
33589
  const outerScroll = readOuterScroll(element);
33486
33590
  if (outerScroll.top !== outerTop) {
33487
33591
  outerTop = outerScroll.top;
33488
33592
  const shift = heldAt.outerScroll.top - outerTop;
33489
- style.setProperty(WINDOW_TOP_PROPERTY, `${heldAt.windowTop + shift}px`);
33490
- style.setProperty(WINDOW_OLD_TOP_PROPERTY, `${heldAt.oldTop + shift}px`);
33593
+ setTransitionValue(WINDOW_TOP_PROPERTY, `${heldAt.windowTop + shift}px`);
33594
+ setTransitionValue(WINDOW_OLD_TOP_PROPERTY, `${heldAt.oldTop + shift}px`);
33491
33595
  }
33492
33596
  if (outerScroll.left !== outerLeft) {
33493
33597
  outerLeft = outerScroll.left;
33494
33598
  const shift = heldAt.outerScroll.left - outerLeft;
33495
- style.setProperty(WINDOW_LEFT_PROPERTY, `${heldAt.windowLeft + shift}px`);
33496
- style.setProperty(
33599
+ setTransitionValue(
33600
+ WINDOW_LEFT_PROPERTY,
33601
+ `${heldAt.windowLeft + shift}px`,
33602
+ );
33603
+ setTransitionValue(
33497
33604
  WINDOW_OLD_LEFT_PROPERTY,
33498
33605
  `${heldAt.oldLeft + shift}px`,
33499
33606
  );
@@ -33856,58 +33963,9 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
33856
33963
  inherits: false
33857
33964
  }
33858
33965
 
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
33966
  :root::view-transition, :root::view-transition-group(*), :root::view-transition-image-pair(*), :root::view-transition-old(*), :root::view-transition-new(*) {
33890
33967
  --navi-route-transition-leave: inherit;
33891
33968
  --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
33969
  }
33912
33970
 
33913
33971
  :root[data-navi-route-transition] [data-navi-route-transition-area] {
@@ -33928,28 +33986,32 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
33928
33986
 
33929
33987
  &[data-navi-route-transition-target="area"] {
33930
33988
  view-transition-name: none;
33931
- --navi-route-transition-clip-top: max(0px,
33932
- min(var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
33933
- var(--navi-transition-old-band-top)) - var(--navi-transition-window-top));
33934
- --navi-route-transition-clip-left: max(0px,
33935
- min(var(--navi-safe-area-inset-left) + var(--navi-transition-cover-left),
33936
- var(--navi-transition-old-band-left)) - var(--navi-transition-window-left));
33937
- --navi-route-transition-clip-bottom: max(0px,
33938
- var(--navi-transition-window-top) +
33939
- var(--navi-transition-window-height) +
33940
- min(var(--navi-safe-area-inset-bottom) +
33941
- var(--navi-transition-cover-bottom),
33942
- var(--navi-transition-old-band-bottom)) -
33943
- 100dvh);
33944
- --navi-route-transition-clip-right: max(0px,
33945
- var(--navi-transition-window-left) +
33946
- var(--navi-transition-window-width) +
33947
- min(var(--navi-safe-area-inset-right) +
33948
- var(--navi-transition-cover-right),
33949
- var(--navi-transition-old-band-right)) -
33950
- 100dvw);
33951
- --navi-route-transition-travel-x: calc(var(--navi-transition-window-width) - var(--navi-route-transition-clip-left) - var(--navi-route-transition-clip-right));
33952
- --navi-route-transition-travel-y: calc(var(--navi-transition-window-height) - var(--navi-route-transition-clip-top) - var(--navi-route-transition-clip-bottom));
33989
+
33990
+ &::view-transition {
33991
+ --navi-route-transition-clip-top: max(0px,
33992
+ min(var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
33993
+ var(--navi-transition-old-band-top)) - var(--navi-transition-window-top));
33994
+ --navi-route-transition-clip-left: max(0px,
33995
+ min(var(--navi-safe-area-inset-left) +
33996
+ var(--navi-transition-cover-left),
33997
+ var(--navi-transition-old-band-left)) - var(--navi-transition-window-left));
33998
+ --navi-route-transition-clip-bottom: max(0px,
33999
+ var(--navi-transition-window-top) +
34000
+ var(--navi-transition-window-height) +
34001
+ min(var(--navi-safe-area-inset-bottom) +
34002
+ var(--navi-transition-cover-bottom),
34003
+ var(--navi-transition-old-band-bottom)) -
34004
+ 100dvh);
34005
+ --navi-route-transition-clip-right: max(0px,
34006
+ var(--navi-transition-window-left) +
34007
+ var(--navi-transition-window-width) +
34008
+ min(var(--navi-safe-area-inset-right) +
34009
+ var(--navi-transition-cover-right),
34010
+ var(--navi-transition-old-band-right)) -
34011
+ 100dvw);
34012
+ --navi-route-transition-travel-x: calc(var(--navi-transition-window-width) - var(--navi-route-transition-clip-left) - var(--navi-route-transition-clip-right));
34013
+ --navi-route-transition-travel-y: calc(var(--navi-transition-window-height) - var(--navi-route-transition-clip-top) - var(--navi-route-transition-clip-bottom));
34014
+ }
33953
34015
 
33954
34016
  &::view-transition-group(*) {
33955
34017
  z-index: 0;
@@ -34977,7 +35039,9 @@ const beginTransition = ({
34977
35039
  decision: navigationDecision,
34978
35040
  walkHome: null,
34979
35041
  releaseReverting: null,
34980
- restoreDuration: null
35042
+ duration: undefined,
35043
+ restoreDuration: null,
35044
+ releaseLeavingPages: null
34981
35045
  };
34982
35046
  // The document wears one transition at a time, and the one this interrupts
34983
35047
  // ends only after this one has begun — too late to take off what it wears
@@ -35030,18 +35094,13 @@ const beginTransition = ({
35030
35094
  if (areaElement) {
35031
35095
  documentElement.setAttribute(TRANSITION_TARGET_ATTRIBUTE, "area");
35032
35096
  }
35033
- // A duration of this relation's own, worn for the length of the transition —
35034
- // and whatever the application had written inline put back afterwards, not
35035
- // erased.
35097
+ // A duration of this relation's own, worn by the pictures for the length of
35098
+ // the transition, over whatever the application says on the root.
35036
35099
  if (duration !== undefined) {
35037
- const durationBefore = documentElement.style.getPropertyValue(TRANSITION_DURATION_PROPERTY);
35038
- documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, typeof duration === "number" ? `${duration}ms` : duration);
35100
+ transition.duration = typeof duration === "number" ? `${duration}ms` : duration;
35101
+ setTransitionValue(TRANSITION_DURATION_PROPERTY, transition.duration);
35039
35102
  transition.restoreDuration = () => {
35040
- if (durationBefore) {
35041
- documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, durationBefore);
35042
- } else {
35043
- documentElement.style.removeProperty(TRANSITION_DURATION_PROPERTY);
35044
- }
35103
+ removeTransitionValue(TRANSITION_DURATION_PROPERTY);
35045
35104
  };
35046
35105
  }
35047
35106
  const releaseRendering = takeoverRoutingRenderingHold();
@@ -35054,7 +35113,9 @@ const beginTransition = ({
35054
35113
  // the only place the silent misconfigurations show. They are all about the
35055
35114
  // same thing — a movement playing on pictures that are not the pages.
35056
35115
  const viewTransitionReady = () => {
35057
- startTransitionFurniture(transition);
35116
+ startTransitionFurniture(transition, {
35117
+ duration: transition.duration
35118
+ });
35058
35119
  const capturedNames = capturedViewTransitionNames();
35059
35120
  if (areaElements.length > 0) {
35060
35121
  if (!capturedNames.has(AREA_NAME)) {
@@ -35080,6 +35141,10 @@ const beginTransition = ({
35080
35141
  // is awaited here must be able to resolve without a frame: the document
35081
35142
  // is frozen for the whole of this callback.
35082
35143
  try {
35144
+ // The page being left is photographed already: it only has to stay out
35145
+ // of the second picture, not be taken down before it (see
35146
+ // keepLeavingPages in route.jsx).
35147
+ transition.releaseLeavingPages = keepLeavingPages();
35083
35148
  // Releasing flushes the held render synchronously, so a route that
35084
35149
  // rendered has already resolved the wait by the next line.
35085
35150
  releaseRendering();
@@ -35132,6 +35197,11 @@ const beginTransition = ({
35132
35197
  // must not strip what that one is wearing.
35133
35198
  renderWait.stop();
35134
35199
  releaseRendering();
35200
+ // Taken down once nobody sees it any more.
35201
+ if (transition.releaseLeavingPages) {
35202
+ transition.releaseLeavingPages();
35203
+ transition.releaseLeavingPages = null;
35204
+ }
35135
35205
  // The hold a way back took, when it is still standing: the pictures were
35136
35206
  // turned round a second time and played out, or something else took the
35137
35207
  // document over mid-walk. A hold nobody gives back freezes the page.
@@ -92004,8 +92074,7 @@ const createSlot = (SlotRenderer = Box) => {
92004
92074
  };
92005
92075
  const SlotFill = props => {
92006
92076
  const fillerRef = useRef();
92007
- filler = fillerRef;
92008
- slotPropsSignal.value = props;
92077
+ const routePage = useContext(RoutePageContext);
92009
92078
  useLayoutEffect(() => {
92010
92079
  return () => {
92011
92080
  // Within one diff preact mounts the newcomer before unmounting the
@@ -92018,6 +92087,26 @@ const createSlot = (SlotRenderer = Box) => {
92018
92087
  slotPropsSignal.value = null;
92019
92088
  };
92020
92089
  }, []);
92090
+ // In a page kept while leaving: out of the slot as if unmounted, since
92091
+ // the page arriving is what the slot must show — or nothing, when it
92092
+ // fills none.
92093
+ if (routePage && routePage.leavingSignal.value) {
92094
+ if (filler === fillerRef) {
92095
+ filler = null;
92096
+ slotPropsSignal.value = null;
92097
+ }
92098
+ return null;
92099
+ }
92100
+ if (!fillerRef.current) {
92101
+ fillerRef.current = true;
92102
+ filler = fillerRef;
92103
+ }
92104
+ if (filler === null) {
92105
+ filler = fillerRef;
92106
+ }
92107
+ if (filler === fillerRef) {
92108
+ slotPropsSignal.value = props;
92109
+ }
92021
92110
  return null;
92022
92111
  };
92023
92112
  return [Slot, SlotFill];