@jsenv/navi 0.29.52 → 0.29.53
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 +375 -140
- package/dist/jsenv_navi.js.map +36 -22
- package/dist/jsenv_navi_side_effects.js +131 -12
- package/dist/jsenv_navi_side_effects.js.map +4 -2
- package/docs/AI_INSTRUCTIONS.md +38 -0
- package/docs/MOBILE_LAYOUT_PITFALLS.md +2 -2
- package/docs/actions.md +3 -1
- package/docs/create_and_edit.md +3 -1
- package/docs/css_architecture.md +10 -8
- package/docs/error_handling.md +174 -0
- package/docs/navigation.md +27 -6
- package/docs/safe_area.md +161 -0
- package/docs/scroll.md +10 -5
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -13083,6 +13083,69 @@ const setActionPrivateProperties = (action, properties) => {
|
|
|
13083
13083
|
actionPrivatePropertiesWeakMap.set(action, properties);
|
|
13084
13084
|
};
|
|
13085
13085
|
|
|
13086
|
+
/**
|
|
13087
|
+
* Where an action error goes when nothing displays it.
|
|
13088
|
+
*
|
|
13089
|
+
* An action that fails writes the error into its `errorSignal` and stops there.
|
|
13090
|
+
* It cannot know whether a screen is going to show it: at the instant it fails,
|
|
13091
|
+
* the screen that will is often not even mounted — a route action runs before
|
|
13092
|
+
* its page renders, which is precisely the case a guess made at failure time
|
|
13093
|
+
* gets wrong. So nothing is guessed. The error is let go, and whoever displays
|
|
13094
|
+
* it SAYS so by marking it; what is still unmarked once the DOM has had its
|
|
13095
|
+
* chance was displayed by nobody, and only that is reported as unhandled.
|
|
13096
|
+
*
|
|
13097
|
+
* The mark is `__handled_by__`, the same one the jsenv supervisor reads to stay
|
|
13098
|
+
* out of the way of an error the app is already showing — one mark, one meaning:
|
|
13099
|
+
* "this is on screen somewhere".
|
|
13100
|
+
*
|
|
13101
|
+
* The whole picture, control errors and validation included: docs/error_handling.md
|
|
13102
|
+
*/
|
|
13103
|
+
|
|
13104
|
+
const markErrorAsDisplayedBy = (error, by) => {
|
|
13105
|
+
if (error && typeof error === "object") {
|
|
13106
|
+
error.__handled_by__ = by;
|
|
13107
|
+
}
|
|
13108
|
+
};
|
|
13109
|
+
|
|
13110
|
+
const errorIsDisplayed = (error) => {
|
|
13111
|
+
return Boolean(error && error.__handled_by__);
|
|
13112
|
+
};
|
|
13113
|
+
|
|
13114
|
+
/**
|
|
13115
|
+
* Reported from a macrotask: every render that could display the error —
|
|
13116
|
+
* Preact's own queue, a Suspense boundary settling on the failure, the error
|
|
13117
|
+
* boundary above it — happens in microtasks, so by the time this runs the
|
|
13118
|
+
* answer is final. A screen that would display the error much later than that
|
|
13119
|
+
* (mounted by something slower than a render) is reported anyway; it is the
|
|
13120
|
+
* one case where this says "nobody" a bit too early, and the report is then a
|
|
13121
|
+
* duplicate of what the screen shows rather than a lie about it.
|
|
13122
|
+
*
|
|
13123
|
+
* Rethrown rather than logged: an error nobody shows is an unhandled error, and
|
|
13124
|
+
* the runtime already knows what to do with those (window "error" event, jsenv
|
|
13125
|
+
* overlay in dev). Same trick preact/debug uses for the same reason.
|
|
13126
|
+
*/
|
|
13127
|
+
const errorReportedSet = new WeakSet();
|
|
13128
|
+
const reportErrorIfNobodyDisplaysIt = (error, { action } = {}) => {
|
|
13129
|
+
setTimeout(() => {
|
|
13130
|
+
if (errorIsDisplayed(error)) {
|
|
13131
|
+
return;
|
|
13132
|
+
}
|
|
13133
|
+
if (error && typeof error === "object") {
|
|
13134
|
+
// The same error can reach here from more than one direction (the run
|
|
13135
|
+
// that produced it, the routing promise carrying it): it is one error and
|
|
13136
|
+
// it is reported once.
|
|
13137
|
+
if (errorReportedSet.has(error)) {
|
|
13138
|
+
return;
|
|
13139
|
+
}
|
|
13140
|
+
errorReportedSet.add(error);
|
|
13141
|
+
}
|
|
13142
|
+
if (action && error && typeof error === "object" && !error.action) {
|
|
13143
|
+
error.action = action;
|
|
13144
|
+
}
|
|
13145
|
+
throw error;
|
|
13146
|
+
});
|
|
13147
|
+
};
|
|
13148
|
+
|
|
13086
13149
|
const SYMBOL_OBJECT_SIGNAL = Symbol.for("navi_object_signal");
|
|
13087
13150
|
|
|
13088
13151
|
let DEBUG$1 = false;
|
|
@@ -13916,7 +13979,6 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
13916
13979
|
const ui = {
|
|
13917
13980
|
renderLoaded: null,
|
|
13918
13981
|
renderLoadedAsync,
|
|
13919
|
-
hasRenderers: false, // Flag to track if action is bound to UI components
|
|
13920
13982
|
};
|
|
13921
13983
|
let sideEffectCleanup;
|
|
13922
13984
|
let completeSideEffectCleanup;
|
|
@@ -14058,28 +14120,27 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
14058
14120
|
return error;
|
|
14059
14121
|
}
|
|
14060
14122
|
if (DEBUG$1) {
|
|
14061
|
-
console.log(
|
|
14062
|
-
`"${action}": failed (error: ${error}, handled by ui: ${ui.hasRenderers})`,
|
|
14063
|
-
);
|
|
14123
|
+
console.log(`"${action}": failed (error: ${error})`);
|
|
14064
14124
|
}
|
|
14125
|
+
error.action = action;
|
|
14065
14126
|
batch(() => {
|
|
14066
14127
|
errorSignal.value = error;
|
|
14067
14128
|
runningStateSignal.value = FAILED;
|
|
14068
14129
|
onError?.(error, { event, action, args });
|
|
14069
14130
|
});
|
|
14070
14131
|
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
//
|
|
14079
|
-
|
|
14132
|
+
// The error is in errorSignal; from here it is the UI's, and running
|
|
14133
|
+
// the action is not the place to decide whether the UI wants it —
|
|
14134
|
+
// that answer does not exist yet at this instant (see
|
|
14135
|
+
// action_error_report.js). So the run never throws: it settles with
|
|
14136
|
+
// the error as its value, and being displayed or not is constated
|
|
14137
|
+
// afterwards, in one place, by one rule.
|
|
14138
|
+
if (onError) {
|
|
14139
|
+
// Asking for the error IS taking it.
|
|
14140
|
+
markErrorAsDisplayedBy(error, "onError");
|
|
14080
14141
|
}
|
|
14081
|
-
error
|
|
14082
|
-
|
|
14142
|
+
reportErrorIfNobodyDisplaysIt(error, { action });
|
|
14143
|
+
return error;
|
|
14083
14144
|
};
|
|
14084
14145
|
|
|
14085
14146
|
try {
|
|
@@ -14440,15 +14501,8 @@ const createActionProxyFromSignal = (
|
|
|
14440
14501
|
performReset: proxyPrivateMethod("performReset"),
|
|
14441
14502
|
ui: currentActionPrivateProperties.ui,
|
|
14442
14503
|
};
|
|
14443
|
-
onActionTargetChange((
|
|
14504
|
+
onActionTargetChange(() => {
|
|
14444
14505
|
proxyPrivateProperties.ui = currentActionPrivateProperties.ui;
|
|
14445
|
-
if (previousTarget && actionTarget) {
|
|
14446
|
-
const previousPrivateProps = getActionPrivateProperties(previousTarget);
|
|
14447
|
-
if (previousPrivateProps.ui.hasRenderers) {
|
|
14448
|
-
const newPrivateProps = getActionPrivateProperties(actionTarget);
|
|
14449
|
-
newPrivateProps.ui.hasRenderers = true;
|
|
14450
|
-
}
|
|
14451
|
-
}
|
|
14452
14506
|
proxyPrivateProperties.childActionWeakSet =
|
|
14453
14507
|
currentActionPrivateProperties.childActionWeakSet;
|
|
14454
14508
|
});
|
|
@@ -21701,7 +21755,18 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
21701
21755
|
// something at the first announcement has a definite place to give it back.
|
|
21702
21756
|
publishBeforeRouting({ url, ...options });
|
|
21703
21757
|
try {
|
|
21704
|
-
|
|
21758
|
+
const routingResult = applyRoutingTask(url, options);
|
|
21759
|
+
if (routingResult && typeof routingResult.then === "function") {
|
|
21760
|
+
// Every caller below drops this value — a click handler has nothing to
|
|
21761
|
+
// do with what the routing returns — so a rejection here would become
|
|
21762
|
+
// an anonymous unhandled one, pointing at the navigation rather than at
|
|
21763
|
+
// what failed. It goes to the single place that knows what to do with
|
|
21764
|
+
// an error nobody displays (see action_error_report.js).
|
|
21765
|
+
routingResult.catch((e) => {
|
|
21766
|
+
reportErrorIfNobodyDisplaysIt(e);
|
|
21767
|
+
});
|
|
21768
|
+
}
|
|
21769
|
+
return routingResult;
|
|
21705
21770
|
} finally {
|
|
21706
21771
|
publishAfterRouting({ url, ...options });
|
|
21707
21772
|
}
|
|
@@ -21777,6 +21842,9 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
21777
21842
|
isVisited,
|
|
21778
21843
|
state,
|
|
21779
21844
|
});
|
|
21845
|
+
if (navigationType === "push") {
|
|
21846
|
+
startAtTop(url);
|
|
21847
|
+
}
|
|
21780
21848
|
executeWithCleanup(
|
|
21781
21849
|
() => allResult,
|
|
21782
21850
|
() => {
|
|
@@ -21925,6 +21993,36 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
21925
21993
|
};
|
|
21926
21994
|
};
|
|
21927
21995
|
|
|
21996
|
+
// A page one arrives at for the first time starts at its top. Only a document
|
|
21997
|
+
// navigation does that on its own: a pushState creates its entry with whatever
|
|
21998
|
+
// scroll happened to be there, so without this the new page opens at the offset
|
|
21999
|
+
// of the one before it — and worse, that borrowed offset is what the browser
|
|
22000
|
+
// then remembers FOR that entry, and hands back on the way forward.
|
|
22001
|
+
//
|
|
22002
|
+
// Push only. A traverse is the browser's business and it is already right: it
|
|
22003
|
+
// keeps a position per entry and restores it. A replace is not an arrival —
|
|
22004
|
+
// it is the same place, said differently (a tab row travelling, see
|
|
22005
|
+
// route_travel.jsx), and resetting there would throw the reader out of a page
|
|
22006
|
+
// they never left.
|
|
22007
|
+
//
|
|
22008
|
+
// After the routes have been told, and that ordering is the whole subtlety:
|
|
22009
|
+
// the routes changing is what sets a travel off, and a travel measures the box
|
|
22010
|
+
// it is leaving as it stands. Reset before that and the picture of the page
|
|
22011
|
+
// being left is taken at the top of a page the reader was not at the top of —
|
|
22012
|
+
// it is then watched jumping back to its first line before it even begins to
|
|
22013
|
+
// leave (see holdTravelGeometry in route_travel.jsx). After pushState too, so
|
|
22014
|
+
// the entry being left keeps the offset it is at.
|
|
22015
|
+
//
|
|
22016
|
+
// The document, because the document is the scrollport in the common case. An
|
|
22017
|
+
// app that scrolls an element of its own scrolls it itself.
|
|
22018
|
+
const startAtTop = (url) => {
|
|
22019
|
+
// A fragment names where to land, and the browser is the one that finds it.
|
|
22020
|
+
if (new URL(url, window.location.href).hash) {
|
|
22021
|
+
return;
|
|
22022
|
+
}
|
|
22023
|
+
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
22024
|
+
};
|
|
22025
|
+
|
|
21928
22026
|
let updateRoutes;
|
|
21929
22027
|
|
|
21930
22028
|
const applyActions = (params) => {
|
|
@@ -32227,18 +32325,6 @@ const ActionRenderer = ({
|
|
|
32227
32325
|
} = useActionStatus(action);
|
|
32228
32326
|
const UIRenderedPromise = useUIRenderedPromise(action);
|
|
32229
32327
|
const [errorBoundary, resetErrorBoundary] = useErrorBoundary();
|
|
32230
|
-
|
|
32231
|
-
// Mark this action as bound to UI components (has renderers)
|
|
32232
|
-
// This tells the action system that errors should be caught and stored
|
|
32233
|
-
// in the action's error state rather than bubbling up
|
|
32234
|
-
useLayoutEffect(() => {
|
|
32235
|
-
if (action) {
|
|
32236
|
-
const {
|
|
32237
|
-
ui
|
|
32238
|
-
} = getActionPrivateProperties(action);
|
|
32239
|
-
ui.hasRenderers = true;
|
|
32240
|
-
}
|
|
32241
|
-
}, [action]);
|
|
32242
32328
|
useLayoutEffect(() => {
|
|
32243
32329
|
resetErrorBoundary();
|
|
32244
32330
|
}, [action, loading, idle, resetErrorBoundary]);
|
|
@@ -32266,6 +32352,8 @@ const ActionRenderer = ({
|
|
|
32266
32352
|
return renderIdle(action);
|
|
32267
32353
|
}
|
|
32268
32354
|
if (errorBoundary) {
|
|
32355
|
+
// Displaying it is what makes it handled (see action_error_report.js)
|
|
32356
|
+
markErrorAsDisplayedBy(errorBoundary, "<ActionRenderer>");
|
|
32269
32357
|
return renderError(errorBoundary, "ui_error", action);
|
|
32270
32358
|
}
|
|
32271
32359
|
if (aborted) {
|
|
@@ -32291,6 +32379,7 @@ const ActionRenderer = ({
|
|
|
32291
32379
|
return renderLoading(action);
|
|
32292
32380
|
}
|
|
32293
32381
|
if (error) {
|
|
32382
|
+
markErrorAsDisplayedBy(error, "<ActionRenderer>");
|
|
32294
32383
|
return renderError(error, "action_error", action);
|
|
32295
32384
|
}
|
|
32296
32385
|
return renderCompletedSafe(data, action);
|
|
@@ -35773,13 +35862,17 @@ const useActionAsyncData = (action, {
|
|
|
35773
35862
|
}
|
|
35774
35863
|
const actionError = action.errorSignal.peek();
|
|
35775
35864
|
if (errorEffect === "use") {
|
|
35865
|
+
// Handed to the component, which is what displays it from here on
|
|
35866
|
+
// (see action_error_report.js)
|
|
35867
|
+
markErrorAsDisplayedBy(actionError, "useAsyncData({ error: true })");
|
|
35776
35868
|
const dismissError = () => {
|
|
35777
35869
|
dismissedActionWeakSet.add(action);
|
|
35778
35870
|
setTick(n => n + 1);
|
|
35779
35871
|
};
|
|
35780
35872
|
return [undefined, false, actionError, dismissError];
|
|
35781
35873
|
}
|
|
35782
|
-
|
|
35874
|
+
// Not marked: nothing is displayed yet — the boundary that catches this is
|
|
35875
|
+
// what says so, and only if it has something to show.
|
|
35783
35876
|
throw actionError;
|
|
35784
35877
|
}
|
|
35785
35878
|
|
|
@@ -35917,8 +36010,34 @@ const LoadingFallback = ({
|
|
|
35917
36010
|
};
|
|
35918
36011
|
|
|
35919
36012
|
// ─── ErrorBoundary ────────────────────────────────────────────────────────────
|
|
35920
|
-
|
|
35921
|
-
|
|
36013
|
+
/**
|
|
36014
|
+
* Displays what its subtree throws — an action failure delegated by
|
|
36015
|
+
* `useAsyncData`, or any render error under it.
|
|
36016
|
+
*
|
|
36017
|
+
* Two things it gets right that a hand-written boundary rarely does, both
|
|
36018
|
+
* explained in docs/error_handling.md:
|
|
36019
|
+
*
|
|
36020
|
+
* - It marks the error as displayed ONLY when it actually displays it.
|
|
36021
|
+
* `preact/debug` rethrows every error a boundary caught in a `setTimeout`, on
|
|
36022
|
+
* purpose (React devtools compatibility), so a handled error still reaches
|
|
36023
|
+
* window and the jsenv overlay covers the app unless `__handled_by__` is set.
|
|
36024
|
+
* Setting it before knowing whether anything is rendered turns a boundary into
|
|
36025
|
+
* a bug swallower: a TypeError in a component becomes a blank page AND a
|
|
36026
|
+
* silent one. Without a `fallback` there is nothing to display, so the error is
|
|
36027
|
+
* left alone and continues up.
|
|
36028
|
+
*
|
|
36029
|
+
* - It resets on navigation, not only on rerun. Rerunning the failed action is
|
|
36030
|
+
* one way out; going somewhere else is the common one. Without a reset on the
|
|
36031
|
+
* document URL, the error stays in place of every page after it, including the
|
|
36032
|
+
* ones that would render fine.
|
|
36033
|
+
*
|
|
36034
|
+
* @param {object} props
|
|
36035
|
+
* @param {Function|import("ignore:preact").VNode} [props.fallback] - what is displayed
|
|
36036
|
+
* instead of the children: an element, or a component receiving
|
|
36037
|
+
* `{ error, resetError }`. Without it the boundary is transparent.
|
|
36038
|
+
* @param {() => void} [props.onReset] - called when the fallback dismisses the
|
|
36039
|
+
* error via its `resetError`.
|
|
36040
|
+
*/
|
|
35922
36041
|
const ErrorBoundary = ({
|
|
35923
36042
|
children,
|
|
35924
36043
|
fallback,
|
|
@@ -35932,9 +36051,30 @@ const ErrorBoundary = ({
|
|
|
35932
36051
|
cleanupRef.current?.();
|
|
35933
36052
|
};
|
|
35934
36053
|
}, []);
|
|
35935
|
-
if (error) {
|
|
35936
|
-
error.__handled_by__ = "<ErrorBoundary>"; // prevent jsenv from displaying it
|
|
35937
36054
|
|
|
36055
|
+
// The error belongs to the page that failed: leaving it means leaving it
|
|
36056
|
+
// behind.
|
|
36057
|
+
useEffect(() => {
|
|
36058
|
+
if (!error) {
|
|
36059
|
+
return undefined;
|
|
36060
|
+
}
|
|
36061
|
+
const documentUrlWhenCaught = documentUrlSignal.peek();
|
|
36062
|
+
return documentUrlSignal.subscribe(documentUrl => {
|
|
36063
|
+
// subscribe() calls back synchronously with the current value
|
|
36064
|
+
if (documentUrl === documentUrlWhenCaught) {
|
|
36065
|
+
return;
|
|
36066
|
+
}
|
|
36067
|
+
setDismissed(false);
|
|
36068
|
+
resetError();
|
|
36069
|
+
});
|
|
36070
|
+
}, [error]);
|
|
36071
|
+
if (error) {
|
|
36072
|
+
if (!fallback) {
|
|
36073
|
+
// Nothing to display means nothing handled: rethrow untouched so the
|
|
36074
|
+
// error reaches whoever can do something with it (an outer boundary, or
|
|
36075
|
+
// the dev overlay).
|
|
36076
|
+
throw error;
|
|
36077
|
+
}
|
|
35938
36078
|
const action = error.action;
|
|
35939
36079
|
if (action) {
|
|
35940
36080
|
cleanupRef.current?.();
|
|
@@ -35964,9 +36104,7 @@ const ErrorBoundary = ({
|
|
|
35964
36104
|
setDismissed(true);
|
|
35965
36105
|
resetError();
|
|
35966
36106
|
};
|
|
35967
|
-
|
|
35968
|
-
return null;
|
|
35969
|
-
}
|
|
36107
|
+
markErrorAsDisplayedBy(error, "<ErrorBoundary>"); // displayed here, so nothing else has to
|
|
35970
36108
|
if (typeof fallback === "function") {
|
|
35971
36109
|
return h(fallback, {
|
|
35972
36110
|
error,
|
|
@@ -37368,10 +37506,24 @@ const debug$1 = (...args) => {
|
|
|
37368
37506
|
}
|
|
37369
37507
|
};
|
|
37370
37508
|
|
|
37371
|
-
|
|
37372
|
-
|
|
37373
|
-
|
|
37374
|
-
|
|
37509
|
+
/**
|
|
37510
|
+
* Dispatches on its props:
|
|
37511
|
+
* - children → RouteContainer (traverses children statically, renders active branch)
|
|
37512
|
+
* - route → RouteLeafRoute (rendered by parent container when URL matches)
|
|
37513
|
+
* - fallback → RouteActive (rendered by parent container when no sibling matches)
|
|
37514
|
+
*
|
|
37515
|
+
* @param {object} props
|
|
37516
|
+
* @param {object} [props.route] - the route this branch is for, from `route()`
|
|
37517
|
+
* @param {object} [props.routeParams] - selects a branch on a param of that route
|
|
37518
|
+
* @param {boolean} [props.fallback] - the branch taken when no sibling matches
|
|
37519
|
+
* @param {Function|import("ignore:preact").VNode} [props.element] - what the branch renders
|
|
37520
|
+
* @param {object} [props.elementProps] - props given to `element`
|
|
37521
|
+
*
|
|
37522
|
+
* A branch says what it renders, not what happens when it cannot: loading and
|
|
37523
|
+
* error states are delegated to `<Loading>` and `<ErrorBoundary>` ancestors,
|
|
37524
|
+
* which may be written between routes (a container reads through them, see
|
|
37525
|
+
* collectBranches).
|
|
37526
|
+
*/
|
|
37375
37527
|
const Route = props => {
|
|
37376
37528
|
if (props.children) {
|
|
37377
37529
|
return jsx(RouteContainer, {
|
|
@@ -37413,6 +37565,9 @@ const collectRoutePages = children => {
|
|
|
37413
37565
|
return;
|
|
37414
37566
|
}
|
|
37415
37567
|
if (child.type !== Route) {
|
|
37568
|
+
// Something written between routes — <Loading>, <ErrorBoundary>, a box of
|
|
37569
|
+
// the app's own. The pages are inside it (see collectBranches).
|
|
37570
|
+
visit(child.props && child.props.children);
|
|
37416
37571
|
return;
|
|
37417
37572
|
}
|
|
37418
37573
|
const {
|
|
@@ -37475,8 +37630,8 @@ const RouteContainer = ({
|
|
|
37475
37630
|
return content;
|
|
37476
37631
|
};
|
|
37477
37632
|
// Walk JSX children vnodes (without rendering) to build a branch list and
|
|
37478
|
-
// find the active one in the same pass.
|
|
37479
|
-
//
|
|
37633
|
+
// find the active one in the same pass. Anything that is not a <Route> is read
|
|
37634
|
+
// through and kept around the branch it holds (see below).
|
|
37480
37635
|
// Returns { matchingBranch, fallbackBranch, activeBranch }.
|
|
37481
37636
|
const collectBranches = children => {
|
|
37482
37637
|
let matchingBranch = null;
|
|
@@ -37492,7 +37647,31 @@ const collectBranches = children => {
|
|
|
37492
37647
|
return;
|
|
37493
37648
|
}
|
|
37494
37649
|
if (child.type !== Route) {
|
|
37495
|
-
|
|
37650
|
+
// Anything else is a wrapper around branches, and the two that matter are
|
|
37651
|
+
// navi's own: a page says what it renders and delegates what it cannot —
|
|
37652
|
+
// loading to <Loading>, failing to <ErrorBoundary> — so those are written
|
|
37653
|
+
// BETWEEN the container and its routes. Reading through them is what lets
|
|
37654
|
+
// a subtree of pages share one, instead of the router demanding that its
|
|
37655
|
+
// children be routes and pushing every boundary outside of it.
|
|
37656
|
+
//
|
|
37657
|
+
// The wrapper is kept around whatever it holds: the container renders the
|
|
37658
|
+
// active branch alone, so the branch has to carry the wrapper with it, or
|
|
37659
|
+
// being selected would mean losing what was written around it.
|
|
37660
|
+
const wrapperChildren = child.props && child.props.children;
|
|
37661
|
+
if (!wrapperChildren) {
|
|
37662
|
+
throw new Error(`A <Route> child must be a <Route>, or hold some: ${String(child.type?.name ?? child.type)} holds nothing.`);
|
|
37663
|
+
}
|
|
37664
|
+
const {
|
|
37665
|
+
matchingBranch: matchingInside,
|
|
37666
|
+
fallbackBranch: fallbackInside
|
|
37667
|
+
} = collectBranches(wrapperChildren);
|
|
37668
|
+
if (matchingInside && !matchingBranch) {
|
|
37669
|
+
matchingBranch = wrapBranch(matchingInside, child);
|
|
37670
|
+
}
|
|
37671
|
+
if (fallbackInside && !fallbackBranch) {
|
|
37672
|
+
fallbackBranch = wrapBranch(fallbackInside, child);
|
|
37673
|
+
}
|
|
37674
|
+
return;
|
|
37496
37675
|
}
|
|
37497
37676
|
const {
|
|
37498
37677
|
children: nodeChildren,
|
|
@@ -37549,6 +37728,12 @@ const collectBranches = children => {
|
|
|
37549
37728
|
activeBranch
|
|
37550
37729
|
};
|
|
37551
37730
|
};
|
|
37731
|
+
const wrapBranch = (branch, wrapper) => {
|
|
37732
|
+
return {
|
|
37733
|
+
...branch,
|
|
37734
|
+
node: cloneElement(wrapper, null, branch.node)
|
|
37735
|
+
};
|
|
37736
|
+
};
|
|
37552
37737
|
const RouteLeaf = props => {
|
|
37553
37738
|
if (props.route) {
|
|
37554
37739
|
return jsx(RouteLeafRoute, {
|
|
@@ -37644,6 +37829,19 @@ const DRAGGED_ATTRIBUTE = "data-navi-route-travel-dragged";
|
|
|
37644
37829
|
const TURNED_ATTRIBUTE = "data-navi-route-travel-turned";
|
|
37645
37830
|
// The name the box wears while it travels, and only then (see nameForTravel).
|
|
37646
37831
|
const TRAVEL_NAME = "navi-route-travel";
|
|
37832
|
+
// Where the two boxes of a travel stand in the window, published for the
|
|
37833
|
+
// length of it. Measurements only: what is DERIVED from them — where a picture
|
|
37834
|
+
// goes, what a bar covers — is derived in the CSS below, so the app's own
|
|
37835
|
+
// numbers (the room its fixed bars take) can take part in it. Only the
|
|
37836
|
+
// measuring needs JS, and only for the one moment both boxes exist (see
|
|
37837
|
+
// holdTravelGeometry).
|
|
37838
|
+
const TRAVEL_TOP_PROPERTY = "--navi-route-travel-top";
|
|
37839
|
+
const TRAVEL_LEFT_PROPERTY = "--navi-route-travel-left";
|
|
37840
|
+
const TRAVEL_WIDTH_PROPERTY = "--navi-route-travel-width";
|
|
37841
|
+
const TRAVEL_HEIGHT_PROPERTY = "--navi-route-travel-height";
|
|
37842
|
+
const TRAVEL_OLD_TOP_PROPERTY = "--navi-route-travel-old-top";
|
|
37843
|
+
const TRAVEL_OLD_LEFT_PROPERTY = "--navi-route-travel-old-left";
|
|
37844
|
+
const TRAVEL_GEOMETRY_PROPERTIES = [TRAVEL_TOP_PROPERTY, TRAVEL_LEFT_PROPERTY, TRAVEL_WIDTH_PROPERTY, TRAVEL_HEIGHT_PROPERTY, TRAVEL_OLD_TOP_PROPERTY, TRAVEL_OLD_LEFT_PROPERTY];
|
|
37647
37845
|
const css$R = /* css */`
|
|
37648
37846
|
/* The name that makes the page inside this box a picture of its own during a
|
|
37649
37847
|
transition — rather than part of the one big picture the document takes, so
|
|
@@ -37716,6 +37914,23 @@ const css$R = /* css */`
|
|
|
37716
37914
|
same page changing its mind. */
|
|
37717
37915
|
mix-blend-mode: normal;
|
|
37718
37916
|
}
|
|
37917
|
+
&::view-transition-old(navi-route-travel) {
|
|
37918
|
+
/* Where the page being left WAS on screen, which is not where the group
|
|
37919
|
+
stands: the group is at the arriving box (its position animation is
|
|
37920
|
+
dropped along with its height one, below), and the two boxes are at the
|
|
37921
|
+
same place in the layout without being at the same place in the window
|
|
37922
|
+
— one page is scrolled and the other is not, so the box being left
|
|
37923
|
+
starts higher up. Left at the group's own corner the page being left
|
|
37924
|
+
would be seen jumping back to its top before it even begins to leave.
|
|
37925
|
+
Offset here rather than by \`translate\`, which the movement itself uses,
|
|
37926
|
+
and at its own size rather than the group's so that nothing is cut off
|
|
37927
|
+
the far side of the shift (see holdTravelGeometry). */
|
|
37928
|
+
top: calc(var(${TRAVEL_OLD_TOP_PROPERTY}) - var(${TRAVEL_TOP_PROPERTY}));
|
|
37929
|
+
left: calc(
|
|
37930
|
+
var(${TRAVEL_OLD_LEFT_PROPERTY}) - var(${TRAVEL_LEFT_PROPERTY})
|
|
37931
|
+
);
|
|
37932
|
+
width: auto;
|
|
37933
|
+
}
|
|
37719
37934
|
/* The pages are cut at the edge of the box they travel in. Said HERE and
|
|
37720
37935
|
nowhere else: these pictures are drawn in the top layer, so no overflow
|
|
37721
37936
|
on any element of the document — not the box's own, not a frame around
|
|
@@ -37727,7 +37942,7 @@ const css$R = /* css */`
|
|
|
37727
37942
|
}
|
|
37728
37943
|
&::view-transition-group(navi-route-travel) {
|
|
37729
37944
|
/* The window the two pictures are seen through, held still for the whole
|
|
37730
|
-
travel at the taller of the two boxes (see
|
|
37945
|
+
travel at the taller of the two boxes (see holdTravelGeometry): the group
|
|
37731
37946
|
is what CLIPS, and the browser animates its height from the box being
|
|
37732
37947
|
left to the box arriving — so the window shrinks under the pictures and
|
|
37733
37948
|
cuts the page leaving from the bottom, progressively. The box does end
|
|
@@ -37738,7 +37953,44 @@ const css$R = /* css */`
|
|
|
37738
37953
|
winning against it with !important — which also drops its position
|
|
37739
37954
|
animation, fine while a travel box stands in the same place from one
|
|
37740
37955
|
route to the next. */
|
|
37741
|
-
height: var(
|
|
37956
|
+
height: var(${TRAVEL_HEIGHT_PROPERTY});
|
|
37957
|
+
|
|
37958
|
+
/* Cut at the safe area, on top of being cut at the box. The pictures are
|
|
37959
|
+
drawn in the top layer, so they cover a fixed bar as easily as anything
|
|
37960
|
+
else — and the box they travel in runs UNDER the bars by design: that
|
|
37961
|
+
is what a fixed bar is for, and what the room it gives back is for. A
|
|
37962
|
+
box scrolled by so much as a pixel therefore starts above the top bar
|
|
37963
|
+
and ends below the bottom one, and the travel would be watched painting
|
|
37964
|
+
over both for its whole length.
|
|
37965
|
+
|
|
37966
|
+
The band left free is the app's own safe area (see layout/safe_area.js)
|
|
37967
|
+
— every kind of furniture at once, not the bars alone, and read rather
|
|
37968
|
+
than asked for, so one that grows, shrinks or unmounts mid-travel is
|
|
37969
|
+
followed without anything being told. What the group cannot know is
|
|
37970
|
+
only where it itself stands, and that is the measured half. */
|
|
37971
|
+
--navi-route-travel-clip-top: max(
|
|
37972
|
+
0px,
|
|
37973
|
+
var(--navi-safe-area-inset-top) - var(${TRAVEL_TOP_PROPERTY})
|
|
37974
|
+
);
|
|
37975
|
+
--navi-route-travel-clip-left: max(
|
|
37976
|
+
0px,
|
|
37977
|
+
var(--navi-safe-area-inset-left) - var(${TRAVEL_LEFT_PROPERTY})
|
|
37978
|
+
);
|
|
37979
|
+
--navi-route-travel-clip-bottom: max(
|
|
37980
|
+
0px,
|
|
37981
|
+
var(${TRAVEL_TOP_PROPERTY}) + var(${TRAVEL_HEIGHT_PROPERTY}) +
|
|
37982
|
+
var(--navi-safe-area-inset-bottom) - 100dvh
|
|
37983
|
+
);
|
|
37984
|
+
--navi-route-travel-clip-right: max(
|
|
37985
|
+
0px,
|
|
37986
|
+
var(${TRAVEL_LEFT_PROPERTY}) + var(${TRAVEL_WIDTH_PROPERTY}) +
|
|
37987
|
+
var(--navi-safe-area-inset-right) - 100dvw
|
|
37988
|
+
);
|
|
37989
|
+
clip-path: inset(
|
|
37990
|
+
var(--navi-route-travel-clip-top) var(--navi-route-travel-clip-right)
|
|
37991
|
+
var(--navi-route-travel-clip-bottom)
|
|
37992
|
+
var(--navi-route-travel-clip-left)
|
|
37993
|
+
);
|
|
37742
37994
|
animation-duration: var(--navi-route-travel-duration, 300ms);
|
|
37743
37995
|
animation-name: none;
|
|
37744
37996
|
}
|
|
@@ -37947,9 +38199,10 @@ const css$R = /* css */`
|
|
|
37947
38199
|
* sections inside the box the whole application travels in — and the class they
|
|
37948
38200
|
* share plus their axis are not enough to tell them apart from the outside.
|
|
37949
38201
|
*
|
|
37950
|
-
* The pages are cut at the edge of this box while they travel,
|
|
37951
|
-
*
|
|
37952
|
-
*
|
|
38202
|
+
* The pages are cut at the edge of this box while they travel, and at the app's
|
|
38203
|
+
* safe area the box runs under, which is written on the transition's own
|
|
38204
|
+
* pseudo-elements — no overflow of the document reaches pictures drawn in the
|
|
38205
|
+
* top layer. It needs nothing of the browser beyond view
|
|
37953
38206
|
* transitions themselves: a browser without them (Firefox) navigates without the
|
|
37954
38207
|
* movement, and the gesture applies its change on release instead of dragging a
|
|
37955
38208
|
* picture that does not exist.
|
|
@@ -38052,8 +38305,8 @@ const RouteTravel = ({
|
|
|
38052
38305
|
}
|
|
38053
38306
|
pageAskedForRef.current = page;
|
|
38054
38307
|
// The box as it stands before anything moves: rendering is held, so this is
|
|
38055
|
-
// still the page being left (see
|
|
38056
|
-
const
|
|
38308
|
+
// still the page being left (see holdTravelGeometry).
|
|
38309
|
+
const rectBefore = elementRef.current.getBoundingClientRect();
|
|
38057
38310
|
// The hold a navigation already took, if this travel is the answer to one:
|
|
38058
38311
|
// taking another would be taking a hold on a page that is holding still.
|
|
38059
38312
|
const releaseRendering = renderingHeldForRouting || holdRendering();
|
|
@@ -38068,6 +38321,11 @@ const RouteTravel = ({
|
|
|
38068
38321
|
// screen and an error nobody asked for.
|
|
38069
38322
|
const renderWait = armRouteRenderWait();
|
|
38070
38323
|
const viewTransition = startViewTransition(async () => {
|
|
38324
|
+
// Whatever is awaited here must be able to resolve without the page being
|
|
38325
|
+
// rendered: the document is frozen for the whole of this callback, and a
|
|
38326
|
+
// frame never comes — waiting for one waits until the browser gives up on
|
|
38327
|
+
// the transition. And it stays frozen exactly this long, so this is also
|
|
38328
|
+
// the shortest thing there is to keep short.
|
|
38071
38329
|
await whilePageRenders(page, async () => {
|
|
38072
38330
|
releaseRendering();
|
|
38073
38331
|
if (change) {
|
|
@@ -38076,7 +38334,7 @@ const RouteTravel = ({
|
|
|
38076
38334
|
}, renderWait);
|
|
38077
38335
|
// The page arriving is in the DOM and the transition has not started
|
|
38078
38336
|
// playing: the one moment both boxes can be known.
|
|
38079
|
-
|
|
38337
|
+
holdTravelGeometry(elementRef.current, rectBefore);
|
|
38080
38338
|
});
|
|
38081
38339
|
travel.viewTransition = viewTransition;
|
|
38082
38340
|
if (scrub) {
|
|
@@ -38405,7 +38663,7 @@ const RouteTravel = ({
|
|
|
38405
38663
|
document.documentElement.removeAttribute(TRAVEL_AXIS_ATTRIBUTE);
|
|
38406
38664
|
document.documentElement.removeAttribute(DRAGGED_ATTRIBUTE);
|
|
38407
38665
|
document.documentElement.removeAttribute(TURNED_ATTRIBUTE);
|
|
38408
|
-
|
|
38666
|
+
releaseTravelGeometry();
|
|
38409
38667
|
}
|
|
38410
38668
|
};
|
|
38411
38669
|
|
|
@@ -38787,23 +39045,43 @@ const releaseHold = travel => {
|
|
|
38787
39045
|
travelHoldingPictures = null;
|
|
38788
39046
|
document.documentElement.removeAttribute(HOLD_ATTRIBUTE);
|
|
38789
39047
|
};
|
|
38790
|
-
|
|
38791
|
-
// The
|
|
38792
|
-
//
|
|
38793
|
-
//
|
|
38794
|
-
//
|
|
38795
|
-
//
|
|
38796
|
-
|
|
38797
|
-
|
|
38798
|
-
|
|
38799
|
-
|
|
39048
|
+
|
|
39049
|
+
// The two boxes of a travel, measured at the one moment both exist: the
|
|
39050
|
+
// arriving page is in the DOM and the transition has not started playing.
|
|
39051
|
+
//
|
|
39052
|
+
// The group stands at the ARRIVING box — its own animation is dropped, so it
|
|
39053
|
+
// takes the geometry the browser declared for it and holds it for the whole
|
|
39054
|
+
// travel. That is why both rectangles have to be published: a group that does
|
|
39055
|
+
// not move says nothing about where the page being left was, and its rectangle
|
|
39056
|
+
// in the window is the only thing CSS cannot work out on its own.
|
|
39057
|
+
const holdTravelGeometry = (element, rectBefore) => {
|
|
39058
|
+
const rectAfter = element.getBoundingClientRect();
|
|
39059
|
+
// The height it is held at is the taller of the two boxes, so neither picture
|
|
39060
|
+
// is ever cut. It cannot be measured from one side alone: a page arriving
|
|
39061
|
+
// shorter than the one it replaces would cut the one leaving, a page arriving
|
|
39062
|
+
// taller would be cut itself.
|
|
39063
|
+
const height = rectBefore.height > rectAfter.height ? rectBefore.height : rectAfter.height;
|
|
39064
|
+
const {
|
|
39065
|
+
style
|
|
39066
|
+
} = document.documentElement;
|
|
39067
|
+
style.setProperty(TRAVEL_TOP_PROPERTY, `${rectAfter.top}px`);
|
|
39068
|
+
style.setProperty(TRAVEL_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
39069
|
+
style.setProperty(TRAVEL_WIDTH_PROPERTY, `${rectAfter.width}px`);
|
|
39070
|
+
style.setProperty(TRAVEL_HEIGHT_PROPERTY, `${height}px`);
|
|
39071
|
+
style.setProperty(TRAVEL_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
|
|
39072
|
+
style.setProperty(TRAVEL_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
|
|
38800
39073
|
};
|
|
38801
39074
|
// The live layout takes the box back. A discontinuity by construction — the
|
|
38802
39075
|
// group stands at the held height, the box is at the new one — and an invisible
|
|
38803
39076
|
// one: the page arriving is fully in place, and the strip below it that the
|
|
38804
39077
|
// group still covers shows the page leaving only while it is still on screen.
|
|
38805
|
-
const
|
|
38806
|
-
|
|
39078
|
+
const releaseTravelGeometry = () => {
|
|
39079
|
+
const {
|
|
39080
|
+
style
|
|
39081
|
+
} = document.documentElement;
|
|
39082
|
+
for (const property of TRAVEL_GEOMETRY_PROPERTIES) {
|
|
39083
|
+
style.removeProperty(property);
|
|
39084
|
+
}
|
|
38807
39085
|
};
|
|
38808
39086
|
|
|
38809
39087
|
// The browser does not take the picture of the page being left when a
|
|
@@ -43278,60 +43556,15 @@ const withPixelUnit = value => {
|
|
|
43278
43556
|
};
|
|
43279
43557
|
|
|
43280
43558
|
/**
|
|
43281
|
-
*
|
|
43282
|
-
*
|
|
43283
|
-
*
|
|
43284
|
-
*
|
|
43285
|
-
*
|
|
43286
|
-
*
|
|
43287
|
-
*
|
|
43288
|
-
* bar. Without it the last screenful stays covered, unreachable.
|
|
43289
|
-
* - **scroll-padding**, so anything the browser scrolls TO lands in front of
|
|
43290
|
-
* the bar rather than under it. An anchor link, `scrollIntoView()`, a focused
|
|
43291
|
-
* field brought into view, restoring a scroll position — all of them align
|
|
43292
|
-
* the target with the edge of the scrollport, which is behind the bar. The
|
|
43293
|
-
* padding above does not help here: it moves the content, not the place the
|
|
43294
|
-
* browser scrolls the target to.
|
|
43295
|
-
*
|
|
43296
|
-
* Published on <html> as CSS variables rather than applied to some element:
|
|
43297
|
-
* which element scrolls is the app's business, and an app with more than one
|
|
43298
|
-
* would have to fight a component that picked for it. The app either marks its
|
|
43299
|
-
* scrolling area with `data-navi-fixed-bar-space` (the rules below) or reads
|
|
43300
|
-
* the variables itself. `:root` gets the scroll-padding unconditionally,
|
|
43301
|
-
* because the document is the scrollport in the common case and an anchor
|
|
43302
|
-
* landing under a bar is never what anyone wants.
|
|
43303
|
-
*
|
|
43304
|
-
* The variables hold the measured size of the bars on that edge — see the
|
|
43305
|
-
* comment where FixedBar sets them.
|
|
43559
|
+
* How much room the fixed bars take on each edge, published for the safe area
|
|
43560
|
+
* to add up (see layout/safe_area.js — it declares the four variables written
|
|
43561
|
+
* here, and what reads them reads the sum, never these).
|
|
43562
|
+
*
|
|
43563
|
+
* Measured rather than declared: a bar's size comes from a prop, a theme
|
|
43564
|
+
* variable, its own content or the device's notch, and only the used value
|
|
43565
|
+
* knows all four.
|
|
43306
43566
|
*/
|
|
43307
43567
|
|
|
43308
|
-
const FIXED_BAR_SPACE_CSS = /* css */ `
|
|
43309
|
-
:root {
|
|
43310
|
-
--navi-fixed-bar-space-top: 0px;
|
|
43311
|
-
--navi-fixed-bar-space-bottom: 0px;
|
|
43312
|
-
--navi-fixed-bar-space-left: 0px;
|
|
43313
|
-
--navi-fixed-bar-space-right: 0px;
|
|
43314
|
-
|
|
43315
|
-
scroll-padding-top: var(--navi-fixed-bar-space-top);
|
|
43316
|
-
scroll-padding-right: var(--navi-fixed-bar-space-right);
|
|
43317
|
-
scroll-padding-bottom: var(--navi-fixed-bar-space-bottom);
|
|
43318
|
-
scroll-padding-left: var(--navi-fixed-bar-space-left);
|
|
43319
|
-
}
|
|
43320
|
-
|
|
43321
|
-
/* Put this on whatever scrolls under the bars. */
|
|
43322
|
-
[data-navi-fixed-bar-space] {
|
|
43323
|
-
padding-top: var(--navi-fixed-bar-space-top);
|
|
43324
|
-
padding-right: var(--navi-fixed-bar-space-right);
|
|
43325
|
-
padding-bottom: var(--navi-fixed-bar-space-bottom);
|
|
43326
|
-
padding-left: var(--navi-fixed-bar-space-left);
|
|
43327
|
-
|
|
43328
|
-
scroll-padding-top: var(--navi-fixed-bar-space-top);
|
|
43329
|
-
scroll-padding-right: var(--navi-fixed-bar-space-right);
|
|
43330
|
-
scroll-padding-bottom: var(--navi-fixed-bar-space-bottom);
|
|
43331
|
-
scroll-padding-left: var(--navi-fixed-bar-space-left);
|
|
43332
|
-
}
|
|
43333
|
-
`;
|
|
43334
|
-
|
|
43335
43568
|
// Several bars can share an edge — during a page transition the outgoing and
|
|
43336
43569
|
// the incoming one are both mounted. They are all pinned to that same edge, so
|
|
43337
43570
|
// they overlap: the room to give back is the largest of them, not their sum,
|
|
@@ -43461,7 +43694,10 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
43461
43694
|
* nearest scrolling ancestor, and an app shell almost always has one (an
|
|
43462
43695
|
* `overflow` somewhere) — the bar would then stick inside that box and
|
|
43463
43696
|
* never to the window. Fixed, centered and bounded by `maxWidth`, it also
|
|
43464
|
-
* stays lined up with the content on a wide screen.
|
|
43697
|
+
* stays lined up with the content on a wide screen. It is pinned to the
|
|
43698
|
+
* app's rectangle rather than to the glass (`--navi-app-inset-*`, see
|
|
43699
|
+
* layout/safe_area.js): an app that declares itself narrower than the window
|
|
43700
|
+
* keeps its bars against its own edges.
|
|
43465
43701
|
* 2. **It gives its space back.** Being fixed it covers the content: without a
|
|
43466
43702
|
* reserve the end of a long page stays under it, unreachable. It publishes
|
|
43467
43703
|
* what it takes on <html> — see fixed_bar_space.js.
|
|
@@ -43493,8 +43729,6 @@ const css$L = /* css */`
|
|
|
43493
43729
|
}
|
|
43494
43730
|
}
|
|
43495
43731
|
|
|
43496
|
-
${FIXED_BAR_SPACE_CSS}
|
|
43497
|
-
|
|
43498
43732
|
.navi_fixed_bar {
|
|
43499
43733
|
position: fixed;
|
|
43500
43734
|
z-index: var(--navi-z-index-bar);
|
|
@@ -43509,8 +43743,8 @@ const css$L = /* css */`
|
|
|
43509
43743
|
whose padding ignored it would put its first item under it. */
|
|
43510
43744
|
&[data-area="top"],
|
|
43511
43745
|
&[data-area="bottom"] {
|
|
43512
|
-
right:
|
|
43513
|
-
left:
|
|
43746
|
+
right: var(--navi-app-inset-right);
|
|
43747
|
+
left: var(--navi-app-inset-left);
|
|
43514
43748
|
/* No width of its own: pinned to both edges, the used width absorbs the
|
|
43515
43749
|
padding instead of being inflated by it. max-width then narrows it and
|
|
43516
43750
|
the auto margins re-center it. */
|
|
@@ -43524,8 +43758,8 @@ const css$L = /* css */`
|
|
|
43524
43758
|
}
|
|
43525
43759
|
&[data-area="left"],
|
|
43526
43760
|
&[data-area="right"] {
|
|
43527
|
-
top:
|
|
43528
|
-
bottom:
|
|
43761
|
+
top: var(--navi-app-inset-top);
|
|
43762
|
+
bottom: var(--navi-app-inset-bottom);
|
|
43529
43763
|
padding-top: calc(
|
|
43530
43764
|
var(--navi-fixed-bar-padding) + env(safe-area-inset-top)
|
|
43531
43765
|
);
|
|
@@ -43539,28 +43773,28 @@ const css$L = /* css */`
|
|
|
43539
43773
|
added to the size: the background then runs under the notch while the
|
|
43540
43774
|
content keeps the whole width/height asked for. */
|
|
43541
43775
|
&[data-area="top"] {
|
|
43542
|
-
top:
|
|
43776
|
+
top: var(--navi-app-inset-top);
|
|
43543
43777
|
height: calc(var(--navi-fixed-bar-height) + env(safe-area-inset-top));
|
|
43544
43778
|
padding-top: env(safe-area-inset-top);
|
|
43545
43779
|
box-shadow: 0 var(--navi-fixed-bar-border-width) 0
|
|
43546
43780
|
var(--navi-fixed-bar-border-color);
|
|
43547
43781
|
}
|
|
43548
43782
|
&[data-area="bottom"] {
|
|
43549
|
-
bottom:
|
|
43783
|
+
bottom: var(--navi-app-inset-bottom);
|
|
43550
43784
|
height: calc(var(--navi-fixed-bar-height) + env(safe-area-inset-bottom));
|
|
43551
43785
|
padding-bottom: env(safe-area-inset-bottom);
|
|
43552
43786
|
box-shadow: 0 calc(-1 * var(--navi-fixed-bar-border-width)) 0
|
|
43553
43787
|
var(--navi-fixed-bar-border-color);
|
|
43554
43788
|
}
|
|
43555
43789
|
&[data-area="left"] {
|
|
43556
|
-
left:
|
|
43790
|
+
left: var(--navi-app-inset-left);
|
|
43557
43791
|
width: calc(var(--navi-fixed-bar-width) + env(safe-area-inset-left));
|
|
43558
43792
|
padding-left: env(safe-area-inset-left);
|
|
43559
43793
|
box-shadow: var(--navi-fixed-bar-border-width) 0 0
|
|
43560
43794
|
var(--navi-fixed-bar-border-color);
|
|
43561
43795
|
}
|
|
43562
43796
|
&[data-area="right"] {
|
|
43563
|
-
right:
|
|
43797
|
+
right: var(--navi-app-inset-right);
|
|
43564
43798
|
width: calc(var(--navi-fixed-bar-width) + env(safe-area-inset-right));
|
|
43565
43799
|
padding-right: env(safe-area-inset-right);
|
|
43566
43800
|
box-shadow: calc(-1 * var(--navi-fixed-bar-border-width)) 0 0
|
|
@@ -54760,12 +54994,13 @@ const css$v = /* css */`
|
|
|
54760
54994
|
pointer-events: none;
|
|
54761
54995
|
}
|
|
54762
54996
|
|
|
54763
|
-
/* Scrolling with the page means sticking to the viewport, and
|
|
54764
|
-
|
|
54765
|
-
|
|
54766
|
-
|
|
54997
|
+
/* Scrolling with the page means sticking to the viewport, and whatever the
|
|
54998
|
+
app puts in front of that viewport — a FixedBar, a band of its own — is
|
|
54999
|
+
in front of the label too: without the offset a sticky label lands behind
|
|
55000
|
+
it. The safe area is what that adds up to (see layout/safe_area.js) and
|
|
55001
|
+
it is 0px when nothing covers the top. */
|
|
54767
55002
|
&[data-scroller="document"] {
|
|
54768
|
-
--x-list-group-label-top: var(--navi-
|
|
55003
|
+
--x-list-group-label-top: var(--navi-safe-area-inset-top);
|
|
54769
55004
|
}
|
|
54770
55005
|
|
|
54771
55006
|
&[data-expand-x] {
|