@jsenv/navi 0.29.424 → 0.29.425
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 +150 -73
- package/dist/dev/jsenv_navi.js.map +8 -8
- package/dist/jsenv_navi.js +143 -69
- package/dist/jsenv_navi.js.map +8 -8
- package/docs/popup_open.md +16 -11
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -15389,6 +15389,9 @@ const createOpenController = (
|
|
|
15389
15389
|
// content is still waiting for a first open to be built. Called below,
|
|
15390
15390
|
// before openEffect, so the popup measures and positions the real thing.
|
|
15391
15391
|
mountContent: null,
|
|
15392
|
+
// Set alongside mountContent: the content asked for outside any opening,
|
|
15393
|
+
// by something that reads it while the popup is closed.
|
|
15394
|
+
buildContent: null,
|
|
15392
15395
|
// The caller's own `onOpen`, set by Dialog/Popover from their props on
|
|
15393
15396
|
// every render (like openEffect). Called BEFORE mountContent, so whatever
|
|
15394
15397
|
// it decides — which record this dialog is opening on — is already true by
|
|
@@ -40243,7 +40246,11 @@ const flushSyncRendering = (fn) => {
|
|
|
40243
40246
|
*
|
|
40244
40247
|
* "always" is for content something else depends on before any opening: a
|
|
40245
40248
|
* value the popup's owner reads off its own children, fields a form around it
|
|
40246
|
-
* collects on submit, a size measured from outside.
|
|
40249
|
+
* collects on submit, a size measured from outside. When the popup's owner
|
|
40250
|
+
* cannot tell at mount whether anything will depend on it, the content waits
|
|
40251
|
+
* and whatever depends on it asks for it, closed, with
|
|
40252
|
+
* `openController.buildContent()` (a picker's drawing reading the value its
|
|
40253
|
+
* popup holds, see usePickerContext).
|
|
40247
40254
|
*
|
|
40248
40255
|
* "idle" is "always" minus the cost on the critical render: the page appears
|
|
40249
40256
|
* without the content, and the browser builds it in an idle moment after
|
|
@@ -40370,6 +40377,14 @@ const usePopupContentMount = (
|
|
|
40370
40377
|
}
|
|
40371
40378
|
}
|
|
40372
40379
|
};
|
|
40380
|
+
// A plain render rather than a flush: the popup stays closed, and nothing is
|
|
40381
|
+
// about to measure it.
|
|
40382
|
+
openController.buildContent = () => {
|
|
40383
|
+
if (contentMountedRef.current) {
|
|
40384
|
+
return;
|
|
40385
|
+
}
|
|
40386
|
+
setContentMounted(true);
|
|
40387
|
+
};
|
|
40373
40388
|
openController.unmountContent =
|
|
40374
40389
|
mount === "while-opened"
|
|
40375
40390
|
? () => {
|
|
@@ -69366,6 +69381,69 @@ const toPopoverAnimation = animation => {
|
|
|
69366
69381
|
return animation;
|
|
69367
69382
|
};
|
|
69368
69383
|
|
|
69384
|
+
const PickerContext = createContext();
|
|
69385
|
+
|
|
69386
|
+
/*
|
|
69387
|
+
* The picker's context, for whatever draws its value. A picker told no value
|
|
69388
|
+
* holds what the control in its popup holds, and builds that popup only when
|
|
69389
|
+
* something reads it (see the mount decision in picker_custom.jsx): what the
|
|
69390
|
+
* picker draws itself it knows about, a drawing given as an element is the
|
|
69391
|
+
* caller's, and says so here by reading. In a layout effect, so the frame that
|
|
69392
|
+
* first shows the drawing already shows the value.
|
|
69393
|
+
*/
|
|
69394
|
+
const usePickerContext = () => {
|
|
69395
|
+
const context = useContext(PickerContext);
|
|
69396
|
+
const requestValue = context?.requestValue;
|
|
69397
|
+
useLayoutEffect(() => {
|
|
69398
|
+
requestValue?.();
|
|
69399
|
+
}, [requestValue]);
|
|
69400
|
+
return context;
|
|
69401
|
+
};
|
|
69402
|
+
|
|
69403
|
+
/*
|
|
69404
|
+
* A typed picker (`type="date"`, `type="array"`, …) draws its value with a ui
|
|
69405
|
+
* of navi's own, installed as the very "ui" prop a caller overrides to draw
|
|
69406
|
+
* that value themselves. Marking navi's own tells the two apart afterwards:
|
|
69407
|
+
* an empty value is greyed as a placeholder only when the drawing is navi's —
|
|
69408
|
+
* a caller's ui may well be their way of writing "no filter", which is an
|
|
69409
|
+
* answer, not a blank (see navi-placeholder in picker.jsx).
|
|
69410
|
+
*/
|
|
69411
|
+
const asPickerOwnUI = PickerUI => {
|
|
69412
|
+
PickerUI.isPickerOwnUI = true;
|
|
69413
|
+
return PickerUI;
|
|
69414
|
+
};
|
|
69415
|
+
const pickerUIIsNaviOwn = ui => {
|
|
69416
|
+
return Boolean(ui) && typeof ui === "object" && Boolean(ui.type?.isPickerOwnUI);
|
|
69417
|
+
};
|
|
69418
|
+
|
|
69419
|
+
/**
|
|
69420
|
+
* What the picker holds, read from inside its `ui` — for a drawing given as
|
|
69421
|
+
* an element (`ui={<MyCard game={game} />}`), which the picker cannot hand
|
|
69422
|
+
* props to. `value` is the state the picker holds right now: the answer the
|
|
69423
|
+
* popup just closed on, before the server has said anything, and put back if
|
|
69424
|
+
* the action fails. `loading` is the run in flight; `interactive` is false
|
|
69425
|
+
* while the picker is disabled, read-only or busy.
|
|
69426
|
+
*
|
|
69427
|
+
* @returns {{ value: any, loading: boolean, interactive: boolean } | null}
|
|
69428
|
+
* null outside a picker.
|
|
69429
|
+
*/
|
|
69430
|
+
const usePickerState = () => {
|
|
69431
|
+
const context = usePickerContext();
|
|
69432
|
+
if (!context) {
|
|
69433
|
+
return null;
|
|
69434
|
+
}
|
|
69435
|
+
const {
|
|
69436
|
+
value,
|
|
69437
|
+
loading,
|
|
69438
|
+
interactive
|
|
69439
|
+
} = context;
|
|
69440
|
+
return {
|
|
69441
|
+
value,
|
|
69442
|
+
loading,
|
|
69443
|
+
interactive
|
|
69444
|
+
};
|
|
69445
|
+
};
|
|
69446
|
+
|
|
69369
69447
|
installImportMetaCssBuild(import.meta);
|
|
69370
69448
|
const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
|
|
69371
69449
|
display: none;
|
|
@@ -69593,6 +69671,12 @@ const PickerCustom = props => {
|
|
|
69593
69671
|
const idDefault = useId();
|
|
69594
69672
|
const controlId = useContext(ControlIdContext);
|
|
69595
69673
|
props.id = props.id || controlId || idDefault;
|
|
69674
|
+
// Given no value, the picker holds what the control in its popup holds (see
|
|
69675
|
+
// the `mount` decision below) — and a group around it collects that from the
|
|
69676
|
+
// moment both exist.
|
|
69677
|
+
const valueInPopup = !isControlValueGivenByProps(props);
|
|
69678
|
+
const parentController = useContext(ParentUIStateControllerContext);
|
|
69679
|
+
const answersToGroup = Boolean(parentController) && !props.standalone;
|
|
69596
69680
|
// Same narrow-container/maxWidth-compact heuristic Popup itself uses (see
|
|
69597
69681
|
// popup_mode.jsx's own useResolvedPopupMode) — frozen for the lifetime of an opening
|
|
69598
69682
|
// (computed when closed, stable while open, so a screen resize mid-session
|
|
@@ -69838,12 +69922,20 @@ const PickerCustom = props => {
|
|
|
69838
69922
|
const requestInteraction = options => {
|
|
69839
69923
|
dispatchRequestInteraction(ref.current, options);
|
|
69840
69924
|
};
|
|
69925
|
+
|
|
69926
|
+
// What a drawing of the caller's calls as it reads the value (see
|
|
69927
|
+
// usePickerContext): the popup holding it, built while closed. Only when
|
|
69928
|
+
// `mount` is left to the picker — a caller who set it has said when.
|
|
69929
|
+
const requestValue = useStableCallback(() => {
|
|
69930
|
+
openController.buildContent();
|
|
69931
|
+
});
|
|
69841
69932
|
const {
|
|
69842
69933
|
onActionStart,
|
|
69843
69934
|
children,
|
|
69844
69935
|
uiAction: uiActionProp
|
|
69845
69936
|
} = props;
|
|
69846
69937
|
Object.assign(pickerProps, {
|
|
69938
|
+
"requestValue": valueInPopup && props.mount === undefined ? requestValue : undefined,
|
|
69847
69939
|
"aria-expanded": Boolean(expanded),
|
|
69848
69940
|
"onActionStart": e => {
|
|
69849
69941
|
onActionStart?.(e);
|
|
@@ -69882,13 +69974,12 @@ const PickerCustom = props => {
|
|
|
69882
69974
|
anchor: props.anchor || props.ref,
|
|
69883
69975
|
openController,
|
|
69884
69976
|
// A picker whose value was never given to it reads it off the control in
|
|
69885
|
-
// its popup (see useUIFacadeStateController)
|
|
69886
|
-
//
|
|
69887
|
-
//
|
|
69888
|
-
//
|
|
69889
|
-
//
|
|
69890
|
-
|
|
69891
|
-
mount: props.mount ?? (isControlValueGivenByProps(props) ? MOUNT_DEFAULT : "always"),
|
|
69977
|
+
// its popup (see useUIFacadeStateController), so that control is built as
|
|
69978
|
+
// soon as something reads the value: at mount when the picker sees the
|
|
69979
|
+
// reader, otherwise when a drawing of the caller's asks (requestValue
|
|
69980
|
+
// above). Told a value — even an empty one — the picker pushes it down,
|
|
69981
|
+
// and the popup waits for its first open (see popup_content_mount.js).
|
|
69982
|
+
mount: props.mount ?? (valueInPopup && isPickerValueReadAtMount(props, answersToGroup) ? "always" : MOUNT_DEFAULT),
|
|
69892
69983
|
// Not on pickerProps (the trigger): commands.js's own
|
|
69893
69984
|
// resolveClosestExpandable() does `el.closest("[aria-expanded]")` to
|
|
69894
69985
|
// find where to dispatch navi_request_open/navi_request_close — and
|
|
@@ -70176,6 +70267,29 @@ const PickerCustom = props => {
|
|
|
70176
70267
|
});
|
|
70177
70268
|
};
|
|
70178
70269
|
|
|
70270
|
+
// Whether something reads a picker's value before its popup has opened, as
|
|
70271
|
+
// far as the picker sees from its own props: the drawing navi renders for it
|
|
70272
|
+
// (its own, or the typed one PickerTypeResolver installs — `ui` is still unset
|
|
70273
|
+
// here), one of navi's Picker.UI.*, a component handed `value`, the clear
|
|
70274
|
+
// cross, a form submitting it by name, a group it answers to. A headless
|
|
70275
|
+
// picker draws nothing, and an element `ui` is the caller's: it reads through
|
|
70276
|
+
// usePickerContext, which asks for the value then.
|
|
70277
|
+
const isPickerValueReadAtMount = (props, answersToGroup) => {
|
|
70278
|
+
if (props.name !== undefined || answersToGroup) {
|
|
70279
|
+
return true;
|
|
70280
|
+
}
|
|
70281
|
+
if (props.variant === "headless") {
|
|
70282
|
+
return false;
|
|
70283
|
+
}
|
|
70284
|
+
if (props.clearable) {
|
|
70285
|
+
return true;
|
|
70286
|
+
}
|
|
70287
|
+
const {
|
|
70288
|
+
ui
|
|
70289
|
+
} = props;
|
|
70290
|
+
return ui === undefined || typeof ui === "function" || pickerUIIsNaviOwn(ui);
|
|
70291
|
+
};
|
|
70292
|
+
|
|
70179
70293
|
// A hold declared on something AROUND the picker (a card opened by
|
|
70180
70294
|
// `openOn="longpress"`, holding this one in its drawing): the finger going
|
|
70181
70295
|
// down may be the start of that hold, so this picker opens on the click — which
|
|
@@ -70691,52 +70805,6 @@ const PickerConfirmBody = ({
|
|
|
70691
70805
|
});
|
|
70692
70806
|
};
|
|
70693
70807
|
|
|
70694
|
-
const PickerContext = createContext();
|
|
70695
|
-
|
|
70696
|
-
/*
|
|
70697
|
-
* A typed picker (`type="date"`, `type="array"`, …) draws its value with a ui
|
|
70698
|
-
* of navi's own, installed as the very "ui" prop a caller overrides to draw
|
|
70699
|
-
* that value themselves. Marking navi's own tells the two apart afterwards:
|
|
70700
|
-
* an empty value is greyed as a placeholder only when the drawing is navi's —
|
|
70701
|
-
* a caller's ui may well be their way of writing "no filter", which is an
|
|
70702
|
-
* answer, not a blank (see navi-placeholder in picker.jsx).
|
|
70703
|
-
*/
|
|
70704
|
-
const asPickerOwnUI = PickerUI => {
|
|
70705
|
-
PickerUI.isPickerOwnUI = true;
|
|
70706
|
-
return PickerUI;
|
|
70707
|
-
};
|
|
70708
|
-
const pickerUIIsNaviOwn = ui => {
|
|
70709
|
-
return Boolean(ui) && typeof ui === "object" && Boolean(ui.type?.isPickerOwnUI);
|
|
70710
|
-
};
|
|
70711
|
-
|
|
70712
|
-
/**
|
|
70713
|
-
* What the picker holds, read from inside its `ui` — for a drawing given as
|
|
70714
|
-
* an element (`ui={<MyCard game={game} />}`), which the picker cannot hand
|
|
70715
|
-
* props to. `value` is the state the picker holds right now: the answer the
|
|
70716
|
-
* popup just closed on, before the server has said anything, and put back if
|
|
70717
|
-
* the action fails. `loading` is the run in flight; `interactive` is false
|
|
70718
|
-
* while the picker is disabled, read-only or busy.
|
|
70719
|
-
*
|
|
70720
|
-
* @returns {{ value: any, loading: boolean, interactive: boolean } | null}
|
|
70721
|
-
* null outside a picker.
|
|
70722
|
-
*/
|
|
70723
|
-
const usePickerState = () => {
|
|
70724
|
-
const context = useContext(PickerContext);
|
|
70725
|
-
if (!context) {
|
|
70726
|
-
return null;
|
|
70727
|
-
}
|
|
70728
|
-
const {
|
|
70729
|
-
value,
|
|
70730
|
-
loading,
|
|
70731
|
-
interactive
|
|
70732
|
-
} = context;
|
|
70733
|
-
return {
|
|
70734
|
-
value,
|
|
70735
|
-
loading,
|
|
70736
|
-
interactive
|
|
70737
|
-
};
|
|
70738
|
-
};
|
|
70739
|
-
|
|
70740
70808
|
const PickerNaviMinute = props => {
|
|
70741
70809
|
const Next = useNextResolver();
|
|
70742
70810
|
const {
|
|
@@ -79253,7 +79321,7 @@ const PickerObjectUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79253
79321
|
const {
|
|
79254
79322
|
value,
|
|
79255
79323
|
placeholder
|
|
79256
|
-
} =
|
|
79324
|
+
} = usePickerContext();
|
|
79257
79325
|
if (uiStateHoldsNothing(value)) {
|
|
79258
79326
|
if (!placeholder) {
|
|
79259
79327
|
return null;
|
|
@@ -79289,7 +79357,7 @@ const PickerArrayUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79289
79357
|
value,
|
|
79290
79358
|
placeholder,
|
|
79291
79359
|
maxLines
|
|
79292
|
-
} =
|
|
79360
|
+
} = usePickerContext();
|
|
79293
79361
|
if (uiStateHoldsNothing(value)) {
|
|
79294
79362
|
if (!placeholder) {
|
|
79295
79363
|
return null;
|
|
@@ -79371,7 +79439,7 @@ const PickerColorUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79371
79439
|
const {
|
|
79372
79440
|
value,
|
|
79373
79441
|
placeholder
|
|
79374
|
-
} =
|
|
79442
|
+
} = usePickerContext();
|
|
79375
79443
|
if (!value) {
|
|
79376
79444
|
if (!placeholder) {
|
|
79377
79445
|
return jsx(Color, {});
|
|
@@ -79395,7 +79463,7 @@ const PickerDateUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79395
79463
|
const {
|
|
79396
79464
|
value,
|
|
79397
79465
|
placeholder
|
|
79398
|
-
} =
|
|
79466
|
+
} = usePickerContext();
|
|
79399
79467
|
if (!value) {
|
|
79400
79468
|
if (!placeholder) {
|
|
79401
79469
|
return jsx(Time, {
|
|
@@ -79427,7 +79495,7 @@ const PickerMonthUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79427
79495
|
const {
|
|
79428
79496
|
value,
|
|
79429
79497
|
placeholder
|
|
79430
|
-
} =
|
|
79498
|
+
} = usePickerContext();
|
|
79431
79499
|
if (!value) {
|
|
79432
79500
|
if (!placeholder) {
|
|
79433
79501
|
return jsx(Time, {
|
|
@@ -79458,7 +79526,7 @@ const PickerWeekUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79458
79526
|
const {
|
|
79459
79527
|
value,
|
|
79460
79528
|
placeholder
|
|
79461
|
-
} =
|
|
79529
|
+
} = usePickerContext();
|
|
79462
79530
|
if (!value) {
|
|
79463
79531
|
if (!placeholder) {
|
|
79464
79532
|
return jsx(Time, {
|
|
@@ -79489,7 +79557,7 @@ const PickerTimeUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79489
79557
|
const {
|
|
79490
79558
|
value,
|
|
79491
79559
|
placeholder
|
|
79492
|
-
} =
|
|
79560
|
+
} = usePickerContext();
|
|
79493
79561
|
if (!value) {
|
|
79494
79562
|
if (!placeholder) {
|
|
79495
79563
|
return jsx(Time, {
|
|
@@ -79520,7 +79588,7 @@ const PickerDurationUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79520
79588
|
const {
|
|
79521
79589
|
value,
|
|
79522
79590
|
placeholder
|
|
79523
|
-
} =
|
|
79591
|
+
} = usePickerContext();
|
|
79524
79592
|
if (!value) {
|
|
79525
79593
|
if (!placeholder) {
|
|
79526
79594
|
return jsx(Time, {
|
|
@@ -79550,7 +79618,7 @@ const PickerDatetimeUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79550
79618
|
const {
|
|
79551
79619
|
value,
|
|
79552
79620
|
placeholder
|
|
79553
|
-
} =
|
|
79621
|
+
} = usePickerContext();
|
|
79554
79622
|
if (!value) {
|
|
79555
79623
|
if (!placeholder) {
|
|
79556
79624
|
return jsx(Time, {
|
|
@@ -79579,7 +79647,7 @@ const PickerFileUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79579
79647
|
const {
|
|
79580
79648
|
value,
|
|
79581
79649
|
placeholder
|
|
79582
|
-
} =
|
|
79650
|
+
} = usePickerContext();
|
|
79583
79651
|
if (!value) {
|
|
79584
79652
|
if (!placeholder) {
|
|
79585
79653
|
return null;
|
|
@@ -80115,7 +80183,11 @@ const PickerButton = props => {
|
|
|
80115
80183
|
// a piece of, waiting as a whole (see the `loadingOutline` doc).
|
|
80116
80184
|
loadingOutline,
|
|
80117
80185
|
// Where the outline runs, in px from the picker's box: negative is outside.
|
|
80118
|
-
loadingOutlineInset = -2
|
|
80186
|
+
loadingOutlineInset = -2,
|
|
80187
|
+
// Builds the popup this picker's value is read from, for a drawing that
|
|
80188
|
+
// reads it (see usePickerContext). Set by PickerCustom, only while that
|
|
80189
|
+
// value lives in the popup.
|
|
80190
|
+
requestValue
|
|
80119
80191
|
} = props;
|
|
80120
80192
|
// A word in a sentence is never truncated — and the clamp's overflow: hidden
|
|
80121
80193
|
// would cut its dotted underline, which sits on the edge of the line box
|
|
@@ -80213,7 +80285,8 @@ const PickerButton = props => {
|
|
|
80213
80285
|
id: inputProps.id,
|
|
80214
80286
|
interactive,
|
|
80215
80287
|
loading,
|
|
80216
|
-
clearConfirm
|
|
80288
|
+
clearConfirm,
|
|
80289
|
+
requestValue
|
|
80217
80290
|
};
|
|
80218
80291
|
return (
|
|
80219
80292
|
/* Read-only crosses into everything the picker is made of: what it really
|
|
@@ -80264,7 +80337,8 @@ const PickerButton = props => {
|
|
|
80264
80337
|
error: undefined,
|
|
80265
80338
|
dayLabel: undefined,
|
|
80266
80339
|
loadingOutline: undefined,
|
|
80267
|
-
loadingOutlineInset: undefined
|
|
80340
|
+
loadingOutlineInset: undefined,
|
|
80341
|
+
requestValue: undefined
|
|
80268
80342
|
// This wrapper will receive keyboard event bubbling from the picker popup content
|
|
80269
80343
|
// we re-dispatch on the input (to get escape to close for instance)
|
|
80270
80344
|
,
|
|
@@ -80478,7 +80552,7 @@ const PickerClear = ({
|
|
|
80478
80552
|
children,
|
|
80479
80553
|
...rest
|
|
80480
80554
|
}) => {
|
|
80481
|
-
const pickerContext =
|
|
80555
|
+
const pickerContext = usePickerContext();
|
|
80482
80556
|
if (!pickerContext) {
|
|
80483
80557
|
warnOnClearOutsidePicker();
|
|
80484
80558
|
return null;
|
|
@@ -80762,7 +80836,7 @@ const PickerDefaultUI = () => {
|
|
|
80762
80836
|
const {
|
|
80763
80837
|
value,
|
|
80764
80838
|
placeholder
|
|
80765
|
-
} =
|
|
80839
|
+
} = usePickerContext();
|
|
80766
80840
|
if (!value) {
|
|
80767
80841
|
if (!placeholder) {
|
|
80768
80842
|
return null;
|
|
@@ -81336,10 +81410,13 @@ const PickerFirstResolver = props => {
|
|
|
81336
81410
|
* @param {string} [cancelTestId]
|
|
81337
81411
|
* @param {"always"|"idle"|"from-first-open"|"while-opened"} [mount] The popup's own
|
|
81338
81412
|
* (see Popup): when the children are built and thrown away. Left out, a
|
|
81339
|
-
* picker told no value
|
|
81340
|
-
*
|
|
81341
|
-
*
|
|
81342
|
-
*
|
|
81413
|
+
* picker told no value holds what the control they hold holds, and builds
|
|
81414
|
+
* them as soon as that value is read: right away (`"always"`) when navi draws
|
|
81415
|
+
* it (no `ui`, a `Picker.UI.*`, a component `ui`), when there is a clear
|
|
81416
|
+
* cross, or when a form or group around the picker collects it; when an
|
|
81417
|
+
* element `ui` calls `usePickerState()`. Read by nothing, it waits for the
|
|
81418
|
+
* first open, like a picker told a value. A `type="confirm"` picker never
|
|
81419
|
+
* builds them early: its popup is a question, there is nothing to read.
|
|
81343
81420
|
*/
|
|
81344
81421
|
// Every picker type is resolved into one of these before it gets here (see
|
|
81345
81422
|
// PickerTypeResolver and resolveInputProps): a native input type, or navi_js
|