@jsenv/navi 0.29.303 → 0.29.305
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 +167 -21
- package/dist/dev/jsenv_navi.js.map +7 -7
- package/dist/jsenv_navi.js +159 -20
- package/dist/jsenv_navi.js.map +7 -7
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -37704,6 +37704,15 @@ const useInteractiveProps = (props, {
|
|
|
37704
37704
|
const debugAction = useDebugAction();
|
|
37705
37705
|
const debugInteraction = useDebugInteraction();
|
|
37706
37706
|
const debugFocus = useDebugFocus();
|
|
37707
|
+
|
|
37708
|
+
// The very instance a run of this control is using, held in a SIGNAL because
|
|
37709
|
+
// the busy state below follows it: it is captured at navi_action_start and
|
|
37710
|
+
// released when the run settles (see onnavi_action_start).
|
|
37711
|
+
const runningActionSignalRef = useRef(null);
|
|
37712
|
+
if (runningActionSignalRef.current === null) {
|
|
37713
|
+
runningActionSignalRef.current = signal(null);
|
|
37714
|
+
}
|
|
37715
|
+
uiStateController.runningActionSignal = runningActionSignalRef.current;
|
|
37707
37716
|
{
|
|
37708
37717
|
const {
|
|
37709
37718
|
autoFocus,
|
|
@@ -37742,7 +37751,15 @@ const useInteractiveProps = (props, {
|
|
|
37742
37751
|
const isActionRequesterComputed = useMemo(() => parentActionRequesterSignal ? computed(() => parentActionRequesterSignal.value === ref.current) : null, [parentActionRequesterSignal]);
|
|
37743
37752
|
const parallelGuard = useContext(ParallelGuardContext);
|
|
37744
37753
|
const parentAction = useContext(ActionContext);
|
|
37745
|
-
|
|
37754
|
+
// The instance the run in flight is using when there is one, and only
|
|
37755
|
+
// then boundAction: boundAction can be a proxy following the UI state
|
|
37756
|
+
// signal, and that state moves while a run is out — a popup content
|
|
37757
|
+
// unmounting takes the group's value with it. The proxy would resolve to
|
|
37758
|
+
// the instance for the new value, which is idle, and the control would
|
|
37759
|
+
// stop waiting on a run that is still going. What it waits on is the run
|
|
37760
|
+
// it started, from navi_action_start to its settlement.
|
|
37761
|
+
const runningAction = uiStateController.runningActionSignal.value;
|
|
37762
|
+
const actionStatus = useActionStatus(runningAction || boundAction);
|
|
37746
37763
|
const networkPolicyReason = useNetworkPolicyReason();
|
|
37747
37764
|
const {
|
|
37748
37765
|
disabled,
|
|
@@ -38103,7 +38120,7 @@ const useInteractiveProps = (props, {
|
|
|
38103
38120
|
// state has already moved to the new value by now — the proxy would
|
|
38104
38121
|
// resolve to the instance for that new value, which is not the one
|
|
38105
38122
|
// running.
|
|
38106
|
-
uiStateController.
|
|
38123
|
+
uiStateController.runningActionSignal.peek()?.abort(`superseded by a newer request on this control`);
|
|
38107
38124
|
return;
|
|
38108
38125
|
}
|
|
38109
38126
|
debugAction(e, `executing action ${e.detail.action.callSource}`);
|
|
@@ -38125,14 +38142,14 @@ const useInteractiveProps = (props, {
|
|
|
38125
38142
|
// this run (see the optimistic queue above), the state — and the
|
|
38126
38143
|
// proxy's resolution — will have moved on.
|
|
38127
38144
|
const runAction = e.detail.action;
|
|
38128
|
-
uiStateController.
|
|
38145
|
+
uiStateController.runningActionSignal.value = runAction.getCurrentAction?.() ?? runAction;
|
|
38129
38146
|
// Fires when the run's underlying work has settled — even for an
|
|
38130
38147
|
// aborted run, whose promise is awaited to completion (see
|
|
38131
38148
|
// performRun in actions.js) — which is exactly what "the server is
|
|
38132
38149
|
// done with it" means, and therefore when the queued request may go.
|
|
38133
38150
|
e.detail.addSideEffect(outcome => {
|
|
38134
38151
|
uiStateController.actionInFlight = false;
|
|
38135
|
-
uiStateController.
|
|
38152
|
+
uiStateController.runningActionSignal.value = null;
|
|
38136
38153
|
uiStateController.parallelGuard?.release(uiStateController);
|
|
38137
38154
|
const queuedEvent = uiStateController.queuedActionAllowedEvent;
|
|
38138
38155
|
if (!queuedEvent) {
|
|
@@ -58332,14 +58349,22 @@ const clipOf = (side, distance) => {
|
|
|
58332
58349
|
// before the next one takes it (see releaseGrowInProgress).
|
|
58333
58350
|
const NAME = "navi-popup-grow";
|
|
58334
58351
|
const NAME_PROPERTY = "view-transition-name";
|
|
58335
|
-
// Worn by the root for the length of the movement
|
|
58336
|
-
//
|
|
58352
|
+
// Worn by the root for the length of the movement, saying which way it goes
|
|
58353
|
+
// ("opening" | "closing") — what the CSS keys the page's own opt-out and the
|
|
58354
|
+
// clip near the anchor's end on, and the movement's only trace in the document.
|
|
58337
58355
|
const ROOT_ATTRIBUTE = "data-navi-popup-grow";
|
|
58356
|
+
// What the two boxes are to each other (Dialog's `grow`: "box" | "scene"),
|
|
58357
|
+
// worn by the root too — it decides how each picture sits in the moving box.
|
|
58358
|
+
const KIND_ATTRIBUTE = "data-navi-popup-grow-kind";
|
|
58338
58359
|
// Inside the popup, the one node that IS the anchor once it has grown.
|
|
58339
58360
|
const TARGET_SELECTOR = "[data-grow]";
|
|
58340
58361
|
// The popup's own animation duration, published on the root because the
|
|
58341
58362
|
// ::view-transition tree hangs off it and inherits from nowhere else.
|
|
58342
58363
|
const DURATION_PROPERTY = "--navi-popup-grow-duration";
|
|
58364
|
+
// The corners of the box being left, published the same way: the pictures are
|
|
58365
|
+
// clipped to the moving box (dialog.jsx), and a card with rounded corners must
|
|
58366
|
+
// not travel with square ones.
|
|
58367
|
+
const BORDER_RADIUS_PROPERTY = "--navi-popup-grow-border-radius";
|
|
58343
58368
|
|
|
58344
58369
|
let releaseGrowInProgress = null;
|
|
58345
58370
|
|
|
@@ -58350,13 +58375,13 @@ let releaseGrowInProgress = null;
|
|
|
58350
58375
|
* `opened` says which way: the box being left is the anchor when the popup is
|
|
58351
58376
|
* opening and the popup when it is closing, and the arriving one is only known
|
|
58352
58377
|
* once the change has been made (the content a popup grows into is built by
|
|
58353
|
-
* that very change).
|
|
58378
|
+
* that very change). `grow` is Dialog's own prop of that name.
|
|
58354
58379
|
*/
|
|
58355
58380
|
const growPopupFromAnchor = (
|
|
58356
58381
|
popupEl,
|
|
58357
58382
|
anchorElement,
|
|
58358
58383
|
applyChange,
|
|
58359
|
-
{ opened },
|
|
58384
|
+
{ opened, grow },
|
|
58360
58385
|
) => {
|
|
58361
58386
|
const startViewTransition = ensureDocumentStartViewTransition();
|
|
58362
58387
|
// A movement still wearing the name would make the name two elements wide,
|
|
@@ -58367,7 +58392,8 @@ const growPopupFromAnchor = (
|
|
|
58367
58392
|
const elementLeaving = opened ? anchorElement : resolveGrowTarget(popupEl);
|
|
58368
58393
|
const giveBackNameLeaving = wearGrowName(elementLeaving);
|
|
58369
58394
|
const root = document.documentElement;
|
|
58370
|
-
root.setAttribute(ROOT_ATTRIBUTE, "");
|
|
58395
|
+
root.setAttribute(ROOT_ATTRIBUTE, opened ? "opening" : "closing");
|
|
58396
|
+
root.setAttribute(KIND_ATTRIBUTE, grow);
|
|
58371
58397
|
const duration = getComputedStyle(popupEl)
|
|
58372
58398
|
.getPropertyValue("--popup-animation-duration")
|
|
58373
58399
|
.trim();
|
|
@@ -58377,6 +58403,10 @@ const growPopupFromAnchor = (
|
|
|
58377
58403
|
// browser's own pace.
|
|
58378
58404
|
root.style.setProperty(DURATION_PROPERTY, duration);
|
|
58379
58405
|
}
|
|
58406
|
+
const borderRadius = getComputedStyle(elementLeaving).borderRadius;
|
|
58407
|
+
if (borderRadius) {
|
|
58408
|
+
root.style.setProperty(BORDER_RADIUS_PROPERTY, borderRadius);
|
|
58409
|
+
}
|
|
58380
58410
|
|
|
58381
58411
|
let giveBackNameArriving = null;
|
|
58382
58412
|
const release = () => {
|
|
@@ -58387,7 +58417,9 @@ const growPopupFromAnchor = (
|
|
|
58387
58417
|
giveBackNameLeaving();
|
|
58388
58418
|
giveBackNameArriving?.();
|
|
58389
58419
|
root.removeAttribute(ROOT_ATTRIBUTE);
|
|
58420
|
+
root.removeAttribute(KIND_ATTRIBUTE);
|
|
58390
58421
|
root.style.removeProperty(DURATION_PROPERTY);
|
|
58422
|
+
root.style.removeProperty(BORDER_RADIUS_PROPERTY);
|
|
58391
58423
|
};
|
|
58392
58424
|
releaseGrowInProgress = release;
|
|
58393
58425
|
|
|
@@ -58750,6 +58782,16 @@ const css$E = /* css */`
|
|
|
58750
58782
|
--backdrop-background: transparent;
|
|
58751
58783
|
--backdrop-filter: none;
|
|
58752
58784
|
}
|
|
58785
|
+
/* A wall-less top-layer dialog reaches the top layer through the Popover
|
|
58786
|
+
API, and the browser generates a ::backdrop for a popover exactly as it
|
|
58787
|
+
does for a modal. Nothing is meant to be between this dialog and the
|
|
58788
|
+
page, so that pseudo-element paints nothing — last, after the variant
|
|
58789
|
+
rules it must win over: they answer how a wall is painted, this one
|
|
58790
|
+
says there is no wall to paint. */
|
|
58791
|
+
&[data-backdrop="none"] {
|
|
58792
|
+
--backdrop-background: transparent;
|
|
58793
|
+
--backdrop-filter: none;
|
|
58794
|
+
}
|
|
58753
58795
|
&::backdrop {
|
|
58754
58796
|
background: var(--backdrop-background);
|
|
58755
58797
|
backdrop-filter: var(--backdrop-filter);
|
|
@@ -58941,6 +58983,73 @@ const css$E = /* css */`
|
|
|
58941
58983
|
&::view-transition-new(navi-popup-grow) {
|
|
58942
58984
|
animation-duration: var(--navi-popup-grow-duration, 0.25s);
|
|
58943
58985
|
}
|
|
58986
|
+
|
|
58987
|
+
/* Each picture is drawn as wide as the box and as tall as it is, so a box
|
|
58988
|
+
that grows in height alone (a card keeping its width) would show the
|
|
58989
|
+
whole taller picture from the first frame, and the movement would read
|
|
58990
|
+
as a fade. Clipped to the box, the picture is uncovered as the box
|
|
58991
|
+
grows and covered back as it shrinks — behind the corners the box
|
|
58992
|
+
has in the page, published by popup_grow.js. */
|
|
58993
|
+
&::view-transition-image-pair(navi-popup-grow) {
|
|
58994
|
+
border-radius: var(--navi-popup-grow-border-radius, 0);
|
|
58995
|
+
overflow: clip;
|
|
58996
|
+
}
|
|
58997
|
+
|
|
58998
|
+
/* One scene through two frames (Dialog's grow="scene"): each picture
|
|
58999
|
+
covers the box, cropped around its centre, so what both frames show
|
|
59000
|
+
lands on itself. */
|
|
59001
|
+
&[data-navi-popup-grow-kind="scene"] {
|
|
59002
|
+
&::view-transition-old(navi-popup-grow),
|
|
59003
|
+
&::view-transition-new(navi-popup-grow) {
|
|
59004
|
+
height: 100%;
|
|
59005
|
+
object-fit: cover;
|
|
59006
|
+
}
|
|
59007
|
+
}
|
|
59008
|
+
|
|
59009
|
+
/* The anchor lives in the page, under the fixed bars; the popup lives in
|
|
59010
|
+
the top layer, over them. The pictures are painted above everything,
|
|
59011
|
+
bars included, so near the anchor's end of the movement the whole tree
|
|
59012
|
+
is clipped to the room between the bars — a thumbnail half under the
|
|
59013
|
+
top bar leaves from under it and comes back under it — and freed near
|
|
59014
|
+
the popup's end, where a tall dialog may stand over them. Clipped for
|
|
59015
|
+
the 65% of the time nearest the anchor: with the group's own ease that
|
|
59016
|
+
leaves the last 35% of the time to cover more than half of the trip,
|
|
59017
|
+
so the picture is out of the bars before the clip closes on it. */
|
|
59018
|
+
&::view-transition {
|
|
59019
|
+
animation: navi-popup-grow-clip var(--navi-popup-grow-duration, 0.25s)
|
|
59020
|
+
ease both;
|
|
59021
|
+
}
|
|
59022
|
+
&[data-navi-popup-grow="closing"]::view-transition {
|
|
59023
|
+
animation-direction: reverse;
|
|
59024
|
+
}
|
|
59025
|
+
}
|
|
59026
|
+
/* The bars sit at the app's own inset (fixed_bar.jsx), so the room they
|
|
59027
|
+
take starts there; both tokens are 0px where nothing takes any. */
|
|
59028
|
+
@keyframes navi-popup-grow-clip {
|
|
59029
|
+
0%,
|
|
59030
|
+
65% {
|
|
59031
|
+
clip-path: inset(
|
|
59032
|
+
calc(
|
|
59033
|
+
var(--navi-app-inset-top, 0px) +
|
|
59034
|
+
var(--navi-fixed-bar-space-top, 0px)
|
|
59035
|
+
)
|
|
59036
|
+
calc(
|
|
59037
|
+
var(--navi-app-inset-right, 0px) +
|
|
59038
|
+
var(--navi-fixed-bar-space-right, 0px)
|
|
59039
|
+
)
|
|
59040
|
+
calc(
|
|
59041
|
+
var(--navi-app-inset-bottom, 0px) +
|
|
59042
|
+
var(--navi-fixed-bar-space-bottom, 0px)
|
|
59043
|
+
)
|
|
59044
|
+
calc(
|
|
59045
|
+
var(--navi-app-inset-left, 0px) +
|
|
59046
|
+
var(--navi-fixed-bar-space-left, 0px)
|
|
59047
|
+
)
|
|
59048
|
+
);
|
|
59049
|
+
}
|
|
59050
|
+
100% {
|
|
59051
|
+
clip-path: inset(0);
|
|
59052
|
+
}
|
|
58944
59053
|
}
|
|
58945
59054
|
|
|
58946
59055
|
${surfaceTextCss}
|
|
@@ -59077,6 +59186,15 @@ const css$E = /* css */`
|
|
|
59077
59186
|
* prop) and grows into whatever inside the dialog carries `data-grow`, the
|
|
59078
59187
|
* dialog itself when nothing does. `"auto"` never picks it: only the caller
|
|
59079
59188
|
* knows the two boxes are one object. See `popup_grow.js`.
|
|
59189
|
+
* @param {"box"|"scene"} [props.grow="box"] - Under `animation="growing"`,
|
|
59190
|
+
* what the anchor and what it grows into are to each other, which decides
|
|
59191
|
+
* how their pictures sit in the box moving between them. `"box"`: one
|
|
59192
|
+
* object at two sizes — a card gaining fields. Each picture is drawn at the
|
|
59193
|
+
* box's width from its top edge, and a box growing in height uncovers more
|
|
59194
|
+
* of it: the header stays where it is, the rest extends. `"scene"`: one
|
|
59195
|
+
* scene through two frames — a thumbnail and the map it is cut from. Each
|
|
59196
|
+
* picture covers the box, cropped around its centre and never distorted, so
|
|
59197
|
+
* what both frames show lands on itself.
|
|
59080
59198
|
* @param {string} [props.animationDuration] - Maps to
|
|
59081
59199
|
* `--popup-animation-duration`.
|
|
59082
59200
|
* @param {Element|{current: Element}|string} [props.anchor] - Never used for
|
|
@@ -59419,6 +59537,7 @@ const useDialogProps = props => {
|
|
|
59419
59537
|
// once, held at that size while open. See this prop's own JSDoc above.
|
|
59420
59538
|
sizing = "auto",
|
|
59421
59539
|
animation,
|
|
59540
|
+
grow = "box",
|
|
59422
59541
|
// Inert unless sizeFromAnchor below (see this file's top comment) —
|
|
59423
59542
|
// Dialog's own positioning is never relative to it.
|
|
59424
59543
|
anchor,
|
|
@@ -59642,7 +59761,8 @@ const useDialogProps = props => {
|
|
|
59642
59761
|
return;
|
|
59643
59762
|
}
|
|
59644
59763
|
growPopupFromAnchor(dialogEl, anchorElement, applyChange, {
|
|
59645
|
-
opened
|
|
59764
|
+
opened,
|
|
59765
|
+
grow
|
|
59646
59766
|
});
|
|
59647
59767
|
} : null;
|
|
59648
59768
|
|
|
@@ -60121,11 +60241,18 @@ const useDialogProps = props => {
|
|
|
60121
60241
|
// (and harmless for a local dialog, whose real backdrop element gets it
|
|
60122
60242
|
// via backdropProps).
|
|
60123
60243
|
"data-backdrop-variant": backdropVariant,
|
|
60244
|
+
// That this dialog has no wall, said in the DOM: the browser generates a
|
|
60245
|
+
// ::backdrop for the popover a wall-less top-layer dialog is shown as,
|
|
60246
|
+
// and the CSS above keys off this to leave it unpainted. Also the only
|
|
60247
|
+
// way anything outside can read the absence — a test measuring pixels is
|
|
60248
|
+
// the alternative.
|
|
60249
|
+
"data-backdrop": backdrop ? undefined : "none",
|
|
60124
60250
|
// Read by the native ::backdrop, which inherits them from here (see this
|
|
60125
60251
|
// file's CSS) — a local dialog's own backdrop element gets them via
|
|
60126
|
-
// backdropProps above.
|
|
60127
|
-
|
|
60128
|
-
|
|
60252
|
+
// backdropProps above. Dropped without a wall: they say how one is
|
|
60253
|
+
// painted, and inline they would beat the rule that says there is none.
|
|
60254
|
+
"backdropColor": backdrop ? backdropColor : undefined,
|
|
60255
|
+
"backdropFilter": backdrop ? backdropFilter : undefined,
|
|
60129
60256
|
"styleCSSVars": DIALOG_STYLE_CSS_VARS,
|
|
60130
60257
|
...rest,
|
|
60131
60258
|
...autoFocusProps,
|
|
@@ -61962,6 +62089,7 @@ const css$C = /* css */`@layer navi {
|
|
|
61962
62089
|
* popup meant to dock on a phone must not declare itself compact — see
|
|
61963
62090
|
* `maxWidth` below.
|
|
61964
62091
|
* @param {boolean} [props.sizeFromAnchor] - **Dialog-only**, same guard.
|
|
62092
|
+
* @param {"box"|"scene"} [props.grow] - **Dialog-only**, same guard.
|
|
61965
62093
|
* @param {string} [props.positionArea] - Forwarded as-is — `Dialog` and
|
|
61966
62094
|
* `Popover` have different own defaults (`"center"` vs. `"bottom"`),
|
|
61967
62095
|
* deliberately not homogenized here (each reads best for its own typical
|
|
@@ -62068,6 +62196,7 @@ const Popup = props => {
|
|
|
62068
62196
|
// in the dialog branch, so they never reach the popover element.
|
|
62069
62197
|
dockedOnSmallTouchScreen,
|
|
62070
62198
|
sizeFromAnchor,
|
|
62199
|
+
grow,
|
|
62071
62200
|
...rest
|
|
62072
62201
|
} = props;
|
|
62073
62202
|
const [mode] = useResolvedPopupMode(modeProp, maxWidth, {
|
|
@@ -62086,6 +62215,7 @@ const Popup = props => {
|
|
|
62086
62215
|
...rest,
|
|
62087
62216
|
dockedOnSmallTouchScreen: dockedOnSmallTouchScreen,
|
|
62088
62217
|
sizeFromAnchor: sizeFromAnchor,
|
|
62218
|
+
grow: grow,
|
|
62089
62219
|
maxWidth: maxWidth,
|
|
62090
62220
|
pointerInteractionOutsideEffect: pointerInteractionOutsideEffect,
|
|
62091
62221
|
backdrop: backdrop,
|
|
@@ -62545,19 +62675,26 @@ const PickerCustom = props => {
|
|
|
62545
62675
|
commitUIStateAsAnswer(controller, closeEvent);
|
|
62546
62676
|
}
|
|
62547
62677
|
}
|
|
62548
|
-
leaveExpanded({
|
|
62549
|
-
isBack: closeEvent.detail.isCancel
|
|
62550
|
-
});
|
|
62551
|
-
// Reset so the next opening re-evaluates screen size
|
|
62552
|
-
resetMode();
|
|
62553
62678
|
const confirmEvent = confirmEventRef.current;
|
|
62554
62679
|
confirmEventRef.current = null;
|
|
62555
62680
|
if (confirmEvent && !closeEvent.detail.isCancel) {
|
|
62556
62681
|
onConfirm?.(confirmEvent);
|
|
62557
62682
|
}
|
|
62558
|
-
//
|
|
62559
|
-
// picker's value as it ends up
|
|
62683
|
+
// After the value bookkeeping above — whoever listens reads the
|
|
62684
|
+
// picker's value as it ends up, committed or restored on a cancel —
|
|
62685
|
+
// and before leaveExpanded below, which is where the caller's
|
|
62686
|
+
// reaction runs for a Dialog too. A close that keeps goes back onto
|
|
62687
|
+
// the entry the popup was opened from while KEEPING the url as it
|
|
62688
|
+
// stands at that moment (see useNavState's leave()), so what onClose
|
|
62689
|
+
// spells into the address — a route param naming what was being
|
|
62690
|
+
// edited, cleared now that nothing is — has to be written before,
|
|
62691
|
+
// or the landing puts it back.
|
|
62560
62692
|
onClose?.(closeEvent);
|
|
62693
|
+
leaveExpanded({
|
|
62694
|
+
isBack: closeEvent.detail.isCancel
|
|
62695
|
+
});
|
|
62696
|
+
// Reset so the next opening re-evaluates screen size
|
|
62697
|
+
resetMode();
|
|
62561
62698
|
}
|
|
62562
62699
|
};
|
|
62563
62700
|
});
|
|
@@ -62951,6 +63088,7 @@ const PickerContentInsidePopup = props => {
|
|
|
62951
63088
|
// its own width once lifted. Dialog's own `sizeFromAnchor`.
|
|
62952
63089
|
dialogSizeFromAnchor,
|
|
62953
63090
|
animation,
|
|
63091
|
+
grow,
|
|
62954
63092
|
animationDuration,
|
|
62955
63093
|
// mode="callout": what the callout says about what it holds, and paints
|
|
62956
63094
|
// in its border and icon — "none" for a plain tooltip (see the callout
|
|
@@ -63029,6 +63167,7 @@ const PickerContentInsidePopup = props => {
|
|
|
63029
63167
|
expandY: isPopover ? undefined : dialogExpandY,
|
|
63030
63168
|
dockedOnSmallTouchScreen: isPopover ? undefined : dockedOnSmallTouchScreen,
|
|
63031
63169
|
sizeFromAnchor: isPopover ? undefined : dialogSizeFromAnchor,
|
|
63170
|
+
grow: isPopover ? undefined : grow,
|
|
63032
63171
|
children: jsx(PopupModeContext.Provider, {
|
|
63033
63172
|
value: mode,
|
|
63034
63173
|
children: children
|