@jsenv/navi 0.29.419 → 0.29.420
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/dev/jsenv_navi.js +392 -153
- package/dist/dev/jsenv_navi.js.map +30 -30
- package/dist/jsenv_navi.js +373 -146
- package/dist/jsenv_navi.js.map +30 -30
- package/docs/create_and_edit.md +10 -0
- package/docs/data_states.md +6 -7
- package/docs/form_changed.md +5 -3
- package/package.json +2 -2
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -426,6 +426,22 @@ const whenRenderingResumes = (callback) => {
|
|
|
426
426
|
renderingHold.waiting.push(callback);
|
|
427
427
|
};
|
|
428
428
|
|
|
429
|
+
// After the render the change being applied has queued, for what depends on
|
|
430
|
+
// the page that change puts on screen — an offset given back to a page is
|
|
431
|
+
// clamped to whatever page the document holds when it is written. Under a hold
|
|
432
|
+
// that render runs when the hold is released, and this right after it.
|
|
433
|
+
// Otherwise the render is Preact's own, queued in a microtask, and every
|
|
434
|
+
// microtask has run by the next frame — before it paints.
|
|
435
|
+
const whenPageRendered = (callback) => {
|
|
436
|
+
if (renderingHold) {
|
|
437
|
+
renderingHold.waiting.push(callback);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
requestAnimationFrame(() => {
|
|
441
|
+
callback();
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
|
|
429
445
|
// The hold a navigation takes on its way in — from before its first write,
|
|
430
446
|
// because by the time a route announces that it matches, Preact has already
|
|
431
447
|
// been told and the render is queued; a hold taken then is a hold taken too
|
|
@@ -800,7 +816,10 @@ const writeTarget = (element) => {
|
|
|
800
816
|
silencedElement.id = addressTargetId;
|
|
801
817
|
}
|
|
802
818
|
window.history.replaceState(historyState, null, address);
|
|
803
|
-
|
|
819
|
+
// Only a state the entry had is put back: an entry nobody wrote one on
|
|
820
|
+
// (via_history.js never does) has nothing to lose, and updateCurrentEntry
|
|
821
|
+
// cannot say "none" — `state` is a required member, and undefined throws.
|
|
822
|
+
if (entry && navigation.currentEntry && entryState !== undefined) {
|
|
804
823
|
navigation.updateCurrentEntry({ state: entryState });
|
|
805
824
|
}
|
|
806
825
|
targetWriteInProgress = false;
|
|
@@ -3675,6 +3694,8 @@ const actionWeakMap = new WeakMap();
|
|
|
3675
3694
|
* than a first load (data_states.md). Called at the start of every run whose
|
|
3676
3695
|
* value is undefined; the answer replaces it as usual, and `undefined` means
|
|
3677
3696
|
* "nothing known". A resource `GET` uses it to draw the row its store holds.
|
|
3697
|
+
* An action declaring it is asked for it by a binding moving to new params,
|
|
3698
|
+
* rather than handed the previous params' answer (see inheritData).
|
|
3678
3699
|
* @param {{ ms?: number, max?: number }} [rootOptions.keep] - how long an
|
|
3679
3700
|
* answer stays good once it has landed. Without it an answer lives exactly as
|
|
3680
3701
|
* long as something references it: the screen that asked goes away, and
|
|
@@ -4342,7 +4363,14 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4342
4363
|
if (rejected) {
|
|
4343
4364
|
return failRun(rejectedValue);
|
|
4344
4365
|
}
|
|
4345
|
-
|
|
4366
|
+
// Turning the answer into a value can throw as well — a resource
|
|
4367
|
+
// write answering nothing to upsert. That fails the run, as the
|
|
4368
|
+
// catch below does when the callback answered synchronously.
|
|
4369
|
+
try {
|
|
4370
|
+
return onRunEnd();
|
|
4371
|
+
} catch (e) {
|
|
4372
|
+
return failRun(e);
|
|
4373
|
+
}
|
|
4346
4374
|
});
|
|
4347
4375
|
} catch (e) {
|
|
4348
4376
|
return failRun(e);
|
|
@@ -4385,6 +4413,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4385
4413
|
|
|
4386
4414
|
const privateProperties = {
|
|
4387
4415
|
valueInitial,
|
|
4416
|
+
provisionalValue,
|
|
4388
4417
|
|
|
4389
4418
|
performRun,
|
|
4390
4419
|
performReset,
|
|
@@ -4416,7 +4445,8 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4416
4445
|
* This enables live updates - for example, performing an HTTP GET request every time
|
|
4417
4446
|
* a list of filters changes, providing real-time results without user interaction.
|
|
4418
4447
|
* @param {boolean} options.inheritData - When false, each new target action starts fresh with no inherited state.
|
|
4419
|
-
* By default (true), the proxy carries over the previous target's value and error into the new action
|
|
4448
|
+
* By default (true), the proxy carries over the previous target's value and error into the new action —
|
|
4449
|
+
* unless the action declares a `provisionalValue`, which then says what the new one holds.
|
|
4420
4450
|
* This keeps the facade in sync with the latest known data: `action.dataSignal.value` only changes when a
|
|
4421
4451
|
* new action completes, not when it starts loading. Code that needs to distinguish loading state can still
|
|
4422
4452
|
* check `action.runningState`, while code that just reads `action.data` always sees the most recent
|
|
@@ -4463,8 +4493,14 @@ const createActionProxyFromSignal = (
|
|
|
4463
4493
|
let currentActionPrivateProperties = getActionPrivateProperties(action);
|
|
4464
4494
|
let actionTargetPreviousWeakRef = null;
|
|
4465
4495
|
|
|
4496
|
+
// An action saying for itself what it holds for params it has not answered
|
|
4497
|
+
// (provisionalValue: a resource GET draws the row its store holds) is not
|
|
4498
|
+
// handed the previous params' answer — an answer about another row. What it
|
|
4499
|
+
// does not know stays unknown: a skeleton, never a different row.
|
|
4500
|
+
const inheritsAnswer =
|
|
4501
|
+
inheritData && !getActionPrivateProperties(action).provisionalValue;
|
|
4466
4502
|
const createTarget = (params) => {
|
|
4467
|
-
if (
|
|
4503
|
+
if (inheritsAnswer) {
|
|
4468
4504
|
const previousActionTarget = actionTargetPreviousWeakRef?.deref();
|
|
4469
4505
|
const previousTarget = previousActionTarget || action;
|
|
4470
4506
|
return action.bindParams(params, {
|
|
@@ -4577,6 +4613,12 @@ const createActionProxyFromSignal = (
|
|
|
4577
4613
|
return signalProxy;
|
|
4578
4614
|
};
|
|
4579
4615
|
|
|
4616
|
+
// Params bound onto this binding make the action bound to both — these over
|
|
4617
|
+
// the ones it follows, as an instance's go over its action's — following the
|
|
4618
|
+
// signals of both: what a control does with the action it is given, and
|
|
4619
|
+
// actionRunEffect. Kept per params like the action's own bindings, since a
|
|
4620
|
+
// control binds its action again at every render.
|
|
4621
|
+
const bindingOnProxyWeakMap = createJsValueWeakMap();
|
|
4580
4622
|
Object.assign(actionProxy, {
|
|
4581
4623
|
isAction: true,
|
|
4582
4624
|
isProxy: true,
|
|
@@ -4601,14 +4643,27 @@ const createActionProxyFromSignal = (
|
|
|
4601
4643
|
});
|
|
4602
4644
|
return currentAction;
|
|
4603
4645
|
},
|
|
4604
|
-
bindParams: () => {
|
|
4605
|
-
|
|
4606
|
-
|
|
4646
|
+
bindParams: (newParamsOrSignal, options) => {
|
|
4647
|
+
const existingBinding = bindingOnProxyWeakMap.get(newParamsOrSignal);
|
|
4648
|
+
if (existingBinding) {
|
|
4649
|
+
return existingBinding;
|
|
4650
|
+
}
|
|
4651
|
+
const binding = action.bindParams(
|
|
4652
|
+
computed(() =>
|
|
4653
|
+
mergeActionParams(
|
|
4654
|
+
paramsSignal.value,
|
|
4655
|
+
readParamsHandedToBind(newParamsOrSignal),
|
|
4656
|
+
),
|
|
4657
|
+
),
|
|
4658
|
+
options,
|
|
4607
4659
|
);
|
|
4660
|
+
bindingOnProxyWeakMap.set(newParamsOrSignal, binding);
|
|
4661
|
+
return binding;
|
|
4608
4662
|
},
|
|
4609
4663
|
replaceParams: null, // Will be set below
|
|
4610
4664
|
toString: () => callSourceSignal.peek(), // peek: see the action's toString
|
|
4611
4665
|
meta: action.meta,
|
|
4666
|
+
debug: action.debug,
|
|
4612
4667
|
|
|
4613
4668
|
paramsSignal: proxyParamsSignal,
|
|
4614
4669
|
paramsSettlingSignal,
|
|
@@ -4751,6 +4806,29 @@ const generateActionCallSource = (name, params) => {
|
|
|
4751
4806
|
return `${name}${argsString}`;
|
|
4752
4807
|
};
|
|
4753
4808
|
|
|
4809
|
+
// What params handed to bindParams say at this moment, read as a binding reads
|
|
4810
|
+
// them (see _bindParams): a signal is its value, and so is each signal a plain
|
|
4811
|
+
// object holds at its first level. Read inside a computed, it follows them.
|
|
4812
|
+
const readParamsHandedToBind = (newParamsOrSignal) => {
|
|
4813
|
+
if (isSignal(newParamsOrSignal)) {
|
|
4814
|
+
return newParamsOrSignal.value;
|
|
4815
|
+
}
|
|
4816
|
+
if (!isPlainObject(newParamsOrSignal)) {
|
|
4817
|
+
return newParamsOrSignal;
|
|
4818
|
+
}
|
|
4819
|
+
const params = {};
|
|
4820
|
+
for (const key of Object.keys(newParamsOrSignal)) {
|
|
4821
|
+
const value = newParamsOrSignal[key];
|
|
4822
|
+
const valueSignal = isSignal(value)
|
|
4823
|
+
? value
|
|
4824
|
+
: value
|
|
4825
|
+
? value[SYMBOL_OBJECT_SIGNAL]
|
|
4826
|
+
: undefined;
|
|
4827
|
+
params[key] = valueSignal ? valueSignal.value : value;
|
|
4828
|
+
}
|
|
4829
|
+
return params;
|
|
4830
|
+
};
|
|
4831
|
+
|
|
4754
4832
|
const isPlainObject = (obj) => {
|
|
4755
4833
|
if (typeof obj !== "object" || obj === null) {
|
|
4756
4834
|
return false;
|
|
@@ -4863,9 +4941,16 @@ const actionRunEffect = (
|
|
|
4863
4941
|
}
|
|
4864
4942
|
return params;
|
|
4865
4943
|
});
|
|
4944
|
+
// Whether the getter asks for a run is read off what it returned, never off
|
|
4945
|
+
// the params of the instance it leads to: an action handed here holding
|
|
4946
|
+
// params of its own (an instance, a binding) keeps them when the getter
|
|
4947
|
+
// returns nothing — and nothing must still run nothing.
|
|
4948
|
+
let asking = Boolean(actionParamsSignal.peek());
|
|
4866
4949
|
const actionRunnedByThisEffect = action.bindParams(actionParamsSignal, {
|
|
4867
4950
|
debounce,
|
|
4868
4951
|
onChange: (actionTarget, actionTargetPrevious, { explicitRunIntent }) => {
|
|
4952
|
+
const askingBefore = asking;
|
|
4953
|
+
asking = Boolean(actionParamsSignal.peek());
|
|
4869
4954
|
if (explicitRunIntent) {
|
|
4870
4955
|
// The caller already issued an explicit run/rerun/prerun/reset/abort —
|
|
4871
4956
|
// don't attempt to also auto-run from the params change to avoid double-runs.
|
|
@@ -4876,7 +4961,7 @@ const actionRunEffect = (
|
|
|
4876
4961
|
}
|
|
4877
4962
|
if (!actionTargetPrevious && actionTarget) {
|
|
4878
4963
|
// first run
|
|
4879
|
-
if (!
|
|
4964
|
+
if (!asking) {
|
|
4880
4965
|
// falsy params, don't run
|
|
4881
4966
|
return;
|
|
4882
4967
|
}
|
|
@@ -4892,12 +4977,12 @@ const actionRunEffect = (
|
|
|
4892
4977
|
actionTarget
|
|
4893
4978
|
) {
|
|
4894
4979
|
// params changed
|
|
4895
|
-
if (!
|
|
4980
|
+
if (!asking) {
|
|
4896
4981
|
// falsy params, don't run
|
|
4897
4982
|
actionTargetPrevious.abort("abortOnFalsyParams");
|
|
4898
4983
|
return;
|
|
4899
4984
|
}
|
|
4900
|
-
if (!
|
|
4985
|
+
if (!askingBefore) {
|
|
4901
4986
|
// coming from falsy-params state: action may already be cached, avoid unnecessary rerun
|
|
4902
4987
|
runUnwatched(() =>
|
|
4903
4988
|
actionTarget.run({ reason: "params restored from falsy state" }),
|
|
@@ -4909,7 +4994,7 @@ const actionRunEffect = (
|
|
|
4909
4994
|
},
|
|
4910
4995
|
...options,
|
|
4911
4996
|
});
|
|
4912
|
-
if (
|
|
4997
|
+
if (asking) {
|
|
4913
4998
|
runUnwatched(() =>
|
|
4914
4999
|
actionRunnedByThisEffect.run({ reason: "initial truthy params" }),
|
|
4915
5000
|
);
|
|
@@ -6981,6 +7066,28 @@ let navDepthMax = 0;
|
|
|
6981
7066
|
|
|
6982
7067
|
const getNavDepth = () => navDepth;
|
|
6983
7068
|
|
|
7069
|
+
// Where an entry's state says it stands, undefined when it says nothing.
|
|
7070
|
+
const readNavDepthInState = (state) => {
|
|
7071
|
+
if (state && typeof state[NAV_DEPTH_STATE_KEY] === "number") {
|
|
7072
|
+
return state[NAV_DEPTH_STATE_KEY];
|
|
7073
|
+
}
|
|
7074
|
+
return undefined;
|
|
7075
|
+
};
|
|
7076
|
+
|
|
7077
|
+
// The state of the entry a document is loaded into, carrying its depth. One
|
|
7078
|
+
// that says a depth was written by this document (a reload in the middle of
|
|
7079
|
+
// the stack) and keeps it. One that says none was not: a url opened cold, the
|
|
7080
|
+
// first of the stack — or, rarely, an entry the browser made for a fragment,
|
|
7081
|
+
// reloaded, which 0 under-reports as above. Written into the entry by the
|
|
7082
|
+
// integrations, because a back that returns there reads where it stands from
|
|
7083
|
+
// the entry alone.
|
|
7084
|
+
const stateWithNavDepthOnLoad = (state) => {
|
|
7085
|
+
if (readNavDepthInState(state) !== undefined) {
|
|
7086
|
+
return state;
|
|
7087
|
+
}
|
|
7088
|
+
return { ...state, [NAV_DEPTH_STATE_KEY]: 0 };
|
|
7089
|
+
};
|
|
7090
|
+
|
|
6984
7091
|
const applyNavigationToNavDepth = (navigationType, state) => {
|
|
6985
7092
|
if (navigationType === "push") {
|
|
6986
7093
|
navDepth++;
|
|
@@ -6988,10 +7095,7 @@ const applyNavigationToNavDepth = (navigationType, state) => {
|
|
|
6988
7095
|
navDepthMax = navDepth;
|
|
6989
7096
|
} else if (navigationType === "replace") ; else {
|
|
6990
7097
|
// load, reload, traverse: the entry itself says where it stands.
|
|
6991
|
-
const depthInState =
|
|
6992
|
-
state && typeof state[NAV_DEPTH_STATE_KEY] === "number"
|
|
6993
|
-
? state[NAV_DEPTH_STATE_KEY]
|
|
6994
|
-
: undefined;
|
|
7098
|
+
const depthInState = readNavDepthInState(state);
|
|
6995
7099
|
if (depthInState !== undefined) {
|
|
6996
7100
|
navDepth = depthInState;
|
|
6997
7101
|
if (navDepth > navDepthMax) {
|
|
@@ -10524,6 +10628,18 @@ const preloadUrl = (url) => {
|
|
|
10524
10628
|
}
|
|
10525
10629
|
};
|
|
10526
10630
|
|
|
10631
|
+
// What a reload asks of each action bound to a route (see routeAction): the
|
|
10632
|
+
// action to run again, or nothing when its params ask nothing.
|
|
10633
|
+
const routeReloadMap = new WeakMap();
|
|
10634
|
+
const registerRouteReload = (route, reload) => {
|
|
10635
|
+
let reloadSet = routeReloadMap.get(route);
|
|
10636
|
+
if (!reloadSet) {
|
|
10637
|
+
reloadSet = new Set();
|
|
10638
|
+
routeReloadMap.set(route, reloadSet);
|
|
10639
|
+
}
|
|
10640
|
+
reloadSet.add(reload);
|
|
10641
|
+
};
|
|
10642
|
+
|
|
10527
10643
|
const routeStatePreloadMap = new WeakMap();
|
|
10528
10644
|
const registerRouteStatePreload = (route, preload) => {
|
|
10529
10645
|
let preloadSet = routeStatePreloadMap.get(route);
|
|
@@ -10613,6 +10729,7 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
10613
10729
|
const updateRoutes = (
|
|
10614
10730
|
url,
|
|
10615
10731
|
{
|
|
10732
|
+
navigationType,
|
|
10616
10733
|
isVisited = () => false,
|
|
10617
10734
|
// state
|
|
10618
10735
|
} = {},
|
|
@@ -10912,6 +11029,27 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
10912
11029
|
}
|
|
10913
11030
|
}
|
|
10914
11031
|
|
|
11032
|
+
// A route's actions run again when their params change (see routeAction),
|
|
11033
|
+
// and a reload changes none: it asks again for the page one is on. So the
|
|
11034
|
+
// actions of every matching route are handed back, to be rerun — except
|
|
11035
|
+
// those whose params ask nothing.
|
|
11036
|
+
if (navigationType === "reload") {
|
|
11037
|
+
const reloadSet = new Set();
|
|
11038
|
+
for (const route of returnValue.matchingRouteSet) {
|
|
11039
|
+
const routeReloadSet = routeReloadMap.get(route);
|
|
11040
|
+
if (!routeReloadSet) {
|
|
11041
|
+
continue;
|
|
11042
|
+
}
|
|
11043
|
+
for (const reload of routeReloadSet) {
|
|
11044
|
+
const actionToRerun = reload();
|
|
11045
|
+
if (actionToRerun) {
|
|
11046
|
+
reloadSet.add(actionToRerun);
|
|
11047
|
+
}
|
|
11048
|
+
}
|
|
11049
|
+
}
|
|
11050
|
+
returnValue.reloadSet = reloadSet;
|
|
11051
|
+
}
|
|
11052
|
+
|
|
10915
11053
|
return returnValue;
|
|
10916
11054
|
};
|
|
10917
11055
|
|
|
@@ -11318,11 +11456,18 @@ const installScrollRestoration = () => {
|
|
|
11318
11456
|
}
|
|
11319
11457
|
};
|
|
11320
11458
|
|
|
11459
|
+
// Where the document lands is the last arrival's to say. A return waits for its
|
|
11460
|
+
// page (see restoreScrollPositionOnReturn), and an arrival decided meanwhile — a
|
|
11461
|
+
// page that sends the reader elsewhere as it mounts — must not be scrolled to
|
|
11462
|
+
// where the return was going.
|
|
11463
|
+
let arrivalCount = 0;
|
|
11464
|
+
|
|
11321
11465
|
// Nothing to put back is not the same as putting back the top: a page arrived
|
|
11322
11466
|
// at for the first time is startAtTop's business, and this must not step on it.
|
|
11323
11467
|
// Whether there was anything, for a caller who has an answer of its own for the
|
|
11324
11468
|
// page that has never been read.
|
|
11325
11469
|
const restoreScrollPosition = (url) => {
|
|
11470
|
+
arrivalCount++;
|
|
11326
11471
|
const position = positionByUrl.get(new URL(url, window.location.href).href);
|
|
11327
11472
|
if (!position) {
|
|
11328
11473
|
return false;
|
|
@@ -11336,6 +11481,28 @@ const restoreScrollPosition = (url) => {
|
|
|
11336
11481
|
return true;
|
|
11337
11482
|
};
|
|
11338
11483
|
|
|
11484
|
+
// A traversal back to `url`. The offset is written once the page returned to is
|
|
11485
|
+
// rendered: written sooner, the document still holds the page being left and
|
|
11486
|
+
// clamps it to that page's height. It is read at once all the same — a page
|
|
11487
|
+
// left taller than the one arriving is clamped by the render itself, and that
|
|
11488
|
+
// clamp is a scroll recorded under the url already returned to.
|
|
11489
|
+
const restoreScrollPositionOnReturn = (url) => {
|
|
11490
|
+
const arrival = ++arrivalCount;
|
|
11491
|
+
const position = positionByUrl.get(new URL(url, window.location.href).href);
|
|
11492
|
+
if (!position) {
|
|
11493
|
+
return;
|
|
11494
|
+
}
|
|
11495
|
+
whenPageRendered(() => {
|
|
11496
|
+
if (arrival !== arrivalCount) {
|
|
11497
|
+
return;
|
|
11498
|
+
}
|
|
11499
|
+
if (documentHoldCount) {
|
|
11500
|
+
return;
|
|
11501
|
+
}
|
|
11502
|
+
scrollTo(position);
|
|
11503
|
+
});
|
|
11504
|
+
};
|
|
11505
|
+
|
|
11339
11506
|
// A page one arrives at for the first time starts at its top. Only a document
|
|
11340
11507
|
// navigation does that on its own: a pushState creates its entry with whatever
|
|
11341
11508
|
// scroll happened to be there, so without this the page opens at the offset of
|
|
@@ -11361,6 +11528,7 @@ const startAtTop = (url, { from } = {}) => {
|
|
|
11361
11528
|
if (!isArrival(url, { from })) {
|
|
11362
11529
|
return;
|
|
11363
11530
|
}
|
|
11531
|
+
arrivalCount++;
|
|
11364
11532
|
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
11365
11533
|
};
|
|
11366
11534
|
const isArrival = (url, { from }) => {
|
|
@@ -11831,12 +11999,12 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
11831
11999
|
isVisited,
|
|
11832
12000
|
state,
|
|
11833
12001
|
});
|
|
11834
|
-
// Where the document lands, said by what kind of arrival this is.
|
|
11835
|
-
//
|
|
11836
|
-
//
|
|
11837
|
-
//
|
|
11838
|
-
//
|
|
11839
|
-
// it is
|
|
12002
|
+
// Where the document lands, said by what kind of arrival this is. Never
|
|
12003
|
+
// before the picture of a transition is taken, which would be of a page
|
|
12004
|
+
// at its top (see rendering_hold.js), and after the history has been
|
|
12005
|
+
// written, so the entry being left keeps the offset it is at. A return
|
|
12006
|
+
// also waits for the page it returns to: an offset is clamped to whatever
|
|
12007
|
+
// page the document holds when it is written.
|
|
11840
12008
|
//
|
|
11841
12009
|
// A replace gets neither: it is the same place said differently — a param
|
|
11842
12010
|
// settling, a state written — and moving the reader for it would throw
|
|
@@ -11847,7 +12015,7 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
11847
12015
|
if (navigationType === "push") {
|
|
11848
12016
|
whenRenderingResumes(() => startAtTop(url, { from: urlLeft }));
|
|
11849
12017
|
} else if (navigationType === "traverse") {
|
|
11850
|
-
|
|
12018
|
+
restoreScrollPositionOnReturn(url);
|
|
11851
12019
|
}
|
|
11852
12020
|
executeWithCleanup(
|
|
11853
12021
|
() => allResult,
|
|
@@ -12082,11 +12250,12 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
12082
12250
|
const init = () => {
|
|
12083
12251
|
const url = window.location.href;
|
|
12084
12252
|
const stateOnEntry = window.history.state;
|
|
12085
|
-
const state = dropGeneratedIdKeys(stateOnEntry);
|
|
12253
|
+
const state = stateWithNavDepthOnLoad(dropGeneratedIdKeys(stateOnEntry));
|
|
12086
12254
|
if (state !== stateOnEntry) {
|
|
12087
|
-
// The entry itself has to
|
|
12088
|
-
// getDocumentState() reads the entry back,
|
|
12089
|
-
//
|
|
12255
|
+
// The entry itself has to carry it, not just the document state:
|
|
12256
|
+
// getDocumentState() reads the entry back, every state write copies what
|
|
12257
|
+
// it finds there onto the next one, and a back returning here reads its
|
|
12258
|
+
// depth from it.
|
|
12090
12259
|
writeHistoryEntry("replace", state, url);
|
|
12091
12260
|
}
|
|
12092
12261
|
handleRoutingTask(url, {
|
|
@@ -12256,7 +12425,6 @@ const applyRouting = (
|
|
|
12256
12425
|
url,
|
|
12257
12426
|
{
|
|
12258
12427
|
globalAbortSignal,
|
|
12259
|
-
abortSignal,
|
|
12260
12428
|
// state
|
|
12261
12429
|
navigationType,
|
|
12262
12430
|
isVisited,
|
|
@@ -12268,45 +12436,36 @@ const applyRouting = (
|
|
|
12268
12436
|
// likely because code does not uses routing at all
|
|
12269
12437
|
return {};
|
|
12270
12438
|
}
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
routeLoadRequestedMap,
|
|
12276
|
-
activeRouteSet,
|
|
12277
|
-
} = updateRoutes(url, {
|
|
12439
|
+
// The routes' actions follow the address by themselves (see routeAction):
|
|
12440
|
+
// what a navigation leaves to the routing is a reload, the one that asks
|
|
12441
|
+
// them again with the address unchanged.
|
|
12442
|
+
const { reloadSet } = updateRoutes(url, {
|
|
12278
12443
|
navigationType,
|
|
12279
12444
|
isVisited,
|
|
12280
12445
|
// state,
|
|
12281
12446
|
});
|
|
12282
|
-
if (
|
|
12283
|
-
(!loadSet || loadSet.size === 0) &&
|
|
12284
|
-
(!reloadSet || reloadSet.size === 0)
|
|
12285
|
-
) {
|
|
12447
|
+
if (!reloadSet || reloadSet.size === 0) {
|
|
12286
12448
|
return {
|
|
12287
12449
|
allResult: undefined,
|
|
12288
12450
|
requestedResult: undefined,
|
|
12289
|
-
activeRouteSet: new Set(),
|
|
12290
12451
|
};
|
|
12291
12452
|
}
|
|
12292
12453
|
const updateActionsResult = updateActions({
|
|
12293
12454
|
globalAbortSignal,
|
|
12294
|
-
|
|
12295
|
-
|
|
12455
|
+
// Not the navigation's own signal: the next navigation aborts that one, and
|
|
12456
|
+
// an action whose route still matches there would be left aborted — its
|
|
12457
|
+
// params do not change, so nothing would run it again.
|
|
12458
|
+
abortSignal: new AbortController().signal,
|
|
12296
12459
|
rerunSet: reloadSet,
|
|
12297
|
-
abortSignalMap,
|
|
12298
12460
|
reason,
|
|
12299
|
-
isReplace: navigationType === "replace",
|
|
12300
12461
|
});
|
|
12301
12462
|
const { allResult, runningActionSet } = updateActionsResult;
|
|
12302
12463
|
const pendingTaskNameArray = [];
|
|
12303
|
-
for (const
|
|
12304
|
-
|
|
12305
|
-
pendingTaskNameArray.push(`${route.relativeUrl} -> ${routeAction.name}`);
|
|
12306
|
-
}
|
|
12464
|
+
for (const runningAction of runningActionSet) {
|
|
12465
|
+
pendingTaskNameArray.push(runningAction.name);
|
|
12307
12466
|
}
|
|
12308
12467
|
routingWhile(() => allResult, pendingTaskNameArray);
|
|
12309
|
-
return
|
|
12468
|
+
return updateActionsResult;
|
|
12310
12469
|
};
|
|
12311
12470
|
|
|
12312
12471
|
const setupBrowserIntegration =
|
|
@@ -12388,6 +12547,12 @@ const stopLoad = (reason = "stopLoad() called") => {
|
|
|
12388
12547
|
browserIntegration.stop(reason);
|
|
12389
12548
|
}
|
|
12390
12549
|
};
|
|
12550
|
+
/**
|
|
12551
|
+
* Asks again for the page one is on, without reloading the document: the
|
|
12552
|
+
* actions of the routes matching the url run again (see routeAction), one that
|
|
12553
|
+
* failed included — what a "Retry" under a page that could not load calls.
|
|
12554
|
+
* The address, the history and the scroll stay as they are.
|
|
12555
|
+
*/
|
|
12391
12556
|
const reload = browserIntegration.reload;
|
|
12392
12557
|
/**
|
|
12393
12558
|
* Go back to the screen this document came from.
|
|
@@ -12410,11 +12575,13 @@ const reload = browserIntegration.reload;
|
|
|
12410
12575
|
* way a screen closed over a url keeps what was written to the url while it
|
|
12411
12576
|
* was open (see useNavState's leave()).
|
|
12412
12577
|
* @returns {Promise<boolean>|undefined}
|
|
12413
|
-
* When there is something to go back to: a promise resolved
|
|
12414
|
-
* has landed and been applied
|
|
12415
|
-
* where it landed
|
|
12416
|
-
* nowhere
|
|
12417
|
-
* the
|
|
12578
|
+
* When there is something to go back to: a promise resolved with `true`
|
|
12579
|
+
* once the back has landed and been applied — the document url and state
|
|
12580
|
+
* then say where it landed. A back preempted by another navigation, which
|
|
12581
|
+
* lands nowhere, resolves `false` only under the Navigation API integration
|
|
12582
|
+
* (see USE_NAVIGATION_API, off today): the History API does not tell it
|
|
12583
|
+
* apart. `undefined` otherwise — nothing happened, or the fallback took the
|
|
12584
|
+
* current entry's place.
|
|
12418
12585
|
*/
|
|
12419
12586
|
const navBack = browserIntegration.navBack;
|
|
12420
12587
|
const navForward = browserIntegration.navForward;
|
|
@@ -13429,10 +13596,11 @@ const isKeyboardModality = () => keyboardNavigationUsed;
|
|
|
13429
13596
|
{
|
|
13430
13597
|
// We implement :focus and :focus-visible with enriched semantics:
|
|
13431
13598
|
// an element is considered focused not only when it natively has focus, but also
|
|
13432
|
-
// when a
|
|
13433
|
-
//
|
|
13434
|
-
//
|
|
13435
|
-
// while the input is
|
|
13599
|
+
// when it is a control proxy whose real control has focus (the proxy is what is
|
|
13600
|
+
// seen, the focus is dealt to the real control — see control_proxy.js) or when
|
|
13601
|
+
// a controlling element has focus (e.g. a combobox input with aria-controls
|
|
13602
|
+
// pointing to a listbox — the listbox should appear focused while the input is
|
|
13603
|
+
// focused).
|
|
13436
13604
|
//
|
|
13437
13605
|
// We intentionally reuse the native :focus / :focus-visible names rather than
|
|
13438
13606
|
// introducing new navi-specific pseudo-classes (e.g. :-navi-focus). This is a
|
|
@@ -13524,7 +13692,7 @@ const isKeyboardModality = () => keyboardNavigationUsed;
|
|
|
13524
13692
|
|
|
13525
13693
|
// Returns true when el holds focus indirectly — either because a controlling
|
|
13526
13694
|
// element (aria-controls) has focus, or because el is a proxy whose target
|
|
13527
|
-
// is itself controlled by a focused element.
|
|
13695
|
+
// has focus or is itself controlled by a focused element.
|
|
13528
13696
|
const hasIndirectFocus = (el, { requireFocusVisible = false } = {}) => {
|
|
13529
13697
|
// No ring inheritance without a keyboard: an editable target draws its own
|
|
13530
13698
|
// ring on any focus (see isMatchingFocusVisible), but propagating that ring
|
|
@@ -22271,10 +22439,11 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
22271
22439
|
* insists.
|
|
22272
22440
|
*
|
|
22273
22441
|
* So the interaction stays declared and says "refuse" in place of what it does.
|
|
22274
|
-
* The threshold is the same one — a mouse travelling
|
|
22275
|
-
*
|
|
22276
|
-
*
|
|
22277
|
-
*
|
|
22442
|
+
* The threshold is the same one — a mouse travelling a few pixels, a finger
|
|
22443
|
+
* holding still, or travelling those same few pixels inside a
|
|
22444
|
+
* `data-drag-on-contact` — and at the instant the grab would have been
|
|
22445
|
+
* acquired there is none: nothing translates, no copy is made, no release is
|
|
22446
|
+
* answered. `refuse` is that instant.
|
|
22278
22447
|
*
|
|
22279
22448
|
* It walks no axis, so it takes none: a box that travels and a surface that pans
|
|
22280
22449
|
* both step over an element that is refusing, and a gesture starting on a locked
|
|
@@ -25991,8 +26160,9 @@ const createResourceLifecycleManager = () => {
|
|
|
25991
26160
|
}
|
|
25992
26161
|
|
|
25993
26162
|
// scopedMany auto-dependency: only rerun parent singular GET on child POST,
|
|
25994
|
-
//
|
|
25995
|
-
// inside its response
|
|
26163
|
+
// only the GET of the owner the child was posted to, and only when that
|
|
26164
|
+
// GET previously returned the sub-resource embedded inside its response
|
|
26165
|
+
// (detected via action._resultProperties).
|
|
25996
26166
|
// GET_MANY is excluded — a list of parents is not stale just because one
|
|
25997
26167
|
// child item was added to one of them.
|
|
25998
26168
|
scoped_many_effect: {
|
|
@@ -26013,6 +26183,15 @@ const createResourceLifecycleManager = () => {
|
|
|
26013
26183
|
if (parentResource !== resourceScope) {
|
|
26014
26184
|
continue;
|
|
26015
26185
|
}
|
|
26186
|
+
if (
|
|
26187
|
+
!isOwnerReadBy(
|
|
26188
|
+
triggerResourceScope,
|
|
26189
|
+
triggeringAction,
|
|
26190
|
+
actionCandidate,
|
|
26191
|
+
)
|
|
26192
|
+
) {
|
|
26193
|
+
break scoped_many_effect;
|
|
26194
|
+
}
|
|
26016
26195
|
// Only rerun if the last GET response included the embedded sub-resource.
|
|
26017
26196
|
if (
|
|
26018
26197
|
!getActionResultProperties(actionCandidate)?.has(propertyName)
|
|
@@ -26129,6 +26308,27 @@ const isParamSubset = (parentParams, childParams) => {
|
|
|
26129
26308
|
return true;
|
|
26130
26309
|
};
|
|
26131
26310
|
|
|
26311
|
+
// Whether a GET of the owner read the item a scoped child's action was answered
|
|
26312
|
+
// for. The child's value names its owner first ([ownerKey, …], see scopedMany);
|
|
26313
|
+
// the GET's value is the id of the item it read. The key may be a unique key
|
|
26314
|
+
// rather than that id: both lead to the owner's one store of children. Values
|
|
26315
|
+
// are read off the signals: the child's action is still completing, and its
|
|
26316
|
+
// `value` property is only written once that batch ends.
|
|
26317
|
+
const isOwnerReadBy = (childResource, childAction, ownerGetAction) => {
|
|
26318
|
+
const childValue = childAction.valueSignal.peek();
|
|
26319
|
+
if (!Array.isArray(childValue)) {
|
|
26320
|
+
return false;
|
|
26321
|
+
}
|
|
26322
|
+
const [ownerKey] = childValue;
|
|
26323
|
+
const ownerId = ownerGetAction.valueSignal.peek();
|
|
26324
|
+
const { getChildStore } = childResource;
|
|
26325
|
+
if (!getChildStore) {
|
|
26326
|
+
return ownerKey === ownerId;
|
|
26327
|
+
}
|
|
26328
|
+
const childStore = getChildStore(ownerKey);
|
|
26329
|
+
return childStore !== undefined && childStore === getChildStore(ownerId);
|
|
26330
|
+
};
|
|
26331
|
+
|
|
26132
26332
|
const paramScopeWeakSet = createIterableWeakSet();
|
|
26133
26333
|
let paramScopeIdCounter = 0;
|
|
26134
26334
|
const getParamScope = (params) => {
|
|
@@ -34729,11 +34929,13 @@ const bandTheCutMayBeRelaxedTo = (bandBefore, bandAfter, rectAfter) => {
|
|
|
34729
34929
|
* where they are going, at the movement's own pace, each animation at the rate
|
|
34730
34930
|
* that gets it there in that time.
|
|
34731
34931
|
*
|
|
34732
|
-
* Where they visibly stand is computed from the clock THROUGH the easing curve
|
|
34733
|
-
*
|
|
34734
|
-
*
|
|
34735
|
-
*
|
|
34736
|
-
*
|
|
34932
|
+
* Where they visibly stand is computed from the clock THROUGH the easing curve:
|
|
34933
|
+
* a fraction of the way, whatever property carries it — getComputedStyle on
|
|
34934
|
+
* the pseudo-elements gives the animated value of each property, with nothing
|
|
34935
|
+
* saying how far along its keyframes that value is. The rates are handed over
|
|
34936
|
+
* with updatePlaybackRate: in Chromium, the playbackRate setter given a
|
|
34937
|
+
* negative rate puts the pictures at their start at once, and leaves them
|
|
34938
|
+
* there while the clock counts down.
|
|
34737
34939
|
*/
|
|
34738
34940
|
|
|
34739
34941
|
const viewTransitionAnimations = () => {
|
|
@@ -35318,11 +35520,11 @@ const css$12 = /* css */`@property --navi-route-transition-leave {
|
|
|
35318
35520
|
*
|
|
35319
35521
|
* It is a real box, and it must be: what is photographed and clipped IS its
|
|
35320
35522
|
* rectangle. So `display: contents` cannot be used on it — an element with no
|
|
35321
|
-
* box is never captured, the movement plays on nothing
|
|
35322
|
-
* the
|
|
35323
|
-
* so `flex`, `className`, `style` and the rest are there for
|
|
35324
|
-
* application that already has an element holding its pages can mark
|
|
35325
|
-
* with `data-navi-route-transition-area` rather than nesting another.
|
|
35523
|
+
* box is never captured, and the movement then plays on nothing (navi warns
|
|
35524
|
+
* once the pictures are taken). Give it the layout the pages need instead —
|
|
35525
|
+
* it is a `Box`, so `flex`, `className`, `style` and the rest are there for
|
|
35526
|
+
* that. An application that already has an element holding its pages can mark
|
|
35527
|
+
* that one with `data-navi-route-transition-area` rather than nesting another.
|
|
35326
35528
|
*
|
|
35327
35529
|
* @type {import("ignore:preact").FunctionComponent<{ children?: any, [key: string]: any }>}
|
|
35328
35530
|
*/
|
|
@@ -35383,10 +35585,14 @@ const RouteTransitionArea = ({
|
|
|
35383
35585
|
* length of the transition the root carries
|
|
35384
35586
|
* `data-navi-route-transition-type="<type>"` next to
|
|
35385
35587
|
* `data-navi-route-transition="forward"|"back"`, and the application's CSS
|
|
35386
|
-
* defines the movement against the view transition pseudo-elements
|
|
35588
|
+
* defines the movement against the view transition pseudo-elements, for
|
|
35589
|
+
* both groups: `root` when the document travels, `navi-route-transition`
|
|
35590
|
+
* when an area is marked — the root is then unnamed, and a rule written for
|
|
35591
|
+
* `(root)` alone never applies:
|
|
35387
35592
|
*
|
|
35388
35593
|
* :root[data-navi-route-transition-type="spin"][data-navi-route-transition="forward"] {
|
|
35389
|
-
* &::view-transition-new(root)
|
|
35594
|
+
* &::view-transition-new(root),
|
|
35595
|
+
* &::view-transition-new(navi-route-transition) {
|
|
35390
35596
|
* animation-name: my-spin-in;
|
|
35391
35597
|
* }
|
|
35392
35598
|
* }
|
|
@@ -36044,7 +36250,7 @@ const beginTransition = ({
|
|
|
36044
36250
|
// and the movement then plays on the document itself.
|
|
36045
36251
|
const areaElements = document.querySelectorAll(`[${TRANSITION_AREA_ATTRIBUTE}]`);
|
|
36046
36252
|
if (areaElements.length > 1) {
|
|
36047
|
-
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
|
|
36253
|
+
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 route transition while this holds. Mark the one element the pages live in.`);
|
|
36048
36254
|
}
|
|
36049
36255
|
const areaElement = areaElements.length > 0 ? areaElements[0] : null;
|
|
36050
36256
|
// Everything about to be photographed goes deaf to the pointer; the controls
|
|
@@ -36788,8 +36994,9 @@ const css$11 = /* css */`.navi_route_travel {
|
|
|
36788
36994
|
* - how to go to a tab. The default REPLACES the current history entry
|
|
36789
36995
|
* rather than pushing one: a swipe is how one browses a page, not a place one
|
|
36790
36996
|
* aimed at, and three swipes back and forth must not bury the way out of the
|
|
36791
|
-
* page under six entries. A tab pressed
|
|
36792
|
-
*
|
|
36997
|
+
* page under six entries. A tab pressed goes the way its <Link> says —
|
|
36998
|
+
* `<Link replace>` for a row of tabs, so a press and a swipe towards the
|
|
36999
|
+
* same neighbour write the same history.
|
|
36793
37000
|
*
|
|
36794
37001
|
* Every other prop lands on the box itself (`id`, `data-testid`, `className`,
|
|
36795
37002
|
* …), which is how one of these is named: a page can hold several — a row of
|
|
@@ -36804,10 +37011,11 @@ const css$11 = /* css */`.navi_route_travel {
|
|
|
36804
37011
|
* gesture applies its change on release instead of dragging a picture that does
|
|
36805
37012
|
* not exist.
|
|
36806
37013
|
*
|
|
36807
|
-
* While a travel plays, the rest of the page is taken as a picture
|
|
36808
|
-
*
|
|
36809
|
-
*
|
|
36810
|
-
*
|
|
37014
|
+
* While a travel plays, the rest of the page is NOT taken as a picture: the
|
|
37015
|
+
* root's name is dropped for that time (`view-transition-name: none`), so what
|
|
37016
|
+
* stands around the box — a tab row beside it — stays live and answers a press
|
|
37017
|
+
* mid-travel. The application's own rule for the root applies again the moment
|
|
37018
|
+
* the travel is over.
|
|
36811
37019
|
*/
|
|
36812
37020
|
const RouteTravel = ({
|
|
36813
37021
|
routes: routesProp,
|
|
@@ -37996,9 +38204,9 @@ const writeCover = (edge, element, size) => {
|
|
|
37996
38204
|
|
|
37997
38205
|
/**
|
|
37998
38206
|
* Binds an action to a route: it runs when the route matches, with the params
|
|
37999
|
-
* the effect reads off the address,
|
|
38000
|
-
* Its data, its wait and its failure are read by the
|
|
38001
|
-
* `useAsyncData`.
|
|
38207
|
+
* the effect reads off the address, runs again on a `reload()`, and is aborted
|
|
38208
|
+
* when the route is left. Its data, its wait and its failure are read by the
|
|
38209
|
+
* page through `useAsyncData`.
|
|
38002
38210
|
*
|
|
38003
38211
|
* A page's CODE is declared the same way — the import is one more thing the
|
|
38004
38212
|
* address asks for, started with the data rather than after it:
|
|
@@ -38042,18 +38250,21 @@ const routeAction = (
|
|
|
38042
38250
|
? routes[0].matchingSignal
|
|
38043
38251
|
: anyMatchingRouteSignal(routes);
|
|
38044
38252
|
const readParams = paramsEffect || (() => true);
|
|
38045
|
-
const
|
|
38046
|
-
|
|
38047
|
-
()
|
|
38048
|
-
|
|
38049
|
-
|
|
38050
|
-
|
|
38051
|
-
|
|
38052
|
-
|
|
38053
|
-
|
|
38054
|
-
|
|
38055
|
-
|
|
38056
|
-
|
|
38253
|
+
const readParamsAsked = () => {
|
|
38254
|
+
const matching = routeMatchingSignal.value;
|
|
38255
|
+
const params = readParams();
|
|
38256
|
+
if (!matching) {
|
|
38257
|
+
return null;
|
|
38258
|
+
}
|
|
38259
|
+
return params;
|
|
38260
|
+
};
|
|
38261
|
+
const actionBoundToRoute = actionRunEffect(action, readParamsAsked, options);
|
|
38262
|
+
for (const route of routes) {
|
|
38263
|
+
// Asked of the route's params as they are, like the effect asks them.
|
|
38264
|
+
registerRouteReload(route, () =>
|
|
38265
|
+
untracked(readParamsAsked) ? actionBoundToRoute : null,
|
|
38266
|
+
);
|
|
38267
|
+
}
|
|
38057
38268
|
if (prefetch) {
|
|
38058
38269
|
for (const route of routes) {
|
|
38059
38270
|
if (paramsEffect) {
|
|
@@ -40038,10 +40249,11 @@ const flushSyncRendering = (fn) => {
|
|
|
40038
40249
|
* without the content, and the browser builds it in an idle moment after
|
|
40039
40250
|
* load — so by the time anyone clicks, it is usually already there.
|
|
40040
40251
|
*
|
|
40041
|
-
* "while-opened" is the opposite end: content
|
|
40042
|
-
*
|
|
40043
|
-
*
|
|
40044
|
-
*
|
|
40252
|
+
* "while-opened" is the opposite end: content rebuilt from scratch every
|
|
40253
|
+
* time, a form whose fresh state is its initial state. What kept content
|
|
40254
|
+
* carries into the next opening is an unsent edit — an untouched field
|
|
40255
|
+
* follows a new `defaultValue` on its own (see followDefaultValue in
|
|
40256
|
+
* ui_state_controller.js) — and this is what throws that edit away.
|
|
40045
40257
|
*
|
|
40046
40258
|
* On top of whichever value is picked, intent on the anchor warms the content:
|
|
40047
40259
|
* a pointer entering the popup's anchor, or focus landing in it, builds the
|
|
@@ -40075,9 +40287,11 @@ const flushSyncRendering = (fn) => {
|
|
|
40075
40287
|
* click coming after the release, the browser taking the gesture
|
|
40076
40288
|
* (`pointercancel`), or the next press starting, before any of its own
|
|
40077
40289
|
* handlers run: one press at a time holds a content. A keyboard opening builds
|
|
40078
|
-
* at open time. A popup with an `onOpen` is
|
|
40079
|
-
* before the content is built (see
|
|
40080
|
-
* itself from what it writes must
|
|
40290
|
+
* at open time. A "while-opened" popup with an `onOpen` is not warmed by the
|
|
40291
|
+
* press: that callback runs before the content is built (see
|
|
40292
|
+
* open_controller.js), and a content seeding itself from what it writes must
|
|
40293
|
+
* be built after it. Hover and focus warming, for the other values, builds
|
|
40294
|
+
* ahead of `onOpen` all the same.
|
|
40081
40295
|
*/
|
|
40082
40296
|
|
|
40083
40297
|
|
|
@@ -50841,12 +51055,13 @@ const positionOfCurrentIndicator = (currentIndicator, vertical) => {
|
|
|
50841
51055
|
* does so by being NAMED, which is all the browser needs: any change played as
|
|
50842
51056
|
* a view transition animates it on the same clock as everything else in that
|
|
50843
51057
|
* transition. Inside a `RouteTravel` that means it follows the pages, and the
|
|
50844
|
-
* thumb dragging them, without either of them being told about the other.
|
|
50845
|
-
*
|
|
50846
|
-
*
|
|
50847
|
-
* arrives with its row.
|
|
50848
|
-
*
|
|
50849
|
-
*
|
|
51058
|
+
* thumb dragging them, without either of them being told about the other.
|
|
51059
|
+
* During a route transition or a route travel it keeps the name only for a
|
|
51060
|
+
* movement between two tabs of the row: when the row is on one side only,
|
|
51061
|
+
* the bar leaves or arrives with its row. Any other view transition finds it
|
|
51062
|
+
* named, a picture of its own. For a nav made of slides (`slideContainer`)
|
|
51063
|
+
* the bar is one element for the whole row, and it reads the travel the
|
|
51064
|
+
* container publishes.
|
|
50850
51065
|
* @param {string} [props.slideContainer] - the id of a `<SlideContainer>` these
|
|
50851
51066
|
* tabs are about: each one says which slide it is (`<Link slide="…">`), the
|
|
50852
51067
|
* container says which one is on screen, and pressing a tab travels there.
|
|
@@ -55358,7 +55573,8 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
55358
55573
|
* `command` runs at once — the popup it sits in closes over the run, which
|
|
55359
55574
|
* goes on behind. A failure then has no popup to be read in: `resetOnError`
|
|
55360
55575
|
* puts the button back and the error callout is drawn on what surrounds the
|
|
55361
|
-
* closed popup (see
|
|
55576
|
+
* closed popup (see
|
|
55577
|
+
* docs/popup_open.md#closing-when-a-button-also-runs-an-action).
|
|
55362
55578
|
* @param {boolean} [actionAbortable] The person waiting may give up on this
|
|
55363
55579
|
* button's action: closing the popup the run holds calls it off and goes
|
|
55364
55580
|
* through, instead of being refused. For a run whose answer may never come —
|
|
@@ -65286,7 +65502,7 @@ const css$E = /* css */`
|
|
|
65286
65502
|
the length of the movement, and the picture of the lifted node comes out
|
|
65287
65503
|
empty (Chrome 153, reproduced in a bare page). The price is a page frozen
|
|
65288
65504
|
and unpressable while the movement plays: under a modal wall at the
|
|
65289
|
-
opening, and for
|
|
65505
|
+
opening, and for the popup's --popup-animation-duration at the closing. */
|
|
65290
65506
|
:root[data-navi-popup-lift] {
|
|
65291
65507
|
/* The popup's own pace, published on the root by popup_lift.js because the
|
|
65292
65508
|
::view-transition tree hangs off it and inherits from nowhere else. */
|
|
@@ -65630,16 +65846,21 @@ const css$E = /* css */`
|
|
|
65630
65846
|
* @param {boolean|"last-resort"|"restore"} [props.autoFocus="last-resort"] -
|
|
65631
65847
|
* Where the keyboard goes when this dialog opens — one rung of the ladder in
|
|
65632
65848
|
* `docs/autofocus.md`, which is what to read for the whole of it.
|
|
65633
|
-
* - `true` — the dialog element itself takes the keyboard
|
|
65634
|
-
*
|
|
65635
|
-
*
|
|
65849
|
+
* - `true` — the dialog element itself takes the keyboard rather than the
|
|
65850
|
+
* first focusable it holds; something inside that asks by name
|
|
65851
|
+
* (`autoFocus`) still comes first. For a dialog whose content is READ
|
|
65852
|
+
* before it is filled: the focus starts at the top of the reading
|
|
65853
|
+
* order and no virtual keyboard rises over it.
|
|
65636
65854
|
* - `"last-resort"` — the dialog takes the keyboard only if it holds nothing
|
|
65637
65855
|
* focusable of its own.
|
|
65638
65856
|
* - `"restore"` — the dialog stays out of the opening focus chain unless it
|
|
65639
65857
|
* held focus when it closed.
|
|
65640
|
-
* - `false` — the dialog
|
|
65641
|
-
*
|
|
65642
|
-
*
|
|
65858
|
+
* - `false` — the dialog says nothing, and the opening still hands out the
|
|
65859
|
+
* focus: a `<dialog>` is focusable and the first focusable of its own
|
|
65860
|
+
* subtree, so it takes the keyboard as with `true`. Where the first
|
|
65861
|
+
* focusable is skipped (a touch device, below), a last resort inside is
|
|
65862
|
+
* tried instead, and nothing there leaves the keyboard where it was.
|
|
65863
|
+
* Unlike `Popover`'s `false`, the transfer runs.
|
|
65643
65864
|
* Wherever the keyboard is a virtual one (a touch device), the surface is
|
|
65644
65865
|
* already what one arrives on: a popup is read before it is reached there, so
|
|
65645
65866
|
* the focus only leaves it for something that asked by name (`autoFocus` on
|
|
@@ -65673,9 +65894,9 @@ const css$E = /* css */`
|
|
|
65673
65894
|
* is "new" or "edit X" depending on the press must know which one it is
|
|
65674
65895
|
* before what it holds is rendered.
|
|
65675
65896
|
* @param {(event: Event) => void} [props.onClose] - Called when the dialog
|
|
65676
|
-
* actually closes — not preventable
|
|
65677
|
-
*
|
|
65678
|
-
*
|
|
65897
|
+
* actually closes — not preventable: it is the close happening, not a
|
|
65898
|
+
* request. What refuses a close is navi's: a control inside still
|
|
65899
|
+
* running its action (see popup_busy.js for the runs that let it through).
|
|
65679
65900
|
* @param {boolean|string|{id?: string, type?: "push"|"replace"}} [props.navState] -
|
|
65680
65901
|
* Keeps the open state in the history entry, so a screen left and come back
|
|
65681
65902
|
* to finds this popup as it was — open, and without an entrance playing: it
|
|
@@ -65701,8 +65922,9 @@ const css$E = /* css */`
|
|
|
65701
65922
|
* `"idle"` builds them in a browser idle moment after load — "always" minus
|
|
65702
65923
|
* the cost on the critical render. `"while-opened"` throws them away once
|
|
65703
65924
|
* the popup has finished closing, for content whose fresh state is its
|
|
65704
|
-
* initial state: an
|
|
65705
|
-
*
|
|
65925
|
+
* initial state: an edit left unsent at the close does not come back at
|
|
65926
|
+
* the next opening (an untouched field needs none of this to show a new
|
|
65927
|
+
* `defaultValue` — it follows one). Intent on the anchor (pointer
|
|
65706
65928
|
* entering it, focus landing in it) builds the content ahead of the click;
|
|
65707
65929
|
* under `"while-opened"`, only a press on what opens the popup does (a
|
|
65708
65930
|
* `--navi-open` button, a picker's trigger), never with an `onOpen`, and a
|
|
@@ -66391,10 +66613,10 @@ const useDialogProps = props => {
|
|
|
66391
66613
|
}
|
|
66392
66614
|
|
|
66393
66615
|
// Suppressed until committed below — same @starting-style-avoidance
|
|
66394
|
-
// reasoning as popover.jsx's own openEffect (see
|
|
66395
|
-
// though Dialog never needs to measure/flip anything: it
|
|
66396
|
-
// genuinely rendered "closed" frame to transition from, not a
|
|
66397
|
-
// straight from not-shown to aria-expanded="true".
|
|
66616
|
+
// reasoning as popover.jsx's own openEffect (see popup_css.js's top
|
|
66617
|
+
// comment), even though Dialog never needs to measure/flip anything: it
|
|
66618
|
+
// still needs a genuinely rendered "closed" frame to transition from, not a
|
|
66619
|
+
// jump straight from not-shown to aria-expanded="true".
|
|
66398
66620
|
dialogEl.style.transitionProperty = "none";
|
|
66399
66621
|
if (backdropEl) {
|
|
66400
66622
|
disarmBackdropHideRef.current?.();
|
|
@@ -67533,10 +67755,11 @@ const css$D = /* css */`
|
|
|
67533
67755
|
* @param {boolean|"last-resort"|"restore"} [props.autoFocus="last-resort"] -
|
|
67534
67756
|
* Where the keyboard goes when this popover opens — one rung of the ladder in
|
|
67535
67757
|
* `docs/autofocus.md`, which is what to read for the whole of it.
|
|
67536
|
-
* - `true` — the popover element itself takes the keyboard
|
|
67537
|
-
*
|
|
67538
|
-
*
|
|
67539
|
-
* it
|
|
67758
|
+
* - `true` — the popover element itself takes the keyboard rather than the
|
|
67759
|
+
* first focusable it holds; something inside that asks by name
|
|
67760
|
+
* (`autoFocus`) still comes first. For a popover whose content is READ
|
|
67761
|
+
* before it is filled: the focus starts at the top of the reading
|
|
67762
|
+
* order and no virtual keyboard rises over it.
|
|
67540
67763
|
* - `"last-resort"` — the popover takes the keyboard only if it holds nothing
|
|
67541
67764
|
* focusable of its own.
|
|
67542
67765
|
* - `"restore"` — the popover stays out of the opening focus chain unless it
|
|
@@ -67577,9 +67800,9 @@ const css$D = /* css */`
|
|
|
67577
67800
|
* is "new" or "edit X" depending on the press must know which one it is
|
|
67578
67801
|
* before what it holds is rendered.
|
|
67579
67802
|
* @param {(event: Event) => void} [props.onClose] - Called when the popover
|
|
67580
|
-
* actually closes — not preventable
|
|
67581
|
-
*
|
|
67582
|
-
*
|
|
67803
|
+
* actually closes — not preventable: it is the close happening, not a
|
|
67804
|
+
* request. What refuses a close is navi's: a control inside still
|
|
67805
|
+
* running its action (see popup_busy.js for the runs that let it through).
|
|
67583
67806
|
* @param {boolean|string|{id?: string, type?: "push"|"replace"}} [props.navState] -
|
|
67584
67807
|
* Keeps the open state in the history entry, so a screen left and come back
|
|
67585
67808
|
* to finds this popup as it was — open, and without an entrance playing: it
|
|
@@ -67603,8 +67826,9 @@ const css$D = /* css */`
|
|
|
67603
67826
|
* `"idle"` builds them in a browser idle moment after load — "always" minus
|
|
67604
67827
|
* the cost on the critical render. `"while-opened"` throws them away once
|
|
67605
67828
|
* the popup has finished closing, for content whose fresh state is its
|
|
67606
|
-
* initial state: an
|
|
67607
|
-
*
|
|
67829
|
+
* initial state: an edit left unsent at the close does not come back at
|
|
67830
|
+
* the next opening (an untouched field needs none of this to show a new
|
|
67831
|
+
* `defaultValue` — it follows one). Intent on the anchor (pointer
|
|
67608
67832
|
* entering it, focus landing in it) builds the content ahead of the click;
|
|
67609
67833
|
* under `"while-opened"`, only a press on what opens the popup does (a
|
|
67610
67834
|
* `--navi-open` button, a picker's trigger), never with an `onOpen`, and a
|
|
@@ -68037,7 +68261,7 @@ const usePopoverProps = props => {
|
|
|
68037
68261
|
});
|
|
68038
68262
|
|
|
68039
68263
|
// Suppressed until the popover is actually measured/positioned below —
|
|
68040
|
-
// see
|
|
68264
|
+
// see popup_css.js's top comment for why @starting-style can't drive the
|
|
68041
68265
|
// opening transition (it needs the popover's actual resting position
|
|
68042
68266
|
// already in place, which requires a layout box that only exists once
|
|
68043
68267
|
// shown).
|
|
@@ -68385,7 +68609,7 @@ const usePopoverProps = props => {
|
|
|
68385
68609
|
// frame set up above as a real rendered state (the reflow), re-enables
|
|
68386
68610
|
// transitions, flips aria-expanded="true" (only then does the CSS
|
|
68387
68611
|
// transition play, from that just-committed frame to the open one,
|
|
68388
|
-
// with no @starting-style involved at all — see
|
|
68612
|
+
// with no @starting-style involved at all — see popup_css.js's top
|
|
68389
68613
|
// comment), suppresses pointer events until it settles, and transfers
|
|
68390
68614
|
// focus in. Inlined rather than a standalone function since it only
|
|
68391
68615
|
// has this one call site.
|
|
@@ -68873,8 +69097,9 @@ const css$C = /* css */`@layer navi {
|
|
|
68873
69097
|
* `"idle"` builds them in a browser idle moment after load — `"always"`
|
|
68874
69098
|
* minus the cost on the critical render.
|
|
68875
69099
|
* `"while-opened"` throws them away once the popup has finished closing, for
|
|
68876
|
-
* content whose fresh state is its initial state: an
|
|
68877
|
-
*
|
|
69100
|
+
* content whose fresh state is its initial state: an edit left unsent at
|
|
69101
|
+
* the close does not come back at the next opening (an untouched field
|
|
69102
|
+
* needs none of this to show a new `defaultValue` — it follows one).
|
|
68878
69103
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
68879
69104
|
*/
|
|
68880
69105
|
const Popup = props => {
|
|
@@ -69173,8 +69398,9 @@ const PickerCustom = props => {
|
|
|
69173
69398
|
open,
|
|
69174
69399
|
defaultOpen,
|
|
69175
69400
|
// What Escape means for this picker. "cancel" (the default) puts back the
|
|
69176
|
-
// value the picker had at open and, for a dialog
|
|
69177
|
-
// so everything written to the url while it was open
|
|
69401
|
+
// value the picker had at open and, for a dialog with an explicit id, goes
|
|
69402
|
+
// back in history — so everything written to the url while it was open
|
|
69403
|
+
// goes back too (see pickerNavType below).
|
|
69178
69404
|
// "close" makes Escape say the same thing as clicking outside: keep what
|
|
69179
69405
|
// was chosen, close the popup.
|
|
69180
69406
|
escapeEffect = "cancel",
|
|
@@ -69842,9 +70068,10 @@ const PickerContentInsidePopup = props => {
|
|
|
69842
70068
|
// itself, and this one does not. Popover ignores it, same as Dialog ignores
|
|
69843
70069
|
// marginWithAnchor.
|
|
69844
70070
|
dockedOnSmallTouchScreen,
|
|
69845
|
-
// Same again: the dialog takes the trigger's box as a floor (
|
|
69846
|
-
//
|
|
69847
|
-
//
|
|
70071
|
+
// Same again: the dialog takes the trigger's box as a floor (Dialog's own
|
|
70072
|
+
// `sizeFromAnchor`) and, unless a dialogMaxWidth says otherwise, as a
|
|
70073
|
+
// ceiling (see maxWidth below) — what keeps a card its own width once
|
|
70074
|
+
// lifted.
|
|
69848
70075
|
dialogSizeFromAnchor,
|
|
69849
70076
|
// The caller's word on the popup's own box. Written on the popup element,
|
|
69850
70077
|
// where Popover/Dialog map them to their own vars, rather than as vars on
|
|
@@ -79824,6 +80051,7 @@ const PickerFirstResolver = props => {
|
|
|
79824
80051
|
* dialogExpandX?: boolean,
|
|
79825
80052
|
* dialogExpandY?: boolean,
|
|
79826
80053
|
* dialogSizeFromAnchor?: boolean,
|
|
80054
|
+
* dockedOnSmallTouchScreen?: boolean | "top" | "bottom",
|
|
79827
80055
|
* marginWithContainer?: number | string,
|
|
79828
80056
|
* anchor?: import("ignore:preact").RefObject<HTMLElement> | HTMLElement,
|
|
79829
80057
|
* escapeEffect?: "cancel" | "close",
|
|
@@ -80202,14 +80430,23 @@ const PickerFirstResolver = props => {
|
|
|
80202
80430
|
* about it changes. `dialogMaxWidth="var(--anchor-width)"` says the same
|
|
80203
80431
|
* ceiling by hand: the popup props are written on the dialog element, where
|
|
80204
80432
|
* that variable lives.
|
|
80433
|
+
* @param {boolean|"top"|"bottom"} [dockedOnSmallTouchScreen] Dialog mode,
|
|
80434
|
+
* Dialog's own: on a small touch screen the dialog becomes a sheet docked
|
|
80435
|
+
* full width to one edge — `true` or `"top"` the top edge, out of the
|
|
80436
|
+
* virtual keyboard's way; `"bottom"` the bottom one, for a sheet that is
|
|
80437
|
+
* read and tapped. Anywhere else it stays the centered box. Ignored in
|
|
80438
|
+
* popover mode.
|
|
80205
80439
|
* @param {"cancel"|"close"} [escapeEffect="cancel"] What Escape does to an open
|
|
80206
80440
|
* picker. "cancel" puts back the value the picker had at open, and a dialog
|
|
80207
|
-
* picker
|
|
80208
|
-
*
|
|
80209
|
-
*
|
|
80210
|
-
*
|
|
80211
|
-
*
|
|
80212
|
-
*
|
|
80441
|
+
* picker with an explicit `id` (its opening being an entry of its own)
|
|
80442
|
+
* also goes back in history — so anything written to the url while it was
|
|
80443
|
+
* open (a route `stateSignal`, a search param) goes back with it. "close"
|
|
80444
|
+
* makes Escape say what clicking outside says: keep what was chosen,
|
|
80445
|
+
* close — a last resort, see
|
|
80446
|
+
* docs/popup_open.md#escape-cancels-the-other-gestures-keep for why Escape
|
|
80447
|
+
* should go on meaning cancel, and
|
|
80448
|
+
* docs/popup_open.md#what-cancel-actually-undoes for what a cancel puts
|
|
80449
|
+
* back.
|
|
80213
80450
|
* @param {import("ignore:preact").RefObject<HTMLElement>|HTMLElement} [anchor] What
|
|
80214
80451
|
* the popup hangs off, when that is not the picker itself: a picker whose
|
|
80215
80452
|
* trigger is one piece of a bigger control (the chevron half of a split
|
|
@@ -80264,7 +80501,9 @@ const PickerFirstResolver = props => {
|
|
|
80264
80501
|
* positioned ancestor for `popupLayer="local"`). Caps the popup's size as
|
|
80265
80502
|
* well as its placement, so what an expanded dialog leaves visible around
|
|
80266
80503
|
* itself is set here. Defaults to `popoverSpacing` in popover mode, and to
|
|
80267
|
-
* Dialog's own
|
|
80504
|
+
* Dialog's own in dialog mode: `3appw` in the top layer, `3cqw` under
|
|
80505
|
+
* `popupLayer="local"`, 0 for a dialog docked by
|
|
80506
|
+
* `dockedOnSmallTouchScreen`.
|
|
80268
80507
|
* @param {string} [popupTestId] The `data-testid` of the popup element — the
|
|
80269
80508
|
* popover or dialog the children open in. A `data-testid` on the picker
|
|
80270
80509
|
* itself names the trigger (see docs/testid.md); this one is for a test that
|