@jsenv/navi 0.29.132 → 0.29.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jsenv_navi.js +630 -224
- package/dist/jsenv_navi.js.map +33 -16
- package/docs/AI_INSTRUCTIONS.md +3 -1
- package/docs/navigation.md +41 -2
- package/docs/scroll.md +5 -0
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visualViewportHeightSignal, visualViewportWidthSignal, getAppHeight, getAppWidth, coarsePointerSignal, smallTouchScreenSignal } from "./jsenv_navi_side_effects.js";
|
|
6
6
|
export { disableVirtualKeyboardOverlay } from "./jsenv_navi_side_effects.js";
|
|
7
|
-
import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, initFocusGroup, scrollIntoViewScoped, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, isTouchDrivenEvent, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen,
|
|
7
|
+
import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, getScrollContainer, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, initFocusGroup, scrollIntoViewScoped, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, isTouchDrivenEvent, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, canScroll, measureWidestChildRow, performTabNavigation, wheelGestureIsTakenFrom, releaseWheelGesture, claimWheelGesture, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
|
|
8
8
|
export { clickIsSuppressed, contrastColor, findEvent, startDragTo } from "@jsenv/dom";
|
|
9
9
|
import { signal, computed, effect, untracked, batch, useComputed, useSignal } from "@preact/signals";
|
|
10
10
|
import { isValidElement, h, Fragment, createContext, render, toChildArray, options, cloneElement } from "preact";
|
|
@@ -2229,6 +2229,17 @@ naviI18n.addAll({
|
|
|
2229
2229
|
pt: "meia-noite",
|
|
2230
2230
|
nl: "middernacht",
|
|
2231
2231
|
},
|
|
2232
|
+
// What <TimeRange> writes between the two bounds of a span — "8h–10h",
|
|
2233
|
+
// "11 mai – 14 mai". An en dash, the mark for a span, not a hyphen.
|
|
2234
|
+
"time.range_separator": {
|
|
2235
|
+
en: "–",
|
|
2236
|
+
fr: "–",
|
|
2237
|
+
de: "–",
|
|
2238
|
+
es: "–",
|
|
2239
|
+
it: "–",
|
|
2240
|
+
pt: "–",
|
|
2241
|
+
nl: "–",
|
|
2242
|
+
},
|
|
2232
2243
|
// Compact duration unit symbols used in "1h30", "45min", "2d", etc.
|
|
2233
2244
|
"time.duration.year_symbol": {
|
|
2234
2245
|
en: "y",
|
|
@@ -6081,10 +6092,11 @@ const css$11 = /* css */`
|
|
|
6081
6092
|
right where it's set) — same reasoning as Popover's identical
|
|
6082
6093
|
attribute (popover.jsx's own top comment has the full case). */
|
|
6083
6094
|
position: fixed;
|
|
6084
|
-
/* Popover resets
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6095
|
+
/* Popover resets. Laid out at its containing block's own origin and moved
|
|
6096
|
+
from there by a translate (applyNewPosition), never by left/top — see
|
|
6097
|
+
applyNewPosition's own doc for why the placement may not go through
|
|
6098
|
+
left/top themselves. */
|
|
6099
|
+
inset: 0 auto auto 0;
|
|
6088
6100
|
/* For some reason callout could end up behing elements when it's redisplayed in a dialog
|
|
6089
6101
|
(behind button relatively positioned in dialog footer while callout is appended into dialog body)
|
|
6090
6102
|
To ensure ti goes above we put a z-index: 1, I hope it won't bite use in the future */
|
|
@@ -6108,10 +6120,9 @@ const css$11 = /* css */`
|
|
|
6108
6120
|
border: none;
|
|
6109
6121
|
outline: none; /* programmatic focus may land here briefly before being redirected to close button */
|
|
6110
6122
|
opacity: 0;
|
|
6111
|
-
/*
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
Popover/Dialog use. */
|
|
6123
|
+
/* opacity only: the placement lives in the translate property, which
|
|
6124
|
+
applyNewPosition (visible_rect.js) owns and animates itself through the
|
|
6125
|
+
Web Animations API, same mechanism Popover/Dialog use. */
|
|
6115
6126
|
transition: opacity 0.2s ease-in-out;
|
|
6116
6127
|
cursor: initial; /* Do not inherit element cursor, inside the element but should use regular cursor */
|
|
6117
6128
|
pointer-events: auto; /* Must be interactive to be closabled (overrid list item pointer-events none for instance) */
|
|
@@ -8082,27 +8093,35 @@ const formatTime = (date, lang) => {
|
|
|
8082
8093
|
* "compact" uses our own notation that omits the minute symbol when hours are present.
|
|
8083
8094
|
*
|
|
8084
8095
|
* @param {number} minutes
|
|
8085
|
-
* @param {{ lang?: string, format?: "long"|"short"|"narrow"|"compact", clockStyle?: boolean, forceUnit?: boolean }} [options]
|
|
8096
|
+
* @param {{ lang?: string, format?: "long"|"short"|"narrow"|"compact", clockStyle?: boolean, pad?: boolean, precision?: "hour"|"minute", forceUnit?: boolean }} [options]
|
|
8086
8097
|
* @param {boolean} [options.forceUnit=false] - Keep the value in minutes
|
|
8087
8098
|
* however big it gets ("2160 minutes" instead of "1 jour et 12 heures").
|
|
8088
8099
|
* Past 24 hours the default promotes to days, which reads better but hides
|
|
8089
8100
|
* the unit the caller works in.
|
|
8090
8101
|
* @param {boolean} [options.clockStyle=false] - Set this when `minutes`
|
|
8091
8102
|
* represents a time-of-day rather than a real duration (used by
|
|
8092
|
-
* `<Time type="time">`, see time.jsx's own TimeTime)
|
|
8093
|
-
*
|
|
8094
|
-
*
|
|
8095
|
-
*
|
|
8096
|
-
*
|
|
8097
|
-
*
|
|
8098
|
-
* doesn't collapse to something indistinguishable from an actual
|
|
8099
|
-
* 5-minute duration.
|
|
8100
|
-
* - `format: "compact"` also zero-pads a single-digit hour to 2 digits
|
|
8101
|
-
* (e.g. "5h30" → "05h30") and keeps a zero-valued minute (e.g. "10h" →
|
|
8102
|
-
* "10h00"), so it reads closer to a "05:30"/"10:00" clock. The other
|
|
8103
|
-
* formats spell out their units, so "10 heures"/"10h" reads fine there
|
|
8104
|
-
* and only "compact" needs the clock shape.
|
|
8103
|
+
* `<Time type="time">`, see time.jsx's own TimeTime). A clock's "0" is a
|
|
8104
|
+
* meaningful hour rather than "no hours": a zero-hours component is
|
|
8105
|
+
* normally dropped entirely (a real 5-minute duration should print as
|
|
8106
|
+
* "5 minutes", not "0 hours 5 minutes"); this keeps it instead (e.g.
|
|
8107
|
+
* "0 h et 5 min"/"0h 5min"/"00h05") so midnight doesn't collapse to
|
|
8108
|
+
* something indistinguishable from an actual 5-minute duration.
|
|
8105
8109
|
* Must not be set for plain duration formatting.
|
|
8110
|
+
* @param {boolean} [options.pad=true] - Zero-pad the hour to 2 digits
|
|
8111
|
+
* ("08h30" rather than "8h30"). `clockStyle` + `format: "compact"` only.
|
|
8112
|
+
* @param {"hour"|"minute"} [options.precision="minute"] - Whether a zero
|
|
8113
|
+
* minute is written: `"minute"` keeps it ("10h00"), `"hour"` drops it
|
|
8114
|
+
* ("10h"). `clockStyle` + `format: "compact"` only.
|
|
8115
|
+
*
|
|
8116
|
+
* These last two are the clock's two independent shape choices, and only
|
|
8117
|
+
* `format: "compact"` has to make them — the spelled-out formats put their
|
|
8118
|
+
* units in words, so "10 heures"/"10 h"/"10h" already reads as a time of
|
|
8119
|
+
* day whatever the padding, and they always write the hour bare and drop a
|
|
8120
|
+
* zero minute. Padded + minute ("08h00") is the column shape, where every
|
|
8121
|
+
* row occupies the same width; bare + hour ("8h", "8h30") is the shape a
|
|
8122
|
+
* person speaks. Bare + minute ("8h00") only ever makes sense next to a
|
|
8123
|
+
* partner that has minutes of its own — see `<TimeRange>`, which is the
|
|
8124
|
+
* only thing that asks for it.
|
|
8106
8125
|
*
|
|
8107
8126
|
* @example
|
|
8108
8127
|
* formatMinuteDuration(90, { lang: "fr" }) // "1 heure 30 minutes" (long, default)
|
|
@@ -8113,6 +8132,9 @@ const formatTime = (date, lang) => {
|
|
|
8113
8132
|
* formatMinuteDuration(5, { lang: "fr", format: "narrow", clockStyle: true }) // "0h 5min"
|
|
8114
8133
|
* formatMinuteDuration(330, { lang: "fr", format: "compact", clockStyle: true }) // "05h30"
|
|
8115
8134
|
* formatMinuteDuration(600, { lang: "fr", format: "compact", clockStyle: true }) // "10h00"
|
|
8135
|
+
* formatMinuteDuration(600, { lang: "fr", format: "compact", clockStyle: true, pad: false }) // "10h00"
|
|
8136
|
+
* formatMinuteDuration(600, { lang: "fr", format: "compact", clockStyle: true, pad: false, precision: "hour" }) // "10h"
|
|
8137
|
+
* formatMinuteDuration(510, { lang: "fr", format: "compact", clockStyle: true, pad: false, precision: "hour" }) // "8h30"
|
|
8116
8138
|
* formatMinuteDuration(2160, { lang: "fr" }) // "1 jour et 12 heures"
|
|
8117
8139
|
* formatMinuteDuration(2160, { lang: "fr", forceUnit: true }) // "2 160 minutes"
|
|
8118
8140
|
*/
|
|
@@ -8122,13 +8144,15 @@ const formatMinuteDuration = (
|
|
|
8122
8144
|
lang = languagesSignal.value,
|
|
8123
8145
|
format = "long",
|
|
8124
8146
|
clockStyle = false,
|
|
8147
|
+
pad = true,
|
|
8148
|
+
precision = "minute",
|
|
8125
8149
|
forceUnit = false,
|
|
8126
8150
|
} = {},
|
|
8127
8151
|
) => {
|
|
8128
8152
|
if (minutes < 0) {
|
|
8129
8153
|
// the d/h/m split below only holds for a positive value; formatting the
|
|
8130
8154
|
// magnitude and putting the sign back is the only reading that works
|
|
8131
|
-
return `-${formatMinuteDuration(-minutes, { lang, format, clockStyle, forceUnit })}`;
|
|
8155
|
+
return `-${formatMinuteDuration(-minutes, { lang, format, clockStyle, pad, precision, forceUnit })}`;
|
|
8132
8156
|
}
|
|
8133
8157
|
if (forceUnit || (minutes === 0 && !clockStyle)) {
|
|
8134
8158
|
// a zero has nothing to promote to, and rendering it as an empty string
|
|
@@ -8162,16 +8186,19 @@ const formatMinuteDuration = (
|
|
|
8162
8186
|
const hSym = naviI18n("time.duration.hour_symbol", undefined, { lang });
|
|
8163
8187
|
const mSym = naviI18n("time.duration.minute_symbol", undefined, { lang });
|
|
8164
8188
|
const dStr = d > 0 ? `${formatCompactNumber(d, lang)}${dSym}` : "";
|
|
8165
|
-
const hStr =
|
|
8166
|
-
|
|
8167
|
-
|
|
8189
|
+
const hStr =
|
|
8190
|
+
clockStyle && pad
|
|
8191
|
+
? String(h).padStart(2, "0")
|
|
8192
|
+
: formatCompactNumber(h, lang);
|
|
8168
8193
|
if (d === 0 && h === 0 && !clockStyle) {
|
|
8169
8194
|
return `${m}${mSym}`;
|
|
8170
8195
|
}
|
|
8171
8196
|
if (m === 0) {
|
|
8172
8197
|
if (clockStyle) {
|
|
8173
|
-
// "10h00" on a clock, "2h" for a real 2 hours duration
|
|
8174
|
-
|
|
8198
|
+
// "10h00" on a clock, "2h" for a real 2 hours duration — except at
|
|
8199
|
+
// precision "hour", where a clock drops the zero minute too ("10h"),
|
|
8200
|
+
// the way one says it out loud
|
|
8201
|
+
return precision === "minute" ? `${hStr}${hSym}00` : `${hStr}${hSym}`;
|
|
8175
8202
|
}
|
|
8176
8203
|
return h === 0 ? dStr : `${dStr}${hStr}${hSym}`;
|
|
8177
8204
|
}
|
|
@@ -25350,6 +25377,43 @@ const updateDocumentState = (value) => {
|
|
|
25350
25377
|
documentStateSignal.value = value;
|
|
25351
25378
|
};
|
|
25352
25379
|
|
|
25380
|
+
/**
|
|
25381
|
+
* What the history entry being written ends up holding.
|
|
25382
|
+
*
|
|
25383
|
+
* `state`:
|
|
25384
|
+
* - `undefined` — a neutral navigation: a link click, a `replaceUrl` writing a
|
|
25385
|
+
* search param. A **replace** stays on the entry the document is already on,
|
|
25386
|
+
* so what that entry holds stays with it. A **push** opens a NEW entry, and
|
|
25387
|
+
* what was written for the one being left — an open dialog, an expanded
|
|
25388
|
+
* picker (see `useNavState`) — describes that entry alone: carried forward it
|
|
25389
|
+
* would reopen on the next screen, and on the one after that, until something
|
|
25390
|
+
* mounting the same id opens out of nowhere.
|
|
25391
|
+
* - `null` — an explicit reset.
|
|
25392
|
+
* - an object — built by the caller (`enter()`/`leave()` copy the current state
|
|
25393
|
+
* themselves), taken as given.
|
|
25394
|
+
*
|
|
25395
|
+
* `sharedState` always wins: it describes the document, not the entry.
|
|
25396
|
+
*
|
|
25397
|
+
* The push/replace split is the Navigation API's own rule for a `navigate()`
|
|
25398
|
+
* carrying no state — which is why via_navigation.js gets it from the browser
|
|
25399
|
+
* and via_history.js has to spell it out to say the same thing.
|
|
25400
|
+
*/
|
|
25401
|
+
const resolveEffectiveDocumentState = (
|
|
25402
|
+
state,
|
|
25403
|
+
{ navigationType, currentState, sharedState },
|
|
25404
|
+
) => {
|
|
25405
|
+
if (state === undefined) {
|
|
25406
|
+
if (navigationType === "push") {
|
|
25407
|
+
return sharedState;
|
|
25408
|
+
}
|
|
25409
|
+
return { ...(currentState || {}), ...sharedState };
|
|
25410
|
+
}
|
|
25411
|
+
if (state === null) {
|
|
25412
|
+
return sharedState;
|
|
25413
|
+
}
|
|
25414
|
+
return { ...state, ...sharedState };
|
|
25415
|
+
};
|
|
25416
|
+
|
|
25353
25417
|
/**
|
|
25354
25418
|
* The document's rendering, held for the one frame a view transition needs.
|
|
25355
25419
|
*
|
|
@@ -25482,6 +25546,13 @@ const takeoverRoutingRenderingHold = () => {
|
|
|
25482
25546
|
* something still loading. Its content is not there at the moment it is put
|
|
25483
25547
|
* back, so a position beyond what has arrived is clamped as before. Only the
|
|
25484
25548
|
* page knows when it is whole.
|
|
25549
|
+
*
|
|
25550
|
+
* WHEN a page is arrived at is not decided here either. A document navigation
|
|
25551
|
+
* lands where its kind says (see via_history.js), and one scrollport can be
|
|
25552
|
+
* shared by pages the browser is never told apart: a row of tabs replaces the
|
|
25553
|
+
* url under the same document, so the arrival — and the deafness the swap
|
|
25554
|
+
* needs, see suspendScrollRecording — is asked for by the row itself (see
|
|
25555
|
+
* route_travel.jsx).
|
|
25485
25556
|
*/
|
|
25486
25557
|
|
|
25487
25558
|
|
|
@@ -25519,6 +25590,29 @@ const storePositions = () => {
|
|
|
25519
25590
|
}
|
|
25520
25591
|
};
|
|
25521
25592
|
|
|
25593
|
+
// The document is one scrollport for every page put in it, so a page swapped
|
|
25594
|
+
// under it for a shorter one is an offset the browser CLAMPS — and a clamp is
|
|
25595
|
+
// a scroll event like any other. It is not the reader scrolling, and by the
|
|
25596
|
+
// time it fires the url is already the arriving page's: written down, it is
|
|
25597
|
+
// that page's own position that the page being left destroys.
|
|
25598
|
+
//
|
|
25599
|
+
// Only whoever swaps the page knows when that is happening, so the deafness is
|
|
25600
|
+
// asked for from there and lasts exactly as long as the swap. Counted rather
|
|
25601
|
+
// than flagged: two swaps overlap — a travel relaying into the next one under
|
|
25602
|
+
// the same finger, a travel being undone while it plays.
|
|
25603
|
+
let suspendCount = 0;
|
|
25604
|
+
const suspendScrollRecording = () => {
|
|
25605
|
+
suspendCount++;
|
|
25606
|
+
let resumed = false;
|
|
25607
|
+
return () => {
|
|
25608
|
+
if (resumed) {
|
|
25609
|
+
return;
|
|
25610
|
+
}
|
|
25611
|
+
resumed = true;
|
|
25612
|
+
suspendCount--;
|
|
25613
|
+
};
|
|
25614
|
+
};
|
|
25615
|
+
|
|
25522
25616
|
let installed = false;
|
|
25523
25617
|
const installScrollRestoration = () => {
|
|
25524
25618
|
if (installed) {
|
|
@@ -25536,6 +25630,9 @@ const installScrollRestoration = () => {
|
|
|
25536
25630
|
window.addEventListener(
|
|
25537
25631
|
"scroll",
|
|
25538
25632
|
() => {
|
|
25633
|
+
if (suspendCount) {
|
|
25634
|
+
return;
|
|
25635
|
+
}
|
|
25539
25636
|
positionByUrl.set(window.location.href, {
|
|
25540
25637
|
x: window.scrollX,
|
|
25541
25638
|
y: window.scrollY,
|
|
@@ -25558,12 +25655,43 @@ const installScrollRestoration = () => {
|
|
|
25558
25655
|
|
|
25559
25656
|
// Nothing to put back is not the same as putting back the top: a page arrived
|
|
25560
25657
|
// at for the first time is startAtTop's business, and this must not step on it.
|
|
25658
|
+
// Whether there was anything, for a caller who has an answer of its own for the
|
|
25659
|
+
// page that has never been read.
|
|
25561
25660
|
const restoreScrollPosition = (url) => {
|
|
25562
25661
|
const position = positionByUrl.get(new URL(url, window.location.href).href);
|
|
25563
25662
|
if (!position) {
|
|
25564
|
-
return;
|
|
25663
|
+
return false;
|
|
25565
25664
|
}
|
|
25566
25665
|
scrollTo(position);
|
|
25666
|
+
return true;
|
|
25667
|
+
};
|
|
25668
|
+
|
|
25669
|
+
// A page one arrives at for the first time starts at its top. Only a document
|
|
25670
|
+
// navigation does that on its own: a pushState creates its entry with whatever
|
|
25671
|
+
// scroll happened to be there, so without this the page opens at the offset of
|
|
25672
|
+
// the one before it — and that borrowed offset is what is then remembered FOR
|
|
25673
|
+
// it, and handed back on the way forward.
|
|
25674
|
+
//
|
|
25675
|
+
// The document, because the document is the scrollport in the common case. An
|
|
25676
|
+
// app that scrolls an element of its own scrolls it itself.
|
|
25677
|
+
const startAtTop = (url) => {
|
|
25678
|
+
// A fragment names where to land, and the browser is the one that finds it.
|
|
25679
|
+
if (new URL(url, window.location.href).hash) {
|
|
25680
|
+
return;
|
|
25681
|
+
}
|
|
25682
|
+
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
25683
|
+
};
|
|
25684
|
+
|
|
25685
|
+
// An arrival at a page whose scrollport is already showing another one: the
|
|
25686
|
+
// tabs of a row share the document, and the offset on it is whichever tab was
|
|
25687
|
+
// last read. Where this one was read, and its top when it never was — leaving
|
|
25688
|
+
// the offset alone would seat the reader wherever the neighbour happened to
|
|
25689
|
+
// be, so here "nothing recorded" and "stay" are not the same thing.
|
|
25690
|
+
const arriveAtScrollPosition = (url) => {
|
|
25691
|
+
if (restoreScrollPosition(url)) {
|
|
25692
|
+
return;
|
|
25693
|
+
}
|
|
25694
|
+
startAtTop(url);
|
|
25567
25695
|
};
|
|
25568
25696
|
|
|
25569
25697
|
const scrollTo = ({ x, y }) => {
|
|
@@ -25907,28 +26035,14 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
25907
26035
|
|
|
25908
26036
|
if (navigationType === "push" || navigationType === "replace") {
|
|
25909
26037
|
markUrlAsVisited(url);
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
|
|
25917
|
-
|
|
25918
|
-
};
|
|
25919
|
-
if (state === undefined) {
|
|
25920
|
-
effectiveState = {
|
|
25921
|
-
...(getDocumentState() || {}),
|
|
25922
|
-
...sharedState,
|
|
25923
|
-
};
|
|
25924
|
-
} else if (state === null) {
|
|
25925
|
-
effectiveState = sharedState;
|
|
25926
|
-
} else if (state) {
|
|
25927
|
-
effectiveState = {
|
|
25928
|
-
...state,
|
|
25929
|
-
...sharedState,
|
|
25930
|
-
};
|
|
25931
|
-
}
|
|
26038
|
+
const effectiveState = resolveEffectiveDocumentState(state, {
|
|
26039
|
+
navigationType,
|
|
26040
|
+
currentState: getDocumentState(),
|
|
26041
|
+
sharedState: {
|
|
26042
|
+
jsenv_visited_urls: Array.from(visitedUrlSet),
|
|
26043
|
+
[NAV_DEPTH_STATE_KEY]: getNavDepth(),
|
|
26044
|
+
},
|
|
26045
|
+
});
|
|
25932
26046
|
if (navigationType === "push") {
|
|
25933
26047
|
window.history.pushState(effectiveState, null, url);
|
|
25934
26048
|
} else {
|
|
@@ -25977,12 +26091,21 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
25977
26091
|
isVisited,
|
|
25978
26092
|
state,
|
|
25979
26093
|
});
|
|
26094
|
+
// Where the document lands, said by what kind of arrival this is. Both are
|
|
26095
|
+
// waited for, and for the same two reasons: the page has to be there to be
|
|
26096
|
+
// scrolled, and a picture taken before it would be of a page at its top
|
|
26097
|
+
// (see rendering_hold.js, which is where the waiting happens). After the
|
|
26098
|
+
// history has been written too, so the entry being left keeps the offset
|
|
26099
|
+
// it is at.
|
|
26100
|
+
//
|
|
26101
|
+
// A replace gets neither: it is the same place said differently — a param
|
|
26102
|
+
// settling, a state written — and moving the reader for it would throw
|
|
26103
|
+
// them out of a page they never left. The one replace that IS an arrival
|
|
26104
|
+
// is a row of tabs travelling, and the row says so for itself (see
|
|
26105
|
+
// route_travel.jsx).
|
|
25980
26106
|
if (navigationType === "push") {
|
|
25981
26107
|
whenRenderingResumes(() => startAtTop(url));
|
|
25982
26108
|
} else if (navigationType === "traverse") {
|
|
25983
|
-
// Where this entry was left. Waited for like the reset above, and for
|
|
25984
|
-
// the same two reasons: the page has to be there to be scrolled, and a
|
|
25985
|
-
// picture taken before it would be of a page at its top.
|
|
25986
26109
|
whenRenderingResumes(() => restoreScrollPosition(url));
|
|
25987
26110
|
}
|
|
25988
26111
|
executeWithCleanup(
|
|
@@ -26177,39 +26300,6 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
26177
26300
|
};
|
|
26178
26301
|
};
|
|
26179
26302
|
|
|
26180
|
-
// A page one arrives at for the first time starts at its top. Only a document
|
|
26181
|
-
// navigation does that on its own: a pushState creates its entry with whatever
|
|
26182
|
-
// scroll happened to be there, so without this the new page opens at the offset
|
|
26183
|
-
// of the one before it — and worse, that borrowed offset is what the browser
|
|
26184
|
-
// then remembers FOR that entry, and hands back on the way forward.
|
|
26185
|
-
//
|
|
26186
|
-
// Push only. A traverse is the browser's business and it is already right: it
|
|
26187
|
-
// keeps a position per entry and restores it. A replace is not an arrival —
|
|
26188
|
-
// it is the same place, said differently (a tab row travelling, see
|
|
26189
|
-
// route_travel.jsx), and resetting there would throw the reader out of a page
|
|
26190
|
-
// they never left.
|
|
26191
|
-
//
|
|
26192
|
-
// After the routes have been told, and after the picture of the page being
|
|
26193
|
-
// left has been taken — that ordering is the whole subtlety. The routes
|
|
26194
|
-
// changing is what sets a movement off, and a movement measures the box it is
|
|
26195
|
-
// leaving as it stands; put the document back to its top any earlier and the
|
|
26196
|
-
// picture is of a page at its first line, which the reader was not at. The
|
|
26197
|
-
// browser paints what the new offset shows and nothing else, so what is kept
|
|
26198
|
-
// of the page being left is the band it had already painted, and the movement
|
|
26199
|
-
// carries a fragment (see rendering_hold.js, which is where the waiting
|
|
26200
|
-
// happens). After pushState too, so the entry being left keeps the offset it
|
|
26201
|
-
// is at.
|
|
26202
|
-
//
|
|
26203
|
-
// The document, because the document is the scrollport in the common case. An
|
|
26204
|
-
// app that scrolls an element of its own scrolls it itself.
|
|
26205
|
-
const startAtTop = (url) => {
|
|
26206
|
-
// A fragment names where to land, and the browser is the one that finds it.
|
|
26207
|
-
if (new URL(url, window.location.href).hash) {
|
|
26208
|
-
return;
|
|
26209
|
-
}
|
|
26210
|
-
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
26211
|
-
};
|
|
26212
|
-
|
|
26213
26303
|
let updateRoutes;
|
|
26214
26304
|
|
|
26215
26305
|
const applyActions = (params) => {
|
|
@@ -26996,8 +27086,38 @@ const releaseTransitionDestination = (owner) => {
|
|
|
26996
27086
|
* the page arriving is in the DOM and the transition has not started playing.
|
|
26997
27087
|
* Everything DERIVED from these numbers — the band a fixed bar covers, how far
|
|
26998
27088
|
* a page travels — is derived in CSS, so the application's own numbers (the
|
|
26999
|
-
* room its bars give back, see layout/safe_area.js
|
|
27089
|
+
* room its bars give back, see layout/safe_area.js; what covers the box from
|
|
27090
|
+
* inside the document, --navi-transition-cover-* below) take part in it.
|
|
27091
|
+
*/
|
|
27092
|
+
|
|
27093
|
+
/**
|
|
27094
|
+
* What covers the box from INSIDE the document: a sticky row of tabs above the
|
|
27095
|
+
* pages, a header pinned to the top of a scroller. Declared at zero and
|
|
27096
|
+
* written by whoever covers it, exactly as a fixed bar publishes the room it
|
|
27097
|
+
* gives back (layout/safe_area.js).
|
|
27098
|
+
*
|
|
27099
|
+
* A slot rather than a measurement, and a slot navi cannot fill itself: the
|
|
27100
|
+
* safe area answers for what is pinned to the WINDOW's edges, and a sticky row
|
|
27101
|
+
* is none of those — it lives in the document, below the bars, and covers the
|
|
27102
|
+
* top of the box exactly as a fixed bar covers the top of the screen. The
|
|
27103
|
+
* pictures of a transition are drawn in the top layer, where no z-index of the
|
|
27104
|
+
* document reaches them, so what the document paints over the box has to be
|
|
27105
|
+
* counted here or it is the pictures that paint over it.
|
|
27106
|
+
*
|
|
27107
|
+
* Each one is a distance inward from the band the safe area already leaves
|
|
27108
|
+
* free, so a row states its own height and nothing else: what the bars above
|
|
27109
|
+
* it take is already counted.
|
|
27000
27110
|
*/
|
|
27111
|
+
const TRANSITION_WINDOW_CSS = /* css */ `
|
|
27112
|
+
@layer navi {
|
|
27113
|
+
:root {
|
|
27114
|
+
--navi-transition-cover-top: 0px;
|
|
27115
|
+
--navi-transition-cover-right: 0px;
|
|
27116
|
+
--navi-transition-cover-bottom: 0px;
|
|
27117
|
+
--navi-transition-cover-left: 0px;
|
|
27118
|
+
}
|
|
27119
|
+
}
|
|
27120
|
+
`;
|
|
27001
27121
|
|
|
27002
27122
|
const WINDOW_TOP_PROPERTY = "--navi-transition-window-top";
|
|
27003
27123
|
const WINDOW_LEFT_PROPERTY = "--navi-transition-window-left";
|
|
@@ -27144,6 +27264,8 @@ const ROUTE_TRAVEL_ATTRIBUTE = "data-navi-route-travel";
|
|
|
27144
27264
|
// included).
|
|
27145
27265
|
|
|
27146
27266
|
const css$10 = /* css */`
|
|
27267
|
+
${TRANSITION_WINDOW_CSS}
|
|
27268
|
+
|
|
27147
27269
|
/* The marked region is a picture of its own for the length of a transition of
|
|
27148
27270
|
OURS, and only then — the name is what makes the pages a picture the
|
|
27149
27271
|
movement below can carry.
|
|
@@ -27228,39 +27350,46 @@ const css$10 = /* css */`
|
|
|
27228
27350
|
height: var(--navi-transition-window-height);
|
|
27229
27351
|
animation-name: none;
|
|
27230
27352
|
|
|
27231
|
-
/* Cut at the
|
|
27232
|
-
pictures are drawn in the top layer, so they cover a fixed bar
|
|
27233
|
-
easily as anything else — and the area runs UNDER the bars by
|
|
27234
|
-
that is what a fixed bar is for, and what the room it gives
|
|
27235
|
-
for. An area taller than the screen therefore ends below the
|
|
27236
|
-
bar, and a scrolled one starts above the top bar, so the
|
|
27237
|
-
would be watched painting over them for its whole length.
|
|
27238
|
-
|
|
27239
|
-
|
|
27240
|
-
layout/safe_area.js)
|
|
27241
|
-
|
|
27242
|
-
|
|
27243
|
-
the
|
|
27244
|
-
|
|
27353
|
+
/* Cut at what covers the area, on top of being cut at the area's own
|
|
27354
|
+
box. The pictures are drawn in the top layer, so they cover a fixed bar
|
|
27355
|
+
as easily as anything else — and the area runs UNDER the bars by
|
|
27356
|
+
design: that is what a fixed bar is for, and what the room it gives
|
|
27357
|
+
back is for. An area taller than the screen therefore ends below the
|
|
27358
|
+
bottom bar, and a scrolled one starts above the top bar, so the
|
|
27359
|
+
movement would be watched painting over them for its whole length.
|
|
27360
|
+
|
|
27361
|
+
Two bands are left free, and they answer for two different things: the
|
|
27362
|
+
app's own safe area (layout/safe_area.js), everything pinned to the
|
|
27363
|
+
WINDOW's edges, and --navi-transition-cover-* (transition_window.js),
|
|
27364
|
+
what covers the area from inside the document — a sticky header above
|
|
27365
|
+
the pages covers the top of the area exactly as a fixed bar covers the
|
|
27366
|
+
top of the screen. Both are read rather than asked for, so one that
|
|
27367
|
+
grows, shrinks or unmounts mid-transition is followed without anything
|
|
27368
|
+
being told. What the window cannot know is only where it itself stands,
|
|
27369
|
+
and that is the measured half. */
|
|
27245
27370
|
--navi-route-transition-clip-top: max(
|
|
27246
27371
|
0px,
|
|
27247
|
-
var(--navi-safe-area-inset-top)
|
|
27372
|
+
var(--navi-safe-area-inset-top) +
|
|
27373
|
+
var(--navi-transition-cover-top) - var(--navi-transition-window-top)
|
|
27248
27374
|
);
|
|
27249
27375
|
--navi-route-transition-clip-left: max(
|
|
27250
27376
|
0px,
|
|
27251
|
-
var(--navi-safe-area-inset-left)
|
|
27377
|
+
var(--navi-safe-area-inset-left) +
|
|
27378
|
+
var(--navi-transition-cover-left) - var(--navi-transition-window-left)
|
|
27252
27379
|
);
|
|
27253
27380
|
--navi-route-transition-clip-bottom: max(
|
|
27254
27381
|
0px,
|
|
27255
27382
|
var(--navi-transition-window-top) +
|
|
27256
27383
|
var(--navi-transition-window-height) +
|
|
27257
|
-
var(--navi-safe-area-inset-bottom)
|
|
27384
|
+
var(--navi-safe-area-inset-bottom) +
|
|
27385
|
+
var(--navi-transition-cover-bottom) - 100dvh
|
|
27258
27386
|
);
|
|
27259
27387
|
--navi-route-transition-clip-right: max(
|
|
27260
27388
|
0px,
|
|
27261
27389
|
var(--navi-transition-window-left) +
|
|
27262
27390
|
var(--navi-transition-window-width) +
|
|
27263
|
-
var(--navi-safe-area-inset-right)
|
|
27391
|
+
var(--navi-safe-area-inset-right) +
|
|
27392
|
+
var(--navi-transition-cover-right) - 100dvw
|
|
27264
27393
|
);
|
|
27265
27394
|
clip-path: inset(
|
|
27266
27395
|
var(--navi-route-transition-clip-top)
|
|
@@ -28278,6 +28407,8 @@ const TURNED_ATTRIBUTE = "data-navi-route-travel-turned";
|
|
|
28278
28407
|
// The name the box wears while it travels, and only then (see nameForTravel).
|
|
28279
28408
|
const TRAVEL_NAME = "navi-route-travel";
|
|
28280
28409
|
const css$$ = /* css */`
|
|
28410
|
+
${TRANSITION_WINDOW_CSS}
|
|
28411
|
+
|
|
28281
28412
|
/* The name that makes the page inside this box a picture of its own during a
|
|
28282
28413
|
transition — rather than part of the one big picture the document takes, so
|
|
28283
28414
|
the two pages can move past each other while everything else stays where it
|
|
@@ -28396,38 +28527,46 @@ const css$$ = /* css */`
|
|
|
28396
28527
|
route to the next. */
|
|
28397
28528
|
height: var(--navi-transition-window-height);
|
|
28398
28529
|
|
|
28399
|
-
/* Cut at the
|
|
28400
|
-
drawn in the top layer, so they cover a fixed bar as
|
|
28401
|
-
else — and the box they travel in runs UNDER the
|
|
28402
|
-
is what a fixed bar is for, and what the room it
|
|
28403
|
-
box scrolled by so much as a pixel therefore
|
|
28404
|
-
and ends below the bottom one, and the travel
|
|
28405
|
-
over both for its whole length.
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28410
|
-
|
|
28411
|
-
|
|
28530
|
+
/* Cut at what covers the box, on top of being cut at the box. The
|
|
28531
|
+
pictures are drawn in the top layer, so they cover a fixed bar as
|
|
28532
|
+
easily as anything else — and the box they travel in runs UNDER the
|
|
28533
|
+
bars by design: that is what a fixed bar is for, and what the room it
|
|
28534
|
+
gives back is for. A box scrolled by so much as a pixel therefore
|
|
28535
|
+
starts above the top bar and ends below the bottom one, and the travel
|
|
28536
|
+
would be watched painting over both for its whole length.
|
|
28537
|
+
|
|
28538
|
+
Two bands are left free, and they answer for two different things: the
|
|
28539
|
+
app's own safe area (layout/safe_area.js), everything pinned to the
|
|
28540
|
+
WINDOW's edges, and --navi-transition-cover-* (transition_window.js),
|
|
28541
|
+
what covers the box from inside the document — a sticky row of tabs
|
|
28542
|
+
above the pages covers the top of the box exactly as a fixed bar covers
|
|
28543
|
+
the top of the screen. Both are read rather than asked for, so one that
|
|
28544
|
+
grows, shrinks or unmounts mid-travel is followed without anything
|
|
28545
|
+
being told. What the group cannot know is only where it itself stands,
|
|
28546
|
+
and that is the measured half. */
|
|
28412
28547
|
--navi-route-travel-clip-top: max(
|
|
28413
28548
|
0px,
|
|
28414
|
-
var(--navi-safe-area-inset-top)
|
|
28549
|
+
var(--navi-safe-area-inset-top) +
|
|
28550
|
+
var(--navi-transition-cover-top) - var(--navi-transition-window-top)
|
|
28415
28551
|
);
|
|
28416
28552
|
--navi-route-travel-clip-left: max(
|
|
28417
28553
|
0px,
|
|
28418
|
-
var(--navi-safe-area-inset-left)
|
|
28554
|
+
var(--navi-safe-area-inset-left) +
|
|
28555
|
+
var(--navi-transition-cover-left) - var(--navi-transition-window-left)
|
|
28419
28556
|
);
|
|
28420
28557
|
--navi-route-travel-clip-bottom: max(
|
|
28421
28558
|
0px,
|
|
28422
28559
|
var(--navi-transition-window-top) +
|
|
28423
28560
|
var(--navi-transition-window-height) +
|
|
28424
|
-
var(--navi-safe-area-inset-bottom)
|
|
28561
|
+
var(--navi-safe-area-inset-bottom) +
|
|
28562
|
+
var(--navi-transition-cover-bottom) - 100dvh
|
|
28425
28563
|
);
|
|
28426
28564
|
--navi-route-travel-clip-right: max(
|
|
28427
28565
|
0px,
|
|
28428
28566
|
var(--navi-transition-window-left) +
|
|
28429
28567
|
var(--navi-transition-window-width) +
|
|
28430
|
-
var(--navi-safe-area-inset-right)
|
|
28568
|
+
var(--navi-safe-area-inset-right) +
|
|
28569
|
+
var(--navi-transition-cover-right) - 100dvw
|
|
28431
28570
|
);
|
|
28432
28571
|
clip-path: inset(
|
|
28433
28572
|
var(--navi-route-travel-clip-top) var(--navi-route-travel-clip-right)
|
|
@@ -28736,6 +28875,19 @@ const RouteTravel = ({
|
|
|
28736
28875
|
ended: false
|
|
28737
28876
|
};
|
|
28738
28877
|
travelRef.current = travel;
|
|
28878
|
+
// Whether the document's offset is this row's business at all (see
|
|
28879
|
+
// pagesScrollTheDocument), asked once and before anything is swapped.
|
|
28880
|
+
travel.scrollsDocument = pagesScrollTheDocument(elementRef.current);
|
|
28881
|
+
if (travel.scrollsDocument) {
|
|
28882
|
+
// One of the tabs is about to be swapped out from under that scrollport,
|
|
28883
|
+
// and whatever the browser does with the offset meanwhile — clamping it
|
|
28884
|
+
// to a page shorter than the trip — is not the reader scrolling. The url
|
|
28885
|
+
// it would be written against is already the arriving tab's, so recorded
|
|
28886
|
+
// it is that tab's own position that the tab being left destroys. Deaf
|
|
28887
|
+
// for the whole travel, which is exactly as long as the offset belongs
|
|
28888
|
+
// to nobody (see endTravel).
|
|
28889
|
+
travel.resumeScrollRecording = suspendScrollRecording();
|
|
28890
|
+
}
|
|
28739
28891
|
// Taken before the picture is: the browser reads the name off the DOM as it
|
|
28740
28892
|
// stands when the transition starts, and this box is only a picture of its
|
|
28741
28893
|
// own for as long as it is the one travelling. Where it travels to is said
|
|
@@ -28778,8 +28930,24 @@ const RouteTravel = ({
|
|
|
28778
28930
|
await change();
|
|
28779
28931
|
}
|
|
28780
28932
|
}, renderWait);
|
|
28933
|
+
// Where the arriving tab was left, or its top when it has never been
|
|
28934
|
+
// read. A travel is an arrival — the tab pressed is another route, and
|
|
28935
|
+
// the replace it navigates by is the only thing about it that says
|
|
28936
|
+
// otherwise — and this is the one place that knows it. Read from the
|
|
28937
|
+
// travel rather than from the page it set off for: a travel aimed
|
|
28938
|
+
// somewhere else while it waited lands where it is aimed now.
|
|
28939
|
+
if (travel.scrollsDocument) {
|
|
28940
|
+
const {
|
|
28941
|
+
route,
|
|
28942
|
+
params
|
|
28943
|
+
} = travel.page;
|
|
28944
|
+
arriveAtScrollPosition(route.buildUrl(params));
|
|
28945
|
+
}
|
|
28781
28946
|
// The page arriving is in the DOM and the transition has not started
|
|
28782
|
-
// playing: the one moment both boxes can be known.
|
|
28947
|
+
// playing: the one moment both boxes can be known. After the scroll,
|
|
28948
|
+
// which is what the box is measured through: the two states are at the
|
|
28949
|
+
// same place in the layout without being at the same place in the window
|
|
28950
|
+
// (see transition_window.js).
|
|
28783
28951
|
holdTransitionWindow(travel, elementRef.current, rectBefore);
|
|
28784
28952
|
});
|
|
28785
28953
|
travel.viewTransition = viewTransition;
|
|
@@ -29000,6 +29168,25 @@ const RouteTravel = ({
|
|
|
29000
29168
|
}
|
|
29001
29169
|
travel.viewTransition.skipTransition();
|
|
29002
29170
|
} finally {
|
|
29171
|
+
if (travel.scrollsDocument) {
|
|
29172
|
+
// The page coming back was read somewhere else, and the offset
|
|
29173
|
+
// currently on the document is the one the page that came in put
|
|
29174
|
+
// there. Given back once the page is really back — the render is
|
|
29175
|
+
// what makes the document tall enough to hold that offset again —
|
|
29176
|
+
// and the recording stays deaf until then, over the clamp the swap
|
|
29177
|
+
// back makes on the way. Its own deafness rather than the travel's:
|
|
29178
|
+
// the travel ends here, and this outlives it by a render.
|
|
29179
|
+
const resumeScrollRecording = suspendScrollRecording();
|
|
29180
|
+
const stopWatchingRender = observeRouteRender(() => {
|
|
29181
|
+
stopWatchingRender();
|
|
29182
|
+
const {
|
|
29183
|
+
route,
|
|
29184
|
+
params
|
|
29185
|
+
} = travel.fromPage;
|
|
29186
|
+
arriveAtScrollPosition(route.buildUrl(params));
|
|
29187
|
+
resumeScrollRecording();
|
|
29188
|
+
});
|
|
29189
|
+
}
|
|
29003
29190
|
releaseRendering();
|
|
29004
29191
|
// A travel ENDS, whatever happened on the way back: put the state back,
|
|
29005
29192
|
// fail to drop the picture, be interrupted by something else — the one
|
|
@@ -29085,6 +29272,11 @@ const RouteTravel = ({
|
|
|
29085
29272
|
travel.ended = true;
|
|
29086
29273
|
travel.dropHold?.();
|
|
29087
29274
|
travel.dropHold = null;
|
|
29275
|
+
// The offset on the scrollport belongs to a page again — the one that
|
|
29276
|
+
// arrived, or the one put back. A travel that never got as far as saying
|
|
29277
|
+
// which still has to give the recording back; the gesture's own
|
|
29278
|
+
// pseudo-travel never took it (see noPicture).
|
|
29279
|
+
travel.resumeScrollRecording?.();
|
|
29088
29280
|
// Its own hold, always — whether or not this travel is still the current
|
|
29089
29281
|
// one. Nobody else will lift it.
|
|
29090
29282
|
releaseHold(travel);
|
|
@@ -29620,6 +29812,32 @@ const whilePageRenders = async (page, change, wait = armRouteRenderWait()) => {
|
|
|
29620
29812
|
}
|
|
29621
29813
|
};
|
|
29622
29814
|
|
|
29815
|
+
// Whether the document's offset belongs to the tabs. The pages of a row scroll
|
|
29816
|
+
// the document when nothing between the box and the viewport is a scroll
|
|
29817
|
+
// container: the tab on screen is then what makes the document tall, and the
|
|
29818
|
+
// offset on it is that tab's — it has to be given back with the tab, and the
|
|
29819
|
+
// browser's clamping of it while pages are swapped has to be ignored.
|
|
29820
|
+
//
|
|
29821
|
+
// A box that lives inside a scroller of its own — a frame in an article, a
|
|
29822
|
+
// panel beside other content — shares nothing with the document: the offset
|
|
29823
|
+
// there is the surrounding page's, the reader never left it, and a travel has
|
|
29824
|
+
// no business moving it. Each of its pages brings its own scrollport, which
|
|
29825
|
+
// goes away with the page and has nothing to restore.
|
|
29826
|
+
//
|
|
29827
|
+
// includeHidden, because an `overflow: hidden` ancestor is one of those
|
|
29828
|
+
// scrollers: it shows no scrollbar and still holds an offset of its own, and
|
|
29829
|
+
// what is under it never reaches the document. A wrapper that merely clips
|
|
29830
|
+
// (`overflow: clip`) holds nothing and is walked straight through — see
|
|
29831
|
+
// is_scrollable.js.
|
|
29832
|
+
const pagesScrollTheDocument = element => {
|
|
29833
|
+
const scrollContainer = getScrollContainer(element, {
|
|
29834
|
+
includeHidden: true
|
|
29835
|
+
});
|
|
29836
|
+
// html and body are one answer: whichever of them the walk stops on, what
|
|
29837
|
+
// scrolls is the viewport, which is what window.scrollTo moves.
|
|
29838
|
+
return scrollContainer === document.documentElement || scrollContainer === document.body;
|
|
29839
|
+
};
|
|
29840
|
+
|
|
29623
29841
|
// A page of the row: a route, and the params that say which of its tabs when
|
|
29624
29842
|
// several of them share it. Written as a bare route by a caller whose tabs are
|
|
29625
29843
|
// routes of their own — which is the same page with nothing to tell apart.
|
|
@@ -52935,6 +53153,8 @@ const TimeTime = ({
|
|
|
52935
53153
|
children,
|
|
52936
53154
|
lang = languagesSignal.value,
|
|
52937
53155
|
format = "long",
|
|
53156
|
+
pad = true,
|
|
53157
|
+
precision = pad ? "minute" : "hour",
|
|
52938
53158
|
...props
|
|
52939
53159
|
}) => {
|
|
52940
53160
|
if (children === undefined) {
|
|
@@ -52943,13 +53163,7 @@ const TimeTime = ({
|
|
|
52943
53163
|
children: "--:--"
|
|
52944
53164
|
});
|
|
52945
53165
|
}
|
|
52946
|
-
const date =
|
|
52947
|
-
if (/^\d{2}:\d{2}(?::\d{2})?$/.test(value)) {
|
|
52948
|
-
const d = new Date(`1970-01-01T${value}`);
|
|
52949
|
-
return isNaN(d.getTime()) ? null : d;
|
|
52950
|
-
}
|
|
52951
|
-
return null;
|
|
52952
|
-
});
|
|
53166
|
+
const date = toTimeOfDay(children);
|
|
52953
53167
|
// toDate turns a non-finite number into an Invalid Date, which is an object
|
|
52954
53168
|
if (!date || isNaN(date.getTime())) {
|
|
52955
53169
|
return jsx(TimeText, {
|
|
@@ -52986,7 +53200,9 @@ const TimeTime = ({
|
|
|
52986
53200
|
text = formatMinuteDuration(totalMinutes, {
|
|
52987
53201
|
lang,
|
|
52988
53202
|
format,
|
|
52989
|
-
clockStyle: true
|
|
53203
|
+
clockStyle: true,
|
|
53204
|
+
pad,
|
|
53205
|
+
precision
|
|
52990
53206
|
});
|
|
52991
53207
|
} else if (format !== "long") {
|
|
52992
53208
|
// short/narrow/compact: keep the "0 h"/"0h" hour part instead of
|
|
@@ -52996,7 +53212,9 @@ const TimeTime = ({
|
|
|
52996
53212
|
text = formatMinuteDuration(totalMinutes, {
|
|
52997
53213
|
lang,
|
|
52998
53214
|
format,
|
|
52999
|
-
clockStyle: true
|
|
53215
|
+
clockStyle: true,
|
|
53216
|
+
pad,
|
|
53217
|
+
precision
|
|
53000
53218
|
});
|
|
53001
53219
|
} else {
|
|
53002
53220
|
const midnightWord = naviI18n("time.midnight", undefined, {
|
|
@@ -53287,6 +53505,111 @@ const TimeText = props => {
|
|
|
53287
53505
|
...props
|
|
53288
53506
|
});
|
|
53289
53507
|
};
|
|
53508
|
+
|
|
53509
|
+
/**
|
|
53510
|
+
* Displays a span between two instants — an opening slot, an availability
|
|
53511
|
+
* window — as the two `<time>` elements `<Time>` would render, around a
|
|
53512
|
+
* separator.
|
|
53513
|
+
*
|
|
53514
|
+
* On top of writing the separator, it makes the two bounds agree on how
|
|
53515
|
+
* precisely they are written, which is a property of the pair and of nothing
|
|
53516
|
+
* else: with `type="time" format="compact" pad={false}`, "08:00"–"10:00" reads
|
|
53517
|
+
* "8h–10h", but "11:30"–"14:00" reads "11h30–14h00" and not "11h30–14h", where
|
|
53518
|
+
* the eye stops on the difference of shape before it reads the hours. Any bound
|
|
53519
|
+
* with minutes gives minutes to both, zero included. The padded clock
|
|
53520
|
+
* (`pad` left at its default) already writes every bound at the same width, and
|
|
53521
|
+
* the spelled-out formats say their units in words, so neither needs the rule
|
|
53522
|
+
* and neither is touched by it.
|
|
53523
|
+
*
|
|
53524
|
+
* @param {Date|number|string} from
|
|
53525
|
+
* The start of the span, in whatever `<Time>` accepts for this `type`.
|
|
53526
|
+
* @param {Date|number|string} to
|
|
53527
|
+
* The end of the span. An undefined bound renders `<Time>`'s own placeholder.
|
|
53528
|
+
* @param {"date"|"month"|"datetime"|"time"|"hour"|"minute"|"second"} [type="time"]
|
|
53529
|
+
* Passed to both bounds. Only `"time"` gets the shared-precision rule; the
|
|
53530
|
+
* other types are written one after the other, with nothing factored out (a
|
|
53531
|
+
* date span reads "11 mai – 14 mai", never "du 11 au 14 mai").
|
|
53532
|
+
* @param {"hour"|"minute"} [precision]
|
|
53533
|
+
* Writes both bounds at this precision instead of the one the pair calls for
|
|
53534
|
+
* — `"minute"` to keep a zero minute on both ("8h00–10h00"), `"hour"` to drop
|
|
53535
|
+
* it on both ("8h–11h30", which is the shape the rule exists to avoid: set it
|
|
53536
|
+
* only when you mean it).
|
|
53537
|
+
* @param {string} [separator]
|
|
53538
|
+
* What goes between the two bounds. Defaults to the `"time.range_separator"`
|
|
53539
|
+
* navi text (an en dash), tightened against both bounds in `format="compact"`
|
|
53540
|
+
* — where the span is one short token — and spaced out otherwise.
|
|
53541
|
+
*
|
|
53542
|
+
* Every other prop is forwarded to both bounds; see `<Time>`.
|
|
53543
|
+
*/
|
|
53544
|
+
const TimeRange = ({
|
|
53545
|
+
from,
|
|
53546
|
+
to,
|
|
53547
|
+
type = "time",
|
|
53548
|
+
format = "long",
|
|
53549
|
+
lang = languagesSignal.value,
|
|
53550
|
+
pad = true,
|
|
53551
|
+
precision,
|
|
53552
|
+
separator = naviI18n("time.range_separator", undefined, {
|
|
53553
|
+
lang
|
|
53554
|
+
}),
|
|
53555
|
+
...props
|
|
53556
|
+
}) => {
|
|
53557
|
+
const boundProps = {
|
|
53558
|
+
type,
|
|
53559
|
+
format,
|
|
53560
|
+
lang
|
|
53561
|
+
};
|
|
53562
|
+
if (type === "time") {
|
|
53563
|
+
boundProps.pad = pad;
|
|
53564
|
+
boundProps.precision = precision ?? resolvePairPrecision(from, to, {
|
|
53565
|
+
format,
|
|
53566
|
+
pad
|
|
53567
|
+
});
|
|
53568
|
+
}
|
|
53569
|
+
// compact writes the whole span as one short token ("8h–10h"): nothing
|
|
53570
|
+
// around the separator, and no break inside it. The other formats are
|
|
53571
|
+
// phrases — they get room around the separator, and may wrap there.
|
|
53572
|
+
const tight = format === "compact";
|
|
53573
|
+
return jsxs(Text, {
|
|
53574
|
+
noWrap: tight,
|
|
53575
|
+
...props,
|
|
53576
|
+
children: [jsx(Time, {
|
|
53577
|
+
...boundProps,
|
|
53578
|
+
children: from
|
|
53579
|
+
}), tight ? separator : ` ${separator} `, jsx(Time, {
|
|
53580
|
+
...boundProps,
|
|
53581
|
+
children: to
|
|
53582
|
+
})]
|
|
53583
|
+
});
|
|
53584
|
+
};
|
|
53585
|
+
|
|
53586
|
+
// The two bounds of a span are written to the same precision, decided by the
|
|
53587
|
+
// pair: "8h–10h" as long as neither has minutes, "11h30–14h00" as soon as one
|
|
53588
|
+
// of them does. Only ever a question for the unpadded compact clock — the
|
|
53589
|
+
// padded one always writes "08h00", and the spelled-out formats name their
|
|
53590
|
+
// units, leaving no shape for the eye to trip on.
|
|
53591
|
+
const resolvePairPrecision = (from, to, {
|
|
53592
|
+
format,
|
|
53593
|
+
pad
|
|
53594
|
+
}) => {
|
|
53595
|
+
if (pad || format !== "compact") {
|
|
53596
|
+
return "minute";
|
|
53597
|
+
}
|
|
53598
|
+
const hasMinutes = value => {
|
|
53599
|
+
const date = toTimeOfDay(value);
|
|
53600
|
+
return Boolean(date) && !isNaN(date.getTime()) && date.getMinutes() !== 0;
|
|
53601
|
+
};
|
|
53602
|
+
return hasMinutes(from) || hasMinutes(to) ? "minute" : "hour";
|
|
53603
|
+
};
|
|
53604
|
+
const toTimeOfDay = value => {
|
|
53605
|
+
return toDate(value, string => {
|
|
53606
|
+
if (/^\d{2}:\d{2}(?::\d{2})?$/.test(string)) {
|
|
53607
|
+
const d = new Date(`1970-01-01T${string}`);
|
|
53608
|
+
return isNaN(d.getTime()) ? null : d;
|
|
53609
|
+
}
|
|
53610
|
+
return null;
|
|
53611
|
+
});
|
|
53612
|
+
};
|
|
53290
53613
|
const toDate = (value, parseString) => {
|
|
53291
53614
|
if (value instanceof Date) {
|
|
53292
53615
|
return value;
|
|
@@ -53768,7 +54091,7 @@ const createOpenController = (
|
|
|
53768
54091
|
// Last: the close effects above are what starts the exit transition the
|
|
53769
54092
|
// content must outlive (see popup_content_mount.js).
|
|
53770
54093
|
controller.unmountContent?.();
|
|
53771
|
-
controller.onOpenedChange?.(false);
|
|
54094
|
+
controller.onOpenedChange?.(false, closeEvent);
|
|
53772
54095
|
};
|
|
53773
54096
|
const controller = {
|
|
53774
54097
|
opened: false,
|
|
@@ -53788,9 +54111,10 @@ const createOpenController = (
|
|
|
53788
54111
|
// away on close (`unmountWhenClosed`). Called from performClose above.
|
|
53789
54112
|
unmountContent: null,
|
|
53790
54113
|
// Told whenever `opened` actually changes, whatever asked for it — an
|
|
53791
|
-
// interaction, a command, a prop. What lets a
|
|
53792
|
-
// popup's real state
|
|
53793
|
-
//
|
|
54114
|
+
// interaction, a command, a prop — with the event that asked. What lets a
|
|
54115
|
+
// `signal` prop reflect the popup's real state, and a `navState` prop write
|
|
54116
|
+
// it into the history entry (see useOpenPropsEffectOnOpenController);
|
|
54117
|
+
// called once the open/close has fully happened rather than mid-sequence.
|
|
53794
54118
|
onOpenedChange: null,
|
|
53795
54119
|
open: (e, detail) => {
|
|
53796
54120
|
if (controller.opened || !controller.openEffect) {
|
|
@@ -53894,7 +54218,7 @@ const createOpenController = (
|
|
|
53894
54218
|
openEffectReturnValue?.(closeEvent);
|
|
53895
54219
|
};
|
|
53896
54220
|
closeHandlers = openHandler(requestOpenEvent) || null;
|
|
53897
|
-
controller.onOpenedChange?.(true);
|
|
54221
|
+
controller.onOpenedChange?.(true, requestOpenEvent);
|
|
53898
54222
|
},
|
|
53899
54223
|
requestClose: (
|
|
53900
54224
|
e = new CustomEvent("programmatic", { detail: {} }),
|
|
@@ -54014,33 +54338,83 @@ const scheduleMountOpen = (run) => {
|
|
|
54014
54338
|
});
|
|
54015
54339
|
};
|
|
54016
54340
|
|
|
54341
|
+
// Where the popup's open state is kept, when it is kept anywhere: `navState`
|
|
54342
|
+
// resolved to the `{ id, type }` useNavState wants.
|
|
54343
|
+
//
|
|
54344
|
+
// `true` takes the popup's own id — a popup a `--navi-open` command can name is
|
|
54345
|
+
// a popup that already has a stable one, and that id is what identifies its
|
|
54346
|
+
// open state too.
|
|
54347
|
+
const NO_NAV_STATE = { id: undefined, type: "replace" };
|
|
54348
|
+
const resolveNavStateProp = (navState, popupId) => {
|
|
54349
|
+
if (!navState) {
|
|
54350
|
+
return NO_NAV_STATE;
|
|
54351
|
+
}
|
|
54352
|
+
if (navState === true) {
|
|
54353
|
+
return { id: popupId, type: "replace" };
|
|
54354
|
+
}
|
|
54355
|
+
if (typeof navState === "string") {
|
|
54356
|
+
return { id: navState, type: "replace" };
|
|
54357
|
+
}
|
|
54358
|
+
return { id: navState.id || popupId, type: navState.type || "replace" };
|
|
54359
|
+
};
|
|
54360
|
+
|
|
54017
54361
|
/**
|
|
54018
|
-
* Keeps an open controller in sync with
|
|
54019
|
-
*
|
|
54020
|
-
*
|
|
54021
|
-
*
|
|
54022
|
-
*
|
|
54023
|
-
*
|
|
54024
|
-
* `
|
|
54362
|
+
* Keeps an open controller in sync with where the caller says the popup should
|
|
54363
|
+
* be: an `open`/`defaultOpen` pair, a `signal`, or a `navState` — the open
|
|
54364
|
+
* state written into the history entry, so a screen left and come back to finds
|
|
54365
|
+
* its popup as it was.
|
|
54366
|
+
*
|
|
54367
|
+
* Shared between `useOpenControllerByProps` below (Dialog/Popover driving their
|
|
54368
|
+
* own controller) and `picker_custom.jsx` (which owns its controller but wants
|
|
54369
|
+
* the same skip-if-already-matching / open-or-requestClose control flow).
|
|
54025
54370
|
*
|
|
54026
54371
|
* @param {{ open: (e: Event, detail?: object) => void, requestClose: (e: Event, detail?: object) => void, opened: boolean }} openController
|
|
54027
|
-
* @param {{ open?: boolean|"interaction", defaultOpen?: boolean|"interaction", signal?: import("@preact/signals").Signal<boolean
|
|
54372
|
+
* @param {{ id?: string, open?: boolean|"interaction", defaultOpen?: boolean|"interaction", signal?: import("@preact/signals").Signal<boolean>, navState?: boolean|string|{id?: string, type?: "push"|"replace"} }} props
|
|
54028
54373
|
*/
|
|
54029
54374
|
const useOpenPropsEffectOnOpenController = (openController, props) => {
|
|
54030
|
-
const { signal, defaultOpen } = props;
|
|
54031
|
-
|
|
54032
|
-
|
|
54033
|
-
|
|
54034
|
-
|
|
54375
|
+
const { signal, defaultOpen, navState } = props;
|
|
54376
|
+
const { id: navStateId, type: navStateType } = resolveNavStateProp(
|
|
54377
|
+
navState,
|
|
54378
|
+
props.id,
|
|
54379
|
+
);
|
|
54380
|
+
// Called unconditionally (it answers with no-ops for an absent id), like
|
|
54381
|
+
// every other hook here.
|
|
54382
|
+
const [navStateValue, enterNavState, leaveNavState] = useNavState(
|
|
54383
|
+
navStateId,
|
|
54384
|
+
{ type: navStateType },
|
|
54385
|
+
);
|
|
54386
|
+
// What the caller holds, however they hold it: the history entry when there
|
|
54387
|
+
// is a `navState`, an `open` they re-render themselves, or a `signal` this
|
|
54388
|
+
// hook also writes (see onOpenedChange below). Reading .value during render
|
|
54389
|
+
// is what subscribes the popup to a signal; reading the document state is
|
|
54390
|
+
// what subscribes it to the history entry, back button included.
|
|
54391
|
+
const open = navStateId
|
|
54392
|
+
? Boolean(navStateValue)
|
|
54393
|
+
: signal
|
|
54394
|
+
? signal.value
|
|
54395
|
+
: props.open;
|
|
54035
54396
|
// Assigned on every render, like openEffect, so it always closes over the
|
|
54036
54397
|
// latest prop: a popup that opens or closes on its own (Escape, backdrop, a
|
|
54037
|
-
// --navi-close command) writes what happened
|
|
54038
|
-
// holds it always reads where the popup is.
|
|
54039
|
-
openController.onOpenedChange =
|
|
54040
|
-
|
|
54041
|
-
|
|
54042
|
-
|
|
54043
|
-
|
|
54398
|
+
// --navi-close command) writes what happened where the caller keeps it, so
|
|
54399
|
+
// whoever holds it always reads where the popup is.
|
|
54400
|
+
openController.onOpenedChange =
|
|
54401
|
+
navStateId || signal
|
|
54402
|
+
? (opened, event) => {
|
|
54403
|
+
if (navStateId) {
|
|
54404
|
+
if (opened) {
|
|
54405
|
+
enterNavState();
|
|
54406
|
+
} else {
|
|
54407
|
+
// Under type "push" a cancel goes back rather than rewriting the
|
|
54408
|
+
// entry, so everything else written to the url while the popup
|
|
54409
|
+
// was open goes back with it (see useNavState's own leave()).
|
|
54410
|
+
leaveNavState({ isBack: Boolean(event?.detail?.isCancel) });
|
|
54411
|
+
}
|
|
54412
|
+
}
|
|
54413
|
+
if (signal) {
|
|
54414
|
+
signal.value = opened;
|
|
54415
|
+
}
|
|
54416
|
+
}
|
|
54417
|
+
: null;
|
|
54044
54418
|
// Tracks whether the effect below has ever run before — only the very
|
|
54045
54419
|
// first run gets the "mount already open" treatment (`open` truthy from
|
|
54046
54420
|
// the start, or the uncontrolled, mount-only `defaultOpen`); every
|
|
@@ -54093,12 +54467,15 @@ const useOpenPropsEffectOnOpenController = (openController, props) => {
|
|
|
54093
54467
|
{ isCancel: true },
|
|
54094
54468
|
);
|
|
54095
54469
|
}
|
|
54470
|
+
// The request can be refused (a busy form denying the close): the popup
|
|
54471
|
+
// then stays where it was, and whoever holds the open state is told so —
|
|
54472
|
+
// otherwise it would keep saying "closed" about a popup still open.
|
|
54096
54473
|
if (signal) {
|
|
54097
|
-
// The request can be refused (a busy form denying the close): the popup
|
|
54098
|
-
// then stays where it was, and the signal is told so — otherwise it
|
|
54099
|
-
// would keep saying "closed" about a popup still open.
|
|
54100
54474
|
signal.value = openController.opened;
|
|
54101
54475
|
}
|
|
54476
|
+
if (navStateId && openController.opened) {
|
|
54477
|
+
enterNavState();
|
|
54478
|
+
}
|
|
54102
54479
|
}, [open]);
|
|
54103
54480
|
};
|
|
54104
54481
|
|
|
@@ -54358,7 +54735,14 @@ const usePopupContentMount = (
|
|
|
54358
54735
|
*
|
|
54359
54736
|
* `animation="slide-from-*"` (anchorReference/point mode only): a real
|
|
54360
54737
|
* translate-based entrance, 8 directions (cardinal + 4 diagonals), each
|
|
54361
|
-
* 100%-of-own-size.
|
|
54738
|
+
* 100%-of-own-size. It travels through `transform`, not through the
|
|
54739
|
+
* `translate` property, which belongs to the popup's own placement
|
|
54740
|
+
* (applyNewPosition in visible_rect.js — see its doc for why the placement is
|
|
54741
|
+
* a transform at all, and why it has to be the outermost one: the individual
|
|
54742
|
+
* transform properties apply translate, then rotate, then scale, then
|
|
54743
|
+
* `transform`, so both the travel here and the `scale` below compose *under* a
|
|
54744
|
+
* placement that stays where it was put).
|
|
54745
|
+
* Popover always resolves `animation="auto"`/`"sliding"`
|
|
54362
54746
|
* to one of these concretely in JS (see popover.jsx's
|
|
54363
54747
|
* `resolveDirectionValue`), so there's no bare `animation="sliding"`
|
|
54364
54748
|
* selector here at all — a point/corner has no anchor edge to grow out of,
|
|
@@ -54399,14 +54783,15 @@ const popupCss = /* css */ `
|
|
|
54399
54783
|
|
|
54400
54784
|
.navi_popover,
|
|
54401
54785
|
.navi_dialog {
|
|
54402
|
-
/*
|
|
54403
|
-
|
|
54404
|
-
|
|
54405
|
-
|
|
54406
|
-
propertyName to filter).
|
|
54786
|
+
/* The translate property is deliberately absent from this list — it
|
|
54787
|
+
carries where the popup stands, and applyNewPosition (visible_rect.js)
|
|
54788
|
+
owns it and drives its own transition through the Web Animations API
|
|
54789
|
+
instead of CSS, so it stays independent of whatever this list contains
|
|
54790
|
+
(no shared transition-property to clobber, no propertyName to filter).
|
|
54791
|
+
What moves here is transform, which composes under it. */
|
|
54407
54792
|
&[navi-animation] {
|
|
54408
54793
|
transition-property:
|
|
54409
|
-
display, overlay, opacity,
|
|
54794
|
+
display, overlay, opacity, transform, scale, box-shadow;
|
|
54410
54795
|
transition-duration:
|
|
54411
54796
|
var(--popup-animation-duration), var(--popup-animation-duration),
|
|
54412
54797
|
var(--popup-opacity-duration), var(--popup-translate-duration),
|
|
@@ -54435,7 +54820,7 @@ const popupCss = /* css */ `
|
|
|
54435
54820
|
centered, no direction involved. */
|
|
54436
54821
|
&[navi-animation="scaling"] {
|
|
54437
54822
|
opacity: 1;
|
|
54438
|
-
|
|
54823
|
+
transform: translate(0px, 0px);
|
|
54439
54824
|
scale: 1;
|
|
54440
54825
|
&[aria-expanded="false"] {
|
|
54441
54826
|
opacity: 0;
|
|
@@ -54487,14 +54872,16 @@ const popupCss = /* css */ `
|
|
|
54487
54872
|
&[navi-animation="slide-from-bottom-left"],
|
|
54488
54873
|
&[navi-animation="slide-from-bottom-right"] {
|
|
54489
54874
|
opacity: 1;
|
|
54490
|
-
|
|
54875
|
+
transform: translate(0px, 0px);
|
|
54491
54876
|
|
|
54492
54877
|
/* No fade: the travel is the whole effect. Fading it out on top would
|
|
54493
54878
|
make the popup disappear before it has finished leaving, which reads as
|
|
54494
54879
|
two things happening rather than one movement. */
|
|
54495
54880
|
&[aria-expanded="false"] {
|
|
54496
|
-
|
|
54497
|
-
calc(var(--x-popup-slide-
|
|
54881
|
+
transform: translate(
|
|
54882
|
+
calc(var(--x-popup-slide-x, 0) * 100%),
|
|
54883
|
+
calc(var(--x-popup-slide-y, -1) * 100%)
|
|
54884
|
+
);
|
|
54498
54885
|
}
|
|
54499
54886
|
}
|
|
54500
54887
|
}
|
|
@@ -54686,14 +55073,17 @@ const createSwipeToClose = (side, { grip } = {}) => {
|
|
|
54686
55073
|
}
|
|
54687
55074
|
}
|
|
54688
55075
|
|
|
54689
|
-
//
|
|
54690
|
-
// distance in screen coordinates, which is exactly what a
|
|
55076
|
+
// How far the panel has been pulled, written on it directly: the gesture
|
|
55077
|
+
// reports a distance in screen coordinates, which is exactly what a
|
|
55078
|
+
// translate takes. It goes through `transform` because the `translate`
|
|
55079
|
+
// property carries where the panel *stands* (applyNewPosition in
|
|
55080
|
+
// visible_rect.js, which owns it) — the pull composes under the placement
|
|
55081
|
+
// instead of replacing it.
|
|
54691
55082
|
const paint = (distance) => {
|
|
54692
|
-
panelEl.style.
|
|
54693
|
-
axis === "x" ? `${distance}px 0px` : `0px ${distance}px`;
|
|
55083
|
+
panelEl.style.transform = translateOf(axis, distance);
|
|
54694
55084
|
};
|
|
54695
55085
|
const restore = () => {
|
|
54696
|
-
panelEl.style.
|
|
55086
|
+
panelEl.style.transform = "";
|
|
54697
55087
|
panelEl.style.transitionProperty = "";
|
|
54698
55088
|
panelEl.style.userSelect = "";
|
|
54699
55089
|
};
|
|
@@ -54705,8 +55095,8 @@ const createSwipeToClose = (side, { grip } = {}) => {
|
|
|
54705
55095
|
paint(to);
|
|
54706
55096
|
const animation = panelEl.animate(
|
|
54707
55097
|
[
|
|
54708
|
-
{
|
|
54709
|
-
{
|
|
55098
|
+
{ transform: translateOf(axis, from) },
|
|
55099
|
+
{ transform: translateOf(axis, to) },
|
|
54710
55100
|
],
|
|
54711
55101
|
{
|
|
54712
55102
|
duration: (covered / sizeOf(panelEl, axis)) * TRAVEL_DURATION,
|
|
@@ -54776,7 +55166,9 @@ const sizeOf = (element, axis) => {
|
|
|
54776
55166
|
return axis === "x" ? rect.width : rect.height;
|
|
54777
55167
|
};
|
|
54778
55168
|
const translateOf = (axis, distance) =>
|
|
54779
|
-
axis === "x"
|
|
55169
|
+
axis === "x"
|
|
55170
|
+
? `translate(${distance}px, 0px)`
|
|
55171
|
+
: `translate(0px, ${distance}px)`;
|
|
54780
55172
|
|
|
54781
55173
|
const PopupClose = ({
|
|
54782
55174
|
label,
|
|
@@ -54973,11 +55365,17 @@ const css$E = /* css */`
|
|
|
54973
55365
|
containing block is genuinely its nearest positioned ancestor,
|
|
54974
55366
|
regardless of positionArea. See the [data-layer="top"] rule below for
|
|
54975
55367
|
why the via-attribute renderer overrides this. Position is always
|
|
54976
|
-
JS-driven (pickPositionRelativeTo
|
|
54977
|
-
|
|
54978
|
-
unlike an earlier version of this file. */
|
|
55368
|
+
JS-driven (pickPositionRelativeTo, see useDialogProps below) — no CSS
|
|
55369
|
+
alignment/inset math here at all. */
|
|
54979
55370
|
position: absolute;
|
|
54980
|
-
|
|
55371
|
+
/* Laid out at its containing block's own origin and moved from there by a
|
|
55372
|
+
translate (applyNewPosition), never by left/top: a shrink-to-fit box
|
|
55373
|
+
placed with left is only ever as wide as what is left of the container
|
|
55374
|
+
to its right, and that width is what decides where it gets placed — see
|
|
55375
|
+
applyNewPosition's own doc. right/bottom stay auto: an inset there would
|
|
55376
|
+
over-constrain the box against the UA's margin: auto and re-center
|
|
55377
|
+
it. */
|
|
55378
|
+
inset: 0 auto auto 0;
|
|
54981
55379
|
/* Custom renderer only — see openLocalDialogCount above */
|
|
54982
55380
|
z-index: calc(var(--navi-z-index-popup) + var(--dialog-stack-order, 0));
|
|
54983
55381
|
min-width: min(
|
|
@@ -55073,11 +55471,12 @@ const css$E = /* css */`
|
|
|
55073
55471
|
&[data-flush-bottom][data-flush-left] {
|
|
55074
55472
|
border-bottom-left-radius: 0;
|
|
55075
55473
|
}
|
|
55076
|
-
/*
|
|
55077
|
-
|
|
55078
|
-
|
|
55079
|
-
|
|
55080
|
-
|
|
55474
|
+
/* The placement is a translate, so the translate property is spoken for
|
|
55475
|
+
here (see applyNewPosition in visible_rect.js, which owns it and animates
|
|
55476
|
+
it itself through the Web Animations API rather than through this file's
|
|
55477
|
+
transitions — no shared transition-property to clobber, no propertyName
|
|
55478
|
+
to filter). An entrance animation moves the dialog through scale and
|
|
55479
|
+
transform instead, which compose under it: see popup_css.js. */
|
|
55081
55480
|
|
|
55082
55481
|
&::backdrop {
|
|
55083
55482
|
background: var(--navi-backdrop-close-background);
|
|
@@ -55157,7 +55556,7 @@ const css$E = /* css */`
|
|
|
55157
55556
|
containing block is the viewport rather than any positioned
|
|
55158
55557
|
ancestor. Not left to the native :modal UA stylesheet's own default
|
|
55159
55558
|
(also position: fixed, but with its own margin/inset assumptions) so
|
|
55160
|
-
that JS-
|
|
55559
|
+
that the JS-driven placement (see useDialogProps below) always wins
|
|
55161
55560
|
cleanly. */
|
|
55162
55561
|
&[data-layer="top"] {
|
|
55163
55562
|
position: fixed;
|
|
@@ -55442,6 +55841,16 @@ const css$E = /* css */`
|
|
|
55442
55841
|
* actually closes — not preventable (see `open_controller.js`'s own
|
|
55443
55842
|
* `onRequestClose`/`onClose` distinction; `onRequestClose` is where you'd
|
|
55444
55843
|
* veto a close instead).
|
|
55844
|
+
* @param {boolean|string|{id?: string, type?: "push"|"replace"}} [props.navState] -
|
|
55845
|
+
* Keeps the open state in the history entry, so a screen left and come back
|
|
55846
|
+
* to finds this popup as it was — open, and without an entrance playing: it
|
|
55847
|
+
* was already open when the page reappeared. `true` stores it under the
|
|
55848
|
+
* popup's own `id`; a string names the key instead.
|
|
55849
|
+
* `{ type: "push" }` also makes the opening a history entry of its own, so
|
|
55850
|
+
* the back button closes the popup rather than leaving the screen — and a
|
|
55851
|
+
* cancel (Escape) goes back, taking whatever was written to the url while it
|
|
55852
|
+
* was open with it. The state belongs to the entry that wrote it: a
|
|
55853
|
+
* navigation that stacks a new entry does not carry it along.
|
|
55445
55854
|
* @param {object} [props.openController] - Advanced: an externally-owned
|
|
55446
55855
|
* open controller (see `open_controller.js`) for a caller that wants to
|
|
55447
55856
|
* drive open/close itself instead of `open`/`defaultOpen`/`onClose` (used
|
|
@@ -55509,6 +55918,7 @@ const UncontrolledDialog = props => {
|
|
|
55509
55918
|
open: undefined,
|
|
55510
55919
|
signal: undefined,
|
|
55511
55920
|
defaultOpen: undefined,
|
|
55921
|
+
navState: undefined,
|
|
55512
55922
|
onClose: undefined,
|
|
55513
55923
|
openController: openController,
|
|
55514
55924
|
onnavi_request_open: e => {
|
|
@@ -56088,8 +56498,8 @@ const useDialogProps = props => {
|
|
|
56088
56498
|
rectEffect.disconnect();
|
|
56089
56499
|
});
|
|
56090
56500
|
// A descendant anchored to something inside this dialog (a Callout, a
|
|
56091
|
-
// nested Popover) needing to know about this dialog's own
|
|
56092
|
-
//
|
|
56501
|
+
// nested Popover) needing to know about this dialog's own repositioning
|
|
56502
|
+
// transition — not just that the target changed
|
|
56093
56503
|
// (navi_position_change above), but that a real, currently-playing
|
|
56094
56504
|
// transition is moving it right now — is handled generically by
|
|
56095
56505
|
// applyNewPosition itself (see its own notifyPositionTransition), since
|
|
@@ -56536,7 +56946,14 @@ const css$D = /* css */`
|
|
|
56536
56946
|
whether it has a real anchor — this file's top comment has the full
|
|
56537
56947
|
reasoning. */
|
|
56538
56948
|
position: absolute;
|
|
56539
|
-
|
|
56949
|
+
/* Laid out at its containing block's own origin and moved from there by a
|
|
56950
|
+
translate (applyNewPosition), never by left/top: a shrink-to-fit box
|
|
56951
|
+
placed with left is only ever as wide as what is left of the container
|
|
56952
|
+
to its right, and that width is what decides which side it gets placed
|
|
56953
|
+
on — see applyNewPosition's own doc. right/bottom stay auto: an inset
|
|
56954
|
+
there would over-constrain the box against the UA's margin: auto and
|
|
56955
|
+
re-center it. */
|
|
56956
|
+
inset: 0 auto auto 0;
|
|
56540
56957
|
/* Custom renderer only: --popover-stack-order is set to
|
|
56541
56958
|
openLocalPopoverCount on every open (see openEffect below) so the
|
|
56542
56959
|
most-recently-opened local popover always outranks an earlier one,
|
|
@@ -56568,35 +56985,13 @@ const css$D = /* css */`
|
|
|
56568
56985
|
(see box.jsx), which is what makes it scroll — and what a
|
|
56569
56986
|
header/footer/body inside it then rearranges. */
|
|
56570
56987
|
overscroll-behavior: none;
|
|
56571
|
-
/*
|
|
56572
|
-
|
|
56573
|
-
|
|
56574
|
-
|
|
56575
|
-
|
|
56576
|
-
|
|
56577
|
-
(see box.jsx), which is what makes it scroll — and what a
|
|
56578
|
-
header/footer/body inside it then rearranges. */
|
|
56579
|
-
overscroll-behavior: none;
|
|
56580
|
-
|
|
56581
|
-
/* overflow is not declared here: the popover carries [data-scrollable]
|
|
56582
|
-
(see box.jsx), which is what makes it scroll — and what a
|
|
56583
|
-
header/footer/body inside it then rearranges. */
|
|
56584
|
-
overscroll-behavior: none;
|
|
56585
|
-
/* left/top are NOT transitioned here — applyNewPosition (visible_rect.js)
|
|
56586
|
-
drives that itself via the Web Animations API instead of CSS, so it
|
|
56587
|
-
stays independent from navi-animation's own opacity/scale/display
|
|
56588
|
-
transition list below (no shared transition-property to clobber, no
|
|
56589
|
-
propertyName to filter). */
|
|
56590
|
-
/* overflow is not declared here: the popover carries [data-scrollable]
|
|
56591
|
-
(see box.jsx), which is what makes it scroll — and what a
|
|
56592
|
-
header/footer/body inside it then rearranges. */
|
|
56593
|
-
overscroll-behavior: none;
|
|
56988
|
+
/* The placement is a translate, so the translate property is spoken for
|
|
56989
|
+
here (see applyNewPosition in visible_rect.js, which owns it and animates
|
|
56990
|
+
it itself through the Web Animations API rather than through this file's
|
|
56991
|
+
transitions — no shared transition-property to clobber, no propertyName
|
|
56992
|
+
to filter). An entrance animation moves the popover through scale and
|
|
56993
|
+
transform instead, which compose under it: see popup_css.js. */
|
|
56594
56994
|
|
|
56595
|
-
/* left/top are NOT transitioned here — applyNewPosition (visible_rect.js)
|
|
56596
|
-
drives that itself via the Web Animations API instead of CSS, so it
|
|
56597
|
-
stays independent from navi-animation's own opacity/scale/display
|
|
56598
|
-
transition list below (no shared transition-property to clobber, no
|
|
56599
|
-
propertyName to filter). */
|
|
56600
56995
|
/* The via-attribute renderer starts hidden for free (native UA default
|
|
56601
56996
|
for any [popover] element, same as <dialog> without [open]) — the
|
|
56602
56997
|
custom renderer is a plain div with no such native default, so
|
|
@@ -56900,6 +57295,16 @@ const css$D = /* css */`
|
|
|
56900
57295
|
* actually closes — not preventable (see `open_controller.js`'s own
|
|
56901
57296
|
* `onRequestClose`/`onClose` distinction; `onRequestClose` is where you'd
|
|
56902
57297
|
* veto a close instead).
|
|
57298
|
+
* @param {boolean|string|{id?: string, type?: "push"|"replace"}} [props.navState] -
|
|
57299
|
+
* Keeps the open state in the history entry, so a screen left and come back
|
|
57300
|
+
* to finds this popup as it was — open, and without an entrance playing: it
|
|
57301
|
+
* was already open when the page reappeared. `true` stores it under the
|
|
57302
|
+
* popup's own `id`; a string names the key instead.
|
|
57303
|
+
* `{ type: "push" }` also makes the opening a history entry of its own, so
|
|
57304
|
+
* the back button closes the popup rather than leaving the screen — and a
|
|
57305
|
+
* cancel (Escape) goes back, taking whatever was written to the url while it
|
|
57306
|
+
* was open with it. The state belongs to the entry that wrote it: a
|
|
57307
|
+
* navigation that stacks a new entry does not carry it along.
|
|
56903
57308
|
* @param {object} [props.openController] - Advanced: an externally-owned
|
|
56904
57309
|
* open controller (see `open_controller.js`) for a caller that wants to
|
|
56905
57310
|
* drive open/close itself instead of `open`/`defaultOpen`/`onClose` (used
|
|
@@ -56965,6 +57370,7 @@ const UncontrolledPopover = props => {
|
|
|
56965
57370
|
open: undefined,
|
|
56966
57371
|
signal: undefined,
|
|
56967
57372
|
defaultOpen: undefined,
|
|
57373
|
+
navState: undefined,
|
|
56968
57374
|
onClose: undefined,
|
|
56969
57375
|
openController: openController,
|
|
56970
57376
|
onnavi_request_open: e => {
|
|
@@ -57580,7 +57986,7 @@ const usePopoverProps = props => {
|
|
|
57580
57986
|
});
|
|
57581
57987
|
// A descendant anchored to something inside this popover (a Callout, a
|
|
57582
57988
|
// further-nested Popover) needing to know about this popover's own
|
|
57583
|
-
//
|
|
57989
|
+
// repositioning transition — not just that the target changed
|
|
57584
57990
|
// (navi_position_change above), but that a real, currently-playing
|
|
57585
57991
|
// transition is moving it right now — is handled generically by
|
|
57586
57992
|
// applyNewPosition itself (see its own notifyPositionTransition in
|
|
@@ -80607,5 +81013,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
80607
81013
|
})
|
|
80608
81014
|
});
|
|
80609
81015
|
|
|
80610
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, CalloutStatusIcon, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, InfoSvg, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, OfflineError, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, constraintFromValidityRule, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isOfflineError, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setNetworkPolicy, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutElement, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useNetworkPolicyReason, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideContainer, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
81016
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, CalloutStatusIcon, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, InfoSvg, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, OfflineError, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRange, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, constraintFromValidityRule, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isOfflineError, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setNetworkPolicy, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutElement, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useNetworkPolicyReason, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideContainer, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
80611
81017
|
//# sourceMappingURL=jsenv_navi.js.map
|