@jsenv/navi 0.29.365 → 0.29.367
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 +270 -18
- package/dist/dev/jsenv_navi.js.map +5 -5
- package/dist/jsenv_navi.js +228 -18
- package/dist/jsenv_navi.js.map +5 -5
- package/docs/popup_lift.md +9 -0
- package/docs/popup_open.md +15 -2
- package/docs/route_transitions.md +61 -15
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -28872,6 +28872,8 @@ const visitedUrlsSignal = browserIntegration.visitedUrlsSignal;
|
|
|
28872
28872
|
browserIntegration.handleActionTask;
|
|
28873
28873
|
|
|
28874
28874
|
const idUsageMap = new Map();
|
|
28875
|
+
// Keys found in the entry with nobody to claim them, already reported.
|
|
28876
|
+
const orphanKeysReported = new Set();
|
|
28875
28877
|
const useNavStateWithWarnings = (id, options) => {
|
|
28876
28878
|
const idRef = useRef(undefined);
|
|
28877
28879
|
if (idRef.current !== id) {
|
|
@@ -28895,6 +28897,15 @@ Consider using unique IDs for each component instance.`,
|
|
|
28895
28897
|
}
|
|
28896
28898
|
|
|
28897
28899
|
useEffect(() => {
|
|
28900
|
+
// Registered here as well as in the render above: preact/compat's
|
|
28901
|
+
// Suspense parks a subtree by running every hook cleanup in it, and the
|
|
28902
|
+
// render resuming it carries the same id.
|
|
28903
|
+
if (!idUsageMap.has(id)) {
|
|
28904
|
+
idUsageMap.set(id, {
|
|
28905
|
+
stackTrace: new Error().stack,
|
|
28906
|
+
});
|
|
28907
|
+
}
|
|
28908
|
+
warnAboutOrphanGeneratedKeys();
|
|
28898
28909
|
return () => {
|
|
28899
28910
|
idUsageMap.delete(id);
|
|
28900
28911
|
};
|
|
@@ -28903,6 +28914,31 @@ Consider using unique IDs for each component instance.`,
|
|
|
28903
28914
|
return useNavStateBasic(id, options);
|
|
28904
28915
|
};
|
|
28905
28916
|
|
|
28917
|
+
// A generated id (preact's useId()) names one mount. State written under one
|
|
28918
|
+
// outlives that mount in the history entry — a page left with a popup open —
|
|
28919
|
+
// and the mount coming back to the entry generates another id: the state is
|
|
28920
|
+
// there, and nothing reads it. Reported when a component mounting on the entry
|
|
28921
|
+
// finds such a key, the moment someone expected the state back.
|
|
28922
|
+
const warnAboutOrphanGeneratedKeys = () => {
|
|
28923
|
+
const state = browserIntegration.getDocumentState();
|
|
28924
|
+
if (!state) {
|
|
28925
|
+
return;
|
|
28926
|
+
}
|
|
28927
|
+
for (const key of Object.keys(state)) {
|
|
28928
|
+
if (
|
|
28929
|
+
!isLikelyPreactGeneratedId(key) ||
|
|
28930
|
+
idUsageMap.has(key) ||
|
|
28931
|
+
orphanKeysReported.has(key)
|
|
28932
|
+
) {
|
|
28933
|
+
continue;
|
|
28934
|
+
}
|
|
28935
|
+
orphanKeysReported.add(key);
|
|
28936
|
+
console.warn(
|
|
28937
|
+
`useNavState: this history entry holds "${key}", written by a component whose id was generated (preact's useId()) and that is not mounted anymore — a Picker without an id, a popup with navState and no id. A generated id names one mount, so nothing will read that state again: a popup open when this screen was left comes back closed. Give the component a stable id if it was meant to be found as it was.`,
|
|
28938
|
+
);
|
|
28939
|
+
}
|
|
28940
|
+
};
|
|
28941
|
+
|
|
28906
28942
|
const NO_OP = () => {};
|
|
28907
28943
|
const NO_ID_GIVEN = [undefined, NO_OP, NO_OP];
|
|
28908
28944
|
// What the computed below answers for a key the document state does not hold:
|
|
@@ -30591,6 +30627,9 @@ const TRANSITION_TARGET_ATTRIBUTE = "data-navi-route-transition-target";
|
|
|
30591
30627
|
// link being pressed (see <Link routeTransition>), or handed to navTo(). It answers
|
|
30592
30628
|
// for that navigation and for no other — the next one is back to the relations.
|
|
30593
30629
|
const TRANSITION_REQUEST_ATTRIBUTE = "data-navi-route-transition-request";
|
|
30630
|
+
// What a history entry remembers of the crossing that created it, in the
|
|
30631
|
+
// entry's own state: `{ from, type, direction, duration }` (see recordCrossing).
|
|
30632
|
+
const CROSSING_STATE_KEY = "jsenv_route_transition";
|
|
30594
30633
|
const AREA_NAME = "navi-route-transition";
|
|
30595
30634
|
// The pictures carrying the movement, among everything else it takes along —
|
|
30596
30635
|
// the pages', or the document's own when the pages ARE the document (see
|
|
@@ -31139,6 +31178,106 @@ const normalizeRequest = transition => {
|
|
|
31139
31178
|
};
|
|
31140
31179
|
};
|
|
31141
31180
|
|
|
31181
|
+
/**
|
|
31182
|
+
* What a TRAVERSAL asks for: the crossing it retraces.
|
|
31183
|
+
*
|
|
31184
|
+
* The entry a push creates remembers the crossing that created it (see
|
|
31185
|
+
* recordCrossing). A back onto the page that crossing came from undoes it —
|
|
31186
|
+
* the same movement, the other way — and a forward onto an entry whose
|
|
31187
|
+
* crossing came from the page being left plays it again as it was. Both
|
|
31188
|
+
* entries are read because each side is the only one that knows its case: the
|
|
31189
|
+
* entry being LEFT says how it was reached (a back), the entry being REACHED
|
|
31190
|
+
* says how it was reached (a forward).
|
|
31191
|
+
*
|
|
31192
|
+
* Both can be true at once — A, B, A again: leaving the second A for B is the
|
|
31193
|
+
* way back of A → B and the way in of B → A. The entries' depths in the stack
|
|
31194
|
+
* tell them apart; without a depth on both, a back is assumed, the traversal
|
|
31195
|
+
* by far the most often made.
|
|
31196
|
+
*
|
|
31197
|
+
* Answers in the shape of readNavigationRequest, with every field said, so
|
|
31198
|
+
* that the relations have nothing left to answer for — and marked as a replay,
|
|
31199
|
+
* because one relation still outranks it: the one written for the exact way
|
|
31200
|
+
* travelled (see the watcher's onMove).
|
|
31201
|
+
*/
|
|
31202
|
+
const readTraversalReplay = ({
|
|
31203
|
+
url,
|
|
31204
|
+
state
|
|
31205
|
+
}, {
|
|
31206
|
+
fromUrl,
|
|
31207
|
+
fromState
|
|
31208
|
+
}) => {
|
|
31209
|
+
const to = absoluteUrl(url);
|
|
31210
|
+
const from = absoluteUrl(fromUrl);
|
|
31211
|
+
if (!to || !from) {
|
|
31212
|
+
return null;
|
|
31213
|
+
}
|
|
31214
|
+
const crossingIn = crossingRecordedOn(state);
|
|
31215
|
+
const crossingOut = crossingRecordedOn(fromState);
|
|
31216
|
+
const isForward = crossingIn !== null && crossingIn.from === from;
|
|
31217
|
+
const isBack = crossingOut !== null && crossingOut.from === to;
|
|
31218
|
+
if (isForward && isBack) {
|
|
31219
|
+
const depthIn = navDepthOf(state);
|
|
31220
|
+
const depthOut = navDepthOf(fromState);
|
|
31221
|
+
if (depthIn !== undefined && depthOut !== undefined && depthIn > depthOut) {
|
|
31222
|
+
return replayOf(crossingIn);
|
|
31223
|
+
}
|
|
31224
|
+
return replayOf(reverseCrossing(crossingOut));
|
|
31225
|
+
}
|
|
31226
|
+
if (isForward) {
|
|
31227
|
+
return replayOf(crossingIn);
|
|
31228
|
+
}
|
|
31229
|
+
if (isBack) {
|
|
31230
|
+
return replayOf(reverseCrossing(crossingOut));
|
|
31231
|
+
}
|
|
31232
|
+
return null;
|
|
31233
|
+
};
|
|
31234
|
+
const crossingRecordedOn = state => {
|
|
31235
|
+
if (!state) {
|
|
31236
|
+
return null;
|
|
31237
|
+
}
|
|
31238
|
+
const crossing = state[CROSSING_STATE_KEY];
|
|
31239
|
+
if (!crossing || typeof crossing.from !== "string") {
|
|
31240
|
+
return null;
|
|
31241
|
+
}
|
|
31242
|
+
return crossing;
|
|
31243
|
+
};
|
|
31244
|
+
const navDepthOf = state => {
|
|
31245
|
+
if (state && typeof state[NAV_DEPTH_STATE_KEY] === "number") {
|
|
31246
|
+
return state[NAV_DEPTH_STATE_KEY];
|
|
31247
|
+
}
|
|
31248
|
+
return undefined;
|
|
31249
|
+
};
|
|
31250
|
+
const reverseCrossing = crossing => {
|
|
31251
|
+
return {
|
|
31252
|
+
...crossing,
|
|
31253
|
+
direction: reverseDirection(crossing.direction)
|
|
31254
|
+
};
|
|
31255
|
+
};
|
|
31256
|
+
|
|
31257
|
+
// A direction that is neither ("" — a default has none) stays what it is.
|
|
31258
|
+
const reverseDirection = direction => {
|
|
31259
|
+
if (direction === "forward") {
|
|
31260
|
+
return "back";
|
|
31261
|
+
}
|
|
31262
|
+
if (direction === "back") {
|
|
31263
|
+
return "forward";
|
|
31264
|
+
}
|
|
31265
|
+
return direction;
|
|
31266
|
+
};
|
|
31267
|
+
const replayOf = ({
|
|
31268
|
+
type,
|
|
31269
|
+
direction,
|
|
31270
|
+
duration
|
|
31271
|
+
}) => {
|
|
31272
|
+
return {
|
|
31273
|
+
type,
|
|
31274
|
+
typeSaid: true,
|
|
31275
|
+
duration,
|
|
31276
|
+
direction,
|
|
31277
|
+
replay: true
|
|
31278
|
+
};
|
|
31279
|
+
};
|
|
31280
|
+
|
|
31142
31281
|
// The request first, field by field, then what was defined for this pair (or
|
|
31143
31282
|
// for everything). Written as one function because both ends of the file
|
|
31144
31283
|
// resolve the same way: the one that knows the pair, and the one that only
|
|
@@ -31214,7 +31353,14 @@ const rebuildWatcher = () => {
|
|
|
31214
31353
|
const fromPage = fromIndex === -1 ? null : pages[fromIndex];
|
|
31215
31354
|
const toPage = index === -1 ? null : pages[index];
|
|
31216
31355
|
const found = findRelation(fromPage, toPage);
|
|
31217
|
-
|
|
31356
|
+
// A traversal retraces its crossing over everything deduced here — the
|
|
31357
|
+
// reverse of a pair, a page from anywhere — and not over a relation
|
|
31358
|
+
// written for this exact way: that line is the author's one tool for
|
|
31359
|
+
// breaking reciprocity, and the back button is the way back it has to
|
|
31360
|
+
// reach. A request made by a link or a navTo() is never dropped: it is
|
|
31361
|
+
// about this one crossing, and a written relation is about every one.
|
|
31362
|
+
const request = navigationRequest && navigationRequest.replay && found && found.written ? null : navigationRequest;
|
|
31363
|
+
if (!found && !request) {
|
|
31218
31364
|
// No relation says anything about these two and this navigation asked
|
|
31219
31365
|
// for nothing: they are side by side, and silence is the fact — not a
|
|
31220
31366
|
// missing case.
|
|
@@ -31223,12 +31369,15 @@ const rebuildWatcher = () => {
|
|
|
31223
31369
|
const {
|
|
31224
31370
|
type,
|
|
31225
31371
|
duration
|
|
31226
|
-
} = resolveTransition(
|
|
31372
|
+
} = resolveTransition(request, found ? found.relation : null);
|
|
31227
31373
|
if (type === "none") {
|
|
31228
31374
|
// Silence said out loud: this way of the pair was written to play
|
|
31229
31375
|
// nothing — or this one navigation asked for nothing — where the reverse
|
|
31230
31376
|
// of the other way, or the default, would have played.
|
|
31231
31377
|
navigationAnimated = true;
|
|
31378
|
+
navigationDecision = {
|
|
31379
|
+
type: "none"
|
|
31380
|
+
};
|
|
31232
31381
|
return;
|
|
31233
31382
|
}
|
|
31234
31383
|
beginTransition({
|
|
@@ -31236,10 +31385,11 @@ const rebuildWatcher = () => {
|
|
|
31236
31385
|
url: navigationUrl,
|
|
31237
31386
|
fromUrl: navigationFromUrl,
|
|
31238
31387
|
// Which way it plays: what the navigation itself said first — the link
|
|
31239
|
-
// being pressed is where the way the app is being walked is known
|
|
31240
|
-
//
|
|
31241
|
-
// between two pages no
|
|
31242
|
-
|
|
31388
|
+
// being pressed is where the way the app is being walked is known, and
|
|
31389
|
+
// a traversal says the way it retraces — then the relation, and forward
|
|
31390
|
+
// for a navigation that asked for a movement between two pages no
|
|
31391
|
+
// relation orders.
|
|
31392
|
+
direction: request && request.direction !== undefined ? request.direction : found && found.direction || "forward",
|
|
31243
31393
|
type,
|
|
31244
31394
|
duration
|
|
31245
31395
|
});
|
|
@@ -31268,6 +31418,13 @@ let navigationUrl = null;
|
|
|
31268
31418
|
// the address has already moved and location would answer with the destination.
|
|
31269
31419
|
let navigationFromUrl = null;
|
|
31270
31420
|
let navigationAnimated = false;
|
|
31421
|
+
// "push", "replace", "traverse", … — a push is the one navigation that creates
|
|
31422
|
+
// the entry a crossing is recorded on.
|
|
31423
|
+
let navigationType = null;
|
|
31424
|
+
// What was decided for the navigation now landing — a movement, or "none" —
|
|
31425
|
+
// which is what its entry remembers (see recordCrossing). Null while nothing
|
|
31426
|
+
// has been decided, and for a navigation nothing was said about.
|
|
31427
|
+
let navigationDecision = null;
|
|
31271
31428
|
|
|
31272
31429
|
// The two ends of every navigation, watched from here on. The picture of the
|
|
31273
31430
|
// page being left has to be honest, so rendering is held from before the
|
|
@@ -31277,9 +31434,18 @@ let navigationAnimated = false;
|
|
|
31277
31434
|
// one moment the DEFAULT can decide: every relation has had its say by then.
|
|
31278
31435
|
observeBeforeRouting(details => {
|
|
31279
31436
|
navigationAnimated = false;
|
|
31280
|
-
|
|
31437
|
+
navigationDecision = null;
|
|
31438
|
+
navigationType = details.navigationType;
|
|
31281
31439
|
navigationUrl = details.url;
|
|
31282
31440
|
navigationFromUrl = documentUrlSignal.peek();
|
|
31441
|
+
// A traversal has no element and no call to ask anything: what it asks is
|
|
31442
|
+
// the crossing it retraces — reversed for a back, as it was for a forward.
|
|
31443
|
+
// Read before the document state moves, so the state peeked is the entry
|
|
31444
|
+
// being left.
|
|
31445
|
+
navigationRequest = navigationType === "traverse" ? readTraversalReplay(details, {
|
|
31446
|
+
fromUrl: navigationFromUrl,
|
|
31447
|
+
fromState: documentStateSignal.peek()
|
|
31448
|
+
}) : readNavigationRequest(details);
|
|
31283
31449
|
if (relations.length === 0 && !defaultTransition && !navigationRequest) {
|
|
31284
31450
|
return;
|
|
31285
31451
|
}
|
|
@@ -31296,17 +31462,23 @@ observeAfterRouting(() => {
|
|
|
31296
31462
|
const request = navigationRequest;
|
|
31297
31463
|
const url = navigationUrl;
|
|
31298
31464
|
const fromUrl = navigationFromUrl;
|
|
31465
|
+
const type = navigationType;
|
|
31299
31466
|
// Read here and dropped here: a request answers for the navigation it was
|
|
31300
31467
|
// made on, and the next one is back to the relations.
|
|
31301
31468
|
navigationRequest = null;
|
|
31302
31469
|
navigationUrl = null;
|
|
31303
31470
|
navigationFromUrl = null;
|
|
31471
|
+
navigationType = null;
|
|
31304
31472
|
if (!navigationAnimated && (request || defaultTransition)) {
|
|
31305
31473
|
const {
|
|
31306
31474
|
type,
|
|
31307
31475
|
duration
|
|
31308
31476
|
} = resolveTransition(request, defaultTransition);
|
|
31309
|
-
if (type
|
|
31477
|
+
if (type === "none") {
|
|
31478
|
+
navigationDecision = {
|
|
31479
|
+
type: "none"
|
|
31480
|
+
};
|
|
31481
|
+
} else {
|
|
31310
31482
|
beginTransition({
|
|
31311
31483
|
page: null,
|
|
31312
31484
|
url,
|
|
@@ -31318,15 +31490,70 @@ observeAfterRouting(() => {
|
|
|
31318
31490
|
// press that names the movement means forward unless it says
|
|
31319
31491
|
// otherwise — and a movement of navi's is written on the direction,
|
|
31320
31492
|
// so left empty it would play nothing at all.
|
|
31321
|
-
direction: request && request.direction
|
|
31493
|
+
direction: request && request.direction !== undefined ? request.direction : request && request.typeSaid ? "forward" : "",
|
|
31322
31494
|
type,
|
|
31323
31495
|
duration
|
|
31324
31496
|
});
|
|
31325
31497
|
}
|
|
31326
31498
|
}
|
|
31499
|
+
if (type === "push") {
|
|
31500
|
+
recordCrossing({
|
|
31501
|
+
url,
|
|
31502
|
+
fromUrl,
|
|
31503
|
+
decision: navigationDecision
|
|
31504
|
+
});
|
|
31505
|
+
}
|
|
31506
|
+
navigationDecision = null;
|
|
31327
31507
|
releaseRoutingRenderingHold();
|
|
31328
31508
|
});
|
|
31329
31509
|
|
|
31510
|
+
// The entry a push created remembers what was decided on the way in, so that
|
|
31511
|
+
// the traversals leaving it or landing on it retrace it (see
|
|
31512
|
+
// readTraversalReplay). Written once the navigation has landed — the decision
|
|
31513
|
+
// needs the pages to be current, which is after the entry was created — as a
|
|
31514
|
+
// state-only replace: it announces nothing and routes nothing. Nothing is
|
|
31515
|
+
// written when nothing was decided: the silence between two unrelated pages is
|
|
31516
|
+
// not a crossing to remember. A replace keeps its entry's state, so an entry
|
|
31517
|
+
// reached by one keeps the crossing that led to where it stands.
|
|
31518
|
+
const recordCrossing = ({
|
|
31519
|
+
url,
|
|
31520
|
+
fromUrl,
|
|
31521
|
+
decision
|
|
31522
|
+
}) => {
|
|
31523
|
+
if (!decision) {
|
|
31524
|
+
return;
|
|
31525
|
+
}
|
|
31526
|
+
const to = absoluteUrl(url);
|
|
31527
|
+
const from = absoluteUrl(fromUrl);
|
|
31528
|
+
if (!to || !from) {
|
|
31529
|
+
return;
|
|
31530
|
+
}
|
|
31531
|
+
if (documentUrlSignal.peek() !== to) {
|
|
31532
|
+
// Superseded before it landed: the entry now current is another one's.
|
|
31533
|
+
return;
|
|
31534
|
+
}
|
|
31535
|
+
const crossing = {
|
|
31536
|
+
from
|
|
31537
|
+
};
|
|
31538
|
+
if (decision.type !== undefined) {
|
|
31539
|
+
crossing.type = decision.type;
|
|
31540
|
+
}
|
|
31541
|
+
if (decision.direction !== undefined) {
|
|
31542
|
+
crossing.direction = decision.direction;
|
|
31543
|
+
}
|
|
31544
|
+
if (decision.duration !== undefined) {
|
|
31545
|
+
crossing.duration = decision.duration;
|
|
31546
|
+
}
|
|
31547
|
+
const state = documentStateSignal.peek();
|
|
31548
|
+
navTo(to, {
|
|
31549
|
+
replace: true,
|
|
31550
|
+
state: {
|
|
31551
|
+
...(state || {}),
|
|
31552
|
+
[CROSSING_STATE_KEY]: crossing
|
|
31553
|
+
}
|
|
31554
|
+
});
|
|
31555
|
+
};
|
|
31556
|
+
|
|
31330
31557
|
// The exact way travelled first, over the whole registry, then the reverses,
|
|
31331
31558
|
// and last the pages written from anywhere.
|
|
31332
31559
|
//
|
|
@@ -31339,6 +31566,11 @@ observeAfterRouting(() => {
|
|
|
31339
31566
|
// same destination still owns its crossing — the map, where it was drawn, is
|
|
31340
31567
|
// more precise than "from wherever". Arriving is read before leaving: between
|
|
31341
31568
|
// two such pages, the one being opened says what plays.
|
|
31569
|
+
//
|
|
31570
|
+
// Only the first answer is `written`: a sentence the author wrote about this
|
|
31571
|
+
// exact way. Every other answer is deduced from a sentence about something
|
|
31572
|
+
// else, and a traversal retracing its own crossing knows better than a
|
|
31573
|
+
// deduction (see readTraversalReplay).
|
|
31342
31574
|
const findRelation = (fromPage, toPage) => {
|
|
31343
31575
|
for (const relation of relations) {
|
|
31344
31576
|
if (!relation.from) {
|
|
@@ -31347,7 +31579,8 @@ const findRelation = (fromPage, toPage) => {
|
|
|
31347
31579
|
if (samePage$1(relation.from, fromPage) && samePage$1(relation.to, toPage)) {
|
|
31348
31580
|
return {
|
|
31349
31581
|
direction: "forward",
|
|
31350
|
-
relation
|
|
31582
|
+
relation,
|
|
31583
|
+
written: true
|
|
31351
31584
|
};
|
|
31352
31585
|
}
|
|
31353
31586
|
}
|
|
@@ -31409,12 +31642,19 @@ const beginTransition = ({
|
|
|
31409
31642
|
console.warn("A RouteTravel is animating this navigation; the route transition defined between these routes is skipped. Animate a pair of routes with RouteTravel or defineRouteTransition, not both.");
|
|
31410
31643
|
return;
|
|
31411
31644
|
}
|
|
31645
|
+
navigationDecision = {
|
|
31646
|
+
type,
|
|
31647
|
+
direction,
|
|
31648
|
+
duration
|
|
31649
|
+
};
|
|
31412
31650
|
// The two ends of the crossing, kept for the length of the movement: they are
|
|
31413
31651
|
// what lets the navigation after this one be recognised as its way back (see
|
|
31414
|
-
// turnRunningTransitionRound)
|
|
31652
|
+
// turnRunningTransitionRound) — and what it decided, which that way back
|
|
31653
|
+
// then undoes.
|
|
31415
31654
|
const transition = {
|
|
31416
31655
|
fromUrl: absoluteUrl(fromUrl),
|
|
31417
31656
|
url: absoluteUrl(url),
|
|
31657
|
+
decision: navigationDecision,
|
|
31418
31658
|
walkHome: null,
|
|
31419
31659
|
releaseReverting: null
|
|
31420
31660
|
};
|
|
@@ -31590,6 +31830,7 @@ const turnRunningTransitionRound = (fromUrl, url) => {
|
|
|
31590
31830
|
}
|
|
31591
31831
|
const animations = viewTransitionAnimations();
|
|
31592
31832
|
if (isWayInAgain) {
|
|
31833
|
+
navigationDecision = running.decision;
|
|
31593
31834
|
// The token is dropped first: the walk it stands for is the one that must
|
|
31594
31835
|
// not arrive anywhere anymore, and its promise is still pending.
|
|
31595
31836
|
running.walkHome = null;
|
|
@@ -31607,6 +31848,9 @@ const turnRunningTransitionRound = (fromUrl, url) => {
|
|
|
31607
31848
|
// played, and nothing was on screen to teleport.
|
|
31608
31849
|
return false;
|
|
31609
31850
|
}
|
|
31851
|
+
// What this navigation plays, for the entry it may create: the movement on
|
|
31852
|
+
// screen, the other way.
|
|
31853
|
+
navigationDecision = reverseCrossing(running.decision);
|
|
31610
31854
|
// Which walk home this is, so the one that arrives is the one still wanted: a
|
|
31611
31855
|
// walk turned round mid-way leaves a promise nobody cancelled, and it settles
|
|
31612
31856
|
// when the pictures reach the far end.
|
|
@@ -31809,7 +32053,7 @@ const warnAboutBothWaysWritten = ({
|
|
|
31809
32053
|
// wrote them in, and the order it will find them in to fix them.
|
|
31810
32054
|
const written = `${describePage(reverse.from)} → ${describePage(reverse.to)}`;
|
|
31811
32055
|
const added = `${describePage(from)} → ${describePage(to)}`;
|
|
31812
|
-
warnOnce(`both-ways-written:${written}|${added}`, `${written} and ${added} are both written with the same movement, so BOTH crossings play forward and this pair can never say "back" — the back button included. A relation written for the exact way travelled wins over being the reverse of another (see findRelation), which is what makes reciprocity the default: write the way back only to give it a DIFFERENT movement, or "none" to silence it. A single crossing that walks the map backwards says so on itself instead: <Link routeTransition={{ direction: "forward" }}>, or navTo(url, { routeTransition: { direction: "forward" } }).`);
|
|
32056
|
+
warnOnce(`both-ways-written:${written}|${added}`, `${written} and ${added} are both written with the same movement, so BOTH crossings play forward and this pair can never say "back" — the back button included. A relation written for the exact way travelled wins over being the reverse of another, and over the crossing a history traversal retraces (see findRelation), which is what makes reciprocity the default: write the way back only to give it a DIFFERENT movement, or "none" to silence it. A single crossing that walks the map backwards says so on itself instead: <Link routeTransition={{ direction: "forward" }}>, or navTo(url, { routeTransition: { direction: "forward" } }).`);
|
|
31813
32057
|
};
|
|
31814
32058
|
const warnPagesBothCurrent = (pageKept, pageIgnored) => {
|
|
31815
32059
|
const kept = describePage(pageKept);
|
|
@@ -65393,8 +65637,8 @@ const PickerCustom = props => {
|
|
|
65393
65637
|
// before computing popupId below, so two Pickers without an explicit id never collide.
|
|
65394
65638
|
// Captured before the fallback chain below overwrites props.id — needed to
|
|
65395
65639
|
// know whether the id actually came from the caller (stable) or from
|
|
65396
|
-
// useId()/ControlIdContext (
|
|
65397
|
-
// pickerNavType below.
|
|
65640
|
+
// useId()/ControlIdContext (a generated id names one mount: a reload, or a
|
|
65641
|
+
// return to this page, generates another), see pickerNavType below.
|
|
65398
65642
|
const hasExplicitId = Boolean(props.id);
|
|
65399
65643
|
const idDefault = useId();
|
|
65400
65644
|
const controlId = useContext(ControlIdContext);
|
|
@@ -65471,10 +65715,12 @@ const PickerCustom = props => {
|
|
|
65471
65715
|
// pushes a history entry so the back button closes it. Every other case
|
|
65472
65716
|
// (popover mode, or a dialog whose id was auto-generated via useId()/
|
|
65473
65717
|
// ControlIdContext) replaces the current history state instead — a
|
|
65474
|
-
// generated id
|
|
65475
|
-
//
|
|
65476
|
-
//
|
|
65477
|
-
//
|
|
65718
|
+
// generated id names one mount, so pushing it would either leave an entry
|
|
65719
|
+
// nothing reads or, worse, collide with a different component's own
|
|
65720
|
+
// generated id (see useNavState's own fallback for the same concern,
|
|
65721
|
+
// applied here proactively for the id we control). What a generated id
|
|
65722
|
+
// costs either way: the state is written, and the mount coming back to
|
|
65723
|
+
// the page (or a reload) finds it under a key it does not have.
|
|
65478
65724
|
const pickerNavType = mode === "dialog" && hasExplicitId ? "push" : "replace";
|
|
65479
65725
|
const [expanded, enterExpanded, leaveExpanded] = useNavState(popupId, {
|
|
65480
65726
|
type: pickerNavType,
|
|
@@ -75743,6 +75989,12 @@ const PickerFirstResolver = props => {
|
|
|
75743
75989
|
* content failed to load, its value could not be resolved…). Shown as a
|
|
75744
75990
|
* callout on the trigger, open or closed — the caller has nothing to place.
|
|
75745
75991
|
* Dismissing it discards that error; a new `error` value raises another one.
|
|
75992
|
+
* @param {string} [id] What the popup's open state is kept under in the
|
|
75993
|
+
* history entry: a screen left and come back to finds the picker open, and
|
|
75994
|
+
* in dialog mode the opening is an entry of its own, closed by the back
|
|
75995
|
+
* button before the screen is left. Left out, the key is a generated id,
|
|
75996
|
+
* which names one mount: the state survives neither leaving the screen nor a
|
|
75997
|
+
* reload. A picker whose popup leads somewhere (a link inside it) has one.
|
|
75746
75998
|
* @param {"popover"|"dialog"|"callout"} [mode] Which popup the children open
|
|
75747
75999
|
* in. Left out, a popover on a large screen and a dialog on a narrow one.
|
|
75748
76000
|
* `"callout"` shows them in the picker's own callout — the speech bubble its
|