@jsenv/navi 0.29.366 → 0.29.368
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 +305 -20
- package/dist/dev/jsenv_navi.js.map +3 -3
- package/dist/jsenv_navi.js +273 -18
- package/dist/jsenv_navi.js.map +3 -3
- package/docs/route_transitions.md +61 -15
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -30627,6 +30627,9 @@ const TRANSITION_TARGET_ATTRIBUTE = "data-navi-route-transition-target";
|
|
|
30627
30627
|
// link being pressed (see <Link routeTransition>), or handed to navTo(). It answers
|
|
30628
30628
|
// for that navigation and for no other — the next one is back to the relations.
|
|
30629
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";
|
|
30630
30633
|
const AREA_NAME = "navi-route-transition";
|
|
30631
30634
|
// The pictures carrying the movement, among everything else it takes along —
|
|
30632
30635
|
// the pages', or the document's own when the pages ARE the document (see
|
|
@@ -31175,6 +31178,106 @@ const normalizeRequest = transition => {
|
|
|
31175
31178
|
};
|
|
31176
31179
|
};
|
|
31177
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
|
+
|
|
31178
31281
|
// The request first, field by field, then what was defined for this pair (or
|
|
31179
31282
|
// for everything). Written as one function because both ends of the file
|
|
31180
31283
|
// resolve the same way: the one that knows the pair, and the one that only
|
|
@@ -31250,7 +31353,14 @@ const rebuildWatcher = () => {
|
|
|
31250
31353
|
const fromPage = fromIndex === -1 ? null : pages[fromIndex];
|
|
31251
31354
|
const toPage = index === -1 ? null : pages[index];
|
|
31252
31355
|
const found = findRelation(fromPage, toPage);
|
|
31253
|
-
|
|
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) {
|
|
31254
31364
|
// No relation says anything about these two and this navigation asked
|
|
31255
31365
|
// for nothing: they are side by side, and silence is the fact — not a
|
|
31256
31366
|
// missing case.
|
|
@@ -31259,12 +31369,15 @@ const rebuildWatcher = () => {
|
|
|
31259
31369
|
const {
|
|
31260
31370
|
type,
|
|
31261
31371
|
duration
|
|
31262
|
-
} = resolveTransition(
|
|
31372
|
+
} = resolveTransition(request, found ? found.relation : null);
|
|
31263
31373
|
if (type === "none") {
|
|
31264
31374
|
// Silence said out loud: this way of the pair was written to play
|
|
31265
31375
|
// nothing — or this one navigation asked for nothing — where the reverse
|
|
31266
31376
|
// of the other way, or the default, would have played.
|
|
31267
31377
|
navigationAnimated = true;
|
|
31378
|
+
navigationDecision = {
|
|
31379
|
+
type: "none"
|
|
31380
|
+
};
|
|
31268
31381
|
return;
|
|
31269
31382
|
}
|
|
31270
31383
|
beginTransition({
|
|
@@ -31272,10 +31385,11 @@ const rebuildWatcher = () => {
|
|
|
31272
31385
|
url: navigationUrl,
|
|
31273
31386
|
fromUrl: navigationFromUrl,
|
|
31274
31387
|
// Which way it plays: what the navigation itself said first — the link
|
|
31275
|
-
// being pressed is where the way the app is being walked is known
|
|
31276
|
-
//
|
|
31277
|
-
// between two pages no
|
|
31278
|
-
|
|
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",
|
|
31279
31393
|
type,
|
|
31280
31394
|
duration
|
|
31281
31395
|
});
|
|
@@ -31304,6 +31418,13 @@ let navigationUrl = null;
|
|
|
31304
31418
|
// the address has already moved and location would answer with the destination.
|
|
31305
31419
|
let navigationFromUrl = null;
|
|
31306
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;
|
|
31307
31428
|
|
|
31308
31429
|
// The two ends of every navigation, watched from here on. The picture of the
|
|
31309
31430
|
// page being left has to be honest, so rendering is held from before the
|
|
@@ -31313,9 +31434,18 @@ let navigationAnimated = false;
|
|
|
31313
31434
|
// one moment the DEFAULT can decide: every relation has had its say by then.
|
|
31314
31435
|
observeBeforeRouting(details => {
|
|
31315
31436
|
navigationAnimated = false;
|
|
31316
|
-
|
|
31437
|
+
navigationDecision = null;
|
|
31438
|
+
navigationType = details.navigationType;
|
|
31317
31439
|
navigationUrl = details.url;
|
|
31318
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);
|
|
31319
31449
|
if (relations.length === 0 && !defaultTransition && !navigationRequest) {
|
|
31320
31450
|
return;
|
|
31321
31451
|
}
|
|
@@ -31332,17 +31462,23 @@ observeAfterRouting(() => {
|
|
|
31332
31462
|
const request = navigationRequest;
|
|
31333
31463
|
const url = navigationUrl;
|
|
31334
31464
|
const fromUrl = navigationFromUrl;
|
|
31465
|
+
const type = navigationType;
|
|
31335
31466
|
// Read here and dropped here: a request answers for the navigation it was
|
|
31336
31467
|
// made on, and the next one is back to the relations.
|
|
31337
31468
|
navigationRequest = null;
|
|
31338
31469
|
navigationUrl = null;
|
|
31339
31470
|
navigationFromUrl = null;
|
|
31471
|
+
navigationType = null;
|
|
31340
31472
|
if (!navigationAnimated && (request || defaultTransition)) {
|
|
31341
31473
|
const {
|
|
31342
31474
|
type,
|
|
31343
31475
|
duration
|
|
31344
31476
|
} = resolveTransition(request, defaultTransition);
|
|
31345
|
-
if (type
|
|
31477
|
+
if (type === "none") {
|
|
31478
|
+
navigationDecision = {
|
|
31479
|
+
type: "none"
|
|
31480
|
+
};
|
|
31481
|
+
} else {
|
|
31346
31482
|
beginTransition({
|
|
31347
31483
|
page: null,
|
|
31348
31484
|
url,
|
|
@@ -31354,15 +31490,70 @@ observeAfterRouting(() => {
|
|
|
31354
31490
|
// press that names the movement means forward unless it says
|
|
31355
31491
|
// otherwise — and a movement of navi's is written on the direction,
|
|
31356
31492
|
// so left empty it would play nothing at all.
|
|
31357
|
-
direction: request && request.direction
|
|
31493
|
+
direction: request && request.direction !== undefined ? request.direction : request && request.typeSaid ? "forward" : "",
|
|
31358
31494
|
type,
|
|
31359
31495
|
duration
|
|
31360
31496
|
});
|
|
31361
31497
|
}
|
|
31362
31498
|
}
|
|
31499
|
+
if (type === "push") {
|
|
31500
|
+
recordCrossing({
|
|
31501
|
+
url,
|
|
31502
|
+
fromUrl,
|
|
31503
|
+
decision: navigationDecision
|
|
31504
|
+
});
|
|
31505
|
+
}
|
|
31506
|
+
navigationDecision = null;
|
|
31363
31507
|
releaseRoutingRenderingHold();
|
|
31364
31508
|
});
|
|
31365
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
|
+
|
|
31366
31557
|
// The exact way travelled first, over the whole registry, then the reverses,
|
|
31367
31558
|
// and last the pages written from anywhere.
|
|
31368
31559
|
//
|
|
@@ -31375,6 +31566,11 @@ observeAfterRouting(() => {
|
|
|
31375
31566
|
// same destination still owns its crossing — the map, where it was drawn, is
|
|
31376
31567
|
// more precise than "from wherever". Arriving is read before leaving: between
|
|
31377
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).
|
|
31378
31574
|
const findRelation = (fromPage, toPage) => {
|
|
31379
31575
|
for (const relation of relations) {
|
|
31380
31576
|
if (!relation.from) {
|
|
@@ -31383,7 +31579,8 @@ const findRelation = (fromPage, toPage) => {
|
|
|
31383
31579
|
if (samePage$1(relation.from, fromPage) && samePage$1(relation.to, toPage)) {
|
|
31384
31580
|
return {
|
|
31385
31581
|
direction: "forward",
|
|
31386
|
-
relation
|
|
31582
|
+
relation,
|
|
31583
|
+
written: true
|
|
31387
31584
|
};
|
|
31388
31585
|
}
|
|
31389
31586
|
}
|
|
@@ -31445,12 +31642,19 @@ const beginTransition = ({
|
|
|
31445
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.");
|
|
31446
31643
|
return;
|
|
31447
31644
|
}
|
|
31645
|
+
navigationDecision = {
|
|
31646
|
+
type,
|
|
31647
|
+
direction,
|
|
31648
|
+
duration
|
|
31649
|
+
};
|
|
31448
31650
|
// The two ends of the crossing, kept for the length of the movement: they are
|
|
31449
31651
|
// what lets the navigation after this one be recognised as its way back (see
|
|
31450
|
-
// turnRunningTransitionRound)
|
|
31652
|
+
// turnRunningTransitionRound) — and what it decided, which that way back
|
|
31653
|
+
// then undoes.
|
|
31451
31654
|
const transition = {
|
|
31452
31655
|
fromUrl: absoluteUrl(fromUrl),
|
|
31453
31656
|
url: absoluteUrl(url),
|
|
31657
|
+
decision: navigationDecision,
|
|
31454
31658
|
walkHome: null,
|
|
31455
31659
|
releaseReverting: null
|
|
31456
31660
|
};
|
|
@@ -31626,6 +31830,7 @@ const turnRunningTransitionRound = (fromUrl, url) => {
|
|
|
31626
31830
|
}
|
|
31627
31831
|
const animations = viewTransitionAnimations();
|
|
31628
31832
|
if (isWayInAgain) {
|
|
31833
|
+
navigationDecision = running.decision;
|
|
31629
31834
|
// The token is dropped first: the walk it stands for is the one that must
|
|
31630
31835
|
// not arrive anywhere anymore, and its promise is still pending.
|
|
31631
31836
|
running.walkHome = null;
|
|
@@ -31643,6 +31848,9 @@ const turnRunningTransitionRound = (fromUrl, url) => {
|
|
|
31643
31848
|
// played, and nothing was on screen to teleport.
|
|
31644
31849
|
return false;
|
|
31645
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);
|
|
31646
31854
|
// Which walk home this is, so the one that arrives is the one still wanted: a
|
|
31647
31855
|
// walk turned round mid-way leaves a promise nobody cancelled, and it settles
|
|
31648
31856
|
// when the pictures reach the far end.
|
|
@@ -31845,7 +32053,7 @@ const warnAboutBothWaysWritten = ({
|
|
|
31845
32053
|
// wrote them in, and the order it will find them in to fix them.
|
|
31846
32054
|
const written = `${describePage(reverse.from)} → ${describePage(reverse.to)}`;
|
|
31847
32055
|
const added = `${describePage(from)} → ${describePage(to)}`;
|
|
31848
|
-
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" } }).`);
|
|
31849
32057
|
};
|
|
31850
32058
|
const warnPagesBothCurrent = (pageKept, pageIgnored) => {
|
|
31851
32059
|
const kept = describePage(pageKept);
|
|
@@ -74482,6 +74690,7 @@ const css$t = /* css */`@layer navi {
|
|
|
74482
74690
|
var(--picker-icon-color) 45%,
|
|
74483
74691
|
transparent);
|
|
74484
74692
|
--picker-icon-color-disabled: var(--picker-icon-color-readonly);
|
|
74693
|
+
--picker-icon-color-hover: var(--picker-icon-color);
|
|
74485
74694
|
--picker-align-x-default: flex-start;
|
|
74486
74695
|
--picker-align-y-default: center;
|
|
74487
74696
|
--picker-text-align-default: initial;
|
|
@@ -74499,6 +74708,7 @@ const css$t = /* css */`@layer navi {
|
|
|
74499
74708
|
--x-picker-press-padding-right: var(--picker-press-padding-right, var(--picker-press-padding-x, var(--picker-press-padding, 0px)));
|
|
74500
74709
|
--x-picker-press-padding-bottom: var(--picker-press-padding-bottom, var(--picker-press-padding-y, var(--picker-press-padding, 0px)));
|
|
74501
74710
|
--x-picker-press-padding-left: var(--picker-press-padding-left, var(--picker-press-padding-x, var(--picker-press-padding, 0px)));
|
|
74711
|
+
--x-picker-chevron-reach: 0px;
|
|
74502
74712
|
--x-picker-color: var(--picker-color);
|
|
74503
74713
|
--x-picker-icon-color: var(--picker-icon-color);
|
|
74504
74714
|
--x-picker-align-x: var(--picker-align-x, var(--picker-align-x-default));
|
|
@@ -74667,7 +74877,10 @@ const css$t = /* css */`@layer navi {
|
|
|
74667
74877
|
& > .navi_picker_box > .navi_picker_input {
|
|
74668
74878
|
top: calc(-1 * (var(--picker-border-width) + var(--x-picker-press-padding-top)));
|
|
74669
74879
|
right: calc(-1 *
|
|
74670
|
-
(
|
|
74880
|
+
(
|
|
74881
|
+
var(--picker-border-width) + var(--x-picker-press-padding-right) +
|
|
74882
|
+
var(--x-picker-chevron-reach)
|
|
74883
|
+
));
|
|
74671
74884
|
bottom: calc(-1 *
|
|
74672
74885
|
(var(--picker-border-width) + var(--x-picker-press-padding-bottom)));
|
|
74673
74886
|
left: calc(-1 * (var(--picker-border-width) + var(--x-picker-press-padding-left)));
|
|
@@ -74713,6 +74926,7 @@ const css$t = /* css */`@layer navi {
|
|
|
74713
74926
|
&[data-hover] {
|
|
74714
74927
|
--x-picker-background-color: var(--picker-background-color-hover);
|
|
74715
74928
|
--x-picker-border-color: var(--picker-border-color-hover);
|
|
74929
|
+
--x-picker-icon-color: var(--picker-icon-color-hover);
|
|
74716
74930
|
}
|
|
74717
74931
|
|
|
74718
74932
|
&[data-readonly] {
|
|
@@ -74822,6 +75036,30 @@ const css$t = /* css */`@layer navi {
|
|
|
74822
75036
|
align-items: var(--x-picker-align-y);
|
|
74823
75037
|
display: flex;
|
|
74824
75038
|
}
|
|
75039
|
+
|
|
75040
|
+
& > .navi_picker_box > .navi_picker_right_slot {
|
|
75041
|
+
margin-right: 0;
|
|
75042
|
+
margin-left: var(--picker-slot-spacing, var(--navi-xxs));
|
|
75043
|
+
align-self: center;
|
|
75044
|
+
}
|
|
75045
|
+
|
|
75046
|
+
&[data-chevron="start"] > .navi_picker_box > .navi_picker_right_slot {
|
|
75047
|
+
margin-right: var(--picker-slot-spacing, var(--navi-xxs));
|
|
75048
|
+
order: -1;
|
|
75049
|
+
margin-left: 0;
|
|
75050
|
+
}
|
|
75051
|
+
|
|
75052
|
+
&[data-chevron="hang-end"] > .navi_picker_box {
|
|
75053
|
+
--x-picker-chevron-reach: calc(var(--x-picker-chevron-size) +
|
|
75054
|
+
var(--picker-slot-spacing, var(--navi-xxs)));
|
|
75055
|
+
|
|
75056
|
+
& > .navi_picker_right_slot {
|
|
75057
|
+
position: absolute;
|
|
75058
|
+
top: 50%;
|
|
75059
|
+
left: 100%;
|
|
75060
|
+
transform: translateY(-50%);
|
|
75061
|
+
}
|
|
75062
|
+
}
|
|
74825
75063
|
}
|
|
74826
75064
|
|
|
74827
75065
|
&[data-variant="button"] {
|
|
@@ -74901,6 +75139,9 @@ const PickerButton = props => {
|
|
|
74901
75139
|
// an <Icon>, which is aria-hidden, and a focusable node under aria-hidden is
|
|
74902
75140
|
// invisible to assistive tech while still being reachable by tab.
|
|
74903
75141
|
rightSlot,
|
|
75142
|
+
// A bare picker's chevron, and where it goes so the drawing keeps its box
|
|
75143
|
+
// (see the `chevron` doc). Left out, a bare picker draws no slot at all.
|
|
75144
|
+
chevron,
|
|
74904
75145
|
placeholder,
|
|
74905
75146
|
ui,
|
|
74906
75147
|
maxLines: maxLinesProp = 1,
|
|
@@ -74961,12 +75202,22 @@ const PickerButton = props => {
|
|
|
74961
75202
|
// icon, a headless one draws nothing, a button says what it opens with its
|
|
74962
75203
|
// label, a word in a sentence has no room for furniture. Nor a picker
|
|
74963
75204
|
// rendering the browser's own control ("default").
|
|
74964
|
-
// Nor a bare one: the picker is that drawing's
|
|
74965
|
-
// navi adds beside it either grows the box or
|
|
74966
|
-
// The pieces are the caller's to place there
|
|
74967
|
-
// their own layout (see
|
|
74968
|
-
|
|
75205
|
+
// Nor a bare one, unless `chevron` says where: the picker is that drawing's
|
|
75206
|
+
// box to the pixel, so anything navi adds beside it either grows the box or
|
|
75207
|
+
// covers what the caller drew. The pieces are the caller's to place there
|
|
75208
|
+
// instead — a <Picker.Clear /> in their own layout (see
|
|
75209
|
+
// warnOnClearableWithoutSlot) — or the slot goes where the drawing is not:
|
|
75210
|
+
// after it, before it, or out of the flow altogether.
|
|
75211
|
+
const isBare = variant === "bare";
|
|
75212
|
+
const bareChevron = isBare && chevron !== undefined ? chevron : undefined;
|
|
75213
|
+
warnOnChevronOffBare(props, isBare);
|
|
75214
|
+
const hasRightSlot = !picksNothing && rightSlot !== null && !isIcon && variant !== "headless" && variant !== "button" && variant !== "text" && (!isBare || bareChevron !== undefined) && ui !== "default";
|
|
74969
75215
|
warnOnClearableWithoutSlot(props, hasRightSlot);
|
|
75216
|
+
// How far the press must follow a hanging chevron: its size, as a length the
|
|
75217
|
+
// input's inset can add up. "inherit" is 1em of the picker's own font, which
|
|
75218
|
+
// the input shares (font: inherit), so the em the icon is drawn in and the
|
|
75219
|
+
// em the reach is measured in are the same.
|
|
75220
|
+
const hangingChevronSize = bareChevron === "hang-end" && hasRightSlot ? rightSlotIconSize === "inherit" ? "1em" : stringifyStyleValue(rightSlotIconSize, "fontSize") : undefined;
|
|
74970
75221
|
const inputRef = useRef(null);
|
|
74971
75222
|
const [pickerRemainingProps, inputProps, facadeChildrenProps] = useControlFacadeProps({
|
|
74972
75223
|
...props,
|
|
@@ -75050,6 +75301,7 @@ const PickerButton = props => {
|
|
|
75050
75301
|
pseudoClasses: PICKER_BUTTON_PSEUDO_CLASSES,
|
|
75051
75302
|
"data-variant": variant,
|
|
75052
75303
|
"data-icon": isIcon ? "" : undefined,
|
|
75304
|
+
"data-chevron": hasRightSlot ? bareChevron : undefined,
|
|
75053
75305
|
"navi-picker": "",
|
|
75054
75306
|
"navi-single-line": isSingleLine ? "" : undefined,
|
|
75055
75307
|
"navi-ui-custom": ui === "default" ? undefined : "",
|
|
@@ -75063,6 +75315,7 @@ const PickerButton = props => {
|
|
|
75063
75315
|
rightSlotIcon: undefined,
|
|
75064
75316
|
rightSlotIconSize: undefined,
|
|
75065
75317
|
rightSlot: undefined,
|
|
75318
|
+
chevron: undefined,
|
|
75066
75319
|
clearConfirm: undefined,
|
|
75067
75320
|
picksNothing: undefined,
|
|
75068
75321
|
openWhileReadOnly: undefined,
|
|
@@ -75099,6 +75352,9 @@ const PickerButton = props => {
|
|
|
75099
75352
|
},
|
|
75100
75353
|
children: [jsx("span", {
|
|
75101
75354
|
className: "navi_picker_box",
|
|
75355
|
+
style: hangingChevronSize === undefined ? undefined : {
|
|
75356
|
+
"--x-picker-chevron-size": hangingChevronSize
|
|
75357
|
+
},
|
|
75102
75358
|
children: jsxs(PickerContext.Provider, {
|
|
75103
75359
|
value: pickerContext,
|
|
75104
75360
|
children: [variant === "headless" ? null : jsx(LoadingOutline, {
|
|
@@ -75640,6 +75896,7 @@ const PickerFirstResolver = props => {
|
|
|
75640
75896
|
* rightSlotIcon?: import("ignore:preact").ComponentChildren,
|
|
75641
75897
|
* rightSlotIconSize?: number | string,
|
|
75642
75898
|
* rightSlot?: import("ignore:preact").ComponentChildren,
|
|
75899
|
+
* chevron?: "end" | "start" | "hang-end",
|
|
75643
75900
|
* clearConfirm?: string | import("ignore:preact").ComponentChildren,
|
|
75644
75901
|
* message?: import("ignore:preact").ComponentChildren,
|
|
75645
75902
|
* confirmLabel?: import("ignore:preact").ComponentChildren,
|
|
@@ -75740,7 +75997,7 @@ const PickerFirstResolver = props => {
|
|
|
75740
75997
|
* a drawing that paints outside it on purpose — an affordance in the margin
|
|
75741
75998
|
* beside it, out of the flow so the rest keeps its width — is drawn and
|
|
75742
75999
|
* opens nothing; `pressPadding` is how far the press area follows it out
|
|
75743
|
-
* there.
|
|
76000
|
+
* there. The chevron itself is the `chevron` prop's to draw and place.
|
|
75744
76001
|
*
|
|
75745
76002
|
* A drawing that follows what the picker HOLDS — the value the popup just
|
|
75746
76003
|
* closed on, before the server has answered, the same state navi's own
|
|
@@ -75844,7 +76101,23 @@ const PickerFirstResolver = props => {
|
|
|
75844
76101
|
|
|
75845
76102
|
* `variant="bare"` has no slot either, for the same reason turned around: the
|
|
75846
76103
|
* picker is the drawing's box to the pixel, so nothing may be added beside it
|
|
75847
|
-
* — the drawing holds what it needs (`<Picker.Clear />`) and places it.
|
|
76104
|
+
* — the drawing holds what it needs (`<Picker.Clear />`) and places it. Its
|
|
76105
|
+
* `chevron` prop is the exception: the slot, put where the drawing is not.
|
|
76106
|
+
* @param {"end"|"start"|"hang-end"} [chevron] `variant="bare"` only: the
|
|
76107
|
+
* picker draws no chevron beside the drawing, so nothing says it opens; this
|
|
76108
|
+
* draws one — the icon the right slot would hold, `rightSlotIcon` or the
|
|
76109
|
+
* chevron, at `rightSlotIconSize`, `slotSpacing` away from the drawing (xxs
|
|
76110
|
+
* by default), taking the clear cross's place under `clearable` like any
|
|
76111
|
+
* slot — and says where, so the drawing keeps the box it has when nothing
|
|
76112
|
+
* opens. Which side depends on where the drawing is anchored in its line.
|
|
76113
|
+
* `"end"`: after it, in the flow — a label with room after it. `"start"`:
|
|
76114
|
+
* before it — a label pushed against the right edge, which the chevron
|
|
76115
|
+
* would otherwise push left by its width. `"hang-end"`: after it and OUT of
|
|
76116
|
+
* the flow, in the margin beside the box — a drawing filling its whole
|
|
76117
|
+
* line; the press follows it out there by its size plus its gap, on top of
|
|
76118
|
+
* any `pressPaddingRight`. The chevron is drawn in `--picker-icon-color`
|
|
76119
|
+
* (`currentColor` here) and `--picker-icon-color-hover`; a dimmed one is
|
|
76120
|
+
* `--picker-icon-color: color-mix(in srgb, currentColor 55%, transparent)`.
|
|
75848
76121
|
* @param {import("ignore:preact").ComponentChildren} [rightSlot] Same place, rendered
|
|
75849
76122
|
* as-is: no `<Icon>` around it, nothing `aria-hidden`. This is where an
|
|
75850
76123
|
* interactive right slot goes. `null` takes the slot away entirely — the
|
|
@@ -76146,6 +76419,18 @@ const warnOnClearableWithoutSlot = (props, hasRightSlot) => {
|
|
|
76146
76419
|
}
|
|
76147
76420
|
console.warn(`[navi] <Picker ${cause} clearable> — that leaves no right slot, so no cross is drawn. ` + `Draw it where it belongs instead: <Picker ${cause} ui={<YourDrawing>… <Picker.Clear /></YourDrawing>}>. ` + `It is the same cross, and it takes itself away when there is nothing left to clear.`);
|
|
76148
76421
|
};
|
|
76422
|
+
let chevronOffBareWarned = false;
|
|
76423
|
+
const warnOnChevronOffBare = (props, isBare) => {
|
|
76424
|
+
const {
|
|
76425
|
+
chevron,
|
|
76426
|
+
variant
|
|
76427
|
+
} = props;
|
|
76428
|
+
if (chevron === undefined || isBare || chevronOffBareWarned) {
|
|
76429
|
+
return;
|
|
76430
|
+
}
|
|
76431
|
+
chevronOffBareWarned = true;
|
|
76432
|
+
console.warn(`[navi] <Picker variant="${variant}" chevron="${chevron}"> — \`chevron\` places the chevron of a bare picker, whose drawing has no slot beside it. ` + `This variant draws its chevron in its right slot already (rightSlotIcon, rightSlot to change it); \`chevron\` is ignored.`);
|
|
76433
|
+
};
|
|
76149
76434
|
const warnOnUnknownPickerType = props => {
|
|
76150
76435
|
const {
|
|
76151
76436
|
type
|