@jsenv/navi 0.29.423 → 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 +273 -80
- package/dist/dev/jsenv_navi.js.map +9 -9
- package/dist/jsenv_navi.js +266 -76
- package/dist/jsenv_navi.js.map +9 -9
- package/docs/popup_open.md +16 -11
- package/package.json +2 -2
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
|
? () => {
|
|
@@ -60814,6 +60829,20 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
|
|
|
60814
60829
|
* `sizing={loading ? "largest" : "frozen"}` and is measured once the real
|
|
60815
60830
|
* content is there. The freeze holds against what happens INSIDE the box, not
|
|
60816
60831
|
* against the room it is given: a window resize measures again.
|
|
60832
|
+
* @param {"always"|"near"} [props.mount="always"] - when a slide's CONTENT is
|
|
60833
|
+
* rendered. The <Slide> element always is, so areas, tabs following the box
|
|
60834
|
+
* and `aria-labelledby` are the same either way. "always": every slide's,
|
|
60835
|
+
* from the start. "near": once a travel can show it — the slide on screen,
|
|
60836
|
+
* the one a travel is going to, and at rest the ones a hand dragging the box
|
|
60837
|
+
* would bring alongside (neighbours on the map, along the axes
|
|
60838
|
+
* `travelByDrag` opens, the other end of a `loop` included) — and kept from
|
|
60839
|
+
* then on. For slides that are costly to build (a grid of a few hundred
|
|
60840
|
+
* controls each): opening builds the slide shown and its neighbours rather
|
|
60841
|
+
* than all of them, and a jump to a far slide builds that one and none of
|
|
60842
|
+
* those in between, which a travel never shows. The box is then as large as
|
|
60843
|
+
* the largest slide BUILT so far (see `sizing`), so it grows as a larger one
|
|
60844
|
+
* is reached: give it a size of its own when the slides differ.
|
|
60845
|
+
* `<Slide mount>` says it for one slide — a heavy one among light ones.
|
|
60817
60846
|
*/
|
|
60818
60847
|
const SlideContainer = ({
|
|
60819
60848
|
layout = "row",
|
|
@@ -60829,6 +60858,7 @@ const SlideContainer = ({
|
|
|
60829
60858
|
travelByScroll = "x",
|
|
60830
60859
|
duration = "300ms",
|
|
60831
60860
|
sizing = "largest",
|
|
60861
|
+
mount = "always",
|
|
60832
60862
|
children,
|
|
60833
60863
|
...rest
|
|
60834
60864
|
}) => {
|
|
@@ -60948,6 +60978,15 @@ const SlideContainer = ({
|
|
|
60948
60978
|
// Undefined at mount on purpose: the first area held outside is news like any
|
|
60949
60979
|
// other, and it is walked to rather than opened on.
|
|
60950
60980
|
const areaAskedSeenRef = useRef(undefined);
|
|
60981
|
+
// An area held outside that this container has not answered yet: the effect
|
|
60982
|
+
// reading currentFromCaller walks to it, after the render that brings it.
|
|
60983
|
+
const requestIsNews = () => currentFromCaller !== undefined && currentFromCaller !== areaAskedSeenRef.current;
|
|
60984
|
+
// The slide about to be on screen, as far as a render can tell: the area
|
|
60985
|
+
// asked from outside while it is news — the picture is walked there before
|
|
60986
|
+
// it is painted, the first request included — and the container's own answer
|
|
60987
|
+
// otherwise. What a slide waiting for a travel reads to have its content in
|
|
60988
|
+
// the very render that shows it (see Slide's `mount`).
|
|
60989
|
+
const areaShownNext = requestIsNews() ? currentFromCaller : current;
|
|
60951
60990
|
// Where the container stands, said where the caller holds it.
|
|
60952
60991
|
//
|
|
60953
60992
|
// Whether a slide is a place one came from is the STATE's own business (see
|
|
@@ -61002,6 +61041,22 @@ const SlideContainer = ({
|
|
|
61002
61041
|
// state: nothing on screen depends on it, and a travel must read what the
|
|
61003
61042
|
// one before it wrote, not what the last render saw.
|
|
61004
61043
|
const cameFromRef = useRef({});
|
|
61044
|
+
// The slides a travel has come near while their content was not rendered
|
|
61045
|
+
// (mount="near", see renderNear): only ever grows, and only for a slide that
|
|
61046
|
+
// was waiting — a container whose slides all render never re-renders for it.
|
|
61047
|
+
const [nearAreas, setNearAreas] = useState([]);
|
|
61048
|
+
// Nothing names the slide on screen (no area held outside, no default): it
|
|
61049
|
+
// is the first one in the DOM, which the first render cannot read yet. The
|
|
61050
|
+
// first Slide to render is that one, so it claims it — the slide on screen
|
|
61051
|
+
// then has its content from the first commit (a size frozen at mount, a
|
|
61052
|
+
// focus landing in it) rather than one render late.
|
|
61053
|
+
const openingClaimRef = useRef(null);
|
|
61054
|
+
const claimOpening = area => {
|
|
61055
|
+
if (openingClaimRef.current === null) {
|
|
61056
|
+
openingClaimRef.current = area;
|
|
61057
|
+
}
|
|
61058
|
+
return openingClaimRef.current === area;
|
|
61059
|
+
};
|
|
61005
61060
|
|
|
61006
61061
|
// The controller has caught up with the slide the container went to on its
|
|
61007
61062
|
// own (commit="rest"): there are no longer two answers to give, so the
|
|
@@ -61142,6 +61197,44 @@ const SlideContainer = ({
|
|
|
61142
61197
|
}
|
|
61143
61198
|
};
|
|
61144
61199
|
|
|
61200
|
+
// The slides a hand dragging the box from this area would bring alongside:
|
|
61201
|
+
// the map's neighbours on the axes it drags, read the way the drag reads them
|
|
61202
|
+
// (areaTowards knows the holes and a loop's two ends).
|
|
61203
|
+
const areasAlongside = area => {
|
|
61204
|
+
const alongside = [];
|
|
61205
|
+
if (dragAxes?.includes("x")) {
|
|
61206
|
+
alongside.push(areaTowards(-1, 0, area), areaTowards(1, 0, area));
|
|
61207
|
+
}
|
|
61208
|
+
if (dragAxes?.includes("y")) {
|
|
61209
|
+
alongside.push(areaTowards(0, -1, area), areaTowards(0, 1, area));
|
|
61210
|
+
}
|
|
61211
|
+
return alongside;
|
|
61212
|
+
};
|
|
61213
|
+
|
|
61214
|
+
// mount="near": the content of a slide is asked for before a travel shows
|
|
61215
|
+
// it. The neighbours are asked for AT REST, never when a travel sets off: the
|
|
61216
|
+
// travel shows the slide it goes to and nothing past it, and a slide built in
|
|
61217
|
+
// the render that starts it is a frame the travel waits for. At rest nothing
|
|
61218
|
+
// moves, and a hand landing next is still answered after the build.
|
|
61219
|
+
const renderNear = (area, {
|
|
61220
|
+
alongside
|
|
61221
|
+
}) => {
|
|
61222
|
+
// Asked on every render: a container with nothing waiting stops here.
|
|
61223
|
+
if (!trackRef.current?.querySelector(":scope > [data-slide-unrendered]")) {
|
|
61224
|
+
return;
|
|
61225
|
+
}
|
|
61226
|
+
const unrendered = readMap().slideElements.filter(slideElement => slideElement.hasAttribute("data-slide-unrendered")).map(readArea);
|
|
61227
|
+
const near = alongside ? [area, ...areasAlongside(area)] : [area];
|
|
61228
|
+
const toRender = near.filter(nearArea => unrendered.includes(nearArea));
|
|
61229
|
+
if (!toRender.length) {
|
|
61230
|
+
return;
|
|
61231
|
+
}
|
|
61232
|
+
setNearAreas(previous => {
|
|
61233
|
+
const added = toRender.filter(nearArea => !previous.includes(nearArea));
|
|
61234
|
+
return added.length ? [...previous, ...added] : previous;
|
|
61235
|
+
});
|
|
61236
|
+
};
|
|
61237
|
+
|
|
61145
61238
|
// The travel is over: the stage is struck and every slide goes back where the
|
|
61146
61239
|
// map says it is. Nothing is seen moving for it — the slide on screen sits at
|
|
61147
61240
|
// the same place whatever the arrangement (its own offset and the track's are
|
|
@@ -61190,6 +61283,9 @@ const SlideContainer = ({
|
|
|
61190
61283
|
const offset = `${-x * 100}% ${-y * 100}%`;
|
|
61191
61284
|
offsetRef.current = offset;
|
|
61192
61285
|
track.style.setProperty("--slide-container-offset", offset);
|
|
61286
|
+
renderNear(currentArea, {
|
|
61287
|
+
alongside: true
|
|
61288
|
+
});
|
|
61193
61289
|
// Arrived, so the change can be told (commit="rest"): the picture is at
|
|
61194
61290
|
// rest and whatever the caller does with it — write the URL, ask a server —
|
|
61195
61291
|
// costs the gesture nothing anymore.
|
|
@@ -61467,6 +61563,15 @@ const SlideContainer = ({
|
|
|
61467
61563
|
if (!stageRef.current) {
|
|
61468
61564
|
drawnAreaRef.current = currentArea;
|
|
61469
61565
|
}
|
|
61566
|
+
// What a travel can show next (mount="near"), around where the picture
|
|
61567
|
+
// SETTLES: a request from outside is walked to by an effect running after
|
|
61568
|
+
// this one, in the same commit, and the slides around a picture about to be
|
|
61569
|
+
// walked away from would be built for nothing. The neighbours only at
|
|
61570
|
+
// rest, see renderNear.
|
|
61571
|
+
const settlingArea = requestIsNews() && reachableTowards(currentArea, currentFromCaller) || currentArea;
|
|
61572
|
+
renderNear(settlingArea, {
|
|
61573
|
+
alongside: !stageRef.current
|
|
61574
|
+
});
|
|
61470
61575
|
// Said last, and after data-current has been written: what a travel would do
|
|
61471
61576
|
// is read off the map from the slide that is now current, and off the locks
|
|
61472
61577
|
// that slide is wearing in this very commit.
|
|
@@ -61704,12 +61809,9 @@ const SlideContainer = ({
|
|
|
61704
61809
|
// request later), and what nothing has answered yet must still be answered
|
|
61705
61810
|
// once it can be.
|
|
61706
61811
|
useLayoutEffect(() => {
|
|
61707
|
-
if (
|
|
61708
|
-
|
|
61709
|
-
|
|
61710
|
-
if (currentFromCaller === areaAskedSeenRef.current) {
|
|
61711
|
-
// Not news: either nothing moved, or this container is reading back what
|
|
61712
|
-
// it wrote itself.
|
|
61812
|
+
if (!requestIsNews()) {
|
|
61813
|
+
// Not news: nothing held outside, nothing moved, or this container
|
|
61814
|
+
// reading back what it wrote itself.
|
|
61713
61815
|
return;
|
|
61714
61816
|
}
|
|
61715
61817
|
// The slide asked for is not rendered yet — no slides at all, or one whose
|
|
@@ -62539,6 +62641,11 @@ const SlideContainer = ({
|
|
|
62539
62641
|
if (!past) {
|
|
62540
62642
|
return false;
|
|
62541
62643
|
}
|
|
62644
|
+
// Two slides from the one at rest, so possibly never rendered: asked
|
|
62645
|
+
// for now, and built before the frame that brings it in is painted.
|
|
62646
|
+
renderNear(past, {
|
|
62647
|
+
alongside: false
|
|
62648
|
+
});
|
|
62542
62649
|
const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
|
|
62543
62650
|
const basePlace = stageRef.current?.placeByArea.get(reached) || placeOf.get(reached) || {
|
|
62544
62651
|
x: 0,
|
|
@@ -62997,6 +63104,12 @@ const SlideContainer = ({
|
|
|
62997
63104
|
done,
|
|
62998
63105
|
valueByArea,
|
|
62999
63106
|
settleFocus,
|
|
63107
|
+
// What a slide reads to know whether its content is rendered yet
|
|
63108
|
+
// (see Slide's `mount`).
|
|
63109
|
+
mount,
|
|
63110
|
+
areaShownNext,
|
|
63111
|
+
nearAreas,
|
|
63112
|
+
claimOpening: areaShownNext === undefined ? claimOpening : null,
|
|
63000
63113
|
// The box itself, for what is written INSIDE it to read the same
|
|
63001
63114
|
// facts what is written outside reads by id (useSlideContainer):
|
|
63002
63115
|
// a way out is a way out on either side of the box.
|
|
@@ -63053,9 +63166,12 @@ const warnOnNonStringArea = area => {
|
|
|
63053
63166
|
* try to leave.
|
|
63054
63167
|
* @param {boolean} [props.preventNavNext] - hold them from going right or down.
|
|
63055
63168
|
* @param {boolean} [props.preventNavPrevious] - …left or up.
|
|
63169
|
+
* @param {"always"|"near"} [props.mount] - when this slide's content is
|
|
63170
|
+
* rendered; the container's `mount` when not said (see there).
|
|
63056
63171
|
*/
|
|
63057
63172
|
const Slide = ({
|
|
63058
63173
|
area,
|
|
63174
|
+
mount,
|
|
63059
63175
|
required,
|
|
63060
63176
|
preventNav,
|
|
63061
63177
|
preventNavNext = preventNav,
|
|
@@ -63068,6 +63184,17 @@ const Slide = ({
|
|
|
63068
63184
|
{
|
|
63069
63185
|
warnOnNonStringArea(slideArea);
|
|
63070
63186
|
}
|
|
63187
|
+
// Asked by every slide, whatever its own mode: the claim goes to the first
|
|
63188
|
+
// slide to RENDER, and a slide that skipped asking would hand it to the one
|
|
63189
|
+
// after it.
|
|
63190
|
+
const opening = container?.claimOpening?.(slideArea);
|
|
63191
|
+
// Kept once rendered (mount="near"): going back builds nothing again, and a
|
|
63192
|
+
// slide being left keeps its content for as long as it is still in the frame.
|
|
63193
|
+
const renderedRef = useRef(false);
|
|
63194
|
+
if (!renderedRef.current && (!container || (mount ?? container.mount) !== "near" || opening || slideArea === container.areaShownNext || container.nearAreas.includes(slideArea))) {
|
|
63195
|
+
renderedRef.current = true;
|
|
63196
|
+
}
|
|
63197
|
+
const rendered = renderedRef.current;
|
|
63071
63198
|
const answered = Boolean(container?.answeredAreas.includes(slideArea));
|
|
63072
63199
|
const holdsUntilAnswered = Boolean(required) && !answered;
|
|
63073
63200
|
const nextIsLocked = Boolean(preventNavNext) || holdsUntilAnswered;
|
|
@@ -63117,13 +63244,17 @@ const Slide = ({
|
|
|
63117
63244
|
"data-slide-area": slideArea,
|
|
63118
63245
|
"data-prevent-nav-next": nextIsLocked ? "" : undefined,
|
|
63119
63246
|
"data-prevent-nav-previous": preventNavPrevious ? "" : undefined
|
|
63247
|
+
// What the container reads to know a slide is waiting for a travel
|
|
63248
|
+
// to come near (see renderNear).
|
|
63249
|
+
,
|
|
63250
|
+
"data-slide-unrendered": rendered ? undefined : ""
|
|
63120
63251
|
// A surface one interacts with says what state it is in, the way every
|
|
63121
63252
|
// other surface does — a dialog carries the very same set.
|
|
63122
63253
|
// :focus-within is the one a slide is really about: it is what tells
|
|
63123
63254
|
// the slide holding the keyboard from the ones waiting.
|
|
63124
63255
|
,
|
|
63125
63256
|
pseudoClasses: SLIDE_PSEUDO_CLASSES,
|
|
63126
|
-
children: children
|
|
63257
|
+
children: rendered ? children : null
|
|
63127
63258
|
})
|
|
63128
63259
|
})
|
|
63129
63260
|
});
|
|
@@ -69250,6 +69381,69 @@ const toPopoverAnimation = animation => {
|
|
|
69250
69381
|
return animation;
|
|
69251
69382
|
};
|
|
69252
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
|
+
|
|
69253
69447
|
installImportMetaCssBuild(import.meta);
|
|
69254
69448
|
const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
|
|
69255
69449
|
display: none;
|
|
@@ -69477,6 +69671,12 @@ const PickerCustom = props => {
|
|
|
69477
69671
|
const idDefault = useId();
|
|
69478
69672
|
const controlId = useContext(ControlIdContext);
|
|
69479
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;
|
|
69480
69680
|
// Same narrow-container/maxWidth-compact heuristic Popup itself uses (see
|
|
69481
69681
|
// popup_mode.jsx's own useResolvedPopupMode) — frozen for the lifetime of an opening
|
|
69482
69682
|
// (computed when closed, stable while open, so a screen resize mid-session
|
|
@@ -69722,12 +69922,20 @@ const PickerCustom = props => {
|
|
|
69722
69922
|
const requestInteraction = options => {
|
|
69723
69923
|
dispatchRequestInteraction(ref.current, options);
|
|
69724
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
|
+
});
|
|
69725
69932
|
const {
|
|
69726
69933
|
onActionStart,
|
|
69727
69934
|
children,
|
|
69728
69935
|
uiAction: uiActionProp
|
|
69729
69936
|
} = props;
|
|
69730
69937
|
Object.assign(pickerProps, {
|
|
69938
|
+
"requestValue": valueInPopup && props.mount === undefined ? requestValue : undefined,
|
|
69731
69939
|
"aria-expanded": Boolean(expanded),
|
|
69732
69940
|
"onActionStart": e => {
|
|
69733
69941
|
onActionStart?.(e);
|
|
@@ -69766,13 +69974,12 @@ const PickerCustom = props => {
|
|
|
69766
69974
|
anchor: props.anchor || props.ref,
|
|
69767
69975
|
openController,
|
|
69768
69976
|
// A picker whose value was never given to it reads it off the control in
|
|
69769
|
-
// its popup (see useUIFacadeStateController)
|
|
69770
|
-
//
|
|
69771
|
-
//
|
|
69772
|
-
//
|
|
69773
|
-
//
|
|
69774
|
-
|
|
69775
|
-
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),
|
|
69776
69983
|
// Not on pickerProps (the trigger): commands.js's own
|
|
69777
69984
|
// resolveClosestExpandable() does `el.closest("[aria-expanded]")` to
|
|
69778
69985
|
// find where to dispatch navi_request_open/navi_request_close — and
|
|
@@ -70060,6 +70267,29 @@ const PickerCustom = props => {
|
|
|
70060
70267
|
});
|
|
70061
70268
|
};
|
|
70062
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
|
+
|
|
70063
70293
|
// A hold declared on something AROUND the picker (a card opened by
|
|
70064
70294
|
// `openOn="longpress"`, holding this one in its drawing): the finger going
|
|
70065
70295
|
// down may be the start of that hold, so this picker opens on the click — which
|
|
@@ -70575,52 +70805,6 @@ const PickerConfirmBody = ({
|
|
|
70575
70805
|
});
|
|
70576
70806
|
};
|
|
70577
70807
|
|
|
70578
|
-
const PickerContext = createContext();
|
|
70579
|
-
|
|
70580
|
-
/*
|
|
70581
|
-
* A typed picker (`type="date"`, `type="array"`, …) draws its value with a ui
|
|
70582
|
-
* of navi's own, installed as the very "ui" prop a caller overrides to draw
|
|
70583
|
-
* that value themselves. Marking navi's own tells the two apart afterwards:
|
|
70584
|
-
* an empty value is greyed as a placeholder only when the drawing is navi's —
|
|
70585
|
-
* a caller's ui may well be their way of writing "no filter", which is an
|
|
70586
|
-
* answer, not a blank (see navi-placeholder in picker.jsx).
|
|
70587
|
-
*/
|
|
70588
|
-
const asPickerOwnUI = PickerUI => {
|
|
70589
|
-
PickerUI.isPickerOwnUI = true;
|
|
70590
|
-
return PickerUI;
|
|
70591
|
-
};
|
|
70592
|
-
const pickerUIIsNaviOwn = ui => {
|
|
70593
|
-
return Boolean(ui) && typeof ui === "object" && Boolean(ui.type?.isPickerOwnUI);
|
|
70594
|
-
};
|
|
70595
|
-
|
|
70596
|
-
/**
|
|
70597
|
-
* What the picker holds, read from inside its `ui` — for a drawing given as
|
|
70598
|
-
* an element (`ui={<MyCard game={game} />}`), which the picker cannot hand
|
|
70599
|
-
* props to. `value` is the state the picker holds right now: the answer the
|
|
70600
|
-
* popup just closed on, before the server has said anything, and put back if
|
|
70601
|
-
* the action fails. `loading` is the run in flight; `interactive` is false
|
|
70602
|
-
* while the picker is disabled, read-only or busy.
|
|
70603
|
-
*
|
|
70604
|
-
* @returns {{ value: any, loading: boolean, interactive: boolean } | null}
|
|
70605
|
-
* null outside a picker.
|
|
70606
|
-
*/
|
|
70607
|
-
const usePickerState = () => {
|
|
70608
|
-
const context = useContext(PickerContext);
|
|
70609
|
-
if (!context) {
|
|
70610
|
-
return null;
|
|
70611
|
-
}
|
|
70612
|
-
const {
|
|
70613
|
-
value,
|
|
70614
|
-
loading,
|
|
70615
|
-
interactive
|
|
70616
|
-
} = context;
|
|
70617
|
-
return {
|
|
70618
|
-
value,
|
|
70619
|
-
loading,
|
|
70620
|
-
interactive
|
|
70621
|
-
};
|
|
70622
|
-
};
|
|
70623
|
-
|
|
70624
70808
|
const PickerNaviMinute = props => {
|
|
70625
70809
|
const Next = useNextResolver();
|
|
70626
70810
|
const {
|
|
@@ -79137,7 +79321,7 @@ const PickerObjectUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79137
79321
|
const {
|
|
79138
79322
|
value,
|
|
79139
79323
|
placeholder
|
|
79140
|
-
} =
|
|
79324
|
+
} = usePickerContext();
|
|
79141
79325
|
if (uiStateHoldsNothing(value)) {
|
|
79142
79326
|
if (!placeholder) {
|
|
79143
79327
|
return null;
|
|
@@ -79173,7 +79357,7 @@ const PickerArrayUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79173
79357
|
value,
|
|
79174
79358
|
placeholder,
|
|
79175
79359
|
maxLines
|
|
79176
|
-
} =
|
|
79360
|
+
} = usePickerContext();
|
|
79177
79361
|
if (uiStateHoldsNothing(value)) {
|
|
79178
79362
|
if (!placeholder) {
|
|
79179
79363
|
return null;
|
|
@@ -79255,7 +79439,7 @@ const PickerColorUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79255
79439
|
const {
|
|
79256
79440
|
value,
|
|
79257
79441
|
placeholder
|
|
79258
|
-
} =
|
|
79442
|
+
} = usePickerContext();
|
|
79259
79443
|
if (!value) {
|
|
79260
79444
|
if (!placeholder) {
|
|
79261
79445
|
return jsx(Color, {});
|
|
@@ -79279,7 +79463,7 @@ const PickerDateUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79279
79463
|
const {
|
|
79280
79464
|
value,
|
|
79281
79465
|
placeholder
|
|
79282
|
-
} =
|
|
79466
|
+
} = usePickerContext();
|
|
79283
79467
|
if (!value) {
|
|
79284
79468
|
if (!placeholder) {
|
|
79285
79469
|
return jsx(Time, {
|
|
@@ -79311,7 +79495,7 @@ const PickerMonthUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79311
79495
|
const {
|
|
79312
79496
|
value,
|
|
79313
79497
|
placeholder
|
|
79314
|
-
} =
|
|
79498
|
+
} = usePickerContext();
|
|
79315
79499
|
if (!value) {
|
|
79316
79500
|
if (!placeholder) {
|
|
79317
79501
|
return jsx(Time, {
|
|
@@ -79342,7 +79526,7 @@ const PickerWeekUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79342
79526
|
const {
|
|
79343
79527
|
value,
|
|
79344
79528
|
placeholder
|
|
79345
|
-
} =
|
|
79529
|
+
} = usePickerContext();
|
|
79346
79530
|
if (!value) {
|
|
79347
79531
|
if (!placeholder) {
|
|
79348
79532
|
return jsx(Time, {
|
|
@@ -79373,7 +79557,7 @@ const PickerTimeUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79373
79557
|
const {
|
|
79374
79558
|
value,
|
|
79375
79559
|
placeholder
|
|
79376
|
-
} =
|
|
79560
|
+
} = usePickerContext();
|
|
79377
79561
|
if (!value) {
|
|
79378
79562
|
if (!placeholder) {
|
|
79379
79563
|
return jsx(Time, {
|
|
@@ -79404,7 +79588,7 @@ const PickerDurationUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79404
79588
|
const {
|
|
79405
79589
|
value,
|
|
79406
79590
|
placeholder
|
|
79407
|
-
} =
|
|
79591
|
+
} = usePickerContext();
|
|
79408
79592
|
if (!value) {
|
|
79409
79593
|
if (!placeholder) {
|
|
79410
79594
|
return jsx(Time, {
|
|
@@ -79434,7 +79618,7 @@ const PickerDatetimeUI = /*#__PURE__*/asPickerOwnUI(props => {
|
|
|
79434
79618
|
const {
|
|
79435
79619
|
value,
|
|
79436
79620
|
placeholder
|
|
79437
|
-
} =
|
|
79621
|
+
} = usePickerContext();
|
|
79438
79622
|
if (!value) {
|
|
79439
79623
|
if (!placeholder) {
|
|
79440
79624
|
return jsx(Time, {
|
|
@@ -79463,7 +79647,7 @@ const PickerFileUI = /*#__PURE__*/asPickerOwnUI(() => {
|
|
|
79463
79647
|
const {
|
|
79464
79648
|
value,
|
|
79465
79649
|
placeholder
|
|
79466
|
-
} =
|
|
79650
|
+
} = usePickerContext();
|
|
79467
79651
|
if (!value) {
|
|
79468
79652
|
if (!placeholder) {
|
|
79469
79653
|
return null;
|
|
@@ -79999,7 +80183,11 @@ const PickerButton = props => {
|
|
|
79999
80183
|
// a piece of, waiting as a whole (see the `loadingOutline` doc).
|
|
80000
80184
|
loadingOutline,
|
|
80001
80185
|
// Where the outline runs, in px from the picker's box: negative is outside.
|
|
80002
|
-
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
|
|
80003
80191
|
} = props;
|
|
80004
80192
|
// A word in a sentence is never truncated — and the clamp's overflow: hidden
|
|
80005
80193
|
// would cut its dotted underline, which sits on the edge of the line box
|
|
@@ -80097,7 +80285,8 @@ const PickerButton = props => {
|
|
|
80097
80285
|
id: inputProps.id,
|
|
80098
80286
|
interactive,
|
|
80099
80287
|
loading,
|
|
80100
|
-
clearConfirm
|
|
80288
|
+
clearConfirm,
|
|
80289
|
+
requestValue
|
|
80101
80290
|
};
|
|
80102
80291
|
return (
|
|
80103
80292
|
/* Read-only crosses into everything the picker is made of: what it really
|
|
@@ -80148,7 +80337,8 @@ const PickerButton = props => {
|
|
|
80148
80337
|
error: undefined,
|
|
80149
80338
|
dayLabel: undefined,
|
|
80150
80339
|
loadingOutline: undefined,
|
|
80151
|
-
loadingOutlineInset: undefined
|
|
80340
|
+
loadingOutlineInset: undefined,
|
|
80341
|
+
requestValue: undefined
|
|
80152
80342
|
// This wrapper will receive keyboard event bubbling from the picker popup content
|
|
80153
80343
|
// we re-dispatch on the input (to get escape to close for instance)
|
|
80154
80344
|
,
|
|
@@ -80362,7 +80552,7 @@ const PickerClear = ({
|
|
|
80362
80552
|
children,
|
|
80363
80553
|
...rest
|
|
80364
80554
|
}) => {
|
|
80365
|
-
const pickerContext =
|
|
80555
|
+
const pickerContext = usePickerContext();
|
|
80366
80556
|
if (!pickerContext) {
|
|
80367
80557
|
warnOnClearOutsidePicker();
|
|
80368
80558
|
return null;
|
|
@@ -80646,7 +80836,7 @@ const PickerDefaultUI = () => {
|
|
|
80646
80836
|
const {
|
|
80647
80837
|
value,
|
|
80648
80838
|
placeholder
|
|
80649
|
-
} =
|
|
80839
|
+
} = usePickerContext();
|
|
80650
80840
|
if (!value) {
|
|
80651
80841
|
if (!placeholder) {
|
|
80652
80842
|
return null;
|
|
@@ -81220,10 +81410,13 @@ const PickerFirstResolver = props => {
|
|
|
81220
81410
|
* @param {string} [cancelTestId]
|
|
81221
81411
|
* @param {"always"|"idle"|"from-first-open"|"while-opened"} [mount] The popup's own
|
|
81222
81412
|
* (see Popup): when the children are built and thrown away. Left out, a
|
|
81223
|
-
* picker told no value
|
|
81224
|
-
*
|
|
81225
|
-
*
|
|
81226
|
-
*
|
|
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.
|
|
81227
81420
|
*/
|
|
81228
81421
|
// Every picker type is resolved into one of these before it gets here (see
|
|
81229
81422
|
// PickerTypeResolver and resolveInputProps): a native input type, or navi_js
|