@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.
@@ -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 (currentFromCaller === undefined) {
61708
- return;
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): the trigger shows what the
69770
- // list inside says is selected, so that list has to exist before anyone
69771
- // opens anything. Told a value — even an empty one — the picker owns it
69772
- // and pushes it down instead, leaving the popup free to build its
69773
- // content only when it is first opened (see popup_content_mount.js).
69774
- // A caller who knows better says so with the popup's own props.
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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
- } = useContext(PickerContext);
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 = useContext(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
- } = useContext(PickerContext);
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 builds them right away (`"always"`) — it reads its
81224
- * value off the control they hold — and one told a value waits for the first
81225
- * open. A `type="confirm"` picker never builds them early: its popup is a
81226
- * question, there is nothing to read.
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