@jsenv/navi 0.29.65 → 0.29.67
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 +1169 -155
- package/dist/jsenv_navi.js.map +93 -57
- package/docs/AI_INSTRUCTIONS.md +6 -0
- package/docs/navigation.md +6 -0
- package/docs/route_transitions.md +187 -0
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -38,7 +38,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
38
38
|
* any of these, and a number is the last resort, not the first tool.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
const css$
|
|
41
|
+
const css$12 = /* css */`
|
|
42
42
|
@layer navi {
|
|
43
43
|
:root {
|
|
44
44
|
/* A control that overlaps its neighbours (the members of a Group share
|
|
@@ -90,7 +90,7 @@ const css$11 = /* css */`
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
`;
|
|
93
|
-
import.meta.css = [css$
|
|
93
|
+
import.meta.css = [css$12, "@jsenv/navi/src/navi_z_indexes.js"];
|
|
94
94
|
|
|
95
95
|
const addIntoArray = (array, ...valuesToAdd) => {
|
|
96
96
|
if (valuesToAdd.length === 1) {
|
|
@@ -359,7 +359,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
359
359
|
* the very first render and the browser does everything on its own.
|
|
360
360
|
*/
|
|
361
361
|
const URL_TARGET_ATTRIBUTE = "data-url-target";
|
|
362
|
-
const css$
|
|
362
|
+
const css$11 = /* css */`
|
|
363
363
|
@layer navi {
|
|
364
364
|
[${URL_TARGET_ATTRIBUTE}] {
|
|
365
365
|
animation: navi_url_target var(--navi-url-target-duration, 2000ms)
|
|
@@ -377,7 +377,7 @@ const css$10 = /* css */`
|
|
|
377
377
|
}
|
|
378
378
|
}
|
|
379
379
|
`;
|
|
380
|
-
import.meta.css = [css$
|
|
380
|
+
import.meta.css = [css$11, "@jsenv/navi/src/nav/url_target/url_target.js"];
|
|
381
381
|
let urlTargetOptions = {
|
|
382
382
|
block: "center",
|
|
383
383
|
behavior: "smooth",
|
|
@@ -7753,7 +7753,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
7753
7753
|
* - Arrow automatically shows when pointing at a valid anchor element
|
|
7754
7754
|
* - Centers in viewport when no anchor element provided or anchor is too big
|
|
7755
7755
|
*/
|
|
7756
|
-
const css
|
|
7756
|
+
const css$10 = /* css */`
|
|
7757
7757
|
@layer navi {
|
|
7758
7758
|
.navi_callout {
|
|
7759
7759
|
/* A callout is parented to what it explains, so it inherits from it — and
|
|
@@ -7996,7 +7996,7 @@ const openCallout = (message, {
|
|
|
7996
7996
|
skipFocus = false,
|
|
7997
7997
|
debug = () => {}
|
|
7998
7998
|
} = {}) => {
|
|
7999
|
-
import.meta.css = [css
|
|
7999
|
+
import.meta.css = [css$10, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
8000
8000
|
if (debug === true) {
|
|
8001
8001
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
8002
8002
|
}
|
|
@@ -20118,7 +20118,7 @@ const setupNetworkMonitoring = () => {
|
|
|
20118
20118
|
};
|
|
20119
20119
|
setupNetworkMonitoring();
|
|
20120
20120
|
|
|
20121
|
-
installImportMetaCssBuild(import.meta);const css
|
|
20121
|
+
installImportMetaCssBuild(import.meta);const css$$ = /* css */`
|
|
20122
20122
|
.navi_loading_indicator_fluid_container {
|
|
20123
20123
|
position: relative;
|
|
20124
20124
|
display: flex;
|
|
@@ -20150,7 +20150,7 @@ const LoadingIndicatorFluid = ({
|
|
|
20150
20150
|
visuallyHidden,
|
|
20151
20151
|
...rest
|
|
20152
20152
|
}) => {
|
|
20153
|
-
import.meta.css = [css
|
|
20153
|
+
import.meta.css = [css$$, "@jsenv/navi/src/graphic/loading/loading_indicator_fluid.jsx"];
|
|
20154
20154
|
const ref = useRef(null);
|
|
20155
20155
|
// The container dimensions can be deduced from the ref itself as the indicator is absolute inset 0
|
|
20156
20156
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
@@ -20355,7 +20355,7 @@ const LoadingRectangleSvg = ({
|
|
|
20355
20355
|
});
|
|
20356
20356
|
};
|
|
20357
20357
|
|
|
20358
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
20358
|
+
installImportMetaCssBuild(import.meta);const css$_ = /* css */`
|
|
20359
20359
|
.navi_loading_outline_wrapper {
|
|
20360
20360
|
position: absolute;
|
|
20361
20361
|
/* Controls place the outline slightly outside their box, right on top of
|
|
@@ -20392,7 +20392,7 @@ installImportMetaCssBuild(import.meta);const css$Z = /* css */`
|
|
|
20392
20392
|
}
|
|
20393
20393
|
`;
|
|
20394
20394
|
const LoadingOutline = props => {
|
|
20395
|
-
import.meta.css = [css$
|
|
20395
|
+
import.meta.css = [css$_, "@jsenv/navi/src/graphic/loading/loading_outline.jsx"];
|
|
20396
20396
|
if (props.containerRef) {
|
|
20397
20397
|
const container = props.containerRef.current;
|
|
20398
20398
|
if (!container) {
|
|
@@ -20682,7 +20682,7 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
20682
20682
|
};
|
|
20683
20683
|
|
|
20684
20684
|
installImportMetaCssBuild(import.meta);// https://jsfiddle.net/v5xzJ/4/
|
|
20685
|
-
const css$
|
|
20685
|
+
const css$Z = /* css */`
|
|
20686
20686
|
@layer navi {
|
|
20687
20687
|
.navi_text {
|
|
20688
20688
|
&[data-skeleton] {
|
|
@@ -21188,7 +21188,7 @@ const TextShrinkWrap = props => {
|
|
|
21188
21188
|
});
|
|
21189
21189
|
};
|
|
21190
21190
|
const TextUI = props => {
|
|
21191
|
-
import.meta.css = [css$
|
|
21191
|
+
import.meta.css = [css$Z, "@jsenv/navi/src/text/text.jsx"];
|
|
21192
21192
|
let {
|
|
21193
21193
|
ref,
|
|
21194
21194
|
spacing,
|
|
@@ -27266,7 +27266,7 @@ const getAssociatedLabels = element => {
|
|
|
27266
27266
|
return Array.from(element.labels);
|
|
27267
27267
|
};
|
|
27268
27268
|
|
|
27269
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
27269
|
+
installImportMetaCssBuild(import.meta);const css$Y = /* css */`
|
|
27270
27270
|
@layer navi {
|
|
27271
27271
|
.navi_button {
|
|
27272
27272
|
--button-border-radius: var(--navi-control-border-radius);
|
|
@@ -27697,7 +27697,7 @@ installImportMetaCssBuild(import.meta);const css$X = /* css */`
|
|
|
27697
27697
|
}
|
|
27698
27698
|
`;
|
|
27699
27699
|
const ButtonUI = props => {
|
|
27700
|
-
import.meta.css = [css$
|
|
27700
|
+
import.meta.css = [css$Y, "@jsenv/navi/src/control/input/button_ui.jsx"];
|
|
27701
27701
|
const {
|
|
27702
27702
|
ref,
|
|
27703
27703
|
// href/link
|
|
@@ -29551,7 +29551,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
29551
29551
|
* reaches the real container.
|
|
29552
29552
|
*/
|
|
29553
29553
|
let openLocalDialogCount = 0;
|
|
29554
|
-
const css$
|
|
29554
|
+
const css$X = /* css */`
|
|
29555
29555
|
@layer navi {
|
|
29556
29556
|
.navi_dialog {
|
|
29557
29557
|
/* Min gap between the dialog and the edges of its container. Written
|
|
@@ -30074,7 +30074,7 @@ const css$W = /* css */`
|
|
|
30074
30074
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
30075
30075
|
*/
|
|
30076
30076
|
const Dialog = props => {
|
|
30077
|
-
import.meta.css = [css$
|
|
30077
|
+
import.meta.css = [css$X, "@jsenv/navi/src/layout/dialog.jsx"];
|
|
30078
30078
|
if (props.openController) {
|
|
30079
30079
|
return jsx(ControlledDialog, {
|
|
30080
30080
|
...props
|
|
@@ -31030,7 +31030,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
31030
31030
|
* and applied.
|
|
31031
31031
|
*/
|
|
31032
31032
|
let openLocalPopoverCount = 0;
|
|
31033
|
-
const css$
|
|
31033
|
+
const css$W = /* css */`
|
|
31034
31034
|
@layer navi {
|
|
31035
31035
|
.navi_popover {
|
|
31036
31036
|
/* soft: user-configurable preferred max-height. Kept as a *default*
|
|
@@ -31467,7 +31467,7 @@ const css$V = /* css */`
|
|
|
31467
31467
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
31468
31468
|
*/
|
|
31469
31469
|
const Popover = props => {
|
|
31470
|
-
import.meta.css = [css$
|
|
31470
|
+
import.meta.css = [css$W, "@jsenv/navi/src/layout/popover.jsx"];
|
|
31471
31471
|
if (props.openController) {
|
|
31472
31472
|
return jsx(ControlledPopover, {
|
|
31473
31473
|
...props
|
|
@@ -32486,7 +32486,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
32486
32486
|
* event, and a caller replacing the body entirely then has one protocol to
|
|
32487
32487
|
* follow — `--navi-confirm` for yes, anything that closes for no.
|
|
32488
32488
|
*/
|
|
32489
|
-
const css$
|
|
32489
|
+
const css$V = /* css */`
|
|
32490
32490
|
/* The width lives on the body rather than on the popup, so that custom
|
|
32491
32491
|
content (which replaces this body entirely) sizes itself instead of
|
|
32492
32492
|
inheriting a ceiling meant for a sentence-long question. */
|
|
@@ -32623,7 +32623,7 @@ const ConfirmPopup = ({
|
|
|
32623
32623
|
onAnswer,
|
|
32624
32624
|
onClosed
|
|
32625
32625
|
}) => {
|
|
32626
|
-
import.meta.css = [css$
|
|
32626
|
+
import.meta.css = [css$V, "@jsenv/navi/src/action/confirm_popup.jsx"];
|
|
32627
32627
|
const {
|
|
32628
32628
|
mode,
|
|
32629
32629
|
confirmLabel,
|
|
@@ -32707,7 +32707,7 @@ const defaultBody = (message, {
|
|
|
32707
32707
|
});
|
|
32708
32708
|
};
|
|
32709
32709
|
|
|
32710
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
32710
|
+
installImportMetaCssBuild(import.meta);const css$U = /* css */`
|
|
32711
32711
|
.action_error {
|
|
32712
32712
|
margin-top: 0;
|
|
32713
32713
|
margin-bottom: 20px;
|
|
@@ -32732,7 +32732,7 @@ const ActionRenderer = ({
|
|
|
32732
32732
|
children,
|
|
32733
32733
|
disabled
|
|
32734
32734
|
}) => {
|
|
32735
|
-
import.meta.css = [css$
|
|
32735
|
+
import.meta.css = [css$U, "@jsenv/navi/src/action/action_renderer.jsx"];
|
|
32736
32736
|
if (action === undefined) {
|
|
32737
32737
|
throw new Error("ActionRenderer requires an action to render, but none was provided.");
|
|
32738
32738
|
}
|
|
@@ -36806,7 +36806,7 @@ const ensureDocumentStartViewTransition = () => {
|
|
|
36806
36806
|
startViewTransitionPolyfill.isPolyfill = true;
|
|
36807
36807
|
document.startViewTransition = startViewTransitionPolyfill;
|
|
36808
36808
|
}
|
|
36809
|
-
return startViewTransition$
|
|
36809
|
+
return startViewTransition$2;
|
|
36810
36810
|
};
|
|
36811
36811
|
|
|
36812
36812
|
// A transition a finger is holding still (see route_travel.jsx): it has to be
|
|
@@ -36828,7 +36828,7 @@ const holdViewTransition = (release) => {
|
|
|
36828
36828
|
};
|
|
36829
36829
|
};
|
|
36830
36830
|
|
|
36831
|
-
const startViewTransition$
|
|
36831
|
+
const startViewTransition$2 = (updateCallback) => {
|
|
36832
36832
|
if (releaseHeldViewTransition) {
|
|
36833
36833
|
const release = releaseHeldViewTransition;
|
|
36834
36834
|
releaseHeldViewTransition = null;
|
|
@@ -38316,6 +38316,1119 @@ const RouteUI = ({
|
|
|
38316
38316
|
return element;
|
|
38317
38317
|
};
|
|
38318
38318
|
|
|
38319
|
+
/**
|
|
38320
|
+
* The document's rendering, held for the one frame a view transition needs.
|
|
38321
|
+
*
|
|
38322
|
+
* The browser does not take the picture of the page being left when a
|
|
38323
|
+
* transition is ASKED for — it takes it at the next frame, just before running
|
|
38324
|
+
* the update callback. Preact renders sooner than that, in a microtask: so a
|
|
38325
|
+
* change nobody asked for (a tab pressed, the back button) has already reached
|
|
38326
|
+
* the DOM when the picture is taken, and the picture is of the page ARRIVING.
|
|
38327
|
+
* Both sides of the animation then show it, and one watches a page slide onto
|
|
38328
|
+
* itself.
|
|
38329
|
+
*
|
|
38330
|
+
* So what Preact has queued waits until the update callback, which is the
|
|
38331
|
+
* moment the API is built around — the change belongs inside it. The whole
|
|
38332
|
+
* document is held: it is about to be frozen under a picture anyway.
|
|
38333
|
+
*
|
|
38334
|
+
* ONE hold for the whole document, whoever animates. The hold is a wrapper
|
|
38335
|
+
* around Preact's `options.debounceRendering`, and two of them installed
|
|
38336
|
+
* independently restore each other in the wrong order when they let go — every
|
|
38337
|
+
* render queued in between is then handed to a wrapper nobody will ever
|
|
38338
|
+
* release. Everything that photographs a navigation (RouteTravel's box, a
|
|
38339
|
+
* route transition) must therefore hold through this module, never through a
|
|
38340
|
+
* wrapper of its own.
|
|
38341
|
+
*/
|
|
38342
|
+
|
|
38343
|
+
|
|
38344
|
+
let renderingHold = null;
|
|
38345
|
+
const holdRendering = () => {
|
|
38346
|
+
if (renderingHold) {
|
|
38347
|
+
return renderingHold.release;
|
|
38348
|
+
}
|
|
38349
|
+
const debounceRenderingBefore = options.debounceRendering;
|
|
38350
|
+
const hold = {
|
|
38351
|
+
render: null,
|
|
38352
|
+
release: () => {
|
|
38353
|
+
// Only the hold that is still standing may be given back: a holder
|
|
38354
|
+
// releasing after another has taken over must not let go of what it
|
|
38355
|
+
// does not hold.
|
|
38356
|
+
if (renderingHold !== hold) {
|
|
38357
|
+
return;
|
|
38358
|
+
}
|
|
38359
|
+
renderingHold = null;
|
|
38360
|
+
options.debounceRendering = debounceRenderingBefore;
|
|
38361
|
+
const { render } = hold;
|
|
38362
|
+
hold.render = null;
|
|
38363
|
+
if (render) {
|
|
38364
|
+
render();
|
|
38365
|
+
}
|
|
38366
|
+
},
|
|
38367
|
+
};
|
|
38368
|
+
renderingHold = hold;
|
|
38369
|
+
options.debounceRendering = (render) => {
|
|
38370
|
+
hold.render = render;
|
|
38371
|
+
};
|
|
38372
|
+
return hold.release;
|
|
38373
|
+
};
|
|
38374
|
+
|
|
38375
|
+
// The hold a navigation takes on its way in — from before its first write,
|
|
38376
|
+
// because by the time a route announces that it matches, Preact has already
|
|
38377
|
+
// been told and the render is queued; a hold taken then is a hold taken too
|
|
38378
|
+
// late. Kept here until whoever animates the change takes it over, or the
|
|
38379
|
+
// navigation turns out to be one nobody animates.
|
|
38380
|
+
let routingRenderingHold = null;
|
|
38381
|
+
const holdRenderingForRouting = () => {
|
|
38382
|
+
routingRenderingHold = holdRendering();
|
|
38383
|
+
};
|
|
38384
|
+
// Nobody had a picture to take: a page held for a change it does not animate
|
|
38385
|
+
// is a page that stutters for nothing.
|
|
38386
|
+
const releaseRoutingRenderingHold = () => {
|
|
38387
|
+
const release = routingRenderingHold;
|
|
38388
|
+
routingRenderingHold = null;
|
|
38389
|
+
if (release) {
|
|
38390
|
+
release();
|
|
38391
|
+
}
|
|
38392
|
+
};
|
|
38393
|
+
// An animator takes the navigation's hold as its own — taking another would be
|
|
38394
|
+
// taking a hold on a page that is holding still — or takes a fresh one when
|
|
38395
|
+
// the change it animates is not a navigation.
|
|
38396
|
+
const takeoverRoutingRenderingHold = () => {
|
|
38397
|
+
const release = routingRenderingHold || holdRendering();
|
|
38398
|
+
routingRenderingHold = null;
|
|
38399
|
+
return release;
|
|
38400
|
+
};
|
|
38401
|
+
|
|
38402
|
+
/**
|
|
38403
|
+
* The window two pages are seen through while one replaces the other, measured
|
|
38404
|
+
* once and published for the length of the movement.
|
|
38405
|
+
*
|
|
38406
|
+
* Both ways of moving from one route to another need the same six numbers, so
|
|
38407
|
+
* they are written under the same names and read by the same CSS formulas —
|
|
38408
|
+
* `route_travel.jsx` for a row a finger pushes, `route_transition.jsx` for a
|
|
38409
|
+
* relation between two pages. Only one of them ever plays at a time (there is
|
|
38410
|
+
* one view transition per document), which is what lets them share the names.
|
|
38411
|
+
*
|
|
38412
|
+
* What the numbers are for:
|
|
38413
|
+
*
|
|
38414
|
+
* - **the box the movement happens in**, in the WINDOW's coordinates. The
|
|
38415
|
+
* pictures of a transition are drawn in the top layer, above everything, so
|
|
38416
|
+
* no overflow of the document reaches them: where to cut them can only be
|
|
38417
|
+
* said from outside the document, and this is where that outside is known.
|
|
38418
|
+
* - **the height it is held at**, the taller of the two states. Left to the
|
|
38419
|
+
* browser the window's height animates from one to the other, and a window
|
|
38420
|
+
* that changes size under the pictures cuts the page being left from the
|
|
38421
|
+
* bottom, progressively. It does end up at the arriving height, and that is
|
|
38422
|
+
* right; what must not happen is the user watching it get there.
|
|
38423
|
+
* - **where the page being left WAS**, which is not where the window stands:
|
|
38424
|
+
* the two states are at the same place in the layout without being at the
|
|
38425
|
+
* same place in the window — one page is scrolled and the other is not.
|
|
38426
|
+
* Drawn at the window's own corner, the page being left would be seen
|
|
38427
|
+
* jumping to its top before it begins to leave.
|
|
38428
|
+
*
|
|
38429
|
+
* Only the measuring needs JS, and only for the one moment both states exist:
|
|
38430
|
+
* the page arriving is in the DOM and the transition has not started playing.
|
|
38431
|
+
* Everything DERIVED from these numbers — the band a fixed bar covers, how far
|
|
38432
|
+
* a page travels — is derived in CSS, so the application's own numbers (the
|
|
38433
|
+
* room its bars give back, see layout/safe_area.js) take part in it.
|
|
38434
|
+
*/
|
|
38435
|
+
|
|
38436
|
+
const WINDOW_TOP_PROPERTY = "--navi-transition-window-top";
|
|
38437
|
+
const WINDOW_LEFT_PROPERTY = "--navi-transition-window-left";
|
|
38438
|
+
const WINDOW_WIDTH_PROPERTY = "--navi-transition-window-width";
|
|
38439
|
+
const WINDOW_HEIGHT_PROPERTY = "--navi-transition-window-height";
|
|
38440
|
+
const WINDOW_OLD_TOP_PROPERTY = "--navi-transition-window-old-top";
|
|
38441
|
+
const WINDOW_OLD_LEFT_PROPERTY = "--navi-transition-window-old-left";
|
|
38442
|
+
const WINDOW_PROPERTIES = [
|
|
38443
|
+
WINDOW_TOP_PROPERTY,
|
|
38444
|
+
WINDOW_LEFT_PROPERTY,
|
|
38445
|
+
WINDOW_WIDTH_PROPERTY,
|
|
38446
|
+
WINDOW_HEIGHT_PROPERTY,
|
|
38447
|
+
WINDOW_OLD_TOP_PROPERTY,
|
|
38448
|
+
WINDOW_OLD_LEFT_PROPERTY,
|
|
38449
|
+
];
|
|
38450
|
+
|
|
38451
|
+
// Whose numbers are currently published. The window belongs to the movement
|
|
38452
|
+
// that measured it, and only that one may take it down: a movement ending
|
|
38453
|
+
// after another has replaced it must not wipe numbers the new one is standing
|
|
38454
|
+
// on.
|
|
38455
|
+
let windowOwner = null;
|
|
38456
|
+
|
|
38457
|
+
const holdTransitionWindow = (owner, element, rectBefore) => {
|
|
38458
|
+
const rectAfter = element.getBoundingClientRect();
|
|
38459
|
+
// It cannot be measured from one side alone: a page arriving shorter than
|
|
38460
|
+
// the one it replaces would cut the one leaving, a page arriving taller
|
|
38461
|
+
// would be cut itself.
|
|
38462
|
+
const height =
|
|
38463
|
+
rectBefore.height > rectAfter.height ? rectBefore.height : rectAfter.height;
|
|
38464
|
+
windowOwner = owner;
|
|
38465
|
+
const { style } = document.documentElement;
|
|
38466
|
+
style.setProperty(WINDOW_TOP_PROPERTY, `${rectAfter.top}px`);
|
|
38467
|
+
style.setProperty(WINDOW_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
38468
|
+
style.setProperty(WINDOW_WIDTH_PROPERTY, `${rectAfter.width}px`);
|
|
38469
|
+
style.setProperty(WINDOW_HEIGHT_PROPERTY, `${height}px`);
|
|
38470
|
+
style.setProperty(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
|
|
38471
|
+
style.setProperty(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
|
|
38472
|
+
};
|
|
38473
|
+
|
|
38474
|
+
// The live layout takes the box back. A discontinuity by construction — the
|
|
38475
|
+
// window stands at the held height, the box is at its own — and an invisible
|
|
38476
|
+
// one: the page arriving is fully in place, and the strip below it that the
|
|
38477
|
+
// window still covers shows the page leaving only while it is still on screen.
|
|
38478
|
+
const releaseTransitionWindow = (owner) => {
|
|
38479
|
+
if (owner !== windowOwner) {
|
|
38480
|
+
return;
|
|
38481
|
+
}
|
|
38482
|
+
windowOwner = null;
|
|
38483
|
+
const { style } = document.documentElement;
|
|
38484
|
+
for (const property of WINDOW_PROPERTIES) {
|
|
38485
|
+
style.removeProperty(property);
|
|
38486
|
+
}
|
|
38487
|
+
};
|
|
38488
|
+
|
|
38489
|
+
installImportMetaCssBuild(import.meta);/**
|
|
38490
|
+
* How two routes move against each other, said one relation at a time —
|
|
38491
|
+
* without putting them in a row, and without a box in the tree.
|
|
38492
|
+
*
|
|
38493
|
+
* A page one goes INTO (a game, a profile, a place) is entered from wherever
|
|
38494
|
+
* one opened it, and left back out the same way. That is a fact about a PAIR
|
|
38495
|
+
* of pages, and only about the pairs it is written for:
|
|
38496
|
+
*
|
|
38497
|
+
* defineRouteTransition(MY_GAMES_PAGE, GAME_PAGE, "slide-x");
|
|
38498
|
+
* defineRouteTransition(RADAR_PAGE, GAME_PAGE, "slide-x");
|
|
38499
|
+
*
|
|
38500
|
+
* Going from the first page to the second plays forward, the reverse plays
|
|
38501
|
+
* back, and two pages never written in the same relation play nothing between
|
|
38502
|
+
* each other — two tabs of a bottom bar are side by side, neither is before
|
|
38503
|
+
* the other, and being animated by the same mechanism does not order them.
|
|
38504
|
+
* This is what tells this apart from <RouteTravel>: a travel box is a ROW — a
|
|
38505
|
+
* total order, plus a drag gesture that walks it — while this declares
|
|
38506
|
+
* individual relations and nothing else.
|
|
38507
|
+
*
|
|
38508
|
+
* A relation is reciprocal by DEFAULT, not by decree: the way back is the same
|
|
38509
|
+
* movement run the other way, because that is what lets a user build a map of
|
|
38510
|
+
* the app — but a relation written for the exact way travelled wins over being
|
|
38511
|
+
* the reverse of another, so B → A can be given a movement of its own, or
|
|
38512
|
+
* silenced with "none", by writing it (see findRelation).
|
|
38513
|
+
*
|
|
38514
|
+
* The relation says WHEN something plays and which way; the transition says
|
|
38515
|
+
* WHAT plays — a movement navi ships, or a name the application defines in its
|
|
38516
|
+
* own CSS (see the JSDoc below). Said without one, the relation plays the
|
|
38517
|
+
* browser's cross-fade.
|
|
38518
|
+
*
|
|
38519
|
+
* There is no box in the tree: by default what animates is the document itself
|
|
38520
|
+
* (its `root` view transition group), which is right for pages that ARE the
|
|
38521
|
+
* whole viewport. An application whose pages live between fixed bars marks the
|
|
38522
|
+
* region they live in with `data-navi-route-transition-area` — one attribute
|
|
38523
|
+
* on an element it already has — and the movement then plays on that region's
|
|
38524
|
+
* own pictures, clipped at its bounds, while the bars simply never move (see
|
|
38525
|
+
* TRANSITION_AREA_ATTRIBUTE for why the root pictures cannot do this job).
|
|
38526
|
+
*
|
|
38527
|
+
* The URL leads and the picture follows, as everywhere in navi: the change is
|
|
38528
|
+
* a navigation somebody else started (a <Link>, the back button), this only
|
|
38529
|
+
* watches it land and photographs the page being left in time (see
|
|
38530
|
+
* rendering_hold.js for how the picture is kept honest). A browser without
|
|
38531
|
+
* view transitions navigates without the movement.
|
|
38532
|
+
*
|
|
38533
|
+
* However many relations are defined, there is ONE watcher: every definition
|
|
38534
|
+
* lands in a shared registry, and the watcher is rebuilt over the whole of it
|
|
38535
|
+
* — a navigation is a single fact about the document, and the first relation
|
|
38536
|
+
* that speaks about it answers for it.
|
|
38537
|
+
*/
|
|
38538
|
+
const startViewTransition$1 = ensureDocumentStartViewTransition();
|
|
38539
|
+
const TRANSITION_ATTRIBUTE = "data-navi-route-transition";
|
|
38540
|
+
const TRANSITION_TYPE_ATTRIBUTE = "data-navi-route-transition-type";
|
|
38541
|
+
const TRANSITION_DURATION_PROPERTY = "--navi-route-transition-duration";
|
|
38542
|
+
// What the movement is played on. The root snapshot spans the viewport, and
|
|
38543
|
+
// the regions of elements captured on their own (a named bar) are BLANK in it
|
|
38544
|
+
// — a page sliding vertically then drags a blank band across the screen where
|
|
38545
|
+
// the top bar was. An application with fixed bars therefore marks the region
|
|
38546
|
+
// its pages live in with this attribute: the marked element is captured on its
|
|
38547
|
+
// own, the movement plays on ITS pictures, clipped at its bounds, and the bars
|
|
38548
|
+
// simply never move. Without the mark the document itself travels, which is
|
|
38549
|
+
// right for a page that IS the whole viewport.
|
|
38550
|
+
const TRANSITION_AREA_ATTRIBUTE = "data-navi-route-transition-area";
|
|
38551
|
+
const TRANSITION_TARGET_ATTRIBUTE = "data-navi-route-transition-target";
|
|
38552
|
+
const AREA_NAME = "navi-route-transition";
|
|
38553
|
+
// route_travel.jsx wears this on the root for the length of one of its
|
|
38554
|
+
// travels (its TRAVEL_ATTRIBUTE — a comment there mirrors this one). Read by
|
|
38555
|
+
// name rather than imported: importing route_travel.jsx would pull the whole
|
|
38556
|
+
// travel machinery into an application that only defines transitions.
|
|
38557
|
+
const ROUTE_TRAVEL_ATTRIBUTE = "data-navi-route-travel";
|
|
38558
|
+
|
|
38559
|
+
// The same movements, written once and played on either target: the document,
|
|
38560
|
+
// or the marked area. The guard keeps the two exclusive — with an area marked,
|
|
38561
|
+
// the root pictures must NOT move (they carry the whole viewport, blank bands
|
|
38562
|
+
// included).
|
|
38563
|
+
|
|
38564
|
+
const css$T = /* css */`
|
|
38565
|
+
/* The marked region is a picture of its own during every view transition of
|
|
38566
|
+
the document — which is what keeps it out of the root snapshot, where its
|
|
38567
|
+
place would otherwise be blank. */
|
|
38568
|
+
[data-navi-route-transition-area] {
|
|
38569
|
+
view-transition-name: navi-route-transition;
|
|
38570
|
+
}
|
|
38571
|
+
|
|
38572
|
+
/* Only while a transition of OURS is playing: everything below changes how
|
|
38573
|
+
the document animates, and the document belongs to the application the
|
|
38574
|
+
rest of the time. */
|
|
38575
|
+
:root[data-navi-route-transition] {
|
|
38576
|
+
/* With an area marked, the page AROUND it is not taken as a picture — so
|
|
38577
|
+
exactly one of the two names below exists at a time, and the movements
|
|
38578
|
+
can be written once for both. It is also what a fixed bar wants: a
|
|
38579
|
+
captured element is not painted where it stands and cannot be pointed
|
|
38580
|
+
at either, so a bar photographed with the document is dead for the
|
|
38581
|
+
length of every transition. Left live, it answers as it always did, and
|
|
38582
|
+
nothing shows through where the pages are — the two pictures cover the
|
|
38583
|
+
area's rectangle between them at every moment. Anything around that must
|
|
38584
|
+
ANIMATE rather than stand still gets a view-transition-name of its own,
|
|
38585
|
+
and the browser moves it on the same clock. */
|
|
38586
|
+
&[data-navi-route-transition-target="area"] {
|
|
38587
|
+
view-transition-name: none;
|
|
38588
|
+
}
|
|
38589
|
+
|
|
38590
|
+
&::view-transition-old(root),
|
|
38591
|
+
&::view-transition-new(root),
|
|
38592
|
+
&::view-transition-old(navi-route-transition),
|
|
38593
|
+
&::view-transition-new(navi-route-transition) {
|
|
38594
|
+
/* Written on the direction alone, so a relation with no type — the
|
|
38595
|
+
browser's cross-fade — answers to it like every other. */
|
|
38596
|
+
animation-duration: var(--navi-route-transition-duration, 300ms);
|
|
38597
|
+
}
|
|
38598
|
+
|
|
38599
|
+
/* The window the pages are seen through, when it is an area. Nothing here
|
|
38600
|
+
names \`root\`: the root picture IS the window when the whole document
|
|
38601
|
+
travels, already the size of the screen and already cut by it. */
|
|
38602
|
+
&::view-transition-group(navi-route-transition),
|
|
38603
|
+
&::view-transition-image-pair(navi-route-transition) {
|
|
38604
|
+
/* The pages are cut at the edge of the area they move in. Said HERE and
|
|
38605
|
+
nowhere else: these pictures are drawn in the top layer, so no
|
|
38606
|
+
overflow on any element of the document can reach them. */
|
|
38607
|
+
overflow: clip;
|
|
38608
|
+
}
|
|
38609
|
+
&::view-transition-group(navi-route-transition) {
|
|
38610
|
+
/* Held still for the whole transition, at the taller of the two states,
|
|
38611
|
+
and standing where the area stands (see transition_window.js). Held by
|
|
38612
|
+
dropping the group's animation rather than by winning against it with
|
|
38613
|
+
!important — which also drops its position animation, fine for an area
|
|
38614
|
+
that stays in the same place from one page to the next. */
|
|
38615
|
+
height: var(--navi-transition-window-height);
|
|
38616
|
+
animation-name: none;
|
|
38617
|
+
|
|
38618
|
+
/* Cut at the safe area, on top of being cut at the area's own box. The
|
|
38619
|
+
pictures are drawn in the top layer, so they cover a fixed bar as
|
|
38620
|
+
easily as anything else — and the area runs UNDER the bars by design:
|
|
38621
|
+
that is what a fixed bar is for, and what the room it gives back is
|
|
38622
|
+
for. An area taller than the screen therefore ends below the bottom
|
|
38623
|
+
bar, and a scrolled one starts above the top bar, so the movement
|
|
38624
|
+
would be watched painting over them for its whole length.
|
|
38625
|
+
|
|
38626
|
+
The band left free is the app's own safe area (see
|
|
38627
|
+
layout/safe_area.js) — every kind of furniture at once, not the bars
|
|
38628
|
+
alone, and read rather than asked for, so one that grows, shrinks or
|
|
38629
|
+
unmounts mid-transition is followed without anything being told. What
|
|
38630
|
+
the window cannot know is only where it itself stands, and that is the
|
|
38631
|
+
measured half. */
|
|
38632
|
+
--navi-route-transition-clip-top: max(
|
|
38633
|
+
0px,
|
|
38634
|
+
var(--navi-safe-area-inset-top) - var(--navi-transition-window-top)
|
|
38635
|
+
);
|
|
38636
|
+
--navi-route-transition-clip-left: max(
|
|
38637
|
+
0px,
|
|
38638
|
+
var(--navi-safe-area-inset-left) - var(--navi-transition-window-left)
|
|
38639
|
+
);
|
|
38640
|
+
--navi-route-transition-clip-bottom: max(
|
|
38641
|
+
0px,
|
|
38642
|
+
var(--navi-transition-window-top) +
|
|
38643
|
+
var(--navi-transition-window-height) +
|
|
38644
|
+
var(--navi-safe-area-inset-bottom) - 100dvh
|
|
38645
|
+
);
|
|
38646
|
+
--navi-route-transition-clip-right: max(
|
|
38647
|
+
0px,
|
|
38648
|
+
var(--navi-transition-window-left) +
|
|
38649
|
+
var(--navi-transition-window-width) +
|
|
38650
|
+
var(--navi-safe-area-inset-right) - 100dvw
|
|
38651
|
+
);
|
|
38652
|
+
clip-path: inset(
|
|
38653
|
+
var(--navi-route-transition-clip-top)
|
|
38654
|
+
var(--navi-route-transition-clip-right)
|
|
38655
|
+
var(--navi-route-transition-clip-bottom)
|
|
38656
|
+
var(--navi-route-transition-clip-left)
|
|
38657
|
+
);
|
|
38658
|
+
|
|
38659
|
+
/* How far a page travels: the WINDOW it is seen through, not its own
|
|
38660
|
+
size. A page is as tall as its content — several screens of it — and a
|
|
38661
|
+
movement measured on the picture would send it thousands of pixels
|
|
38662
|
+
away, off screen for most of the transition and flying past at the
|
|
38663
|
+
end. What one page crossing another means is one window's worth of
|
|
38664
|
+
movement, whatever the pages are made of. Inherited by the pictures,
|
|
38665
|
+
which is where it is used (see the keyframes). */
|
|
38666
|
+
--navi-route-transition-travel-x: calc(
|
|
38667
|
+
var(--navi-transition-window-width) - var(
|
|
38668
|
+
--navi-route-transition-clip-left
|
|
38669
|
+
) - var(--navi-route-transition-clip-right)
|
|
38670
|
+
);
|
|
38671
|
+
--navi-route-transition-travel-y: calc(
|
|
38672
|
+
var(--navi-transition-window-height) - var(
|
|
38673
|
+
--navi-route-transition-clip-top
|
|
38674
|
+
) - var(--navi-route-transition-clip-bottom)
|
|
38675
|
+
);
|
|
38676
|
+
}
|
|
38677
|
+
&::view-transition-old(navi-route-transition) {
|
|
38678
|
+
/* Where the area WAS on screen, which is not where the window stands
|
|
38679
|
+
(see transition_window.js). Offset here rather than by \`translate\`,
|
|
38680
|
+
which the movement itself uses. */
|
|
38681
|
+
top: calc(
|
|
38682
|
+
var(--navi-transition-window-old-top) - var(
|
|
38683
|
+
--navi-transition-window-top
|
|
38684
|
+
)
|
|
38685
|
+
);
|
|
38686
|
+
left: calc(
|
|
38687
|
+
var(--navi-transition-window-old-left) - var(
|
|
38688
|
+
--navi-transition-window-left
|
|
38689
|
+
)
|
|
38690
|
+
);
|
|
38691
|
+
}
|
|
38692
|
+
|
|
38693
|
+
/* ------------------------------------------------------------------
|
|
38694
|
+
The movements. One of \`root\` and \`navi-route-transition\` exists at a
|
|
38695
|
+
time (see the opt-out above), so each is written for both.
|
|
38696
|
+
------------------------------------------------------------------ */
|
|
38697
|
+
&[data-navi-route-transition-type="slide-x"],
|
|
38698
|
+
&[data-navi-route-transition-type="slide-y"],
|
|
38699
|
+
&[data-navi-route-transition-type="cover-x"],
|
|
38700
|
+
&[data-navi-route-transition-type="cover-y"] {
|
|
38701
|
+
&::view-transition-old(root),
|
|
38702
|
+
&::view-transition-new(root),
|
|
38703
|
+
&::view-transition-old(navi-route-transition),
|
|
38704
|
+
&::view-transition-new(navi-route-transition) {
|
|
38705
|
+
width: auto;
|
|
38706
|
+
/* Each picture at the size it was taken at: a page is not resized by
|
|
38707
|
+
the page it crosses. The picture is as wide as the box the browser
|
|
38708
|
+
gives it — the arriving one's — so a page leaving a narrower box (a
|
|
38709
|
+
scrollbar appeared, a side panel closed) would be seen zooming over
|
|
38710
|
+
the length of the movement. Left to the untyped cross-fade, where
|
|
38711
|
+
scaling one picture into the other is the whole idea. */
|
|
38712
|
+
height: auto;
|
|
38713
|
+
object-fit: none;
|
|
38714
|
+
object-position: top left;
|
|
38715
|
+
/* The default cross-fade, dropped: two pages sliding past each other
|
|
38716
|
+
are two solid things, and seeing through one to the other says they
|
|
38717
|
+
are the same page changing its mind. */
|
|
38718
|
+
mix-blend-mode: normal;
|
|
38719
|
+
animation-timing-function: ease;
|
|
38720
|
+
animation-fill-mode: both;
|
|
38721
|
+
}
|
|
38722
|
+
}
|
|
38723
|
+
|
|
38724
|
+
&[data-navi-route-transition-type="slide-x"] {
|
|
38725
|
+
&[data-navi-route-transition="forward"] {
|
|
38726
|
+
&::view-transition-old(root),
|
|
38727
|
+
&::view-transition-old(navi-route-transition) {
|
|
38728
|
+
animation-name: navi-route-transition-leave-towards-start;
|
|
38729
|
+
}
|
|
38730
|
+
&::view-transition-new(root),
|
|
38731
|
+
&::view-transition-new(navi-route-transition) {
|
|
38732
|
+
animation-name: navi-route-transition-enter-from-end;
|
|
38733
|
+
}
|
|
38734
|
+
}
|
|
38735
|
+
&[data-navi-route-transition="back"] {
|
|
38736
|
+
&::view-transition-old(root),
|
|
38737
|
+
&::view-transition-old(navi-route-transition) {
|
|
38738
|
+
animation-name: navi-route-transition-leave-towards-end;
|
|
38739
|
+
}
|
|
38740
|
+
&::view-transition-new(root),
|
|
38741
|
+
&::view-transition-new(navi-route-transition) {
|
|
38742
|
+
animation-name: navi-route-transition-enter-from-start;
|
|
38743
|
+
}
|
|
38744
|
+
}
|
|
38745
|
+
}
|
|
38746
|
+
|
|
38747
|
+
/* The same four movements, along the other axis: the start of a column is
|
|
38748
|
+
its top, so going forward there is the page rising and the next one
|
|
38749
|
+
coming up from below. */
|
|
38750
|
+
&[data-navi-route-transition-type="slide-y"] {
|
|
38751
|
+
&[data-navi-route-transition="forward"] {
|
|
38752
|
+
&::view-transition-old(root),
|
|
38753
|
+
&::view-transition-old(navi-route-transition) {
|
|
38754
|
+
animation-name: navi-route-transition-leave-towards-top;
|
|
38755
|
+
}
|
|
38756
|
+
&::view-transition-new(root),
|
|
38757
|
+
&::view-transition-new(navi-route-transition) {
|
|
38758
|
+
animation-name: navi-route-transition-enter-from-bottom;
|
|
38759
|
+
}
|
|
38760
|
+
}
|
|
38761
|
+
&[data-navi-route-transition="back"] {
|
|
38762
|
+
&::view-transition-old(root),
|
|
38763
|
+
&::view-transition-old(navi-route-transition) {
|
|
38764
|
+
animation-name: navi-route-transition-leave-towards-bottom;
|
|
38765
|
+
}
|
|
38766
|
+
&::view-transition-new(root),
|
|
38767
|
+
&::view-transition-new(navi-route-transition) {
|
|
38768
|
+
animation-name: navi-route-transition-enter-from-top;
|
|
38769
|
+
}
|
|
38770
|
+
}
|
|
38771
|
+
}
|
|
38772
|
+
|
|
38773
|
+
/* One page over the other, the way a sheet covers a desk: the page
|
|
38774
|
+
arriving slides in ON TOP of one that does not move, and going back it
|
|
38775
|
+
slides off, uncovering it. The still page is animated all the same — to
|
|
38776
|
+
a keyframe that goes nowhere — because left to the browser it would
|
|
38777
|
+
fade. */
|
|
38778
|
+
&[data-navi-route-transition-type="cover-x"] {
|
|
38779
|
+
&[data-navi-route-transition="forward"] {
|
|
38780
|
+
&::view-transition-old(root),
|
|
38781
|
+
&::view-transition-old(navi-route-transition) {
|
|
38782
|
+
animation-name: navi-route-transition-still;
|
|
38783
|
+
}
|
|
38784
|
+
&::view-transition-new(root),
|
|
38785
|
+
&::view-transition-new(navi-route-transition) {
|
|
38786
|
+
animation-name: navi-route-transition-enter-from-end;
|
|
38787
|
+
}
|
|
38788
|
+
}
|
|
38789
|
+
&[data-navi-route-transition="back"] {
|
|
38790
|
+
&::view-transition-old(root),
|
|
38791
|
+
&::view-transition-old(navi-route-transition) {
|
|
38792
|
+
/* The page leaving is the cover: it must slide off ABOVE the one it
|
|
38793
|
+
uncovers, against the browser's default of drawing the new page on
|
|
38794
|
+
top. */
|
|
38795
|
+
z-index: 1;
|
|
38796
|
+
animation-name: navi-route-transition-leave-towards-end;
|
|
38797
|
+
}
|
|
38798
|
+
&::view-transition-new(root),
|
|
38799
|
+
&::view-transition-new(navi-route-transition) {
|
|
38800
|
+
animation-name: navi-route-transition-still;
|
|
38801
|
+
}
|
|
38802
|
+
}
|
|
38803
|
+
}
|
|
38804
|
+
&[data-navi-route-transition-type="cover-y"] {
|
|
38805
|
+
&[data-navi-route-transition="forward"] {
|
|
38806
|
+
&::view-transition-old(root),
|
|
38807
|
+
&::view-transition-old(navi-route-transition) {
|
|
38808
|
+
animation-name: navi-route-transition-still;
|
|
38809
|
+
}
|
|
38810
|
+
&::view-transition-new(root),
|
|
38811
|
+
&::view-transition-new(navi-route-transition) {
|
|
38812
|
+
animation-name: navi-route-transition-enter-from-bottom;
|
|
38813
|
+
}
|
|
38814
|
+
}
|
|
38815
|
+
&[data-navi-route-transition="back"] {
|
|
38816
|
+
&::view-transition-old(root),
|
|
38817
|
+
&::view-transition-old(navi-route-transition) {
|
|
38818
|
+
z-index: 1;
|
|
38819
|
+
animation-name: navi-route-transition-leave-towards-bottom;
|
|
38820
|
+
}
|
|
38821
|
+
&::view-transition-new(root),
|
|
38822
|
+
&::view-transition-new(navi-route-transition) {
|
|
38823
|
+
animation-name: navi-route-transition-still;
|
|
38824
|
+
}
|
|
38825
|
+
}
|
|
38826
|
+
}
|
|
38827
|
+
|
|
38828
|
+
/* Going deeper is coming closer: the page arriving lands from slightly too
|
|
38829
|
+
big, and going back it is the page leaving that grows away. The other
|
|
38830
|
+
side keeps the browser's own fade under it. */
|
|
38831
|
+
&[data-navi-route-transition-type="zoom"] {
|
|
38832
|
+
&::view-transition-old(root),
|
|
38833
|
+
&::view-transition-new(root),
|
|
38834
|
+
&::view-transition-old(navi-route-transition),
|
|
38835
|
+
&::view-transition-new(navi-route-transition) {
|
|
38836
|
+
animation-fill-mode: both;
|
|
38837
|
+
}
|
|
38838
|
+
&[data-navi-route-transition="forward"] {
|
|
38839
|
+
&::view-transition-new(root),
|
|
38840
|
+
&::view-transition-new(navi-route-transition) {
|
|
38841
|
+
animation-name: navi-route-transition-zoom-in;
|
|
38842
|
+
}
|
|
38843
|
+
}
|
|
38844
|
+
&[data-navi-route-transition="back"] {
|
|
38845
|
+
&::view-transition-old(root),
|
|
38846
|
+
&::view-transition-old(navi-route-transition) {
|
|
38847
|
+
animation-name: navi-route-transition-zoom-out;
|
|
38848
|
+
}
|
|
38849
|
+
}
|
|
38850
|
+
}
|
|
38851
|
+
}
|
|
38852
|
+
|
|
38853
|
+
/* One window's worth of movement. The fallback is the picture's own size,
|
|
38854
|
+
which is what the window is when the whole document travels: the root
|
|
38855
|
+
picture IS the viewport. */
|
|
38856
|
+
@keyframes navi-route-transition-leave-towards-start {
|
|
38857
|
+
to {
|
|
38858
|
+
translate: calc(-1 * var(--navi-route-transition-travel-x, 100%)) 0;
|
|
38859
|
+
}
|
|
38860
|
+
}
|
|
38861
|
+
@keyframes navi-route-transition-enter-from-end {
|
|
38862
|
+
from {
|
|
38863
|
+
translate: var(--navi-route-transition-travel-x, 100%) 0;
|
|
38864
|
+
}
|
|
38865
|
+
}
|
|
38866
|
+
@keyframes navi-route-transition-leave-towards-end {
|
|
38867
|
+
to {
|
|
38868
|
+
translate: var(--navi-route-transition-travel-x, 100%) 0;
|
|
38869
|
+
}
|
|
38870
|
+
}
|
|
38871
|
+
@keyframes navi-route-transition-enter-from-start {
|
|
38872
|
+
from {
|
|
38873
|
+
translate: calc(-1 * var(--navi-route-transition-travel-x, 100%)) 0;
|
|
38874
|
+
}
|
|
38875
|
+
}
|
|
38876
|
+
@keyframes navi-route-transition-leave-towards-top {
|
|
38877
|
+
to {
|
|
38878
|
+
translate: 0 calc(-1 * var(--navi-route-transition-travel-y, 100%));
|
|
38879
|
+
}
|
|
38880
|
+
}
|
|
38881
|
+
@keyframes navi-route-transition-enter-from-bottom {
|
|
38882
|
+
from {
|
|
38883
|
+
translate: 0 var(--navi-route-transition-travel-y, 100%);
|
|
38884
|
+
}
|
|
38885
|
+
}
|
|
38886
|
+
@keyframes navi-route-transition-leave-towards-bottom {
|
|
38887
|
+
to {
|
|
38888
|
+
translate: 0 var(--navi-route-transition-travel-y, 100%);
|
|
38889
|
+
}
|
|
38890
|
+
}
|
|
38891
|
+
@keyframes navi-route-transition-enter-from-top {
|
|
38892
|
+
from {
|
|
38893
|
+
translate: 0 calc(-1 * var(--navi-route-transition-travel-y, 100%));
|
|
38894
|
+
}
|
|
38895
|
+
}
|
|
38896
|
+
@keyframes navi-route-transition-zoom-in {
|
|
38897
|
+
from {
|
|
38898
|
+
opacity: 0;
|
|
38899
|
+
scale: 1.1;
|
|
38900
|
+
}
|
|
38901
|
+
}
|
|
38902
|
+
@keyframes navi-route-transition-zoom-out {
|
|
38903
|
+
to {
|
|
38904
|
+
opacity: 0;
|
|
38905
|
+
scale: 1.1;
|
|
38906
|
+
}
|
|
38907
|
+
}
|
|
38908
|
+
/* Standing still, said as an animation: naming it replaces the browser's own
|
|
38909
|
+
fade on that side, which is the whole point. */
|
|
38910
|
+
@keyframes navi-route-transition-still {
|
|
38911
|
+
to {
|
|
38912
|
+
translate: 0 0;
|
|
38913
|
+
}
|
|
38914
|
+
}
|
|
38915
|
+
`;
|
|
38916
|
+
|
|
38917
|
+
/**
|
|
38918
|
+
* The region the pages live in — where the movements play.
|
|
38919
|
+
*
|
|
38920
|
+
* Wrap the `<Route>` tree with it in an application that has fixed furniture
|
|
38921
|
+
* (a top bar, a tab bar): the movements then play on THIS element's pictures,
|
|
38922
|
+
* clipped at its bounds, and the bars never move. Without it the document
|
|
38923
|
+
* itself travels, which is right only when the pages are the whole viewport —
|
|
38924
|
+
* with bars around, the moving root picture drags a blank band across the
|
|
38925
|
+
* screen where they stand.
|
|
38926
|
+
*
|
|
38927
|
+
* It is a real box, and it must be: what is photographed and clipped IS its
|
|
38928
|
+
* rectangle. So `display: contents` cannot be used on it — an element with no
|
|
38929
|
+
* box is never captured, the movement plays on nothing and the browser aborts
|
|
38930
|
+
* the transition. Give it the layout the pages need instead — it is a `Box`,
|
|
38931
|
+
* so `flex`, `className`, `style` and the rest are there for that. An
|
|
38932
|
+
* application that already has an element holding its pages can mark that one
|
|
38933
|
+
* with `data-navi-route-transition-area` rather than nesting another.
|
|
38934
|
+
*
|
|
38935
|
+
* @type {import("ignore:preact").FunctionComponent<{ children?: any, [key: string]: any }>}
|
|
38936
|
+
*/
|
|
38937
|
+
const RouteTransitionArea = ({
|
|
38938
|
+
children,
|
|
38939
|
+
...rest
|
|
38940
|
+
}) => {
|
|
38941
|
+
import.meta.css = [css$T, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
38942
|
+
const props = {
|
|
38943
|
+
...rest,
|
|
38944
|
+
[TRANSITION_AREA_ATTRIBUTE]: ""
|
|
38945
|
+
};
|
|
38946
|
+
return jsx(Box, {
|
|
38947
|
+
...props,
|
|
38948
|
+
children: children
|
|
38949
|
+
});
|
|
38950
|
+
};
|
|
38951
|
+
|
|
38952
|
+
/**
|
|
38953
|
+
* Declare how a pair of routes moves against each other.
|
|
38954
|
+
*
|
|
38955
|
+
* @param {object} from - a route, or `{ route, params }` when the page is a
|
|
38956
|
+
* param of a route rather than a route of its own.
|
|
38957
|
+
* @param {object} to - same forms. Going from `from` to `to` plays forward,
|
|
38958
|
+
* the reverse plays back — unless the reverse is written as a relation of
|
|
38959
|
+
* its own, which then owns that way (a movement of its own, or `"none"` for
|
|
38960
|
+
* a plain cut). A change between two pages no relation was written for plays
|
|
38961
|
+
* nothing.
|
|
38962
|
+
* @param {string|{type?: string, duration?: number|string}} [transition] -
|
|
38963
|
+
* what plays: a type name, or `{ type, duration }` to also say how long
|
|
38964
|
+
* (`--navi-route-transition-duration` says it for everyone otherwise).
|
|
38965
|
+
* Omitted, the browser's own cross-fade. Shipped with navi:
|
|
38966
|
+
* - `"slide-x"`, `"slide-y"`: the two pages slide past each other, forward
|
|
38967
|
+
* towards the start of the axis;
|
|
38968
|
+
* - `"cover-x"`, `"cover-y"`: the page arriving slides in OVER one that does
|
|
38969
|
+
* not move, and slides off it on the way back;
|
|
38970
|
+
* - `"zoom"`: the deeper page is the closer one — it lands from slightly too
|
|
38971
|
+
* big, and grows away when left;
|
|
38972
|
+
* - `"none"`: nothing, said out loud — written on one way of a pair, it cuts
|
|
38973
|
+
* where the reverse of the other way (or the default) would have played;
|
|
38974
|
+
* - `"cross-fade"`: the omitted case, nameable — so one way of a pair can
|
|
38975
|
+
* fade while the other way moves.
|
|
38976
|
+
* Every type plays on the document, or on the element marked
|
|
38977
|
+
* `data-navi-route-transition-area` when the application has one (see the
|
|
38978
|
+
* top of this file). Any other name belongs to the application: for the
|
|
38979
|
+
* length of the transition the root carries
|
|
38980
|
+
* `data-navi-route-transition-type="<type>"` next to
|
|
38981
|
+
* `data-navi-route-transition="forward"|"back"`, and the application's CSS
|
|
38982
|
+
* defines the movement against the view transition pseudo-elements:
|
|
38983
|
+
*
|
|
38984
|
+
* :root[data-navi-route-transition-type="spin"][data-navi-route-transition="forward"] {
|
|
38985
|
+
* &::view-transition-new(root) {
|
|
38986
|
+
* animation-name: my-spin-in;
|
|
38987
|
+
* }
|
|
38988
|
+
* }
|
|
38989
|
+
* @returns {() => void} remove this relation.
|
|
38990
|
+
*/
|
|
38991
|
+
const defineRouteTransition = (from, to, transition) => {
|
|
38992
|
+
import.meta.css = [css$T, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
38993
|
+
const {
|
|
38994
|
+
type,
|
|
38995
|
+
duration
|
|
38996
|
+
} = normalizeTransition(transition);
|
|
38997
|
+
const relation = {
|
|
38998
|
+
from: normalizePage$1(from),
|
|
38999
|
+
to: normalizePage$1(to),
|
|
39000
|
+
type,
|
|
39001
|
+
duration
|
|
39002
|
+
};
|
|
39003
|
+
relations.push(relation);
|
|
39004
|
+
rebuildWatcher();
|
|
39005
|
+
updateRoutingObservers();
|
|
39006
|
+
return () => {
|
|
39007
|
+
const index = relations.indexOf(relation);
|
|
39008
|
+
if (index > -1) {
|
|
39009
|
+
relations.splice(index, 1);
|
|
39010
|
+
rebuildWatcher();
|
|
39011
|
+
updateRoutingObservers();
|
|
39012
|
+
}
|
|
39013
|
+
};
|
|
39014
|
+
};
|
|
39015
|
+
|
|
39016
|
+
/**
|
|
39017
|
+
* What plays on a navigation no relation was written for: every route change
|
|
39018
|
+
* then plays this transition, and the written relations keep their own.
|
|
39019
|
+
*
|
|
39020
|
+
* A default has no direction — nothing says which of two arbitrary pages is
|
|
39021
|
+
* "before" the other — so give it a movement that does not need one:
|
|
39022
|
+
* `"cross-fade"`, or a custom type whose CSS is keyed on the type alone.
|
|
39023
|
+
*
|
|
39024
|
+
* @param {string|{type?: string, duration?: number|string}} transition - same
|
|
39025
|
+
* forms as defineRouteTransition's. `"none"` (or removing the default) puts
|
|
39026
|
+
* the silence back.
|
|
39027
|
+
* @returns {() => void} remove this default.
|
|
39028
|
+
*/
|
|
39029
|
+
const defineRouteDefaultTransition = transition => {
|
|
39030
|
+
import.meta.css = [css$T, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
39031
|
+
const value = normalizeTransition(transition);
|
|
39032
|
+
defaultTransition = value;
|
|
39033
|
+
updateRoutingObservers();
|
|
39034
|
+
return () => {
|
|
39035
|
+
if (defaultTransition === value) {
|
|
39036
|
+
defaultTransition = null;
|
|
39037
|
+
updateRoutingObservers();
|
|
39038
|
+
}
|
|
39039
|
+
};
|
|
39040
|
+
};
|
|
39041
|
+
|
|
39042
|
+
// "cross-fade" is a name for what plays when nothing is asked for — the
|
|
39043
|
+
// browser's own animation — so it normalizes to asking for nothing. Having the
|
|
39044
|
+
// name lets one way of a pair say it out loud while the other way slides.
|
|
39045
|
+
const normalizeTransition = transition => {
|
|
39046
|
+
const {
|
|
39047
|
+
type,
|
|
39048
|
+
duration
|
|
39049
|
+
} = typeof transition === "string" ? {
|
|
39050
|
+
type: transition
|
|
39051
|
+
} : transition || {};
|
|
39052
|
+
return {
|
|
39053
|
+
type: type === "cross-fade" ? undefined : type,
|
|
39054
|
+
duration
|
|
39055
|
+
};
|
|
39056
|
+
};
|
|
39057
|
+
|
|
39058
|
+
// Every relation defined, and the single watcher standing over all of them.
|
|
39059
|
+
const relations = [];
|
|
39060
|
+
let watcher = null;
|
|
39061
|
+
const rebuildWatcher = () => {
|
|
39062
|
+
if (watcher) {
|
|
39063
|
+
watcher.stop();
|
|
39064
|
+
watcher = null;
|
|
39065
|
+
}
|
|
39066
|
+
if (relations.length === 0) {
|
|
39067
|
+
return;
|
|
39068
|
+
}
|
|
39069
|
+
// Every page any relation mentions, each once: the position of the current
|
|
39070
|
+
// page in this list is what turns "some signal moved" into "the document
|
|
39071
|
+
// went from page A to page B".
|
|
39072
|
+
const pages = [];
|
|
39073
|
+
for (const {
|
|
39074
|
+
from,
|
|
39075
|
+
to
|
|
39076
|
+
} of relations) {
|
|
39077
|
+
if (pageIndexOf$1(pages, from) === -1) {
|
|
39078
|
+
pages.push(from);
|
|
39079
|
+
}
|
|
39080
|
+
if (pageIndexOf$1(pages, to) === -1) {
|
|
39081
|
+
pages.push(to);
|
|
39082
|
+
}
|
|
39083
|
+
}
|
|
39084
|
+
const currentIndexSignal = computed(() => currentPageIndex$1(pages));
|
|
39085
|
+
let currentIndex;
|
|
39086
|
+
let firstReading = true;
|
|
39087
|
+
const onMove = index => {
|
|
39088
|
+
const fromIndex = currentIndex;
|
|
39089
|
+
currentIndex = index;
|
|
39090
|
+
if (firstReading) {
|
|
39091
|
+
// Where the document already is — nothing changed, there is nothing to
|
|
39092
|
+
// animate. Also the first reading after a definition landed mid-life:
|
|
39093
|
+
// the watcher is standing again on whatever page is current.
|
|
39094
|
+
firstReading = false;
|
|
39095
|
+
return;
|
|
39096
|
+
}
|
|
39097
|
+
if (index === -1 || fromIndex === -1 || fromIndex === index) {
|
|
39098
|
+
return;
|
|
39099
|
+
}
|
|
39100
|
+
const found = findRelation(pages[fromIndex], pages[index]);
|
|
39101
|
+
if (!found) {
|
|
39102
|
+
// No relation says anything about these two: they are side by side, and
|
|
39103
|
+
// silence is the fact — not a missing case.
|
|
39104
|
+
return;
|
|
39105
|
+
}
|
|
39106
|
+
const {
|
|
39107
|
+
direction,
|
|
39108
|
+
relation
|
|
39109
|
+
} = found;
|
|
39110
|
+
if (relation.type === "none") {
|
|
39111
|
+
// Silence said out loud: this way of the pair was written to play
|
|
39112
|
+
// nothing, where the reverse of the other way — or the default — would
|
|
39113
|
+
// have played.
|
|
39114
|
+
navigationAnimated = true;
|
|
39115
|
+
return;
|
|
39116
|
+
}
|
|
39117
|
+
beginTransition({
|
|
39118
|
+
page: pages[index],
|
|
39119
|
+
direction,
|
|
39120
|
+
type: relation.type,
|
|
39121
|
+
duration: relation.duration
|
|
39122
|
+
});
|
|
39123
|
+
};
|
|
39124
|
+
// `subscribe` rather than `effect`: it hands the value to a callback that is
|
|
39125
|
+
// not being tracked, and starting a view transition releases holds that make
|
|
39126
|
+
// the very signals this is watched through move again.
|
|
39127
|
+
const unsubscribe = currentIndexSignal.subscribe(onMove);
|
|
39128
|
+
watcher = {
|
|
39129
|
+
stop: unsubscribe
|
|
39130
|
+
};
|
|
39131
|
+
};
|
|
39132
|
+
|
|
39133
|
+
// What plays when no relation matched (see defineRouteDefaultTransition), and
|
|
39134
|
+
// whether the navigation now landing found an answer already — a relation's
|
|
39135
|
+
// transition, a "none", a RouteTravel travel. The flag is reset when a
|
|
39136
|
+
// navigation begins, so it is always about the latest one.
|
|
39137
|
+
let defaultTransition = null;
|
|
39138
|
+
let navigationAnimated = false;
|
|
39139
|
+
|
|
39140
|
+
// The two ends of a navigation, watched while there is anyone to animate it.
|
|
39141
|
+
// The picture of the page being left has to be honest, so rendering is held
|
|
39142
|
+
// from before the navigation's first write (see rendering_hold.js) — and given
|
|
39143
|
+
// back at the far end when the change turns out to be one nobody animates,
|
|
39144
|
+
// which is also the one moment the DEFAULT can decide: every relation has had
|
|
39145
|
+
// its say by then.
|
|
39146
|
+
let stopRoutingObservers = null;
|
|
39147
|
+
const updateRoutingObservers = () => {
|
|
39148
|
+
const wanted = relations.length > 0 || defaultTransition !== null;
|
|
39149
|
+
if (wanted && !stopRoutingObservers) {
|
|
39150
|
+
const stopWatchingStart = observeBeforeRouting(() => {
|
|
39151
|
+
navigationAnimated = false;
|
|
39152
|
+
holdRenderingForRouting();
|
|
39153
|
+
});
|
|
39154
|
+
const stopWatchingEnd = observeAfterRouting(() => {
|
|
39155
|
+
if (defaultTransition && defaultTransition.type !== "none" && !navigationAnimated) {
|
|
39156
|
+
beginTransition({
|
|
39157
|
+
page: null,
|
|
39158
|
+
// A default has no direction: nothing says which of two arbitrary
|
|
39159
|
+
// pages is before the other. The attribute is worn empty — present
|
|
39160
|
+
// for whoever keys on "one of ours is playing", silent on the way.
|
|
39161
|
+
direction: "",
|
|
39162
|
+
type: defaultTransition.type,
|
|
39163
|
+
duration: defaultTransition.duration
|
|
39164
|
+
});
|
|
39165
|
+
}
|
|
39166
|
+
releaseRoutingRenderingHold();
|
|
39167
|
+
});
|
|
39168
|
+
stopRoutingObservers = () => {
|
|
39169
|
+
stopWatchingStart();
|
|
39170
|
+
stopWatchingEnd();
|
|
39171
|
+
};
|
|
39172
|
+
return;
|
|
39173
|
+
}
|
|
39174
|
+
if (!wanted && stopRoutingObservers) {
|
|
39175
|
+
stopRoutingObservers();
|
|
39176
|
+
stopRoutingObservers = null;
|
|
39177
|
+
}
|
|
39178
|
+
};
|
|
39179
|
+
|
|
39180
|
+
// The exact way travelled first, over the whole registry, and only then the
|
|
39181
|
+
// reverses: a relation written B → A owns that way, and being the reverse of
|
|
39182
|
+
// one written A → B never outranks it. This is what makes reciprocity a
|
|
39183
|
+
// default rather than a decree — write the way back to give it a movement of
|
|
39184
|
+
// its own, or "none" to silence it.
|
|
39185
|
+
const findRelation = (fromPage, toPage) => {
|
|
39186
|
+
for (const relation of relations) {
|
|
39187
|
+
if (samePage$1(relation.from, fromPage) && samePage$1(relation.to, toPage)) {
|
|
39188
|
+
return {
|
|
39189
|
+
direction: "forward",
|
|
39190
|
+
relation
|
|
39191
|
+
};
|
|
39192
|
+
}
|
|
39193
|
+
}
|
|
39194
|
+
for (const relation of relations) {
|
|
39195
|
+
if (samePage$1(relation.from, toPage) && samePage$1(relation.to, fromPage)) {
|
|
39196
|
+
return {
|
|
39197
|
+
direction: "back",
|
|
39198
|
+
relation
|
|
39199
|
+
};
|
|
39200
|
+
}
|
|
39201
|
+
}
|
|
39202
|
+
return null;
|
|
39203
|
+
};
|
|
39204
|
+
|
|
39205
|
+
// The transition whose direction the document is currently wearing. One per
|
|
39206
|
+
// document, as with view transitions themselves: a new one starting takes the
|
|
39207
|
+
// attributes over, and only their owner may take them off.
|
|
39208
|
+
let currentTransition = null;
|
|
39209
|
+
const beginTransition = ({
|
|
39210
|
+
page,
|
|
39211
|
+
direction,
|
|
39212
|
+
type,
|
|
39213
|
+
duration
|
|
39214
|
+
}) => {
|
|
39215
|
+
navigationAnimated = true;
|
|
39216
|
+
const documentElement = document.documentElement;
|
|
39217
|
+
// One navigation, one animator. A RouteTravel box already travelling this
|
|
39218
|
+
// change owns the document's transition — and possibly a finger; starting
|
|
39219
|
+
// one here on top would skip its pictures mid-slide. A pair of routes must
|
|
39220
|
+
// be animated by RouteTravel or by a route transition, never both.
|
|
39221
|
+
if (documentElement.hasAttribute(ROUTE_TRAVEL_ATTRIBUTE)) {
|
|
39222
|
+
console.warn("A RouteTravel is animating this navigation; the route transition defined between these routes is skipped. Animate a pair of routes with RouteTravel or defineRouteTransition, not both.");
|
|
39223
|
+
return;
|
|
39224
|
+
}
|
|
39225
|
+
const transition = {};
|
|
39226
|
+
currentTransition = transition;
|
|
39227
|
+
documentElement.setAttribute(TRANSITION_ATTRIBUTE, direction);
|
|
39228
|
+
if (type) {
|
|
39229
|
+
documentElement.setAttribute(TRANSITION_TYPE_ATTRIBUTE, type);
|
|
39230
|
+
}
|
|
39231
|
+
// Looked up per transition, not once: the area is the application's own
|
|
39232
|
+
// element and follows its lifecycle — a page layout without bars has none,
|
|
39233
|
+
// and the movement then plays on the document itself.
|
|
39234
|
+
const areaElements = document.querySelectorAll(`[${TRANSITION_AREA_ATTRIBUTE}]`);
|
|
39235
|
+
if (areaElements.length > 1) {
|
|
39236
|
+
warnOnce("several-areas", `${areaElements.length} elements carry ${TRANSITION_AREA_ATTRIBUTE}. They all take the same view-transition-name, and a name belongs to one element at a time: the browser refuses EVERY view transition of the document while this holds. Mark the one element the pages live in.`);
|
|
39237
|
+
}
|
|
39238
|
+
const areaElement = areaElements.length > 0 ? areaElements[0] : null;
|
|
39239
|
+
// The area as it stands before anything moves: rendering is held, so this is
|
|
39240
|
+
// still the page being left (see holdAreaGeometry).
|
|
39241
|
+
let areaRectBefore = null;
|
|
39242
|
+
if (areaElement) {
|
|
39243
|
+
documentElement.setAttribute(TRANSITION_TARGET_ATTRIBUTE, "area");
|
|
39244
|
+
areaRectBefore = areaElement.getBoundingClientRect();
|
|
39245
|
+
}
|
|
39246
|
+
// A duration of this relation's own, worn for the length of the transition —
|
|
39247
|
+
// and whatever the application had written inline put back afterwards, not
|
|
39248
|
+
// erased.
|
|
39249
|
+
let restoreDuration = null;
|
|
39250
|
+
if (duration !== undefined) {
|
|
39251
|
+
const durationBefore = documentElement.style.getPropertyValue(TRANSITION_DURATION_PROPERTY);
|
|
39252
|
+
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, typeof duration === "number" ? `${duration}ms` : duration);
|
|
39253
|
+
restoreDuration = () => {
|
|
39254
|
+
if (durationBefore) {
|
|
39255
|
+
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, durationBefore);
|
|
39256
|
+
} else {
|
|
39257
|
+
documentElement.style.removeProperty(TRANSITION_DURATION_PROPERTY);
|
|
39258
|
+
}
|
|
39259
|
+
};
|
|
39260
|
+
}
|
|
39261
|
+
const releaseRendering = takeoverRoutingRenderingHold();
|
|
39262
|
+
// Armed from here rather than from inside the callback below: the browser
|
|
39263
|
+
// calls that callback a frame later, and a navigation that has already
|
|
39264
|
+
// been decided renders its page in between — a wait armed then waits for
|
|
39265
|
+
// something that has already happened.
|
|
39266
|
+
const renderWait = armRouteRenderWait$1();
|
|
39267
|
+
// What the browser ACTUALLY captured, read once the pictures exist: it is
|
|
39268
|
+
// the only place the two silent misconfigurations show. Both are about the
|
|
39269
|
+
// same thing — a movement playing on pictures that are not the pages.
|
|
39270
|
+
const viewTransitionReady = () => {
|
|
39271
|
+
const capturedNames = capturedViewTransitionNames();
|
|
39272
|
+
if (areaElements.length > 0) {
|
|
39273
|
+
if (!capturedNames.has(AREA_NAME)) {
|
|
39274
|
+
warnOnce("area-not-captured", `The element marked ${TRANSITION_AREA_ATTRIBUTE} was not captured, so the movement plays on nothing. An element is captured only if it generates a box: \`display: contents\` (or an element not rendered) cannot be the area — its rectangle is what gets photographed and clipped.`);
|
|
39275
|
+
}
|
|
39276
|
+
return;
|
|
39277
|
+
}
|
|
39278
|
+
for (const name of capturedNames) {
|
|
39279
|
+
if (name === "root") {
|
|
39280
|
+
continue;
|
|
39281
|
+
}
|
|
39282
|
+
// Something stands still while the whole document travels under it. The
|
|
39283
|
+
// root picture spans the viewport and has a HOLE where that thing was
|
|
39284
|
+
// captured, so what crosses the screen is a blank band.
|
|
39285
|
+
warnOnce("pages-travel-under-named-elements", `The movement plays on the whole document while "${name}" is captured on its own, so a blank band travels where it stands. Wrap the pages in <RouteTransitionArea> (or mark their element with ${TRANSITION_AREA_ATTRIBUTE}) so the movement plays on them rather than on the document.`);
|
|
39286
|
+
break;
|
|
39287
|
+
}
|
|
39288
|
+
};
|
|
39289
|
+
const viewTransition = startViewTransition$1(async () => {
|
|
39290
|
+
// The picture the browser is about to take must be of the page that was
|
|
39291
|
+
// asked for, and a route matching is not yet a page rendered. Whatever
|
|
39292
|
+
// is awaited here must be able to resolve without a frame: the document
|
|
39293
|
+
// is frozen for the whole of this callback.
|
|
39294
|
+
try {
|
|
39295
|
+
// Releasing flushes the held render synchronously, so a route that
|
|
39296
|
+
// rendered has already resolved the wait by the next line.
|
|
39297
|
+
releaseRendering();
|
|
39298
|
+
if (page === null) {
|
|
39299
|
+
// A default transition: which page is arriving is unknown, and some
|
|
39300
|
+
// navigations render no route at all (a search param bound to a
|
|
39301
|
+
// signal) — waited on, those would freeze the page until the browser
|
|
39302
|
+
// gives up. The wait is raced with a short timer instead.
|
|
39303
|
+
await Promise.race([renderWait.rendered, waitMs(50)]);
|
|
39304
|
+
} else if (pageIsCurrent$1(page)) {
|
|
39305
|
+
await renderWait.rendered;
|
|
39306
|
+
}
|
|
39307
|
+
} finally {
|
|
39308
|
+
renderWait.stop();
|
|
39309
|
+
}
|
|
39310
|
+
// The page arriving is in the DOM and the transition has not started
|
|
39311
|
+
// playing: the one moment both states of the area can be known.
|
|
39312
|
+
if (areaElement) {
|
|
39313
|
+
holdTransitionWindow(transition, areaElement, areaRectBefore);
|
|
39314
|
+
}
|
|
39315
|
+
});
|
|
39316
|
+
const end = () => {
|
|
39317
|
+
// Whatever ends it — played out, skipped by another transition starting,
|
|
39318
|
+
// failed before its callback ever ran — the hold is given back and the
|
|
39319
|
+
// document is handed back to the application. Both are idempotent, and
|
|
39320
|
+
// the attributes belong to the LAST transition begun: an earlier one
|
|
39321
|
+
// ending late must not strip what a later one is wearing.
|
|
39322
|
+
renderWait.stop();
|
|
39323
|
+
releaseRendering();
|
|
39324
|
+
if (currentTransition === transition) {
|
|
39325
|
+
currentTransition = null;
|
|
39326
|
+
documentElement.removeAttribute(TRANSITION_ATTRIBUTE);
|
|
39327
|
+
documentElement.removeAttribute(TRANSITION_TYPE_ATTRIBUTE);
|
|
39328
|
+
documentElement.removeAttribute(TRANSITION_TARGET_ATTRIBUTE);
|
|
39329
|
+
releaseTransitionWindow(transition);
|
|
39330
|
+
if (restoreDuration) {
|
|
39331
|
+
restoreDuration();
|
|
39332
|
+
}
|
|
39333
|
+
}
|
|
39334
|
+
};
|
|
39335
|
+
viewTransition.ready.then(viewTransitionReady, ignoreSkipped$1);
|
|
39336
|
+
viewTransition.finished.then(end, end);
|
|
39337
|
+
};
|
|
39338
|
+
|
|
39339
|
+
// A transition skipped by another one starting is an outcome, not a failure.
|
|
39340
|
+
const ignoreSkipped$1 = () => {};
|
|
39341
|
+
|
|
39342
|
+
// The names the browser captured, read off the pictures themselves: what was
|
|
39343
|
+
// asked for in CSS and what was taken are not the same question (see
|
|
39344
|
+
// viewTransitionReady).
|
|
39345
|
+
const capturedViewTransitionNames = () => {
|
|
39346
|
+
const names = new Set();
|
|
39347
|
+
for (const animation of document.getAnimations()) {
|
|
39348
|
+
const pseudoElement = animation.effect?.pseudoElement;
|
|
39349
|
+
if (!pseudoElement || !pseudoElement.startsWith("::view-transition")) {
|
|
39350
|
+
continue;
|
|
39351
|
+
}
|
|
39352
|
+
const nameStart = pseudoElement.indexOf("(");
|
|
39353
|
+
if (nameStart === -1) {
|
|
39354
|
+
continue;
|
|
39355
|
+
}
|
|
39356
|
+
names.add(pseudoElement.slice(nameStart + 1, -1));
|
|
39357
|
+
}
|
|
39358
|
+
return names;
|
|
39359
|
+
};
|
|
39360
|
+
|
|
39361
|
+
// Said once per kind, whatever the number of navigations: a misconfiguration
|
|
39362
|
+
// is one fact about the application, and repeating it every time the user
|
|
39363
|
+
// moves would bury it.
|
|
39364
|
+
const warningsSaid = new Set();
|
|
39365
|
+
const warnOnce = (id, message) => {
|
|
39366
|
+
if (warningsSaid.has(id)) {
|
|
39367
|
+
return;
|
|
39368
|
+
}
|
|
39369
|
+
warningsSaid.add(id);
|
|
39370
|
+
console.warn(message);
|
|
39371
|
+
};
|
|
39372
|
+
|
|
39373
|
+
// A route matching is a signal changing; how many passes Preact takes to
|
|
39374
|
+
// answer it is its own business, and the render is the moment the picture can
|
|
39375
|
+
// be taken. Listening starts before the change, or a render landing while the
|
|
39376
|
+
// change settles is a render nobody heard.
|
|
39377
|
+
const armRouteRenderWait$1 = () => {
|
|
39378
|
+
let stopListening;
|
|
39379
|
+
const rendered = new Promise(resolve => {
|
|
39380
|
+
stopListening = observeRouteRender(resolve);
|
|
39381
|
+
});
|
|
39382
|
+
return {
|
|
39383
|
+
rendered,
|
|
39384
|
+
stop: () => stopListening()
|
|
39385
|
+
};
|
|
39386
|
+
};
|
|
39387
|
+
const waitMs = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
39388
|
+
const normalizePage$1 = page => page.isRoute ? {
|
|
39389
|
+
route: page,
|
|
39390
|
+
params: undefined
|
|
39391
|
+
} : page;
|
|
39392
|
+
|
|
39393
|
+
// Two pages are the same page when they select the same thing, not when they
|
|
39394
|
+
// were written by the same hand.
|
|
39395
|
+
const samePage$1 = (a, b) => {
|
|
39396
|
+
if (a === b) {
|
|
39397
|
+
return true;
|
|
39398
|
+
}
|
|
39399
|
+
if (!a || !b) {
|
|
39400
|
+
return false;
|
|
39401
|
+
}
|
|
39402
|
+
return a.route === b.route && compareTwoJsValues(a.params, b.params);
|
|
39403
|
+
};
|
|
39404
|
+
const pageIndexOf$1 = (pages, page) => pages.findIndex(candidate => samePage$1(candidate, page));
|
|
39405
|
+
|
|
39406
|
+
// Whether this page is the one on screen — same reading as route_travel.jsx's
|
|
39407
|
+
// own: matchingSignal is the necessary condition and is read whatever happens,
|
|
39408
|
+
// params only for a route that matches (the params of a route that does not
|
|
39409
|
+
// match are not params).
|
|
39410
|
+
const pageIsCurrent$1 = ({
|
|
39411
|
+
route,
|
|
39412
|
+
params
|
|
39413
|
+
}) => {
|
|
39414
|
+
if (!route.matchingSignal.value) {
|
|
39415
|
+
return false;
|
|
39416
|
+
}
|
|
39417
|
+
return params ? route.matchesParams(params) : true;
|
|
39418
|
+
};
|
|
39419
|
+
// The FIRST page that answers, and every page read all the same: a page that
|
|
39420
|
+
// is not the current one today is the one that must wake the reader tomorrow.
|
|
39421
|
+
const currentPageIndex$1 = pages => {
|
|
39422
|
+
let currentIndex = -1;
|
|
39423
|
+
for (let i = 0; i < pages.length; i++) {
|
|
39424
|
+
const isCurrent = pageIsCurrent$1(pages[i]);
|
|
39425
|
+
if (isCurrent && currentIndex === -1) {
|
|
39426
|
+
currentIndex = i;
|
|
39427
|
+
}
|
|
39428
|
+
}
|
|
39429
|
+
return currentIndex;
|
|
39430
|
+
};
|
|
39431
|
+
|
|
38319
39432
|
installImportMetaCssBuild(import.meta);/**
|
|
38320
39433
|
* Dragging from one route to the next, when the tabs of a page are URLs.
|
|
38321
39434
|
*
|
|
@@ -38355,6 +39468,10 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
38355
39468
|
*/
|
|
38356
39469
|
const CAN_KEEP_PICTURE = Boolean(document.startViewTransition && !document.startViewTransition.isPolyfill);
|
|
38357
39470
|
const startViewTransition = ensureDocumentStartViewTransition();
|
|
39471
|
+
|
|
39472
|
+
// route_transition.js reads this attribute by name (not imported, see its
|
|
39473
|
+
// ROUTE_TRAVEL_ATTRIBUTE) to refuse animating a navigation a travel already
|
|
39474
|
+
// animates — keep the two in sync.
|
|
38358
39475
|
const TRAVEL_ATTRIBUTE = "data-navi-route-travel";
|
|
38359
39476
|
// Which way the pages move, said on the document: the pictures of a transition
|
|
38360
39477
|
// hang off the root, not off the box that travels, so the box's own `axis` has
|
|
@@ -38374,19 +39491,6 @@ const DRAGGED_ATTRIBUTE = "data-navi-route-travel-dragged";
|
|
|
38374
39491
|
const TURNED_ATTRIBUTE = "data-navi-route-travel-turned";
|
|
38375
39492
|
// The name the box wears while it travels, and only then (see nameForTravel).
|
|
38376
39493
|
const TRAVEL_NAME = "navi-route-travel";
|
|
38377
|
-
// Where the two boxes of a travel stand in the window, published for the
|
|
38378
|
-
// length of it. Measurements only: what is DERIVED from them — where a picture
|
|
38379
|
-
// goes, what a bar covers — is derived in the CSS below, so the app's own
|
|
38380
|
-
// numbers (the room its fixed bars take) can take part in it. Only the
|
|
38381
|
-
// measuring needs JS, and only for the one moment both boxes exist (see
|
|
38382
|
-
// holdTravelGeometry).
|
|
38383
|
-
const TRAVEL_TOP_PROPERTY = "--navi-route-travel-top";
|
|
38384
|
-
const TRAVEL_LEFT_PROPERTY = "--navi-route-travel-left";
|
|
38385
|
-
const TRAVEL_WIDTH_PROPERTY = "--navi-route-travel-width";
|
|
38386
|
-
const TRAVEL_HEIGHT_PROPERTY = "--navi-route-travel-height";
|
|
38387
|
-
const TRAVEL_OLD_TOP_PROPERTY = "--navi-route-travel-old-top";
|
|
38388
|
-
const TRAVEL_OLD_LEFT_PROPERTY = "--navi-route-travel-old-left";
|
|
38389
|
-
const TRAVEL_GEOMETRY_PROPERTIES = [TRAVEL_TOP_PROPERTY, TRAVEL_LEFT_PROPERTY, TRAVEL_WIDTH_PROPERTY, TRAVEL_HEIGHT_PROPERTY, TRAVEL_OLD_TOP_PROPERTY, TRAVEL_OLD_LEFT_PROPERTY];
|
|
38390
39494
|
const css$S = /* css */`
|
|
38391
39495
|
/* The name that makes the page inside this box a picture of its own during a
|
|
38392
39496
|
transition — rather than part of the one big picture the document takes, so
|
|
@@ -38469,10 +39573,16 @@ const css$S = /* css */`
|
|
|
38469
39573
|
would be seen jumping back to its top before it even begins to leave.
|
|
38470
39574
|
Offset here rather than by \`translate\`, which the movement itself uses,
|
|
38471
39575
|
and at its own size rather than the group's so that nothing is cut off
|
|
38472
|
-
the far side of the shift (see
|
|
38473
|
-
top: calc(
|
|
39576
|
+
the far side of the shift (see transition_window.js). */
|
|
39577
|
+
top: calc(
|
|
39578
|
+
var(--navi-transition-window-old-top) - var(
|
|
39579
|
+
--navi-transition-window-top
|
|
39580
|
+
)
|
|
39581
|
+
);
|
|
38474
39582
|
left: calc(
|
|
38475
|
-
var(
|
|
39583
|
+
var(--navi-transition-window-old-left) - var(
|
|
39584
|
+
--navi-transition-window-left
|
|
39585
|
+
)
|
|
38476
39586
|
);
|
|
38477
39587
|
width: auto;
|
|
38478
39588
|
}
|
|
@@ -38487,7 +39597,7 @@ const css$S = /* css */`
|
|
|
38487
39597
|
}
|
|
38488
39598
|
&::view-transition-group(navi-route-travel) {
|
|
38489
39599
|
/* The window the two pictures are seen through, held still for the whole
|
|
38490
|
-
travel at the taller of the two boxes (see
|
|
39600
|
+
travel at the taller of the two boxes (see transition_window.js): the group
|
|
38491
39601
|
is what CLIPS, and the browser animates its height from the box being
|
|
38492
39602
|
left to the box arriving — so the window shrinks under the pictures and
|
|
38493
39603
|
cuts the page leaving from the bottom, progressively. The box does end
|
|
@@ -38498,7 +39608,7 @@ const css$S = /* css */`
|
|
|
38498
39608
|
winning against it with !important — which also drops its position
|
|
38499
39609
|
animation, fine while a travel box stands in the same place from one
|
|
38500
39610
|
route to the next. */
|
|
38501
|
-
height: var(
|
|
39611
|
+
height: var(--navi-transition-window-height);
|
|
38502
39612
|
|
|
38503
39613
|
/* Cut at the safe area, on top of being cut at the box. The pictures are
|
|
38504
39614
|
drawn in the top layer, so they cover a fixed bar as easily as anything
|
|
@@ -38515,20 +39625,22 @@ const css$S = /* css */`
|
|
|
38515
39625
|
only where it itself stands, and that is the measured half. */
|
|
38516
39626
|
--navi-route-travel-clip-top: max(
|
|
38517
39627
|
0px,
|
|
38518
|
-
var(--navi-safe-area-inset-top) - var(
|
|
39628
|
+
var(--navi-safe-area-inset-top) - var(--navi-transition-window-top)
|
|
38519
39629
|
);
|
|
38520
39630
|
--navi-route-travel-clip-left: max(
|
|
38521
39631
|
0px,
|
|
38522
|
-
var(--navi-safe-area-inset-left) - var(
|
|
39632
|
+
var(--navi-safe-area-inset-left) - var(--navi-transition-window-left)
|
|
38523
39633
|
);
|
|
38524
39634
|
--navi-route-travel-clip-bottom: max(
|
|
38525
39635
|
0px,
|
|
38526
|
-
var(
|
|
39636
|
+
var(--navi-transition-window-top) +
|
|
39637
|
+
var(--navi-transition-window-height) +
|
|
38527
39638
|
var(--navi-safe-area-inset-bottom) - 100dvh
|
|
38528
39639
|
);
|
|
38529
39640
|
--navi-route-travel-clip-right: max(
|
|
38530
39641
|
0px,
|
|
38531
|
-
var(
|
|
39642
|
+
var(--navi-transition-window-left) +
|
|
39643
|
+
var(--navi-transition-window-width) +
|
|
38532
39644
|
var(--navi-safe-area-inset-right) - 100dvw
|
|
38533
39645
|
);
|
|
38534
39646
|
clip-path: inset(
|
|
@@ -38850,12 +39962,11 @@ const RouteTravel = ({
|
|
|
38850
39962
|
}
|
|
38851
39963
|
pageAskedForRef.current = page;
|
|
38852
39964
|
// The box as it stands before anything moves: rendering is held, so this is
|
|
38853
|
-
// still the page being left (see
|
|
39965
|
+
// still the page being left (see transition_window.js).
|
|
38854
39966
|
const rectBefore = elementRef.current.getBoundingClientRect();
|
|
38855
39967
|
// The hold a navigation already took, if this travel is the answer to one:
|
|
38856
39968
|
// taking another would be taking a hold on a page that is holding still.
|
|
38857
|
-
const releaseRendering =
|
|
38858
|
-
renderingHeldForRouting = null;
|
|
39969
|
+
const releaseRendering = takeoverRoutingRenderingHold();
|
|
38859
39970
|
// The picture the browser is about to take must be of the page that was
|
|
38860
39971
|
// asked for, and a route matching is not yet a page rendered. Watched from
|
|
38861
39972
|
// here rather than from inside the callback below: the browser calls that
|
|
@@ -38879,7 +39990,7 @@ const RouteTravel = ({
|
|
|
38879
39990
|
}, renderWait);
|
|
38880
39991
|
// The page arriving is in the DOM and the transition has not started
|
|
38881
39992
|
// playing: the one moment both boxes can be known.
|
|
38882
|
-
|
|
39993
|
+
holdTransitionWindow(travel, elementRef.current, rectBefore);
|
|
38883
39994
|
});
|
|
38884
39995
|
travel.viewTransition = viewTransition;
|
|
38885
39996
|
if (scrub) {
|
|
@@ -38998,25 +40109,14 @@ const RouteTravel = ({
|
|
|
38998
40109
|
}, [pages]);
|
|
38999
40110
|
|
|
39000
40111
|
// Rendering is held for the length of a navigation, so that whatever picture
|
|
39001
|
-
// this box is about to take is of the page being LEFT (see
|
|
39002
|
-
//
|
|
39003
|
-
// announces that it matches, Preact has already been told and the render is
|
|
39004
|
-
// queued — a hold taken then is a hold taken too late.
|
|
40112
|
+
// this box is about to take is of the page being LEFT (see
|
|
40113
|
+
// rendering_hold.js).
|
|
39005
40114
|
useLayoutEffect(() => {
|
|
39006
|
-
const stopWatchingStart = observeBeforeRouting(
|
|
39007
|
-
renderingHeldForRouting = holdRendering();
|
|
39008
|
-
});
|
|
40115
|
+
const stopWatchingStart = observeBeforeRouting(holdRenderingForRouting);
|
|
39009
40116
|
// Nobody may have had a picture to take: this navigation is not always one
|
|
39010
|
-
// this box travels
|
|
39011
|
-
//
|
|
39012
|
-
|
|
39013
|
-
const stopWatchingEnd = observeAfterRouting(() => {
|
|
39014
|
-
const release = renderingHeldForRouting;
|
|
39015
|
-
renderingHeldForRouting = null;
|
|
39016
|
-
if (release) {
|
|
39017
|
-
release();
|
|
39018
|
-
}
|
|
39019
|
-
});
|
|
40117
|
+
// this box travels. Whoever wanted the hold took it over (beginTravel)
|
|
40118
|
+
// while the change was being applied, and left nothing here.
|
|
40119
|
+
const stopWatchingEnd = observeAfterRouting(releaseRoutingRenderingHold);
|
|
39020
40120
|
return () => {
|
|
39021
40121
|
stopWatchingStart();
|
|
39022
40122
|
stopWatchingEnd();
|
|
@@ -39208,7 +40308,7 @@ const RouteTravel = ({
|
|
|
39208
40308
|
document.documentElement.removeAttribute(TRAVEL_AXIS_ATTRIBUTE);
|
|
39209
40309
|
document.documentElement.removeAttribute(DRAGGED_ATTRIBUTE);
|
|
39210
40310
|
document.documentElement.removeAttribute(TURNED_ATTRIBUTE);
|
|
39211
|
-
|
|
40311
|
+
releaseTransitionWindow(travel);
|
|
39212
40312
|
}
|
|
39213
40313
|
};
|
|
39214
40314
|
|
|
@@ -39591,92 +40691,6 @@ const releaseHold = travel => {
|
|
|
39591
40691
|
document.documentElement.removeAttribute(HOLD_ATTRIBUTE);
|
|
39592
40692
|
};
|
|
39593
40693
|
|
|
39594
|
-
// The two boxes of a travel, measured at the one moment both exist: the
|
|
39595
|
-
// arriving page is in the DOM and the transition has not started playing.
|
|
39596
|
-
//
|
|
39597
|
-
// The group stands at the ARRIVING box — its own animation is dropped, so it
|
|
39598
|
-
// takes the geometry the browser declared for it and holds it for the whole
|
|
39599
|
-
// travel. That is why both rectangles have to be published: a group that does
|
|
39600
|
-
// not move says nothing about where the page being left was, and its rectangle
|
|
39601
|
-
// in the window is the only thing CSS cannot work out on its own.
|
|
39602
|
-
const holdTravelGeometry = (element, rectBefore) => {
|
|
39603
|
-
const rectAfter = element.getBoundingClientRect();
|
|
39604
|
-
// The height it is held at is the taller of the two boxes, so neither picture
|
|
39605
|
-
// is ever cut. It cannot be measured from one side alone: a page arriving
|
|
39606
|
-
// shorter than the one it replaces would cut the one leaving, a page arriving
|
|
39607
|
-
// taller would be cut itself.
|
|
39608
|
-
const height = rectBefore.height > rectAfter.height ? rectBefore.height : rectAfter.height;
|
|
39609
|
-
const {
|
|
39610
|
-
style
|
|
39611
|
-
} = document.documentElement;
|
|
39612
|
-
style.setProperty(TRAVEL_TOP_PROPERTY, `${rectAfter.top}px`);
|
|
39613
|
-
style.setProperty(TRAVEL_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
39614
|
-
style.setProperty(TRAVEL_WIDTH_PROPERTY, `${rectAfter.width}px`);
|
|
39615
|
-
style.setProperty(TRAVEL_HEIGHT_PROPERTY, `${height}px`);
|
|
39616
|
-
style.setProperty(TRAVEL_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
|
|
39617
|
-
style.setProperty(TRAVEL_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
|
|
39618
|
-
};
|
|
39619
|
-
// The live layout takes the box back. A discontinuity by construction — the
|
|
39620
|
-
// group stands at the held height, the box is at the new one — and an invisible
|
|
39621
|
-
// one: the page arriving is fully in place, and the strip below it that the
|
|
39622
|
-
// group still covers shows the page leaving only while it is still on screen.
|
|
39623
|
-
const releaseTravelGeometry = () => {
|
|
39624
|
-
const {
|
|
39625
|
-
style
|
|
39626
|
-
} = document.documentElement;
|
|
39627
|
-
for (const property of TRAVEL_GEOMETRY_PROPERTIES) {
|
|
39628
|
-
style.removeProperty(property);
|
|
39629
|
-
}
|
|
39630
|
-
};
|
|
39631
|
-
|
|
39632
|
-
// The browser does not take the picture of the page being left when a
|
|
39633
|
-
// transition is ASKED for — it takes it at the next frame, just before running
|
|
39634
|
-
// the update callback. Preact renders sooner than that, in a microtask: so a
|
|
39635
|
-
// change nobody here asked for (a tab pressed, the back button) has already
|
|
39636
|
-
// reached the DOM when the picture is taken, and the picture is of the page
|
|
39637
|
-
// ARRIVING. Both sides of the travel then show it, and one watches a page slide
|
|
39638
|
-
// onto itself.
|
|
39639
|
-
//
|
|
39640
|
-
// So what Preact has queued waits until the update callback, which is the
|
|
39641
|
-
// moment the API is built around — the change belongs inside it. The whole
|
|
39642
|
-
// document is held, for the one frame the browser needs: it is about to be
|
|
39643
|
-
// frozen under a picture anyway.
|
|
39644
|
-
let renderingHold = null;
|
|
39645
|
-
// The hold a navigation took on its way in, until a travel takes it over or the
|
|
39646
|
-
// navigation turns out to be one nobody here animates.
|
|
39647
|
-
let renderingHeldForRouting = null;
|
|
39648
|
-
const holdRendering = () => {
|
|
39649
|
-
if (renderingHold) {
|
|
39650
|
-
return renderingHold.release;
|
|
39651
|
-
}
|
|
39652
|
-
const debounceRenderingBefore = options.debounceRendering;
|
|
39653
|
-
const hold = {
|
|
39654
|
-
render: null,
|
|
39655
|
-
release: () => {
|
|
39656
|
-
// Only the hold that is still standing may be given back: a travel
|
|
39657
|
-
// ending after another has taken over must not let go of what it does
|
|
39658
|
-
// not hold.
|
|
39659
|
-
if (renderingHold !== hold) {
|
|
39660
|
-
return;
|
|
39661
|
-
}
|
|
39662
|
-
renderingHold = null;
|
|
39663
|
-
options.debounceRendering = debounceRenderingBefore;
|
|
39664
|
-
const {
|
|
39665
|
-
render
|
|
39666
|
-
} = hold;
|
|
39667
|
-
hold.render = null;
|
|
39668
|
-
if (render) {
|
|
39669
|
-
render();
|
|
39670
|
-
}
|
|
39671
|
-
}
|
|
39672
|
-
};
|
|
39673
|
-
renderingHold = hold;
|
|
39674
|
-
options.debounceRendering = render => {
|
|
39675
|
-
hold.render = render;
|
|
39676
|
-
};
|
|
39677
|
-
return hold.release;
|
|
39678
|
-
};
|
|
39679
|
-
|
|
39680
40694
|
// The animations of the pictures, asked for again until there are some: they
|
|
39681
40695
|
// come into existence with the transition, several frames after it was asked
|
|
39682
40696
|
// for, and the gesture has already begun by then. Kept once found — the set
|
|
@@ -73013,5 +74027,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
73013
74027
|
})
|
|
73014
74028
|
});
|
|
73015
74029
|
|
|
73016
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
74030
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, 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, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
73017
74031
|
//# sourceMappingURL=jsenv_navi.js.map
|