@jsenv/navi 0.29.300 → 0.29.302
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 +892 -226
- package/dist/dev/jsenv_navi.js.map +25 -20
- package/dist/jsenv_navi.js +826 -207
- package/dist/jsenv_navi.js.map +25 -20
- package/docs/dialog_shape.md +23 -8
- package/docs/interactions.md +100 -16
- package/docs/pan_zoom.md +2 -2
- package/docs/popup_backdrop.md +28 -7
- package/docs/popup_open.md +43 -7
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visualViewportHeightSignal, visualViewportWidthSignal, getAppHeight, getAppWidth, coarsePointerSignal, smallTouchScreenSignal } from "./jsenv_navi_side_effects.js";
|
|
6
6
|
export { disableVirtualKeyboardOverlay } from "./jsenv_navi_side_effects.js";
|
|
7
|
-
import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, scrollIntoViewThroughScrollables, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, keepTouchRefusable, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, refuseDragTo, startDragTo, installPanZoom, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, getScrollContainer, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, releaseWheelGesture, getScrollIntoViewScopedOffsets, wheelGestureIsTakenFrom, claimWheelGesture, scrollIntoViewScoped, initFocusGroup, isTouchDrivenEvent,
|
|
7
|
+
import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, scrollIntoViewThroughScrollables, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, keepTouchRefusable, isPressDrivenClick, waitForTap, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, refuseDragTo, startDragTo, installPanZoom, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, getScrollContainer, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, releaseWheelGesture, getScrollIntoViewScopedOffsets, wheelGestureIsTakenFrom, claimWheelGesture, scrollIntoViewScoped, initFocusGroup, isTouchDrivenEvent, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, getVirtualKeyboardOverlayHeight, onAncestorReopen, isPressDisputedByDrag, canScroll, measureWidestChildRow, performTabNavigation, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
|
|
8
8
|
export { chainEvent, clickIsSuppressed, contrastColor, createDragGestureController, dragAfterIntent, findEvent, markDragSource, startDragTo } from "@jsenv/dom";
|
|
9
9
|
import { signal, computed, effect, untracked, batch, useComputed, useSignal } from "@preact/signals";
|
|
10
10
|
import { isValidElement, createContext, render, h, toChildArray, options, cloneElement, Fragment as Fragment$1 } from "preact";
|
|
@@ -10127,6 +10127,11 @@ const readNumberFromDom = (element, attribute, defaultValue) => {
|
|
|
10127
10127
|
* happened. Registered through the same door an application uses (see
|
|
10128
10128
|
* interaction_registry.js) — navi has no private one.
|
|
10129
10129
|
*
|
|
10130
|
+
* `dblclick` is not among them, and deliberately: the browser only fires it for a
|
|
10131
|
+
* mouse, so an element declaring it would answer half the hands that reach it.
|
|
10132
|
+
* What a hand does twice is `double_click`, counted from the pointer in
|
|
10133
|
+
* interaction_press.js.
|
|
10134
|
+
*
|
|
10130
10135
|
* `contextmenu` is the only one that takes something away: the browser's own menu
|
|
10131
10136
|
* would cover the answer to the request it is. It takes it away AFTER, though —
|
|
10132
10137
|
* a native interaction IS its own event, the very one the gate reads, and a gate
|
|
@@ -10140,7 +10145,6 @@ const NATIVE_TYPE_SET = new Set([
|
|
|
10140
10145
|
"mousedown",
|
|
10141
10146
|
"mouseup",
|
|
10142
10147
|
"click",
|
|
10143
|
-
"dblclick",
|
|
10144
10148
|
"contextmenu",
|
|
10145
10149
|
]);
|
|
10146
10150
|
|
|
@@ -10198,6 +10202,15 @@ const SWIPE_THRESHOLD_DEFAULT = 0.33;
|
|
|
10198
10202
|
const LONGPRESS_DELAY_DEFAULT = 450;
|
|
10199
10203
|
// Past this the finger is going somewhere: it swipes, it does not hold.
|
|
10200
10204
|
const LONGPRESS_SLOP_DEFAULT = 8;
|
|
10205
|
+
// The window a double click is counted in, opened by the FIRST press. Under the
|
|
10206
|
+
// hold's delay on purpose: a pause longer than the wait navi calls "held" is
|
|
10207
|
+
// longer than one gesture.
|
|
10208
|
+
const DOUBLE_CLICK_DELAY_DEFAULT = 400;
|
|
10209
|
+
// How far apart the two presses may be, and how far either of them may travel
|
|
10210
|
+
// before it is a gesture rather than a tap. A fingertip and not a pixel: between
|
|
10211
|
+
// the two the finger leaves the glass and lands again where it means to, which is
|
|
10212
|
+
// a wider question than the hold's slop above (has this finger stood still?).
|
|
10213
|
+
const DOUBLE_CLICK_SLOP_DEFAULT = 30;
|
|
10201
10214
|
// How long the element takes to reach where the gesture leaves it, or to come
|
|
10202
10215
|
// back. Written into the CSS below from here: the state is cleaned up when the
|
|
10203
10216
|
// movement is over, so a duration living only in the stylesheet would be a timing
|
|
@@ -10209,11 +10222,19 @@ const SETTLE_DURATION_MS = 200;
|
|
|
10209
10222
|
const SWIPE_THRESHOLD_ATTRIBUTE = "data-swipe-threshold";
|
|
10210
10223
|
const LONGPRESS_DELAY_ATTRIBUTE = "data-longpress-delay";
|
|
10211
10224
|
const LONGPRESS_SLOP_ATTRIBUTE = "data-longpress-slop";
|
|
10225
|
+
const DOUBLE_CLICK_DELAY_ATTRIBUTE = "data-double-click-delay";
|
|
10226
|
+
const DOUBLE_CLICK_SLOP_ATTRIBUTE = "data-double-click-slop";
|
|
10212
10227
|
|
|
10213
|
-
// Which axes this element takes a swipe on, and that it takes a hold
|
|
10214
|
-
// DOM at render time, for the CSS below and for the boxes above
|
|
10228
|
+
// Which axes this element takes a swipe on, and that it takes a hold or counts
|
|
10229
|
+
// taps: said in the DOM at render time, for the CSS below and for the boxes above
|
|
10230
|
+
// to read.
|
|
10215
10231
|
const SWIPE_AXES_ATTRIBUTE = "data-swipe";
|
|
10216
10232
|
const LONGPRESS_ATTRIBUTE = "data-longpress";
|
|
10233
|
+
const DOUBLE_CLICK_ATTRIBUTE = "data-double-click";
|
|
10234
|
+
|
|
10235
|
+
const LONGPRESS = "longpress";
|
|
10236
|
+
const DOUBLE_CLICK = "double_click";
|
|
10237
|
+
const SINGLE_CLICK = "single_click";
|
|
10217
10238
|
|
|
10218
10239
|
import.meta.css = /* css */ [`@property --swipe-progress {
|
|
10219
10240
|
syntax: "<number>";
|
|
@@ -10239,15 +10260,19 @@ import.meta.css = /* css */ [`@property --swipe-progress {
|
|
|
10239
10260
|
touch-action: none;
|
|
10240
10261
|
}
|
|
10241
10262
|
|
|
10263
|
+
:where([data-double-click]) {
|
|
10264
|
+
touch-action: manipulation;
|
|
10265
|
+
}
|
|
10266
|
+
|
|
10242
10267
|
[data-longpress] {
|
|
10243
10268
|
-webkit-touch-callout: none;
|
|
10244
10269
|
}
|
|
10245
10270
|
|
|
10246
|
-
[data-longpress], [data-swipe] {
|
|
10271
|
+
[data-longpress], [data-swipe], [data-double-click] {
|
|
10247
10272
|
user-select: none;
|
|
10248
10273
|
}
|
|
10249
10274
|
|
|
10250
|
-
:is([data-longpress], [data-swipe]) :is([data-drag-ignore], [popover], dialog), :is([data-longpress], [data-swipe]) :is(input:not([data-press-only]), textarea), :is([data-longpress], [data-swipe]) :is([contenteditable=""], [contenteditable="true"]) {
|
|
10275
|
+
:is([data-longpress], [data-swipe], [data-double-click]) :is([data-drag-ignore], [popover], dialog), :is([data-longpress], [data-swipe], [data-double-click]) :is(input:not([data-press-only]), textarea), :is([data-longpress], [data-swipe], [data-double-click]) :is([contenteditable=""], [contenteditable="true"]) {
|
|
10251
10276
|
user-select: text;
|
|
10252
10277
|
}
|
|
10253
10278
|
|
|
@@ -10266,9 +10291,13 @@ import.meta.css = /* css */ [`@property --swipe-progress {
|
|
|
10266
10291
|
|
|
10267
10292
|
defineInteractionDetector({
|
|
10268
10293
|
name: "press",
|
|
10269
|
-
claims: (type) =>
|
|
10270
|
-
|
|
10271
|
-
|
|
10294
|
+
claims: (type) =>
|
|
10295
|
+
type in AXIS_BY_SWIPE_TYPE ||
|
|
10296
|
+
type === LONGPRESS ||
|
|
10297
|
+
type === DOUBLE_CLICK ||
|
|
10298
|
+
type === SINGLE_CLICK,
|
|
10299
|
+
// Every one of them is "what this press turns out to be": until it turns out,
|
|
10300
|
+
// the press is theirs.
|
|
10272
10301
|
disputesPress: true,
|
|
10273
10302
|
setup: (element, trigger, { types, readConfig }) => {
|
|
10274
10303
|
let axes = "";
|
|
@@ -10278,7 +10307,10 @@ defineInteractionDetector({
|
|
|
10278
10307
|
axes += axis;
|
|
10279
10308
|
}
|
|
10280
10309
|
}
|
|
10281
|
-
const hasLongPress = types.includes(
|
|
10310
|
+
const hasLongPress = types.includes(LONGPRESS);
|
|
10311
|
+
const hasDoubleClick = types.includes(DOUBLE_CLICK);
|
|
10312
|
+
const hasSingleClick = types.includes(SINGLE_CLICK);
|
|
10313
|
+
const countsTaps = hasDoubleClick || hasSingleClick;
|
|
10282
10314
|
|
|
10283
10315
|
const undo = [];
|
|
10284
10316
|
const mark = (attribute, value) => {
|
|
@@ -10331,6 +10363,79 @@ defineInteractionDetector({
|
|
|
10331
10363
|
if (hasLongPress) {
|
|
10332
10364
|
mark(LONGPRESS_ATTRIBUTE, "");
|
|
10333
10365
|
}
|
|
10366
|
+
if (countsTaps) {
|
|
10367
|
+
mark(DOUBLE_CLICK_ATTRIBUTE, "");
|
|
10368
|
+
}
|
|
10369
|
+
|
|
10370
|
+
// The tap a next press may pair with, and the window it is waited for in. It
|
|
10371
|
+
// outlives the press that made it — which is what a double click IS — so it
|
|
10372
|
+
// lives here rather than in the pointerdown below.
|
|
10373
|
+
let firstTap = null;
|
|
10374
|
+
let tapWindowTimeout = null;
|
|
10375
|
+
let tapWait = null;
|
|
10376
|
+
const forgetTaps = () => {
|
|
10377
|
+
clearTimeout(tapWindowTimeout);
|
|
10378
|
+
tapWindowTimeout = null;
|
|
10379
|
+
firstTap = null;
|
|
10380
|
+
tapWait?.cancel();
|
|
10381
|
+
tapWait = null;
|
|
10382
|
+
};
|
|
10383
|
+
undo.push(forgetTaps);
|
|
10384
|
+
// The window closes on a tap nothing came back for. Counted from the press
|
|
10385
|
+
// that opened it rather than from the tap that ends it, so what the caller
|
|
10386
|
+
// tunes is one rhythm and not a press plus a pause — a press slow enough to be
|
|
10387
|
+
// a hold therefore closes its own window on the spot, and cannot start a
|
|
10388
|
+
// double click.
|
|
10389
|
+
const openTapWindow = (pressEvent, tapEvent, delay) => {
|
|
10390
|
+
firstTap = {
|
|
10391
|
+
at: pressEvent.timeStamp,
|
|
10392
|
+
x: tapEvent.clientX,
|
|
10393
|
+
y: tapEvent.clientY,
|
|
10394
|
+
pointerType: tapEvent.pointerType,
|
|
10395
|
+
};
|
|
10396
|
+
if (hasSingleClick) {
|
|
10397
|
+
// The element's click is navi's from here: it is held for the length of
|
|
10398
|
+
// the window and `single_click` is what says it happened (see the top of
|
|
10399
|
+
// this file).
|
|
10400
|
+
const clickSuppressionIsOver = suppressClickAfterGesture();
|
|
10401
|
+
clickSuppressionIsOver();
|
|
10402
|
+
}
|
|
10403
|
+
clearTimeout(tapWindowTimeout);
|
|
10404
|
+
tapWindowTimeout = setTimeout(
|
|
10405
|
+
() => {
|
|
10406
|
+
tapWindowTimeout = null;
|
|
10407
|
+
firstTap = null;
|
|
10408
|
+
if (hasSingleClick) {
|
|
10409
|
+
trigger(SINGLE_CLICK, tapEvent, {
|
|
10410
|
+
pointerType: tapEvent.pointerType,
|
|
10411
|
+
});
|
|
10412
|
+
}
|
|
10413
|
+
},
|
|
10414
|
+
delay - (tapEvent.timeStamp - pressEvent.timeStamp),
|
|
10415
|
+
);
|
|
10416
|
+
};
|
|
10417
|
+
|
|
10418
|
+
if (hasSingleClick) {
|
|
10419
|
+
// A click no press made — a keyboard activation, an `element.click()`. The
|
|
10420
|
+
// window exists to find out whether a second press is coming, and there is
|
|
10421
|
+
// no press here, so it is not held: said at once, the way the browser's own
|
|
10422
|
+
// click would have been.
|
|
10423
|
+
const onClick = (clickEvent) => {
|
|
10424
|
+
if (isPressDrivenClick(clickEvent)) {
|
|
10425
|
+
// A press's own click, swallowed by the suppression armed at its tap:
|
|
10426
|
+
// it never reaches here, and if it does (the suppression having already
|
|
10427
|
+
// been spent) the tap has said it or is about to.
|
|
10428
|
+
return;
|
|
10429
|
+
}
|
|
10430
|
+
trigger(SINGLE_CLICK, clickEvent, {
|
|
10431
|
+
pointerType: clickEvent.pointerType,
|
|
10432
|
+
});
|
|
10433
|
+
};
|
|
10434
|
+
element.addEventListener("click", onClick);
|
|
10435
|
+
undo.push(() => {
|
|
10436
|
+
element.removeEventListener("click", onClick);
|
|
10437
|
+
});
|
|
10438
|
+
}
|
|
10334
10439
|
|
|
10335
10440
|
const onPointerDown = (pointerDownEvent) => {
|
|
10336
10441
|
if (pointerDownEvent.button !== 0) {
|
|
@@ -10338,6 +10443,52 @@ defineInteractionDetector({
|
|
|
10338
10443
|
}
|
|
10339
10444
|
let swipe = null;
|
|
10340
10445
|
let press = null;
|
|
10446
|
+
// Set below, with the hold; a no-op until then so the swipe can call it.
|
|
10447
|
+
let forgetInnerLongPress = () => {};
|
|
10448
|
+
|
|
10449
|
+
if (countsTaps) {
|
|
10450
|
+
const delay = readConfig(
|
|
10451
|
+
DOUBLE_CLICK_DELAY_ATTRIBUTE,
|
|
10452
|
+
DOUBLE_CLICK_DELAY_DEFAULT,
|
|
10453
|
+
);
|
|
10454
|
+
const slop = readConfig(
|
|
10455
|
+
DOUBLE_CLICK_SLOP_ATTRIBUTE,
|
|
10456
|
+
DOUBLE_CLICK_SLOP_DEFAULT,
|
|
10457
|
+
);
|
|
10458
|
+
// Whether this press is the second of a pair is settled HERE, when it
|
|
10459
|
+
// lands, and not when it is let go of: the window is about where the
|
|
10460
|
+
// hand went and how soon, and a second press held a little longer than
|
|
10461
|
+
// the window would otherwise be answered as a lone click while it is
|
|
10462
|
+
// still down.
|
|
10463
|
+
const completesTheFirstTap =
|
|
10464
|
+
firstTap && continuesTap(firstTap, pointerDownEvent, delay, slop);
|
|
10465
|
+
if (completesTheFirstTap) {
|
|
10466
|
+
clearTimeout(tapWindowTimeout);
|
|
10467
|
+
tapWindowTimeout = null;
|
|
10468
|
+
} else {
|
|
10469
|
+
forgetTaps();
|
|
10470
|
+
}
|
|
10471
|
+
tapWait = waitForTap(pointerDownEvent, {
|
|
10472
|
+
slop,
|
|
10473
|
+
onTap: (tapEvent) => {
|
|
10474
|
+
tapWait = null;
|
|
10475
|
+
if (completesTheFirstTap) {
|
|
10476
|
+
firstTap = null;
|
|
10477
|
+
// The click this second press leaves behind was already answered,
|
|
10478
|
+
// by the gesture the two of them made. Armed and released at once:
|
|
10479
|
+
// the release does not lift the suppression, it says the gesture is
|
|
10480
|
+
// over (see suppressClickAfterGesture).
|
|
10481
|
+
const clickSuppressionIsOver = suppressClickAfterGesture();
|
|
10482
|
+
clickSuppressionIsOver();
|
|
10483
|
+
trigger(DOUBLE_CLICK, tapEvent, {
|
|
10484
|
+
pointerType: tapEvent.pointerType,
|
|
10485
|
+
});
|
|
10486
|
+
return;
|
|
10487
|
+
}
|
|
10488
|
+
openTapWindow(pointerDownEvent, tapEvent, delay);
|
|
10489
|
+
},
|
|
10490
|
+
});
|
|
10491
|
+
}
|
|
10341
10492
|
|
|
10342
10493
|
if (axes) {
|
|
10343
10494
|
swipe = startSwipe(pointerDownEvent, {
|
|
@@ -10357,18 +10508,46 @@ defineInteractionDetector({
|
|
|
10357
10508
|
onSwipeStart: () => {
|
|
10358
10509
|
press?.cancel();
|
|
10359
10510
|
press = null;
|
|
10511
|
+
forgetInnerLongPress();
|
|
10512
|
+
// …and it is not a tap either, nor the second of a pair: the press
|
|
10513
|
+
// has left, and what it left is a swipe.
|
|
10514
|
+
forgetTaps();
|
|
10360
10515
|
},
|
|
10361
10516
|
});
|
|
10362
10517
|
}
|
|
10363
10518
|
if (hasLongPress) {
|
|
10519
|
+
// A hold declared INSIDE this element, on this same press: the nearer
|
|
10520
|
+
// one answers, the way a click is the innermost target's, and this
|
|
10521
|
+
// wait is given up — two sheets opening from one hold is nobody's
|
|
10522
|
+
// intention. Delays being equal, the inner timer was set first (the
|
|
10523
|
+
// press reached it first) and fires first; the inner hold arrives here
|
|
10524
|
+
// as its own event, bubbling, before this timer runs.
|
|
10525
|
+
const onInnerLongPress = (longPressEvent) => {
|
|
10526
|
+
if (longPressEvent.target === element || !press) {
|
|
10527
|
+
return;
|
|
10528
|
+
}
|
|
10529
|
+
press.cancel();
|
|
10530
|
+
press = null;
|
|
10531
|
+
forgetInnerLongPress();
|
|
10532
|
+
warnOnceAboutHoldTakenByInner(element, longPressEvent.target);
|
|
10533
|
+
};
|
|
10534
|
+
forgetInnerLongPress = () => {
|
|
10535
|
+
element.removeEventListener("longpress", onInnerLongPress);
|
|
10536
|
+
};
|
|
10537
|
+
element.addEventListener("longpress", onInnerLongPress);
|
|
10364
10538
|
press = waitForPressHeld(pointerDownEvent, {
|
|
10365
10539
|
delay: readConfig(LONGPRESS_DELAY_ATTRIBUTE, LONGPRESS_DELAY_DEFAULT),
|
|
10366
10540
|
slop: readConfig(LONGPRESS_SLOP_ATTRIBUTE, LONGPRESS_SLOP_DEFAULT),
|
|
10541
|
+
onPressCancel: forgetInnerLongPress,
|
|
10367
10542
|
onPressHeld: (pressEvent, { endPress }) => {
|
|
10543
|
+
forgetInnerLongPress();
|
|
10368
10544
|
// The hold won the arbitration: the swipe never got the distance it
|
|
10369
|
-
// needed, and must not get it from whatever the finger does next.
|
|
10545
|
+
// needed, and must not get it from whatever the finger does next. The
|
|
10546
|
+
// double click loses the same way — a press held this long is the
|
|
10547
|
+
// hold's whether it is the first of a pair or the second.
|
|
10370
10548
|
swipe?.stop();
|
|
10371
10549
|
swipe = null;
|
|
10550
|
+
forgetTaps();
|
|
10372
10551
|
const clickSuppressionIsOver = suppressClickAfterGesture();
|
|
10373
10552
|
const onPointerEnd = () => {
|
|
10374
10553
|
window.removeEventListener("pointerup", onPointerEnd, true);
|
|
@@ -10397,6 +10576,11 @@ defineInteractionDetector({
|
|
|
10397
10576
|
};
|
|
10398
10577
|
},
|
|
10399
10578
|
});
|
|
10579
|
+
const warnOnceAboutHoldTakenByInner = (element, innerElement) => {
|
|
10580
|
+
{
|
|
10581
|
+
return;
|
|
10582
|
+
}
|
|
10583
|
+
};
|
|
10400
10584
|
|
|
10401
10585
|
const startSwipe = (
|
|
10402
10586
|
pointerDownEvent,
|
|
@@ -10498,6 +10682,21 @@ const startSwipe = (
|
|
|
10498
10682
|
});
|
|
10499
10683
|
};
|
|
10500
10684
|
|
|
10685
|
+
// Whether a press landing now completes the tap before it: the same hand, soon
|
|
10686
|
+
// enough, and near enough. Axis by axis rather than as a distance, the way every
|
|
10687
|
+
// other slop in this family is read.
|
|
10688
|
+
const continuesTap = (tap, pointerDownEvent, delay, slop) => {
|
|
10689
|
+
if (pointerDownEvent.pointerType !== tap.pointerType) {
|
|
10690
|
+
return false;
|
|
10691
|
+
}
|
|
10692
|
+
if (pointerDownEvent.timeStamp - tap.at >= delay) {
|
|
10693
|
+
return false;
|
|
10694
|
+
}
|
|
10695
|
+
const xApart = Math.abs(pointerDownEvent.clientX - tap.x);
|
|
10696
|
+
const yApart = Math.abs(pointerDownEvent.clientY - tap.y);
|
|
10697
|
+
return xApart < slop && yApart < slop;
|
|
10698
|
+
};
|
|
10699
|
+
|
|
10501
10700
|
const swipeTypeOf = (axis, pulled) => {
|
|
10502
10701
|
const { positive, negative } = SWIPE_TYPE_BY_AXIS[axis];
|
|
10503
10702
|
return pulled > 0 ? positive : negative;
|
|
@@ -26192,6 +26391,31 @@ const getHrefTargetInfo = (href) => {
|
|
|
26192
26391
|
};
|
|
26193
26392
|
};
|
|
26194
26393
|
|
|
26394
|
+
/*
|
|
26395
|
+
* An app can ship more than one document — an admin panel, a status page, an
|
|
26396
|
+
* entry point of its own — and a link to one of them is not a route, it is a
|
|
26397
|
+
* page load. The address does not say which of the two it is, and guessing
|
|
26398
|
+
* would be worse than not knowing: "no route matches, so it must be another
|
|
26399
|
+
* document" turns a typo'd in-app url into a silent reload of the app instead
|
|
26400
|
+
* of its own not-found screen. So the link says it.
|
|
26401
|
+
*
|
|
26402
|
+
* `<Link document>` / `<Button document>` is that, and the point of saying it
|
|
26403
|
+
* on a link rather than navigating from an action is everything a link is: an
|
|
26404
|
+
* address in the status bar, a middle click, "open in new tab", something for
|
|
26405
|
+
* assistive technology to read. Only the interception steps aside; the element
|
|
26406
|
+
* stays a real link and the browser loads the page.
|
|
26407
|
+
*
|
|
26408
|
+
* It travels as an attribute for the same reason `replace` does (see
|
|
26409
|
+
* link_replace.js): whoever answers the press sees the element, not the
|
|
26410
|
+
* component that rendered it.
|
|
26411
|
+
*/
|
|
26412
|
+
|
|
26413
|
+
const LINK_DOCUMENT_ATTRIBUTE = "data-navi-document";
|
|
26414
|
+
|
|
26415
|
+
const linkAsksForDocument = (linkElement) => {
|
|
26416
|
+
return linkElement.hasAttribute(LINK_DOCUMENT_ATTRIBUTE);
|
|
26417
|
+
};
|
|
26418
|
+
|
|
26195
26419
|
/*
|
|
26196
26420
|
* A press aims at a place; it does not always go one step deeper. A row of tabs
|
|
26197
26421
|
* is a lateral move — the neighbour is one finger away — so the whole row should
|
|
@@ -26525,6 +26749,12 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
26525
26749
|
}
|
|
26526
26750
|
return;
|
|
26527
26751
|
}
|
|
26752
|
+
if (linkAsksForDocument(linkElement)) {
|
|
26753
|
+
// The link says its address is another document of this origin (see
|
|
26754
|
+
// link_document.js). Routing to it would land on the fallback route
|
|
26755
|
+
// instead of loading the page.
|
|
26756
|
+
return;
|
|
26757
|
+
}
|
|
26528
26758
|
// Nothing here declared a route, so there is nothing to route to: the
|
|
26529
26759
|
// page is a plain document and a link in it is a plain link. Taking it
|
|
26530
26760
|
// over anyway would push the url and then have nothing to show for it —
|
|
@@ -27074,6 +27304,160 @@ const Head = ({
|
|
|
27074
27304
|
return null;
|
|
27075
27305
|
};
|
|
27076
27306
|
|
|
27307
|
+
/**
|
|
27308
|
+
* A page is `{ route, params }`, never the route alone: a section of a page is
|
|
27309
|
+
* as often a PARAM as it is a route of its own, and three branches of the same
|
|
27310
|
+
* route told apart by their params are three pages one walks between.
|
|
27311
|
+
* `params` is undefined for a page that is a route on its own.
|
|
27312
|
+
*
|
|
27313
|
+
* Whether such a page is the one on screen is asked from three places — the
|
|
27314
|
+
* row a <RouteTravel> walks, the relations a movement is written between, and
|
|
27315
|
+
* the fallback naming the pages it is the absence of — and they must all get
|
|
27316
|
+
* the same answer, which is why the reading lives here rather than next to any
|
|
27317
|
+
* one of them.
|
|
27318
|
+
*/
|
|
27319
|
+
|
|
27320
|
+
// `matchesParams` reads paramsSignal, so a caller reading this during a render
|
|
27321
|
+
// is subscribed to the param changes that walk from one tab to the next —
|
|
27322
|
+
// matchingSignal alone never moves there, and a row whose tabs are params of
|
|
27323
|
+
// one route would never re-render.
|
|
27324
|
+
//
|
|
27325
|
+
// The params are read only for a route that matches, and that is not a signal
|
|
27326
|
+
// left unread: a reader wakes on anything it read last time, so what matters is
|
|
27327
|
+
// that everything able to make this answer change is among them.
|
|
27328
|
+
// matchingSignal is read whatever happens, and it is a NECESSARY condition —
|
|
27329
|
+
// while it is false no param of that route can put this page on screen, and the
|
|
27330
|
+
// day one could, matchingSignal itself has to turn true to say so, which is the
|
|
27331
|
+
// read that brings the params back in. (Asking anyway would be worse than
|
|
27332
|
+
// useless: the params of a route that does not match are not params.)
|
|
27333
|
+
const pageIsCurrent = ({ route, params }) => {
|
|
27334
|
+
if (!route.matchingSignal.value) {
|
|
27335
|
+
return false;
|
|
27336
|
+
}
|
|
27337
|
+
return params ? route.matchesParams(params) : true;
|
|
27338
|
+
};
|
|
27339
|
+
|
|
27340
|
+
/**
|
|
27341
|
+
* The page a <Route> container shows when none of its branches matched, given
|
|
27342
|
+
* a name.
|
|
27343
|
+
*
|
|
27344
|
+
* Every other branch of a container is a route, and a route is a thing one can
|
|
27345
|
+
* talk ABOUT from outside the tree: write a movement between two of them (see
|
|
27346
|
+
* route_transition.jsx), put one in a row (see route_travel.jsx), read whether
|
|
27347
|
+
* it is on screen. The fallback branch is the one page defined by an ABSENCE —
|
|
27348
|
+
* "an address none of these claim" — so it has no url pattern to be declared
|
|
27349
|
+
* from, and until it is named, nothing can be said about it.
|
|
27350
|
+
*
|
|
27351
|
+
* const NOT_FOUND_PAGE = routeFallback();
|
|
27352
|
+
* <Route fallback={NOT_FOUND_PAGE} element={NotFoundPage} />
|
|
27353
|
+
* defineRouteTransition(null, NOT_FOUND_PAGE, "slide-x");
|
|
27354
|
+
*
|
|
27355
|
+
* The absence is of a PARTICULAR set of claims — the branches of the container
|
|
27356
|
+
* holding it — and that set is not knowable from the routes of the application:
|
|
27357
|
+
* a route matching every address is a normal thing to declare (a carrier for
|
|
27358
|
+
* search params and route actions the whole app shares), and it would answer
|
|
27359
|
+
* "something matched" everywhere. So the container fills it in from the
|
|
27360
|
+
* children it was written with (see collectBranches in route.jsx), and a
|
|
27361
|
+
* fallback no container holds claims nothing.
|
|
27362
|
+
*
|
|
27363
|
+
* That leaves what a container cannot see: the address its own subtree lives
|
|
27364
|
+
* under. A section owning its sub-router renders it several components below
|
|
27365
|
+
* the leaf that matched the section's prefix, and "none of these five matched"
|
|
27366
|
+
* is then true on every screen of the application. Such a fallback names the
|
|
27367
|
+
* route it is under, and answers only there:
|
|
27368
|
+
*
|
|
27369
|
+
* const DASHBOARD_NOT_FOUND_PAGE = routeFallback(DASHBOARD_SECTION_ROUTE);
|
|
27370
|
+
*/
|
|
27371
|
+
|
|
27372
|
+
|
|
27373
|
+
const routeFallbackPrivatePropertiesMap = new WeakMap();
|
|
27374
|
+
|
|
27375
|
+
/**
|
|
27376
|
+
* Names the fallback branch of a <Route> container, so relations can be
|
|
27377
|
+
* written about the page it shows and its `matchingSignal` read from anywhere.
|
|
27378
|
+
*
|
|
27379
|
+
* @param {object} [scopeRoute] - the route this container's subtree lives
|
|
27380
|
+
* under, when the container is not the application's own router. Without it
|
|
27381
|
+
* the fallback answers wherever its container is on screen.
|
|
27382
|
+
* @returns {object} pass it as `<Route fallback={...}>`.
|
|
27383
|
+
*/
|
|
27384
|
+
const routeFallback = (scopeRoute) => {
|
|
27385
|
+
const pagesSignal = signal(null);
|
|
27386
|
+
const nothingMatches = () => {
|
|
27387
|
+
const pages = pagesSignal.value;
|
|
27388
|
+
if (!pages) {
|
|
27389
|
+
return false;
|
|
27390
|
+
}
|
|
27391
|
+
for (const page of pages) {
|
|
27392
|
+
if (pageIsCurrent(page)) {
|
|
27393
|
+
return false;
|
|
27394
|
+
}
|
|
27395
|
+
}
|
|
27396
|
+
return true;
|
|
27397
|
+
};
|
|
27398
|
+
const routeFallback = {
|
|
27399
|
+
isRouteFallback: true,
|
|
27400
|
+
scopeRoute,
|
|
27401
|
+
matchingSignal: scopeRoute
|
|
27402
|
+
? computed(() => {
|
|
27403
|
+
if (!scopeRoute.matchingSignal.value) {
|
|
27404
|
+
return false;
|
|
27405
|
+
}
|
|
27406
|
+
return nothingMatches();
|
|
27407
|
+
})
|
|
27408
|
+
: computed(nothingMatches),
|
|
27409
|
+
toString: () => {
|
|
27410
|
+
if (scopeRoute) {
|
|
27411
|
+
return `route fallback under ${scopeRoute}`;
|
|
27412
|
+
}
|
|
27413
|
+
return `route fallback`;
|
|
27414
|
+
},
|
|
27415
|
+
};
|
|
27416
|
+
Object.preventExtensions(routeFallback);
|
|
27417
|
+
routeFallbackPrivatePropertiesMap.set(routeFallback, { pagesSignal });
|
|
27418
|
+
return routeFallback;
|
|
27419
|
+
};
|
|
27420
|
+
|
|
27421
|
+
/**
|
|
27422
|
+
* The pages the fallback is the absence of, handed over by the container
|
|
27423
|
+
* holding it. Read as a signal rather than at render: a movement is decided
|
|
27424
|
+
* while rendering is held, before the container has had a chance to choose a
|
|
27425
|
+
* branch (see rendering_hold.js), so "we are on the fallback" has to be
|
|
27426
|
+
* answerable from the route signals alone.
|
|
27427
|
+
*/
|
|
27428
|
+
const wireRouteFallback = (routeFallback, pages) => {
|
|
27429
|
+
const { pagesSignal } = routeFallbackPrivatePropertiesMap.get(routeFallback);
|
|
27430
|
+
const pagesCurrent = pagesSignal.peek();
|
|
27431
|
+
if (pagesCurrent && samePages(pagesCurrent, pages)) {
|
|
27432
|
+
return;
|
|
27433
|
+
}
|
|
27434
|
+
pagesSignal.value = pages;
|
|
27435
|
+
};
|
|
27436
|
+
|
|
27437
|
+
const unwireRouteFallback = (routeFallback) => {
|
|
27438
|
+
const { pagesSignal } = routeFallbackPrivatePropertiesMap.get(routeFallback);
|
|
27439
|
+
pagesSignal.value = null;
|
|
27440
|
+
};
|
|
27441
|
+
|
|
27442
|
+
const samePages = (pages, otherPages) => {
|
|
27443
|
+
if (pages.length !== otherPages.length) {
|
|
27444
|
+
return false;
|
|
27445
|
+
}
|
|
27446
|
+
let i = 0;
|
|
27447
|
+
while (i < pages.length) {
|
|
27448
|
+
const page = pages[i];
|
|
27449
|
+
const otherPage = otherPages[i];
|
|
27450
|
+
if (page.route !== otherPage.route) {
|
|
27451
|
+
return false;
|
|
27452
|
+
}
|
|
27453
|
+
if (!compareTwoJsValues(page.params, otherPage.params)) {
|
|
27454
|
+
return false;
|
|
27455
|
+
}
|
|
27456
|
+
i++;
|
|
27457
|
+
}
|
|
27458
|
+
return true;
|
|
27459
|
+
};
|
|
27460
|
+
|
|
27077
27461
|
/**
|
|
27078
27462
|
* Route is the single primitive for URL-based rendering.
|
|
27079
27463
|
*
|
|
@@ -27092,10 +27476,15 @@ const Head = ({
|
|
|
27092
27476
|
* <Route element={AuthLayout}>
|
|
27093
27477
|
* <Route route={PROFILE_ROUTE} element={ProfilePage} />
|
|
27094
27478
|
* <Route route={SETTINGS_ROUTE} element={SettingsPage} />
|
|
27095
|
-
* <Route fallback element={AuthNotFoundPage} />
|
|
27096
27479
|
* </Route>
|
|
27097
27480
|
* ```
|
|
27098
27481
|
*
|
|
27482
|
+
* Such a container holds no fallback, and cannot: what makes this one the right
|
|
27483
|
+
* layout is a child matching, and having no prefix is the very reason it was
|
|
27484
|
+
* reached for — so there is no address that belongs to it and to none of its
|
|
27485
|
+
* children. An address nothing claims is the router's to answer; write the
|
|
27486
|
+
* fallback there.
|
|
27487
|
+
*
|
|
27099
27488
|
* ## Self-contained section pattern
|
|
27100
27489
|
* Use this when routes share a common URL prefix (e.g. `/dashboard/`).
|
|
27101
27490
|
* A single leaf Route in the top-level router matches the prefix; the component
|
|
@@ -27176,7 +27565,10 @@ const debug$1 = (...args) => {
|
|
|
27176
27565
|
* @param {object} props
|
|
27177
27566
|
* @param {object} [props.route] - the route this branch is for, from `route()`
|
|
27178
27567
|
* @param {object} [props.routeParams] - selects a branch on a param of that route
|
|
27179
|
-
* @param {boolean} [props.fallback] - the branch taken when no sibling
|
|
27568
|
+
* @param {boolean|object} [props.fallback] - the branch taken when no sibling
|
|
27569
|
+
* matches. `routeFallback()` gives that page a name, which is what lets a
|
|
27570
|
+
* movement be written about it and its `matchingSignal` be read from outside
|
|
27571
|
+
* the tree (see route_fallback.js).
|
|
27180
27572
|
* @param {Function|import("ignore:preact").VNode} [props.element] - what the branch renders
|
|
27181
27573
|
* @param {object} [props.elementProps] - props given to `element`
|
|
27182
27574
|
*
|
|
@@ -27254,7 +27646,7 @@ const collectRoutePages = children => {
|
|
|
27254
27646
|
// RouteContainer: traverses children statically per render, finds the active branch,
|
|
27255
27647
|
// and renders only that branch — or the fallback if nothing matches.
|
|
27256
27648
|
// No contexts, no state of its own: it reads the route signals directly, and
|
|
27257
|
-
// its
|
|
27649
|
+
// its effects only tell the outside what it has just done.
|
|
27258
27650
|
const RouteContainer = ({
|
|
27259
27651
|
id,
|
|
27260
27652
|
element,
|
|
@@ -27262,8 +27654,11 @@ const RouteContainer = ({
|
|
|
27262
27654
|
children
|
|
27263
27655
|
}) => {
|
|
27264
27656
|
const {
|
|
27265
|
-
activeBranch
|
|
27657
|
+
activeBranch,
|
|
27658
|
+
fallbackBranch,
|
|
27659
|
+
pages
|
|
27266
27660
|
} = collectBranches(children);
|
|
27661
|
+
const fallbackPage = fallbackBranch ? fallbackBranch.page : null;
|
|
27267
27662
|
|
|
27268
27663
|
// Told to hold still: what is on screen stays on screen. Kept as the very
|
|
27269
27664
|
// vnode that was rendered last time, which is how Preact is told there is
|
|
@@ -27275,11 +27670,25 @@ const RouteContainer = ({
|
|
|
27275
27670
|
}
|
|
27276
27671
|
const branch = shownBranchRef.current || activeBranch;
|
|
27277
27672
|
|
|
27278
|
-
// The
|
|
27279
|
-
//
|
|
27673
|
+
// The two things this component knows that nobody outside can find out: the
|
|
27674
|
+
// pages a named fallback is the absence of — the children it was written
|
|
27675
|
+
// with — and that the branch it chose is in the DOM.
|
|
27280
27676
|
useLayoutEffect(() => {
|
|
27677
|
+
if (fallbackPage) {
|
|
27678
|
+
wireRouteFallback(fallbackPage, pages);
|
|
27679
|
+
}
|
|
27281
27680
|
publishRouteRender();
|
|
27282
27681
|
});
|
|
27682
|
+
// Being wired means the container holding it is on screen: a router taken out
|
|
27683
|
+
// of the document leaves no fallback behind still claiming an address.
|
|
27684
|
+
useLayoutEffect(() => {
|
|
27685
|
+
if (!fallbackPage) {
|
|
27686
|
+
return undefined;
|
|
27687
|
+
}
|
|
27688
|
+
return () => {
|
|
27689
|
+
unwireRouteFallback(fallbackPage);
|
|
27690
|
+
};
|
|
27691
|
+
}, [fallbackPage]);
|
|
27283
27692
|
debug$1(`[container "${id}"] RENDER, active=${branch ? branch.type : "none"}`);
|
|
27284
27693
|
const content = branch ? branch.node : null;
|
|
27285
27694
|
if (!content) {
|
|
@@ -27293,10 +27702,16 @@ const RouteContainer = ({
|
|
|
27293
27702
|
// Walk JSX children vnodes (without rendering) to build a branch list and
|
|
27294
27703
|
// find the active one in the same pass. Anything that is not a <Route> is read
|
|
27295
27704
|
// through and kept around the branch it holds (see below).
|
|
27296
|
-
// Returns { matchingBranch, fallbackBranch, activeBranch }.
|
|
27705
|
+
// Returns { matchingBranch, fallbackBranch, activeBranch, pages }.
|
|
27297
27706
|
const collectBranches = children => {
|
|
27298
27707
|
let matchingBranch = null;
|
|
27299
27708
|
let fallbackBranch = null;
|
|
27709
|
+
// Every page a branch can be on, at any depth: what a named fallback is the
|
|
27710
|
+
// ABSENCE of (see route_fallback.js). A container with a guard route counts
|
|
27711
|
+
// as a page of its own AND contributes the pages inside it — either its guard
|
|
27712
|
+
// matching or a leaf matching under it is enough to select it, and the
|
|
27713
|
+
// fallback then stands down.
|
|
27714
|
+
const pages = [];
|
|
27300
27715
|
const visit = child => {
|
|
27301
27716
|
if (!child || child === true || child === false) {
|
|
27302
27717
|
return;
|
|
@@ -27324,8 +27739,12 @@ const collectBranches = children => {
|
|
|
27324
27739
|
}
|
|
27325
27740
|
const {
|
|
27326
27741
|
matchingBranch: matchingInside,
|
|
27327
|
-
fallbackBranch: fallbackInside
|
|
27742
|
+
fallbackBranch: fallbackInside,
|
|
27743
|
+
pages: pagesInside
|
|
27328
27744
|
} = collectBranches(wrapperChildren);
|
|
27745
|
+
for (const pageInside of pagesInside) {
|
|
27746
|
+
pages.push(pageInside);
|
|
27747
|
+
}
|
|
27329
27748
|
if (matchingInside && !matchingBranch) {
|
|
27330
27749
|
matchingBranch = wrapBranch(matchingInside, child);
|
|
27331
27750
|
}
|
|
@@ -27342,8 +27761,31 @@ const collectBranches = children => {
|
|
|
27342
27761
|
} = child.props;
|
|
27343
27762
|
if (nodeChildren) {
|
|
27344
27763
|
const {
|
|
27345
|
-
matchingBranch: matchingChild
|
|
27764
|
+
matchingBranch: matchingChild,
|
|
27765
|
+
fallbackBranch: fallbackChild,
|
|
27766
|
+
pages: pagesChild
|
|
27346
27767
|
} = collectBranches(nodeChildren);
|
|
27768
|
+
if (fallbackChild && !route) {
|
|
27769
|
+
// A fallback is the branch taken for an address none of its siblings
|
|
27770
|
+
// claim — so something has to make that address BELONG to this
|
|
27771
|
+
// container, and only a route of its own does. Without one the
|
|
27772
|
+
// container is selected by a child matching, which is the one case
|
|
27773
|
+
// where its fallback is not wanted: the branch could never render, and
|
|
27774
|
+
// an unmatched address is the router's to answer, not a layout's.
|
|
27775
|
+
throw new Error(`${describeContainer(child)} has no route of its own, so its fallback could never render: it is selected by one of its children matching, and an address none of them claim belongs to the router around it. Give it a route to be the fallback of (<Route route={SECTION}>), or write the fallback in that router.`);
|
|
27776
|
+
}
|
|
27777
|
+
if (route) {
|
|
27778
|
+
// Params are left out on purpose: a guard selects its container on
|
|
27779
|
+
// matching alone (see guardMatching below), so the fallback stands
|
|
27780
|
+
// down for the whole route, not for one of its cases.
|
|
27781
|
+
pages.push({
|
|
27782
|
+
route,
|
|
27783
|
+
params: undefined
|
|
27784
|
+
});
|
|
27785
|
+
}
|
|
27786
|
+
for (const pageChild of pagesChild) {
|
|
27787
|
+
pages.push(pageChild);
|
|
27788
|
+
}
|
|
27347
27789
|
const branch = {
|
|
27348
27790
|
type: "container",
|
|
27349
27791
|
node: child
|
|
@@ -27363,7 +27805,8 @@ const collectBranches = children => {
|
|
|
27363
27805
|
if (!fallbackBranch) {
|
|
27364
27806
|
fallbackBranch = {
|
|
27365
27807
|
type: "fallback",
|
|
27366
|
-
node: child
|
|
27808
|
+
node: child,
|
|
27809
|
+
page: fallback.isRouteFallback ? fallback : null
|
|
27367
27810
|
};
|
|
27368
27811
|
}
|
|
27369
27812
|
} else {
|
|
@@ -27371,6 +27814,10 @@ const collectBranches = children => {
|
|
|
27371
27814
|
type: "leaf",
|
|
27372
27815
|
node: child
|
|
27373
27816
|
};
|
|
27817
|
+
pages.push({
|
|
27818
|
+
route,
|
|
27819
|
+
params: routeParams
|
|
27820
|
+
});
|
|
27374
27821
|
// every signal is read even once a match is found: reading is what
|
|
27375
27822
|
// subscribes the container to it, and a branch that is skipped today is
|
|
27376
27823
|
// the one that must wake the container up tomorrow
|
|
@@ -27386,9 +27833,19 @@ const collectBranches = children => {
|
|
|
27386
27833
|
return {
|
|
27387
27834
|
matchingBranch,
|
|
27388
27835
|
fallbackBranch,
|
|
27389
|
-
activeBranch
|
|
27836
|
+
activeBranch,
|
|
27837
|
+
pages
|
|
27390
27838
|
};
|
|
27391
27839
|
};
|
|
27840
|
+
const describeContainer = ({
|
|
27841
|
+
props
|
|
27842
|
+
}) => {
|
|
27843
|
+
const {
|
|
27844
|
+
element
|
|
27845
|
+
} = props;
|
|
27846
|
+
const name = typeof element === "function" ? element.name : null;
|
|
27847
|
+
return name ? `<Route element={${name}}>` : `<Route>`;
|
|
27848
|
+
};
|
|
27392
27849
|
const wrapBranch = (branch, wrapper) => {
|
|
27393
27850
|
return {
|
|
27394
27851
|
...branch,
|
|
@@ -29188,7 +29645,7 @@ const beginTransition = ({
|
|
|
29188
29645
|
// on, those would freeze the page until the browser gives up. The wait
|
|
29189
29646
|
// is raced with a short timer instead.
|
|
29190
29647
|
await Promise.race([renderWait.rendered, waitMs(50)]);
|
|
29191
|
-
} else if (pageIsCurrent
|
|
29648
|
+
} else if (pageIsCurrent(page)) {
|
|
29192
29649
|
await renderWait.rendered;
|
|
29193
29650
|
}
|
|
29194
29651
|
} finally {
|
|
@@ -29403,16 +29860,21 @@ const armRouteRenderWait$1 = () => {
|
|
|
29403
29860
|
};
|
|
29404
29861
|
const waitMs = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
29405
29862
|
|
|
29406
|
-
// A page written as the route itself,
|
|
29407
|
-
//
|
|
29863
|
+
// A page written as the route itself, the page named by the absence of every
|
|
29864
|
+
// route around it (see route_fallback.js), and the page written nowhere:
|
|
29865
|
+
// `from` may be left out, which is a relation about arriving at `to` from
|
|
29866
|
+
// anywhere.
|
|
29408
29867
|
const normalizePage$1 = page => {
|
|
29409
29868
|
if (!page) {
|
|
29410
29869
|
return null;
|
|
29411
29870
|
}
|
|
29412
|
-
|
|
29413
|
-
|
|
29414
|
-
|
|
29415
|
-
|
|
29871
|
+
if (page.isRoute || page.isRouteFallback) {
|
|
29872
|
+
return {
|
|
29873
|
+
route: page,
|
|
29874
|
+
params: undefined
|
|
29875
|
+
};
|
|
29876
|
+
}
|
|
29877
|
+
return page;
|
|
29416
29878
|
};
|
|
29417
29879
|
|
|
29418
29880
|
// Two pages are the same page when they select the same thing, not when they
|
|
@@ -29428,19 +29890,6 @@ const samePage$1 = (a, b) => {
|
|
|
29428
29890
|
};
|
|
29429
29891
|
const pageIndexOf$1 = (pages, page) => pages.findIndex(candidate => samePage$1(candidate, page));
|
|
29430
29892
|
|
|
29431
|
-
// Whether this page is the one on screen — same reading as route_travel.jsx's
|
|
29432
|
-
// own: matchingSignal is the necessary condition and is read whatever happens,
|
|
29433
|
-
// params only for a route that matches (the params of a route that does not
|
|
29434
|
-
// match are not params).
|
|
29435
|
-
const pageIsCurrent$1 = ({
|
|
29436
|
-
route,
|
|
29437
|
-
params
|
|
29438
|
-
}) => {
|
|
29439
|
-
if (!route.matchingSignal.value) {
|
|
29440
|
-
return false;
|
|
29441
|
-
}
|
|
29442
|
-
return params ? route.matchesParams(params) : true;
|
|
29443
|
-
};
|
|
29444
29893
|
// The FIRST page that answers, and every page read all the same: a page that
|
|
29445
29894
|
// is not the current one today is the one that must wake the reader tomorrow.
|
|
29446
29895
|
//
|
|
@@ -29453,7 +29902,7 @@ const pageIsCurrent$1 = ({
|
|
|
29453
29902
|
const currentPageIndex$1 = pages => {
|
|
29454
29903
|
let currentIndex = -1;
|
|
29455
29904
|
for (let i = 0; i < pages.length; i++) {
|
|
29456
|
-
const isCurrent = pageIsCurrent
|
|
29905
|
+
const isCurrent = pageIsCurrent(pages[i]);
|
|
29457
29906
|
if (!isCurrent) {
|
|
29458
29907
|
continue;
|
|
29459
29908
|
}
|
|
@@ -30806,28 +31255,6 @@ const samePage = (a, b) => {
|
|
|
30806
31255
|
};
|
|
30807
31256
|
const pageIndexOf = (pages, page) => pages.findIndex(candidate => samePage(candidate, page));
|
|
30808
31257
|
|
|
30809
|
-
// Whether this page is the one on screen. `matchesParams` reads paramsSignal,
|
|
30810
|
-
// so a caller reading this during a render is subscribed to the param changes
|
|
30811
|
-
// that walk from one tab to the next — matchingSignal alone never moves there,
|
|
30812
|
-
// and a row whose tabs are params of one route would never re-render.
|
|
30813
|
-
//
|
|
30814
|
-
// The params are read only for a route that matches, and that is not a signal
|
|
30815
|
-
// left unread: a reader wakes on anything it read last time, so what matters is
|
|
30816
|
-
// that everything able to make this answer change is among them.
|
|
30817
|
-
// matchingSignal is read whatever happens, and it is a NECESSARY condition —
|
|
30818
|
-
// while it is false no param of that route can put this page on screen, and the
|
|
30819
|
-
// day one could, matchingSignal itself has to turn true to say so, which is the
|
|
30820
|
-
// read that brings the params back in. (Asking anyway would be worse than
|
|
30821
|
-
// useless: the params of a route that does not match are not params.)
|
|
30822
|
-
const pageIsCurrent = ({
|
|
30823
|
-
route,
|
|
30824
|
-
params
|
|
30825
|
-
}) => {
|
|
30826
|
-
if (!route.matchingSignal.value) {
|
|
30827
|
-
return false;
|
|
30828
|
-
}
|
|
30829
|
-
return params ? route.matchesParams(params) : true;
|
|
30830
|
-
};
|
|
30831
31258
|
// The FIRST page that answers, as with the branches of a <Route>: several
|
|
30832
31259
|
// routes match at once — a literal one and the parameterized one it is a case of
|
|
30833
31260
|
// ("/games/new" is also a "/games/:gameId"), a section and the page inside it —
|
|
@@ -33727,6 +34154,17 @@ const useUIStateController = (
|
|
|
33727
34154
|
`merging button state into parent control group:`,
|
|
33728
34155
|
mergedState,
|
|
33729
34156
|
);
|
|
34157
|
+
// The sender's value is part of the answer, and whoever mirrors
|
|
34158
|
+
// this group — a picker, through its façade — reads the group's
|
|
34159
|
+
// state, not its children's: told the way a mount sync is
|
|
34160
|
+
// (silently, nobody has answered yet), so the send that follows
|
|
34161
|
+
// finds the value there. Not through the group's own onChange:
|
|
34162
|
+
// that re-aggregates from the children, and a button is not one.
|
|
34163
|
+
parentController.parentUIStateController?.onChildUIAction?.(
|
|
34164
|
+
parentController,
|
|
34165
|
+
e,
|
|
34166
|
+
{ stateChanged: true, silent: true },
|
|
34167
|
+
);
|
|
33730
34168
|
}
|
|
33731
34169
|
}
|
|
33732
34170
|
// Trigger uiAction/command side effects without changing UI state.
|
|
@@ -34095,6 +34533,12 @@ const useUIStateController = (
|
|
|
34095
34533
|
},
|
|
34096
34534
|
resetUIState: (e) => {
|
|
34097
34535
|
controller.setUIState(controller.state, e);
|
|
34536
|
+
// What the control holds moved back, and a bound signal mirrors what
|
|
34537
|
+
// it holds (see PROPAGATE_DOWN_EVENT_SET). Left on the value the
|
|
34538
|
+
// reset just undid — written there by the control itself at the
|
|
34539
|
+
// pick — the signal would hand it straight back on the next render,
|
|
34540
|
+
// and the refused value would win over the rollback.
|
|
34541
|
+
writeBoundSignal(controller.state);
|
|
34098
34542
|
},
|
|
34099
34543
|
// Read by the callout manager when it has nowhere else to point.
|
|
34100
34544
|
getCalloutAnchorElement: (event) =>
|
|
@@ -34198,9 +34642,27 @@ const useUIStateController = (
|
|
|
34198
34642
|
controller.hasStateProp = true;
|
|
34199
34643
|
const currentState = controller.state;
|
|
34200
34644
|
if (!compareTwoJsValues(state, currentState)) {
|
|
34201
|
-
|
|
34202
|
-
|
|
34203
|
-
|
|
34645
|
+
// A bound signal controls the state the way a `value` prop does —
|
|
34646
|
+
// and, unlike a `value`, it is written by the control itself at
|
|
34647
|
+
// every ui action. Read back here on a control that runs an action,
|
|
34648
|
+
// a signal matching the ui state is that echo (see the signal branch
|
|
34649
|
+
// below for the same rule on an uncontrolled control): `state` is
|
|
34650
|
+
// the rollback target, and taking the echo would make resetOnError
|
|
34651
|
+
// put back the very value the action is about to send, or has just
|
|
34652
|
+
// been refused. The action settles it — success acknowledges,
|
|
34653
|
+
// failure resets to what is kept here.
|
|
34654
|
+
const signalEchoesOwnWrite =
|
|
34655
|
+
Boolean(controlInfo.signal) &&
|
|
34656
|
+
Boolean(props.action) &&
|
|
34657
|
+
compareTwoJsValues(state, controller.uiState);
|
|
34658
|
+
if (!signalEchoesOwnWrite) {
|
|
34659
|
+
controller.state = state;
|
|
34660
|
+
if (!optimisticWorkInFlight) {
|
|
34661
|
+
controller.setUIState(
|
|
34662
|
+
state,
|
|
34663
|
+
new CustomEvent("state_prop_change"),
|
|
34664
|
+
);
|
|
34665
|
+
}
|
|
34204
34666
|
}
|
|
34205
34667
|
}
|
|
34206
34668
|
} else {
|
|
@@ -35360,6 +35822,10 @@ const useUIGroupStateController = (
|
|
|
35360
35822
|
const { controller } = s;
|
|
35361
35823
|
const prevDefaultValue = controller.defaultValue;
|
|
35362
35824
|
controller.props = props;
|
|
35825
|
+
// Published like a leaf's (see the leaf update above): a child's named
|
|
35826
|
+
// button reaches the group's owner through it (see the button branch of
|
|
35827
|
+
// onUIAction).
|
|
35828
|
+
controller.parentUIStateController = parentUIStateController;
|
|
35363
35829
|
controller.ref = ref;
|
|
35364
35830
|
controller.id = id;
|
|
35365
35831
|
controller.name = name;
|
|
@@ -41477,6 +41943,17 @@ Object.assign(PSEUDO_CLASSES, {
|
|
|
41477
41943
|
* way there changes. What a row of tabs wants — the neighbour is a lateral
|
|
41478
41944
|
* move, not a step deeper, so the whole row weighs one entry and the back
|
|
41479
41945
|
* button leaves by where the reader came in.
|
|
41946
|
+
* @param {boolean} [props.document] - The address is ANOTHER DOCUMENT of this
|
|
41947
|
+
* origin — an admin panel, a status page, anything built as its own entry
|
|
41948
|
+
* point — so the press is left to the browser and loads that page. Without
|
|
41949
|
+
* it navi routes every same-origin address it can intercept, and one no
|
|
41950
|
+
* route matches lands on the app's not-found screen. Said rather than
|
|
41951
|
+
* guessed: "no route matches" is also what a typo in an in-app url looks
|
|
41952
|
+
* like, and that one must show the not-found screen instead of silently
|
|
41953
|
+
* reloading the app. The element stays a real link — an address in the
|
|
41954
|
+
* status bar, a middle click, "open in new tab" — which is what navigating
|
|
41955
|
+
* from an `action` costs; `target="_blank"` is a different thing, a new tab.
|
|
41956
|
+
* Nothing is prefetched on the way there: the routes do not lead there.
|
|
41480
41957
|
* @param {boolean} [props.prefetch=true] Fetch the code of where this leads when the
|
|
41481
41958
|
* pointer or the focus arrives, ahead of the press (see
|
|
41482
41959
|
* docs/dynamic_import.md): the route actions that ask nothing of the
|
|
@@ -41579,13 +42056,17 @@ const LinkPlain = props => {
|
|
|
41579
42056
|
routeTransition,
|
|
41580
42057
|
pressableDuringRouteTransition,
|
|
41581
42058
|
replace,
|
|
42059
|
+
document: isDocument,
|
|
41582
42060
|
prefetch = true,
|
|
41583
42061
|
children
|
|
41584
42062
|
} = props;
|
|
41585
42063
|
if (anchor && !props.id) {
|
|
41586
42064
|
props.id = href.slice(1);
|
|
41587
42065
|
}
|
|
41588
|
-
|
|
42066
|
+
// A document link leads out of the routes, so there is nothing there to
|
|
42067
|
+
// prefetch — and the address would otherwise be preloaded against the very
|
|
42068
|
+
// routes it is escaping.
|
|
42069
|
+
usePreloadOnIntent(props.ref, href, isDocument ? false : prefetch);
|
|
41589
42070
|
const selectionContext = useContext(SelectionContext);
|
|
41590
42071
|
const nav = useContext(NavContext);
|
|
41591
42072
|
const visited = useIsVisited(href);
|
|
@@ -41700,6 +42181,12 @@ const LinkPlain = props => {
|
|
|
41700
42181
|
const replaceRequest = replace ? {
|
|
41701
42182
|
[LINK_REPLACE_ATTRIBUTE]: ""
|
|
41702
42183
|
} : null;
|
|
42184
|
+
|
|
42185
|
+
// That this address is another document is the link's to say, worn where the
|
|
42186
|
+
// interception looks for it (see link_document.js).
|
|
42187
|
+
const documentRequest = isDocument ? {
|
|
42188
|
+
[LINK_DOCUMENT_ATTRIBUTE]: ""
|
|
42189
|
+
} : null;
|
|
41703
42190
|
const innerChildren = children || (hrefFallback ? href : children);
|
|
41704
42191
|
const startIconEl = startIcon;
|
|
41705
42192
|
const endIconEl = innerEndIcon;
|
|
@@ -41753,12 +42240,14 @@ const LinkPlain = props => {
|
|
|
41753
42240
|
routeTransition: undefined,
|
|
41754
42241
|
pressableDuringRouteTransition: undefined,
|
|
41755
42242
|
replace: undefined,
|
|
42243
|
+
document: undefined,
|
|
41756
42244
|
prefetch: undefined,
|
|
41757
42245
|
"data-navi-route-transition-request": routeTransitionRequest,
|
|
41758
42246
|
...(pressableDuringRouteTransition ? {
|
|
41759
42247
|
[PRESSABLE_ATTRIBUTE]: ""
|
|
41760
42248
|
} : null),
|
|
41761
42249
|
...replaceRequest,
|
|
42250
|
+
...documentRequest,
|
|
41762
42251
|
// The control's own handlers first — the interaction gate, the caller's
|
|
41763
42252
|
// onClick/onKeyDown, the command and the action — then what only a link
|
|
41764
42253
|
// does. Written over the spread above, so they have to be called here.
|
|
@@ -44883,9 +45372,15 @@ const createOpenController = (
|
|
|
44883
45372
|
change();
|
|
44884
45373
|
};
|
|
44885
45374
|
const runChange = (change, { opened, event }) => {
|
|
45375
|
+
const applyChange = () => {
|
|
45376
|
+
// Recorded with the change itself: what the DOM shows is what a render
|
|
45377
|
+
// landing between the ask and the picture must draw (see `openedInDom`).
|
|
45378
|
+
controller.openedInDom = opened;
|
|
45379
|
+
change();
|
|
45380
|
+
};
|
|
44886
45381
|
const { transitionChange } = controller;
|
|
44887
45382
|
if (!transitionChange) {
|
|
44888
|
-
|
|
45383
|
+
applyChange();
|
|
44889
45384
|
return;
|
|
44890
45385
|
}
|
|
44891
45386
|
// What the controller answers about itself does not wait for the picture:
|
|
@@ -44894,10 +45389,10 @@ const createOpenController = (
|
|
|
44894
45389
|
// caller's own signal), and a request arriving before the change lands
|
|
44895
45390
|
// runs it first rather than reading a DOM that disagrees.
|
|
44896
45391
|
controller.opened = opened;
|
|
44897
|
-
changeAwaitingTransition =
|
|
45392
|
+
changeAwaitingTransition = applyChange;
|
|
44898
45393
|
transitionChange(
|
|
44899
45394
|
() => {
|
|
44900
|
-
if (changeAwaitingTransition ===
|
|
45395
|
+
if (changeAwaitingTransition === applyChange) {
|
|
44901
45396
|
flushChangeAwaitingTransition();
|
|
44902
45397
|
}
|
|
44903
45398
|
},
|
|
@@ -45015,6 +45510,13 @@ const createOpenController = (
|
|
|
45015
45510
|
};
|
|
45016
45511
|
const controller = {
|
|
45017
45512
|
opened: false,
|
|
45513
|
+
// What the DOM currently shows, as opposed to what the controller has
|
|
45514
|
+
// decided: the two differ for the frame a change spends waiting for the
|
|
45515
|
+
// browser to photograph the state being left (see runChange). A render
|
|
45516
|
+
// landing in that frame — a picker re-rendering because its action just
|
|
45517
|
+
// started — must draw THIS, or it paints the closed state before the
|
|
45518
|
+
// picture is taken and the movement has nothing to leave from.
|
|
45519
|
+
openedInDom: false,
|
|
45018
45520
|
// Which press the popup opened during, written at every open (see
|
|
45019
45521
|
// openedDuringThisPress). Never any press before there has been one.
|
|
45020
45522
|
pressCountAtOpen: null,
|
|
@@ -47583,6 +48085,7 @@ const ButtonUI = props => {
|
|
|
47583
48085
|
target,
|
|
47584
48086
|
rel,
|
|
47585
48087
|
replace,
|
|
48088
|
+
document: isDocument,
|
|
47586
48089
|
pressableDuringRouteTransition,
|
|
47587
48090
|
prefetch = true,
|
|
47588
48091
|
// visual
|
|
@@ -47637,13 +48140,21 @@ const ButtonUI = props => {
|
|
|
47637
48140
|
[LINK_REPLACE_ATTRIBUTE]: ""
|
|
47638
48141
|
} : null;
|
|
47639
48142
|
|
|
48143
|
+
// And this one says the address is another document of this origin, so the
|
|
48144
|
+
// press is the browser's (see link_document.js).
|
|
48145
|
+
const documentRequest = isDocument ? {
|
|
48146
|
+
[LINK_DOCUMENT_ATTRIBUTE]: ""
|
|
48147
|
+
} : null;
|
|
48148
|
+
|
|
47640
48149
|
// Worn as an attribute too, and read at the document by whoever catches the
|
|
47641
48150
|
// press a movement would have swallowed (see transition_press.js).
|
|
47642
48151
|
const pressableRequest = pressableDuringRouteTransition ? {
|
|
47643
48152
|
[PRESSABLE_ATTRIBUTE]: ""
|
|
47644
48153
|
} : null;
|
|
47645
48154
|
const visualSelector = ".navi_button_content";
|
|
47646
|
-
|
|
48155
|
+
// Nothing to prefetch on the way to another document: the routes do not
|
|
48156
|
+
// lead there.
|
|
48157
|
+
usePreloadOnIntent(ref, href, isDocument ? false : prefetch);
|
|
47647
48158
|
useAccentColorAttributes(ref, null, {
|
|
47648
48159
|
elementSelector: visualSelector
|
|
47649
48160
|
});
|
|
@@ -47671,6 +48182,8 @@ const ButtonUI = props => {
|
|
|
47671
48182
|
rel: innerRel,
|
|
47672
48183
|
replace: undefined,
|
|
47673
48184
|
...replaceRequest,
|
|
48185
|
+
document: undefined,
|
|
48186
|
+
...documentRequest,
|
|
47674
48187
|
pressableDuringRouteTransition: undefined,
|
|
47675
48188
|
...pressableRequest,
|
|
47676
48189
|
prefetch: undefined
|
|
@@ -48114,6 +48627,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
48114
48627
|
* selfInteractions?: string,
|
|
48115
48628
|
* whenSelfInteractionsBlocked?: "hide" | "refuse" | "ignore",
|
|
48116
48629
|
* replace?: boolean,
|
|
48630
|
+
* document?: boolean,
|
|
48117
48631
|
* actionStandalone?: boolean,
|
|
48118
48632
|
* actionAbortable?: boolean,
|
|
48119
48633
|
* optimistic?: boolean,
|
|
@@ -48123,6 +48637,13 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
48123
48637
|
* `--navi-nav-to` command — by TAKING THE PLACE of the current history entry
|
|
48124
48638
|
* rather than stacking on it: what `<Link replace>` says, for a press drawn
|
|
48125
48639
|
* as a button.
|
|
48640
|
+
* @param {boolean} [document] The `href` is ANOTHER DOCUMENT of this origin —
|
|
48641
|
+
* an admin panel, a status page, anything built as its own entry point — so
|
|
48642
|
+
* the press is left to the browser and loads that page, instead of being
|
|
48643
|
+
* routed to the app's not-found screen. What `<Link document>` says, for a
|
|
48644
|
+
* press drawn as a button; and the reason to say it rather than navigate
|
|
48645
|
+
* from an `action` is that the button stays a real link (an address, a
|
|
48646
|
+
* middle click, "open in new tab"). Nothing is prefetched on the way there.
|
|
48126
48647
|
* @param {boolean} [prefetch=true] Fetch the code of where this leads when the
|
|
48127
48648
|
* pointer or the focus arrives, ahead of the press (see
|
|
48128
48649
|
* docs/dynamic_import.md): the route actions that ask nothing of the
|
|
@@ -58061,10 +58582,10 @@ const css$E = /* css */`
|
|
|
58061
58582
|
var(--dialog-maxmax-height)
|
|
58062
58583
|
);
|
|
58063
58584
|
|
|
58064
|
-
/* Base default: also
|
|
58585
|
+
/* Base default: also a local dialog's own permanent value — its
|
|
58065
58586
|
containing block is genuinely its nearest positioned ancestor,
|
|
58066
58587
|
regardless of positionArea. See the [data-layer="top"] rule below for
|
|
58067
|
-
why
|
|
58588
|
+
why a top-layer one overrides this. Position is always
|
|
58068
58589
|
JS-driven (pickPositionRelativeTo, see useDialogProps below) — no CSS
|
|
58069
58590
|
alignment/inset math here at all. */
|
|
58070
58591
|
position: absolute;
|
|
@@ -58147,13 +58668,8 @@ const css$E = /* css */`
|
|
|
58147
58668
|
var(--container-position-remaining-width, var(--dialog-maxmax-width)),
|
|
58148
58669
|
var(--dialog-maxmax-width)
|
|
58149
58670
|
);
|
|
58150
|
-
/* The sheet rests on the screen's bottom edge, which on a phone is the
|
|
58151
|
-
home indicator and, since iOS 26, Safari's own floating bar — a band
|
|
58152
|
-
the browser does not paint fixed content into. The surface still
|
|
58153
|
-
reaches the edge (the sheet comes out from behind the bar); what it
|
|
58154
|
-
holds stops above it. */
|
|
58155
|
-
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
58156
58671
|
}
|
|
58672
|
+
|
|
58157
58673
|
/* The clamped max, not --dialog-maxmax-*: that one is the viewport minus
|
|
58158
58674
|
the spacing, which is only the real ceiling for layer="top". A local
|
|
58159
58675
|
dialog is confined to its positioned ancestor, whose size reaches here
|
|
@@ -58181,6 +58697,35 @@ const css$E = /* css */`
|
|
|
58181
58697
|
border-bottom-left-radius: 0;
|
|
58182
58698
|
}
|
|
58183
58699
|
|
|
58700
|
+
/* An edge the dialog sits flush against is an edge of the screen, and the
|
|
58701
|
+
device keeps a band there it paints nothing into: the notch, the home
|
|
58702
|
+
indicator, Safari's own floating bar since iOS 26. The surface still
|
|
58703
|
+
reaches the edge — a sheet comes out from behind the bar rather than
|
|
58704
|
+
stopping short of it — and what it holds stops at the band.
|
|
58705
|
+
|
|
58706
|
+
env(), not --navi-safe-area-inset-*: that one is the app's own free
|
|
58707
|
+
region and counts its fixed bars, which this dialog is in front of. The
|
|
58708
|
+
device's inset is the only thing left to keep.
|
|
58709
|
+
|
|
58710
|
+
layer="top" only. A local dialog is flush with the box it was declared
|
|
58711
|
+
in, and whether that box reaches the screen's edge is something only the
|
|
58712
|
+
app knows — it gives that band back on its own container (see
|
|
58713
|
+
safe_area.js's [data-navi-safe-area]). */
|
|
58714
|
+
&[data-layer="top"] {
|
|
58715
|
+
&[data-flush-top] {
|
|
58716
|
+
padding-top: env(safe-area-inset-top, 0px);
|
|
58717
|
+
}
|
|
58718
|
+
&[data-flush-right] {
|
|
58719
|
+
padding-right: env(safe-area-inset-right, 0px);
|
|
58720
|
+
}
|
|
58721
|
+
&[data-flush-bottom] {
|
|
58722
|
+
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
58723
|
+
}
|
|
58724
|
+
&[data-flush-left] {
|
|
58725
|
+
padding-left: env(safe-area-inset-left, 0px);
|
|
58726
|
+
}
|
|
58727
|
+
}
|
|
58728
|
+
|
|
58184
58729
|
/* The placement is a translate, so the translate property is spoken for
|
|
58185
58730
|
here (see applyNewPosition in visible_rect.js, which owns it and animates
|
|
58186
58731
|
it itself through the Web Animations API rather than through this file's
|
|
@@ -58193,8 +58738,8 @@ const css$E = /* css */`
|
|
|
58193
58738
|
--backdrop-filter: var(--navi-backdrop-capture-backdrop-filter);
|
|
58194
58739
|
}
|
|
58195
58740
|
/* backdropVariant, after the rules it overrides: same specificity, so
|
|
58196
|
-
order is what decides.
|
|
58197
|
-
|
|
58741
|
+
order is what decides. The wall is still there either way — only the
|
|
58742
|
+
paint goes away. */
|
|
58198
58743
|
&[data-backdrop-variant="discrete"] {
|
|
58199
58744
|
--backdrop-background: var(--navi-backdrop-discrete-background);
|
|
58200
58745
|
--backdrop-filter: var(--navi-backdrop-discrete-backdrop-filter);
|
|
@@ -58258,7 +58803,10 @@ const css$E = /* css */`
|
|
|
58258
58803
|
--navi-focus-outline-style: none;
|
|
58259
58804
|
}
|
|
58260
58805
|
|
|
58261
|
-
|
|
58806
|
+
/* :popover-open too — a top-layer dialog with no wall is shown with
|
|
58807
|
+
showPopover(), which never sets the [open] attribute. */
|
|
58808
|
+
&[open],
|
|
58809
|
+
&:popover-open {
|
|
58262
58810
|
display: flex;
|
|
58263
58811
|
}
|
|
58264
58812
|
|
|
@@ -58295,14 +58843,12 @@ const css$E = /* css */`
|
|
|
58295
58843
|
}
|
|
58296
58844
|
}
|
|
58297
58845
|
|
|
58298
|
-
/*
|
|
58846
|
+
/* layer="local" only — a .show()'d dialog gets no ::backdrop, so its wall is
|
|
58299
58847
|
a real sibling element instead, same idea/CSS shape as Popover's own
|
|
58300
|
-
.navi_popover_backdrop (see popover.jsx's top comment for the design
|
|
58301
|
-
|
|
58302
|
-
|
|
58303
|
-
|
|
58304
|
-
via-attribute renderer for free regardless of
|
|
58305
|
-
pointerInteractionOutsideEffect. */
|
|
58848
|
+
.navi_popover_backdrop (see popover.jsx's top comment for the design this
|
|
58849
|
+
mirrors). Rendered whenever backdrop={true}, whatever
|
|
58850
|
+
pointerInteractionOutsideEffect says: a wall that closes nothing still
|
|
58851
|
+
absorbs the press, matching what showModal() gives a modal for free. */
|
|
58306
58852
|
.navi_dialog_backdrop {
|
|
58307
58853
|
--popup-animation-duration: 0.18s;
|
|
58308
58854
|
|
|
@@ -58317,7 +58863,7 @@ const css$E = /* css */`
|
|
|
58317
58863
|
in openEffect) gets pointer-events: none mid-transition. */
|
|
58318
58864
|
pointer-events: auto;
|
|
58319
58865
|
|
|
58320
|
-
/* Painted through the same two variables as
|
|
58866
|
+
/* Painted through the same two variables as a modal's own
|
|
58321
58867
|
::backdrop (see them for what each rule is for) — here they resolve on
|
|
58322
58868
|
the element that paints, which is also the element the
|
|
58323
58869
|
backdropColor/backdropFilter props are set on. Declared unconditionally
|
|
@@ -58409,12 +58955,15 @@ const css$E = /* css */`
|
|
|
58409
58955
|
* grammar, anchor's sizing-only role, backdrop mechanics).
|
|
58410
58956
|
*
|
|
58411
58957
|
* @param {object} props
|
|
58412
|
-
* @param {"top"|"local"} [props.layer="top"] -
|
|
58413
|
-
*
|
|
58414
|
-
*
|
|
58415
|
-
* shown via the non-modal `.show()` instead, staying in normal document
|
|
58958
|
+
* @param {"top"|"local"} [props.layer="top"] - What the dialog is placed
|
|
58959
|
+
* against. `"top"`: the browser's own top layer, placed against the screen,
|
|
58960
|
+
* never clipped by anything the app declared. `"local"`: normal document
|
|
58416
58961
|
* flow inside its own positioned ancestor — confined to (and clipped by)
|
|
58417
|
-
* that container instead of the whole viewport.
|
|
58962
|
+
* that container instead of the whole viewport. Whether the top-layer one
|
|
58963
|
+
* is modal is `backdrop`'s answer, not this one's: with a backdrop it is
|
|
58964
|
+
* `showModal()`'d (native focus trap, `Escape`-to-cancel, hardware
|
|
58965
|
+
* back-button dismissal, rest-of-document made inert), without one it is
|
|
58966
|
+
* shown through the Popover API and the page behind stays live.
|
|
58418
58967
|
* @param {boolean} [props.dockedOnSmallTouchScreen] - Turns the dialog into a
|
|
58419
58968
|
* bottom sheet (docked flush to the bottom edge, full width) on a small touch
|
|
58420
58969
|
* screen, and leaves it alone otherwise. For a dialog meant to be interacted
|
|
@@ -58477,9 +59026,12 @@ const css$E = /* css */`
|
|
|
58477
59026
|
* outside press does or how the backdrop is painted. `false` leaves the page
|
|
58478
59027
|
* reachable: a press outside closes the dialog (per
|
|
58479
59028
|
* `pointerInteractionOutsideEffect`) *and* is answered by whatever it landed
|
|
58480
|
-
* on, in the same gesture
|
|
58481
|
-
*
|
|
58482
|
-
*
|
|
59029
|
+
* on, in the same gesture; focus is free to leave too, the page behind being
|
|
59030
|
+
* meant to be reached. Works in either layer — a top-layer dialog with no
|
|
59031
|
+
* wall is shown through the Popover API rather than `showModal()`, which is
|
|
59032
|
+
* what a sheet docked to the screen's bottom edge over a still-readable map
|
|
59033
|
+
* needs. What it gives up is what only a modal gets natively: the hardware
|
|
59034
|
+
* back button no longer dismisses it. See docs/popup_backdrop.md.
|
|
58483
59035
|
* @param {"close"|"cancel"|"capture"|"none"} [props.pointerInteractionOutsideEffect="close"]
|
|
58484
59036
|
* - `"close"` closes the dialog on an outside click. `"capture"`/`"none"`
|
|
58485
59037
|
* both just absorb the click without closing (visually dimmed backdrop vs.
|
|
@@ -58739,7 +59291,7 @@ const UncontrolledDialog = props => {
|
|
|
58739
59291
|
// Picks which rendering strategy actually mounts, from `layer` alone — see
|
|
58740
59292
|
// this file's top comment. Done after the controlled/uncontrolled split
|
|
58741
59293
|
// above, so an openController is always already resolved by the time
|
|
58742
|
-
//
|
|
59294
|
+
// DialogInTopLayer/DialogLocal (and the useDialogProps hook they share) ever
|
|
58743
59295
|
// run.
|
|
58744
59296
|
const ControlledDialog = props => {
|
|
58745
59297
|
if (props.layer === "local") {
|
|
@@ -58747,11 +59299,11 @@ const ControlledDialog = props => {
|
|
|
58747
59299
|
...props
|
|
58748
59300
|
});
|
|
58749
59301
|
}
|
|
58750
|
-
return jsx(
|
|
59302
|
+
return jsx(DialogInTopLayer, {
|
|
58751
59303
|
...props
|
|
58752
59304
|
});
|
|
58753
59305
|
};
|
|
58754
|
-
const
|
|
59306
|
+
const DialogInTopLayer = props => {
|
|
58755
59307
|
const [backdropProps, contentProps] = useDialogProps(props);
|
|
58756
59308
|
return jsxs(Fragment, {
|
|
58757
59309
|
children: [backdropProps && jsx(Box, {
|
|
@@ -58783,12 +59335,14 @@ const DialogLocal = props => {
|
|
|
58783
59335
|
/**
|
|
58784
59336
|
* Everything both rendering strategies share once an `openController` is
|
|
58785
59337
|
* already resolved: focus/debug/id plumbing, the open-commit sequence, the
|
|
58786
|
-
* close handler — inlined in `openEffect`, branching on
|
|
59338
|
+
* close handler — inlined in `openEffect`, branching on how the dialog is
|
|
59339
|
+
* shown at each
|
|
58787
59340
|
* point the two renderers genuinely differ (same pattern as popover.jsx's
|
|
58788
59341
|
* own usePopoverProps — see its top comment for why this stays inline
|
|
58789
59342
|
* rather than split into two functions). Returns `[backdropProps,
|
|
58790
|
-
* contentProps]` — `backdropProps` is `null`
|
|
58791
|
-
*
|
|
59343
|
+
* contentProps]` — `backdropProps` is `null` unless there is a wall of our
|
|
59344
|
+
* own to render: a modal's is the native `::backdrop`, and `backdrop={false}`
|
|
59345
|
+
* has none at all.
|
|
58792
59346
|
*/
|
|
58793
59347
|
// What a dialog turns into on a small touch screen. "bottom" is not a taste:
|
|
58794
59348
|
// it puts the dialog in the zone a phone is actually operated from — where the
|
|
@@ -58820,10 +59374,10 @@ const useDialogProps = props => {
|
|
|
58820
59374
|
props.ref = props.ref || defaultRef;
|
|
58821
59375
|
const {
|
|
58822
59376
|
openController,
|
|
58823
|
-
// "top" (default) →
|
|
58824
|
-
//
|
|
58825
|
-
// .
|
|
58826
|
-
//
|
|
59377
|
+
// "top" (default) → the browser's own top layer, placed against the
|
|
59378
|
+
// screen. "local" → normal document flow, position: absolute relative to
|
|
59379
|
+
// its own positioned ancestor. Which show call each takes also depends on
|
|
59380
|
+
// `backdrop` — see this file's top comment.
|
|
58827
59381
|
layer = "top",
|
|
58828
59382
|
dockedOnSmallTouchScreen,
|
|
58829
59383
|
// Same grammar as Popover's own positionArea — see this file's top
|
|
@@ -58837,17 +59391,16 @@ const useDialogProps = props => {
|
|
|
58837
59391
|
expandX: expandXProp,
|
|
58838
59392
|
expandY: expandYProp,
|
|
58839
59393
|
// "close" (default) closes on an outside click. "capture"/"none" both
|
|
58840
|
-
// just absorb it without closing — for
|
|
58841
|
-
//
|
|
58842
|
-
//
|
|
58843
|
-
//
|
|
58844
|
-
// actually makes "capture"/"none" behave the same way here too.
|
|
59394
|
+
// just absorb it without closing — for a modal, showModal() already makes
|
|
59395
|
+
// the rest of the page inert, so there's nothing for a click to reach
|
|
59396
|
+
// either way; otherwise there's no native inert-ing, so the real backdrop
|
|
59397
|
+
// below is what makes "capture"/"none" behave the same way here too.
|
|
58845
59398
|
pointerInteractionOutsideEffect = "close",
|
|
58846
59399
|
// Whether there is a wall between the dialog and the page at all, asked
|
|
58847
59400
|
// before what a press on it does (pointerInteractionOutsideEffect) and
|
|
58848
|
-
// before how it is painted (backdropVariant below).
|
|
58849
|
-
//
|
|
58850
|
-
//
|
|
59401
|
+
// before how it is painted (backdropVariant below). It also picks the show
|
|
59402
|
+
// call for layer="top": a wall is what showModal() is for, and without one
|
|
59403
|
+
// the dialog goes to the same top layer through the Popover API instead.
|
|
58851
59404
|
backdrop = true,
|
|
58852
59405
|
// How loudly the backdrop says it is there — independent of what it
|
|
58853
59406
|
// *does* (that's pointerInteractionOutsideEffect above). "invisible" is a
|
|
@@ -58904,10 +59457,11 @@ const useDialogProps = props => {
|
|
|
58904
59457
|
anchor
|
|
58905
59458
|
});
|
|
58906
59459
|
const children = contentMounted ? childrenProp : null;
|
|
58907
|
-
|
|
58908
|
-
|
|
58909
|
-
|
|
58910
|
-
|
|
59460
|
+
// Where it is placed, and whether there is a wall — the two questions that
|
|
59461
|
+
// pick the show call (see this file's top comment).
|
|
59462
|
+
const isTopLayer = layer === "top";
|
|
59463
|
+
const isModal = isTopLayer && backdrop;
|
|
59464
|
+
const isTopLayerPopover = isTopLayer && !backdrop;
|
|
58911
59465
|
if (!backdrop && pointerInteractionOutsideEffect === "capture") {
|
|
58912
59466
|
console.warn(`Dialog: pointerInteractionOutsideEffect="capture" needs a backdrop. Absorbing a press is what a wall does, and backdrop={false} takes it away.`);
|
|
58913
59467
|
}
|
|
@@ -58928,7 +59482,7 @@ const useDialogProps = props => {
|
|
|
58928
59482
|
// container being that screen (the viewport, unless the app declared a
|
|
58929
59483
|
// narrower one) — and the positioned ancestor for a local one, where
|
|
58930
59484
|
// reading 3% of the screen gives an absurd gap inside a small box.
|
|
58931
|
-
|
|
59485
|
+
isTopLayer ? "3appw" : "3cqw");
|
|
58932
59486
|
// "expand || expandX", the shorthand semantics Popup used to apply before
|
|
58933
59487
|
// handing them over — the docked default only applies when neither was said
|
|
58934
59488
|
const expandXUnset = expand === undefined && expandXProp === undefined;
|
|
@@ -59117,14 +59671,14 @@ const useDialogProps = props => {
|
|
|
59117
59671
|
// from dialogEl.parentElement, which for DialogLocal is the
|
|
59118
59672
|
// .navi_dialog_clip_wrapper (itself position: absolute) rather than the
|
|
59119
59673
|
// real, meaningful ancestor beyond it.
|
|
59120
|
-
const positionedAncestor =
|
|
59674
|
+
const positionedAncestor = isTopLayer ? document.documentElement : getPositionedParent(dialogEl.parentElement /* dialogEl is inside the clip_wrapper */);
|
|
59121
59675
|
const [cleanup, addCleanup] = createPubSub(true);
|
|
59122
59676
|
const anchorElement = resolveAnchorElement(e);
|
|
59123
59677
|
// Kept for the closing, which has no opening event to read it back from:
|
|
59124
59678
|
// the box the dialog shrinks back into is the one it came out of.
|
|
59125
59679
|
anchorElementRef.current = anchorElement;
|
|
59126
59680
|
debugPopup(`"${e.type}" on ${getElementSignature(e.target)} -> openDialog`);
|
|
59127
|
-
if (!
|
|
59681
|
+
if (!isTopLayer) {
|
|
59128
59682
|
// see openLocalDialogCount's own comment
|
|
59129
59683
|
dialogEl.style.setProperty("--dialog-stack-order", openLocalDialogCount++);
|
|
59130
59684
|
}
|
|
@@ -59167,10 +59721,12 @@ const useDialogProps = props => {
|
|
|
59167
59721
|
}
|
|
59168
59722
|
if (isModal) {
|
|
59169
59723
|
dialogEl.showModal();
|
|
59724
|
+
} else if (isTopLayerPopover) {
|
|
59725
|
+
dialogEl.showPopover();
|
|
59170
59726
|
} else {
|
|
59171
59727
|
dialogEl.show();
|
|
59172
59728
|
}
|
|
59173
|
-
//
|
|
59729
|
+
// Whichever show call ran — see the backdrop's own [navi-hidden] CSS rule
|
|
59174
59730
|
// and popover.jsx's identical reasoning: showModal()/show() alone only
|
|
59175
59731
|
// wins over a stray, still-present [navi-hidden] { display: none }
|
|
59176
59732
|
// default when nothing else authored also sets display on dialogEl —
|
|
@@ -59178,24 +59734,23 @@ const useDialogProps = props => {
|
|
|
59178
59734
|
// property (e.g. Popup's own flex prop) defeats the UA stylesheet's own
|
|
59179
59735
|
// dialog:not([open]) default the same way it can for Popover.
|
|
59180
59736
|
dialogEl.removeAttribute("navi-hidden");
|
|
59181
|
-
if (isModal) ; else {
|
|
59737
|
+
if (isModal) ; else if (backdrop) {
|
|
59182
59738
|
addCleanup(trapFocusInside(dialogEl, {
|
|
59183
59739
|
debug: debugFocus,
|
|
59184
59740
|
boundaryElement: positionedAncestor,
|
|
59185
|
-
//
|
|
59186
|
-
//
|
|
59187
|
-
//
|
|
59188
|
-
// from it either, not just a Tab press.
|
|
59741
|
+
// The wall stops the keyboard too — a mousedown on some other
|
|
59742
|
+
// focusable element behind it must not steal the focus away either,
|
|
59743
|
+
// not just a Tab press.
|
|
59189
59744
|
pointerTrap: true
|
|
59190
59745
|
}));
|
|
59191
59746
|
}
|
|
59192
59747
|
if (scrollCapture) {
|
|
59193
|
-
// A modal dialog always has its own ::backdrop;
|
|
59194
|
-
//
|
|
59748
|
+
// A modal dialog always has its own ::backdrop; a local one has the
|
|
59749
|
+
// backdrop element when it renders one.
|
|
59195
59750
|
addCleanup(trapScrollInside(dialogEl, {
|
|
59196
59751
|
backdrop: isModal || backdropEl
|
|
59197
59752
|
}));
|
|
59198
|
-
} else if (!
|
|
59753
|
+
} else if (!isTopLayer) {
|
|
59199
59754
|
// A local dialog is confined to its positioned ancestor, and so is its
|
|
59200
59755
|
// backdrop (inset: 0 covers the scrollport, not the scrolled content):
|
|
59201
59756
|
// letting that ancestor scroll would slide the dialog away and reveal
|
|
@@ -59212,7 +59767,7 @@ const useDialogProps = props => {
|
|
|
59212
59767
|
// Positioning: dialogEl is already shown (display: flex, per this
|
|
59213
59768
|
// file's own [open] CSS) by this point, so its own dimensions are real
|
|
59214
59769
|
// — pickPositionRelativeTo's own no-anchor/docked mode (no `anchor`
|
|
59215
|
-
// argument at all) docks it against the viewport (layer="top"
|
|
59770
|
+
// argument at all) docks it against the viewport (layer="top")
|
|
59216
59771
|
// or its own positioned ancestor (layer="local", the same
|
|
59217
59772
|
// positionedAncestor computed above), same mechanism as Popover's own
|
|
59218
59773
|
// custom renderer. applyDialogPosition sets --container-position-remaining-height/-width
|
|
@@ -59244,7 +59799,7 @@ const useDialogProps = props => {
|
|
|
59244
59799
|
// box is what the caps must read, and nothing in CSS tracks it.
|
|
59245
59800
|
const applyDialogPosition = position => {
|
|
59246
59801
|
applyNewPosition(dialogEl, position);
|
|
59247
|
-
if (
|
|
59802
|
+
if (isTopLayer) {
|
|
59248
59803
|
dialogEl.style.removeProperty("--container-position-remaining-height");
|
|
59249
59804
|
dialogEl.style.removeProperty("--container-position-remaining-width");
|
|
59250
59805
|
}
|
|
@@ -59343,7 +59898,7 @@ const useDialogProps = props => {
|
|
|
59343
59898
|
|
|
59344
59899
|
// Reposition on the same triggers Popover's own visibleRectEffect
|
|
59345
59900
|
// already reacts to generically — window resize/scroll/visual-viewport
|
|
59346
|
-
// changes for layer="top"
|
|
59901
|
+
// changes for layer="top" (positionedAncestor is already
|
|
59347
59902
|
// document.documentElement there, see its own computation above;
|
|
59348
59903
|
// visibleRectEffect already debounces visualViewport resize by 100ms
|
|
59349
59904
|
// to avoid the mobile tap-to-tap-input keyboard flicker, so no
|
|
@@ -59441,15 +59996,19 @@ const useDialogProps = props => {
|
|
|
59441
59996
|
debugPopup(`"${closeEvent.type}" on ${getElementSignature(closeEvent.target)} -> closeDialog`);
|
|
59442
59997
|
clearTextSelectionInside(dialogEl);
|
|
59443
59998
|
dialogEl.setAttribute("aria-expanded", "false");
|
|
59444
|
-
if (!
|
|
59999
|
+
if (!isTopLayer) {
|
|
59445
60000
|
openLocalDialogCount = Math.max(0, openLocalDialogCount - 1);
|
|
59446
60001
|
dialogEl.style.removeProperty("--dialog-stack-order");
|
|
59447
60002
|
}
|
|
59448
60003
|
// See openEffect's own identical comment for why this is needed
|
|
59449
|
-
//
|
|
60004
|
+
// whichever show call ran, not just when a stray authored display
|
|
59450
60005
|
// property is actually present — harmless the rest of the time.
|
|
59451
60006
|
dialogEl.setAttribute("navi-hidden", "");
|
|
59452
|
-
|
|
60007
|
+
if (isTopLayerPopover) {
|
|
60008
|
+
dialogEl.hidePopover();
|
|
60009
|
+
} else {
|
|
60010
|
+
dialogEl.close();
|
|
60011
|
+
}
|
|
59453
60012
|
// Held at the size it has right now, for the whole way out. cleanup()
|
|
59454
60013
|
// below already stops the JS repositioning, but the size is CSS-driven
|
|
59455
60014
|
// (--x-dialog-max-height, and `height` outright under expandY) and
|
|
@@ -59476,9 +60035,8 @@ const useDialogProps = props => {
|
|
|
59476
60035
|
};
|
|
59477
60036
|
const onKeyDownShortcuts = createOnKeyDownForShortcuts({
|
|
59478
60037
|
escape: e => {
|
|
59479
|
-
// Only
|
|
59480
|
-
//
|
|
59481
|
-
// non-modal .show()'d one doesn't.
|
|
60038
|
+
// Only a modal <dialog> fires "cancel" on Escape natively (handled via
|
|
60039
|
+
// onCancel below); neither a .show()n nor a showPopover()n one does.
|
|
59482
60040
|
if (isModal || !openController.opened) {
|
|
59483
60041
|
return null;
|
|
59484
60042
|
}
|
|
@@ -59497,10 +60055,10 @@ const useDialogProps = props => {
|
|
|
59497
60055
|
// most fields are shared: renderer-specific bits (the outside-click
|
|
59498
60056
|
// handler below, in particular) are just assigned onto whichever of the
|
|
59499
60057
|
// two actually owns that concern for a given renderer, instead of one
|
|
59500
|
-
// object's own field branching internally
|
|
59501
|
-
//
|
|
59502
|
-
//
|
|
59503
|
-
//
|
|
60058
|
+
// object's own field branching internally. backdropProps only gets returned
|
|
60059
|
+
// (see the bottom of this function) for the one dialog whose wall is a real
|
|
60060
|
+
// element of ours: a modal's is native (::backdrop), and backdrop={false}
|
|
60061
|
+
// has none at all.
|
|
59504
60062
|
Object.assign(backdropProps, {
|
|
59505
60063
|
"ref": backdropRef,
|
|
59506
60064
|
// Out of flow like the popup it belongs to — see the content element's own
|
|
@@ -59516,14 +60074,14 @@ const useDialogProps = props => {
|
|
|
59516
60074
|
// and matching what a descendant relying on
|
|
59517
60075
|
// use_displayed_layout_effect.js's own aria-expanded-presence check
|
|
59518
60076
|
// needs — see popover.jsx's own identical prop for the full reasoning.
|
|
59519
|
-
"aria-expanded": openController.
|
|
60077
|
+
"aria-expanded": openController.openedInDom ? "true" : "false",
|
|
59520
60078
|
// Present from this very first render (recomputed fresh on every one
|
|
59521
60079
|
// from openController.opened, not a frozen mount-time constant) so
|
|
59522
60080
|
// there's no gap for the browser to ever paint this plain-div backdrop
|
|
59523
60081
|
// visible before anything has actually opened it — see popover.jsx's
|
|
59524
60082
|
// own identical prop for the full reasoning, and this file's own CSS
|
|
59525
60083
|
// for the rule it drives.
|
|
59526
|
-
"navi-hidden": openController.
|
|
60084
|
+
"navi-hidden": openController.openedInDom ? undefined : "",
|
|
59527
60085
|
"styleCSSVars": DIALOG_STYLE_CSS_VARS,
|
|
59528
60086
|
"animationDuration": rest.animationDuration,
|
|
59529
60087
|
"data-pointer-interaction-outside": pointerInteractionOutsideEffect,
|
|
@@ -59535,7 +60093,7 @@ const useDialogProps = props => {
|
|
|
59535
60093
|
tabIndex,
|
|
59536
60094
|
// See backdropProps' own identical prop above for the full reasoning
|
|
59537
60095
|
// (kept once, not repeated here).
|
|
59538
|
-
"aria-expanded": openController.
|
|
60096
|
+
"aria-expanded": openController.openedInDom ? "true" : "false",
|
|
59539
60097
|
// Present from the very first render (recomputed fresh from
|
|
59540
60098
|
// openController.opened every time, not a frozen mount-time constant —
|
|
59541
60099
|
// see popover.jsx's own identical prop for the full reasoning) so a
|
|
@@ -59543,28 +60101,28 @@ const useDialogProps = props => {
|
|
|
59543
60101
|
// can't silently defeat showModal()/close()'s native open/close — see
|
|
59544
60102
|
// this file's own CSS rule for dialogEl and the open/close steps below
|
|
59545
60103
|
// for how it's toggled.
|
|
59546
|
-
"navi-hidden": openController.
|
|
60104
|
+
"navi-hidden": openController.openedInDom ? undefined : "",
|
|
59547
60105
|
// Unlike Popover (which genuinely can't resolve "auto" until it
|
|
59548
60106
|
// measures against a real anchor), resolvedAnimation is already fully
|
|
59549
60107
|
// known synchronously here — a dialog never needs to flip anything
|
|
59550
60108
|
// after measuring (see this file's top comment) — so there's no reason
|
|
59551
60109
|
// to withhold the attribute for the auto case the way Popover has to.
|
|
59552
60110
|
"navi-animation": resolvedAnimation,
|
|
59553
|
-
// Only meaningful for
|
|
59554
|
-
//
|
|
60111
|
+
// Only meaningful for a modal's own native ::backdrop (see this file's
|
|
60112
|
+
// CSS for the "capture" glass effect) — a
|
|
59555
60113
|
// pseudo-element can't carry its own attributes, so this has to live on
|
|
59556
60114
|
// the originating .navi_dialog element instead, same reasoning as
|
|
59557
|
-
// navi-animation above. Harmless for
|
|
59558
|
-
//
|
|
59559
|
-
//
|
|
60115
|
+
// navi-animation above. Harmless for a local dialog too (its own real
|
|
60116
|
+
// backdrop element already gets the same attribute via backdropProps
|
|
60117
|
+
// above, which is what its own CSS actually keys off).
|
|
59560
60118
|
"data-pointer-interaction-outside": pointerInteractionOutsideEffect,
|
|
59561
|
-
// Only load-bearing for
|
|
59562
|
-
//
|
|
59563
|
-
//
|
|
59564
|
-
//
|
|
60119
|
+
// Only load-bearing for a modal's own native ::backdrop, same "a
|
|
60120
|
+
// pseudo-element can't carry attributes" reasoning as the prop just above
|
|
60121
|
+
// (and harmless for a local dialog, whose real backdrop element gets it
|
|
60122
|
+
// via backdropProps).
|
|
59565
60123
|
"data-backdrop-variant": backdropVariant,
|
|
59566
60124
|
// Read by the native ::backdrop, which inherits them from here (see this
|
|
59567
|
-
// file's CSS) —
|
|
60125
|
+
// file's CSS) — a local dialog's own backdrop element gets them via
|
|
59568
60126
|
// backdropProps above.
|
|
59569
60127
|
backdropColor,
|
|
59570
60128
|
backdropFilter,
|
|
@@ -59593,6 +60151,12 @@ const useDialogProps = props => {
|
|
|
59593
60151
|
// absolutely placed by its own code — so it says so once, here (group.jsx
|
|
59594
60152
|
// reads this attribute to tell a member from anything else on its line).
|
|
59595
60153
|
"navi-out-of-flow": "",
|
|
60154
|
+
// The top layer without a wall: showPopover() is what puts it there, and
|
|
60155
|
+
// it only accepts an element that declares itself a popover. "manual" so
|
|
60156
|
+
// the browser's own light dismiss stays out of it — what an outside press
|
|
60157
|
+
// does is pointerInteractionOutsideEffect's answer, given by the
|
|
60158
|
+
// document-level listener in openEffect above.
|
|
60159
|
+
"popover": isTopLayerPopover ? "manual" : undefined,
|
|
59596
60160
|
"baseClassName": "navi_dialog",
|
|
59597
60161
|
"pseudoClasses": DIALOG_PSEUDO_CLASSES,
|
|
59598
60162
|
// Distinguishes the two renderers for the CSS above (position: fixed
|
|
@@ -59642,8 +60206,8 @@ const useDialogProps = props => {
|
|
|
59642
60206
|
},
|
|
59643
60207
|
"onCancel": e => {
|
|
59644
60208
|
// The dialog's own "cancel" (Escape on a modal showModal() dialog —
|
|
59645
|
-
// the
|
|
59646
|
-
// above) fires on the dialog element itself. But a child
|
|
60209
|
+
// the Escape handling for the other two shows lives in
|
|
60210
|
+
// onKeyDownShortcuts above) fires on the dialog element itself. But a child
|
|
59647
60211
|
// <input type="file"> also fires a BUBBLING "cancel" when the user
|
|
59648
60212
|
// dismisses the file chooser (per the HTML spec), and it lands here
|
|
59649
60213
|
// too; only the dialog's own cancel means "close".
|
|
@@ -59657,12 +60221,12 @@ const useDialogProps = props => {
|
|
|
59657
60221
|
children
|
|
59658
60222
|
});
|
|
59659
60223
|
|
|
59660
|
-
// Outside-click handling for
|
|
59661
|
-
//
|
|
59662
|
-
//
|
|
59663
|
-
//
|
|
59664
|
-
//
|
|
59665
|
-
if (!
|
|
60224
|
+
// Outside-click handling for the wall we render ourselves. A modal's is a
|
|
60225
|
+
// plain document-level listener instead, set up in openEffect above (see
|
|
60226
|
+
// this file's top comment for why: neither a real backdrop element nor
|
|
60227
|
+
// dialogEl's own mousedown reliably fires for a native ::backdrop click),
|
|
60228
|
+
// and so is a wall-less dialog's.
|
|
60229
|
+
if (!isTopLayer && backdrop) {
|
|
59666
60230
|
backdropProps.onMouseDown = mouseDownEvent => {
|
|
59667
60231
|
if (mouseDownEvent.button !== 0) {
|
|
59668
60232
|
return;
|
|
@@ -59672,7 +60236,7 @@ const useDialogProps = props => {
|
|
|
59672
60236
|
if (openedDuringThisPress(openController)) {
|
|
59673
60237
|
return;
|
|
59674
60238
|
}
|
|
59675
|
-
// See the
|
|
60239
|
+
// See the document-level listener in openEffect above: a click inside
|
|
59676
60240
|
// another popup is a click on what is in front, not an outside click.
|
|
59677
60241
|
const dialogEl = ref.current;
|
|
59678
60242
|
const popupUnderPointer = mouseDownEvent.target.closest?.(`[navi-control="dialog"], [navi-control="popover"]`);
|
|
@@ -59689,7 +60253,7 @@ const useDialogProps = props => {
|
|
|
59689
60253
|
// the same behavior for Dialog.
|
|
59690
60254
|
};
|
|
59691
60255
|
}
|
|
59692
|
-
return [
|
|
60256
|
+
return [!isTopLayer && backdrop ? backdropProps : null, contentProps];
|
|
59693
60257
|
};
|
|
59694
60258
|
const DIALOG_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible", ":focus-within"];
|
|
59695
60259
|
|
|
@@ -61160,12 +61724,12 @@ const usePopoverProps = props => {
|
|
|
61160
61724
|
// commit (a plain prop is, no effect needed) — see
|
|
61161
61725
|
// use_displayed_layout_effect.js's own comments for why a descendant
|
|
61162
61726
|
// relying on aria-expanded's mere presence needs that.
|
|
61163
|
-
"aria-expanded": openController.
|
|
61727
|
+
"aria-expanded": openController.openedInDom ? "true" : "false",
|
|
61164
61728
|
// Read fresh on every render (not frozen at mount), so it stays
|
|
61165
61729
|
// correct even across a re-render that happens to occur while open —
|
|
61166
61730
|
// see contentProps' own identical prop just below for the full
|
|
61167
61731
|
// reasoning (kept once, not repeated here).
|
|
61168
|
-
"navi-hidden": openController.
|
|
61732
|
+
"navi-hidden": openController.openedInDom ? undefined : "",
|
|
61169
61733
|
"styleCSSVars": POPUP_STYLE_CSS_VARS,
|
|
61170
61734
|
"animationDuration": rest.animationDuration,
|
|
61171
61735
|
"data-pointer-interaction-outside": pointerInteractionOutsideEffect,
|
|
@@ -61214,7 +61778,7 @@ const usePopoverProps = props => {
|
|
|
61214
61778
|
"navi-animation": isAutoAnimation ? undefined : animation,
|
|
61215
61779
|
// See backdropProps' own identical prop above for the full reasoning
|
|
61216
61780
|
// (kept once, not repeated here).
|
|
61217
|
-
"aria-expanded": openController.
|
|
61781
|
+
"aria-expanded": openController.openedInDom ? "true" : "false",
|
|
61218
61782
|
// Only load-bearing for the custom renderer (a plain div has no native
|
|
61219
61783
|
// starting-hidden default) — present from this very first render so
|
|
61220
61784
|
// there's no gap for the browser to ever paint it visible before
|
|
@@ -61224,7 +61788,7 @@ const usePopoverProps = props => {
|
|
|
61224
61788
|
// the last render, so as long as this always reflects the *current*
|
|
61225
61789
|
// truth, it never fights the imperative
|
|
61226
61790
|
// removeAttribute/setAttribute openEffect/close do directly.
|
|
61227
|
-
"navi-hidden": openController.
|
|
61791
|
+
"navi-hidden": openController.openedInDom ? undefined : "",
|
|
61228
61792
|
"styleCSSVars": POPUP_STYLE_CSS_VARS,
|
|
61229
61793
|
...rest,
|
|
61230
61794
|
...autoFocusProps,
|
|
@@ -61408,22 +61972,18 @@ const css$C = /* css */`@layer navi {
|
|
|
61408
61972
|
* — without this, the exact same `<Popup>` usage would behave
|
|
61409
61973
|
* differently (close-on-outside-click or not) purely based on which mode
|
|
61410
61974
|
* the screen-size check happens to pick, which defeats the point of
|
|
61411
|
-
* having one shared API in the first place. Note this
|
|
61412
|
-
*
|
|
61413
|
-
*
|
|
61414
|
-
*
|
|
61415
|
-
* `dialog.jsx`'s own doc) — a popover's fully passive, click-through
|
|
61416
|
-
* backdrop has no dialog-mode equivalent. Whatever content `Popup` opens
|
|
61417
|
-
* is unavoidably *more* intrusive once it switches to dialog mode than
|
|
61418
|
-
* the exact same usage would be as a popover — worth keeping in mind for
|
|
61419
|
-
* anything that relies on `Popup` and can end up on a small screen.
|
|
61975
|
+
* having one shared API in the first place. Note this only says what a
|
|
61976
|
+
* press outside *does*; whether it reaches the page at all is `backdrop`
|
|
61977
|
+
* below, and `"none"`/`"capture"` describe a wall either way — the popup
|
|
61978
|
+
* absorbs the press without closing, dimmed or not.
|
|
61420
61979
|
* @param {boolean} [props.backdrop] - Whether anything is laid between the
|
|
61421
61980
|
* popup and the page at all: `false` lets a press outside both dismiss the
|
|
61422
|
-
* popup and reach whatever it landed on, in one gesture.
|
|
61423
|
-
*
|
|
61424
|
-
*
|
|
61425
|
-
*
|
|
61426
|
-
*
|
|
61981
|
+
* popup and reach whatever it landed on, in one gesture. Forwarded as-is and
|
|
61982
|
+
* honoured in either mode — a wall-less `Dialog` is shown through the
|
|
61983
|
+
* Popover API rather than `showModal()` — so which mode the screen-size
|
|
61984
|
+
* resolution picks says nothing about whether the page behind stays live.
|
|
61985
|
+
* It is also what makes a sheet docked to the bottom of a phone's screen
|
|
61986
|
+
* (`dockedOnSmallTouchScreen`) non-modal.
|
|
61427
61987
|
* @param {"auto"|"discrete"|"invisible"} [props.backdropVariant] - Forwarded
|
|
61428
61988
|
* as-is to whichever component renders (both understand it identically):
|
|
61429
61989
|
* how visible the backdrop is, independently of what an outside click
|
|
@@ -61494,9 +62054,6 @@ const Popup = props => {
|
|
|
61494
62054
|
// exact same <Popup> usage behaves identically regardless of which
|
|
61495
62055
|
// mode the automatic screen-size resolution happens to pick.
|
|
61496
62056
|
pointerInteractionOutsideEffect = "close",
|
|
61497
|
-
// Read here rather than left in ...rest so the dialog branch can drop it:
|
|
61498
|
-
// a top-layer dialog is modal and has no press left to let through, and
|
|
61499
|
-
// warns when asked (see Dialog's own backdrop prop).
|
|
61500
62057
|
backdrop,
|
|
61501
62058
|
// Popover-only (see this component's own doc) — destructured out so
|
|
61502
62059
|
// they're never part of ...rest, and therefore never forwarded to
|
|
@@ -61531,7 +62088,7 @@ const Popup = props => {
|
|
|
61531
62088
|
sizeFromAnchor: sizeFromAnchor,
|
|
61532
62089
|
maxWidth: maxWidth,
|
|
61533
62090
|
pointerInteractionOutsideEffect: pointerInteractionOutsideEffect,
|
|
61534
|
-
backdrop:
|
|
62091
|
+
backdrop: backdrop,
|
|
61535
62092
|
className: withPropsClassName("navi_popup", className),
|
|
61536
62093
|
expand: expand,
|
|
61537
62094
|
expandX: expandX,
|
|
@@ -61628,6 +62185,10 @@ const css$B = /* css */`.navi_picker {
|
|
|
61628
62185
|
}
|
|
61629
62186
|
}
|
|
61630
62187
|
}
|
|
62188
|
+
|
|
62189
|
+
&[data-dialog-size-from-anchor] .navi_dialog {
|
|
62190
|
+
--dialog-max-width: var(--picker-dialog-max-width, var(--anchor-width));
|
|
62191
|
+
}
|
|
61631
62192
|
}
|
|
61632
62193
|
|
|
61633
62194
|
&[data-popup-width-fit-content] {
|
|
@@ -62276,7 +62837,7 @@ const PickerCustom = props => {
|
|
|
62276
62837
|
// because that is where such a box says what it travels by — and
|
|
62277
62838
|
// asked at every press, since what the picker sits in is not the
|
|
62278
62839
|
// picker's to know at mount.
|
|
62279
|
-
if (!opensOnPress || interactionsDispute || isPressDisputedByDrag(e.target)) {
|
|
62840
|
+
if (!opensOnPress || interactionsDispute || isPressDisputedByDrag(e.target) || isPressDisputedByHold(ref.current)) {
|
|
62280
62841
|
return null;
|
|
62281
62842
|
}
|
|
62282
62843
|
return {
|
|
@@ -62326,6 +62887,13 @@ const PickerCustom = props => {
|
|
|
62326
62887
|
mode: mode
|
|
62327
62888
|
});
|
|
62328
62889
|
};
|
|
62890
|
+
|
|
62891
|
+
// A hold declared on something AROUND the picker (a card opened by
|
|
62892
|
+
// `openOn="longpress"`, holding this one in its drawing): the finger going
|
|
62893
|
+
// down may be the start of that hold, so this picker opens on the click — which
|
|
62894
|
+
// the hold, if it completes, swallows — rather than on the press. The picker's
|
|
62895
|
+
// own hold (its `openOn`) is not "around" it, and has already stepped back.
|
|
62896
|
+
const isPressDisputedByHold = pickerEl => Boolean(pickerEl?.parentElement?.closest(`[${LONGPRESS_ATTRIBUTE}]`));
|
|
62329
62897
|
const getPickerInput = pickerEl => {
|
|
62330
62898
|
return pickerEl.querySelector(".navi_picker_input");
|
|
62331
62899
|
};
|
|
@@ -62399,6 +62967,7 @@ const PickerContentInsidePopup = props => {
|
|
|
62399
62967
|
return jsx(Next, {
|
|
62400
62968
|
"aria-haspopup": isPopover ? "listbox" : "dialog",
|
|
62401
62969
|
"navi-popover-mode": isPopover ? popoverMode : undefined,
|
|
62970
|
+
"data-dialog-size-from-anchor": !isPopover && dialogSizeFromAnchor ? "" : undefined,
|
|
62402
62971
|
...rest,
|
|
62403
62972
|
// On popupProps already (see the picker's popup assembly); they mean
|
|
62404
62973
|
// nothing to the picker element.
|
|
@@ -62740,6 +63309,34 @@ const pickerUIIsNaviOwn = ui => {
|
|
|
62740
63309
|
return Boolean(ui) && typeof ui === "object" && Boolean(ui.type?.isPickerOwnUI);
|
|
62741
63310
|
};
|
|
62742
63311
|
|
|
63312
|
+
/**
|
|
63313
|
+
* What the picker holds, read from inside its `ui` — for a drawing given as
|
|
63314
|
+
* an element (`ui={<MyCard game={game} />}`), which the picker cannot hand
|
|
63315
|
+
* props to. `value` is the state the picker holds right now: the answer the
|
|
63316
|
+
* popup just closed on, before the server has said anything, and put back if
|
|
63317
|
+
* the action fails. `loading` is the run in flight; `interactive` is false
|
|
63318
|
+
* while the picker is disabled, read-only or busy.
|
|
63319
|
+
*
|
|
63320
|
+
* @returns {{ value: any, loading: boolean, interactive: boolean } | null}
|
|
63321
|
+
* null outside a picker.
|
|
63322
|
+
*/
|
|
63323
|
+
const usePickerState = () => {
|
|
63324
|
+
const context = useContext(PickerContext);
|
|
63325
|
+
if (!context) {
|
|
63326
|
+
return null;
|
|
63327
|
+
}
|
|
63328
|
+
const {
|
|
63329
|
+
value,
|
|
63330
|
+
loading,
|
|
63331
|
+
interactive
|
|
63332
|
+
} = context;
|
|
63333
|
+
return {
|
|
63334
|
+
value,
|
|
63335
|
+
loading,
|
|
63336
|
+
interactive
|
|
63337
|
+
};
|
|
63338
|
+
};
|
|
63339
|
+
|
|
62743
63340
|
const PickerNaviMinute = props => {
|
|
62744
63341
|
const Next = useNextResolver();
|
|
62745
63342
|
const {
|
|
@@ -70424,6 +71021,16 @@ const css$t = /* css */`@layer navi {
|
|
|
70424
71021
|
user-select: none;
|
|
70425
71022
|
}
|
|
70426
71023
|
|
|
71024
|
+
&[data-open-on] {
|
|
71025
|
+
& > .navi_picker_box > .navi_picker_input {
|
|
71026
|
+
pointer-events: none;
|
|
71027
|
+
}
|
|
71028
|
+
|
|
71029
|
+
& > .navi_picker_box > .navi_picker_value {
|
|
71030
|
+
pointer-events: auto;
|
|
71031
|
+
}
|
|
71032
|
+
}
|
|
71033
|
+
|
|
70427
71034
|
&[navi-ui-custom] {
|
|
70428
71035
|
& .navi_picker_input {
|
|
70429
71036
|
top: calc(-1 * (var(--picker-border-width) + var(--x-picker-press-padding-top)));
|
|
@@ -70749,12 +71356,15 @@ const PickerButton = props => {
|
|
|
70749
71356
|
// What the picker knows about itself, for the pieces it does not place: the
|
|
70750
71357
|
// drawings of its value (Picker.UI.*), and the affordances a caller may put
|
|
70751
71358
|
// in their own `ui` (Picker.Clear) as much as the ones it puts in its slot.
|
|
71359
|
+
// `ui={MyCard}`: a component the picker renders itself, told what it holds.
|
|
71360
|
+
const UIComponent = typeof ui === "function" ? ui : null;
|
|
70752
71361
|
const pickerContext = {
|
|
70753
71362
|
value,
|
|
70754
71363
|
placeholder,
|
|
70755
71364
|
maxLines,
|
|
70756
71365
|
id: inputProps.id,
|
|
70757
71366
|
interactive,
|
|
71367
|
+
loading,
|
|
70758
71368
|
clearConfirm
|
|
70759
71369
|
};
|
|
70760
71370
|
return (
|
|
@@ -70945,7 +71555,17 @@ const PickerButton = props => {
|
|
|
70945
71555
|
size: rightSlotIconSize,
|
|
70946
71556
|
lineOverflow: "allow",
|
|
70947
71557
|
children: rightSlotIcon === undefined ? jsx(ChevronDownSvg$1, {}) : rightSlotIcon
|
|
70948
|
-
}) : jsx(PickerDefaultUI, {}) :
|
|
71558
|
+
}) : jsx(PickerDefaultUI, {}) : UIComponent ?
|
|
71559
|
+
// Told what the picker HOLDS — the value it just closed
|
|
71560
|
+
// on, before the server has said anything — so a façade
|
|
71561
|
+
// shows the answer at once, and the wait with it. The
|
|
71562
|
+
// same state usePickerState() hands an element (see the
|
|
71563
|
+
// `ui` doc).
|
|
71564
|
+
jsx(UIComponent, {
|
|
71565
|
+
value: value,
|
|
71566
|
+
loading: loading,
|
|
71567
|
+
interactive: interactive
|
|
71568
|
+
}) : ui
|
|
70949
71569
|
})
|
|
70950
71570
|
})
|
|
70951
71571
|
}), hasRightSlot ? jsx("span", {
|
|
@@ -82935,11 +83555,11 @@ const css = /* css */`.navi_side_panel {
|
|
|
82935
83555
|
* @param {boolean} [props.closeOnClickOutside=false] - `false` (default):
|
|
82936
83556
|
* maps to `pointerInteractionOutsideEffect="none"` — in popover mode, no
|
|
82937
83557
|
* backdrop at all, outside clicks pass straight through; in dialog mode,
|
|
82938
|
-
* the outside click is
|
|
82939
|
-
*
|
|
82940
|
-
*
|
|
82941
|
-
* panel on an outside click instead, and also enables trapping
|
|
82942
|
-
* navigation inside the panel (`focusCapture`) — closing on outside
|
|
83558
|
+
* the outside click is absorbed by the panel's own wall but changes
|
|
83559
|
+
* nothing. Pass `backdrop={false}` (forwarded to `Popup`) for a panel with
|
|
83560
|
+
* no wall in either mode, whose outside presses reach the page. `true`:
|
|
83561
|
+
* closes the panel on an outside click instead, and also enables trapping
|
|
83562
|
+
* Tab navigation inside the panel (`focusCapture`) — closing on outside
|
|
82943
83563
|
* interaction only makes sense paired with not letting focus silently
|
|
82944
83564
|
* leave the panel first.
|
|
82945
83565
|
* @param {boolean} [props.swipeToClose=true] - Pushing the panel back
|
|
@@ -82949,12 +83569,11 @@ const css = /* css */`.navi_side_panel {
|
|
|
82949
83569
|
* deliberately. See `swipe_to_close.js` for when the gesture is claimed.
|
|
82950
83570
|
* @param {"dialog"|"popover"} [props.mode] - Forwarded to `Popup` — forces
|
|
82951
83571
|
* one underlying renderer instead of its automatic screen-size
|
|
82952
|
-
* resolution. Note that if `Popup` ends up in dialog mode (small screen,
|
|
82953
|
-
*
|
|
82954
|
-
* `closeOnClickOutside`/`pointerInteractionOutsideEffect
|
|
82955
|
-
*
|
|
82956
|
-
*
|
|
82957
|
-
* popover's fully passive, click-through backdrop.
|
|
83572
|
+
* resolution. Note that if `Popup` ends up in dialog mode (small screen, or
|
|
83573
|
+
* forced here), the panel is modal unless it says `backdrop={false}`:
|
|
83574
|
+
* `closeOnClickOutside`/`pointerInteractionOutsideEffect` only say what a
|
|
83575
|
+
* press on the wall does, not whether there is one (see `dialog.jsx`'s own
|
|
83576
|
+
* doc).
|
|
82958
83577
|
* @param {import("ignore:preact").ComponentChildren} props.children - No built-in
|
|
82959
83578
|
* close button — add one wherever it makes sense for the layout (e.g. a
|
|
82960
83579
|
* plain `<Button command="--navi-close">`), use `SidePanel.Head`'s own
|
|
@@ -83259,5 +83878,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
83259
83878
|
})
|
|
83260
83879
|
});
|
|
83261
83880
|
|
|
83262
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, CalloutStatusIcon, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, ControlSwap, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, InfoSvg, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, OfflineError, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRange, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, constraintFromValidityRule, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, dispatchRequestSetUIState, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, getNowHours, getNowHoursRoundedToStep, isCellSelected, isColumnSelected, isOfflineError, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markAsOutsideTextFlow, markErrorAsDisplayedBy, moveArrayItemByIndex, moveFocusTo, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, preloadUrl, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setNetworkPolicy, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutElement, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useNetworkPolicyReason, useOrderedColumns, usePopupMode, useRouteStatus, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideContainer, useSlideValue, useStateArray, useTitleLevel, useTransitionCover, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
83881
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, CalloutStatusIcon, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, ControlSwap, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, Expandable, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, InfoSvg, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, OfflineError, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTransitionArea, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, Step, StepList, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRange, TimeRangeSpin, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, constraintFromValidityRule, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineRouteDefaultTransition, defineRouteTransition, detectHorizontalOverflow, dispatchRequestSetUIState, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, getNowHours, getNowHoursRoundedToStep, isCellSelected, isColumnSelected, isOfflineError, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markAsOutsideTextFlow, markErrorAsDisplayedBy, moveArrayItemByIndex, moveFocusTo, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, preloadUrl, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, routeFallback, scrollActivitySignal, setBaseUrl, setNetworkPolicy, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutElement, useCalloutRequestClose, useCanNavBack, useCanNavForward, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useNetworkPolicyReason, useOrderedColumns, usePickerState, usePopupMode, useRouteStatus, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideContainer, useSlideValue, useStateArray, useTitleLevel, useTransitionCover, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
83263
83882
|
//# sourceMappingURL=jsenv_navi.js.map
|