@jsenv/navi 0.29.471 → 0.29.473

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.
@@ -42894,6 +42894,7 @@ const useRenderScope = (init, update) => {
42894
42894
  * registerChild(child): void; // Called on child mount
42895
42895
  * onChildUIAction(child, e, { stateChanged: boolean }): void; // Called when a child fires a UI action
42896
42896
  * unregisterChild(child): void; // Called on child unmount
42897
+ * collectsChild(child): boolean; // Whether registerChild(child) would put it in a value, here or above
42897
42898
  * props: Object;
42898
42899
  * ref: Ref; // Used to dispatch DOM events
42899
42900
  * getManagedControls(): UIStateController[]; // Returns controls whose validity is managed by this controller
@@ -43596,6 +43597,10 @@ const useUIStateController = (
43596
43597
  unregisterChild: (childUIStateController) => {
43597
43598
  s.parentUIStateController?.unregisterChild(childUIStateController);
43598
43599
  },
43600
+ collectsChild: (childUIStateController) =>
43601
+ Boolean(
43602
+ s.parentUIStateController?.collectsChild(childUIStateController),
43603
+ ),
43599
43604
  onChildUIAction: (childUIStateController, e, options) => {
43600
43605
  s.parentUIStateController?.onChildUIAction(
43601
43606
  childUIStateController,
@@ -43982,10 +43987,13 @@ const readNamedChildUIStates = (children, { warnNameless }) => {
43982
43987
  // Looked up in useUIGroupStateController to fill in omitted aggregateChildStates /
43983
43988
  // distributeChildUIState. If neither a default nor an explicit impl is found for a
43984
43989
  // group, creation throws so the caller knows it must supply them.
43990
+ // The radio/checkbox filters read `props.type`, which controlHostProps copies:
43991
+ // a child is also asked about while rendering, before its first render has
43992
+ // built controlHostProps (see collectsChild).
43985
43993
  const GROUP_DEFAULTS = {
43986
43994
  radio_group: {
43987
43995
  childControlFilter: (child) =>
43988
- child.controlType === "input" && child.controlHostProps?.type === "radio",
43996
+ child.controlType === "input" && child.props?.type === "radio",
43989
43997
  aggregateChildStates: keptOrFirstDefinedChildUIState,
43990
43998
  distributeChildUIState: (newUIState, childUIStateController) => {
43991
43999
  const childSelected = childUIStateController.props.value === newUIState;
@@ -43997,8 +44005,7 @@ const GROUP_DEFAULTS = {
43997
44005
  },
43998
44006
  checkbox_group: {
43999
44007
  childControlFilter: (child) =>
44000
- child.controlType === "input" &&
44001
- child.controlHostProps?.type === "checkbox",
44008
+ child.controlType === "input" && child.props?.type === "checkbox",
44002
44009
  aggregateChildStates: keptAndCheckedChildUIStates,
44003
44010
  distributeChildUIState: (newUIState, childUIStateController) => {
44004
44011
  const childSelected =
@@ -44533,6 +44540,9 @@ const useUIGroupStateController = (
44533
44540
 
44534
44541
  const controller = {
44535
44542
  controlType,
44543
+ // Published from the first render, like a leaf's: a group asks its
44544
+ // parent whether anyone listens before its first update.
44545
+ parentUIStateController,
44536
44546
  id,
44537
44547
  name,
44538
44548
  value,
@@ -44756,6 +44766,18 @@ const useUIGroupStateController = (
44756
44766
  notifyExternal: "silent",
44757
44767
  });
44758
44768
  },
44769
+ // registerChild above, asked ahead: monitored here, handed up, or
44770
+ // captured and dropped.
44771
+ collectsChild: (childUIStateController) => {
44772
+ if (isMonitoringChild(childUIStateController)) {
44773
+ return true;
44774
+ }
44775
+ const currentParent = s.parentUIStateController;
44776
+ if (allowCapture || !currentParent) {
44777
+ return false;
44778
+ }
44779
+ return currentParent.collectsChild(childUIStateController);
44780
+ },
44759
44781
  onChildUIAction: (
44760
44782
  childUIStateController,
44761
44783
  e,
@@ -45228,6 +45250,9 @@ const useUIFacadeStateController = (props, realUIStateController) => {
45228
45250
  s.realUIStateController.facadeChild = null;
45229
45251
  }
45230
45252
  },
45253
+ // A refused child is not handed further up: the popup's controls
45254
+ // answer to the picker or to nobody.
45255
+ collectsChild: (child) => canRegisterAsFacadeChild(child),
45231
45256
  getManagedControls: () => {
45232
45257
  const child = firstChildControllerRef.current;
45233
45258
  if (!child) {
@@ -45769,8 +45794,6 @@ const useControlProps = (props, {
45769
45794
  const controlInfo = createControlInfo(props, {
45770
45795
  controlType
45771
45796
  });
45772
- const readOnlyUncontrolled = useReadOnlyUncontrolled(props, controlInfo);
45773
- controlInfo.readOnlyUncontrolled = readOnlyUncontrolled;
45774
45797
  const uiStateController = useUIStateController(props, {
45775
45798
  controlInfo,
45776
45799
  syncDomState,
@@ -45778,6 +45801,9 @@ const useControlProps = (props, {
45778
45801
  persists,
45779
45802
  uiActionInternal
45780
45803
  });
45804
+ // After the controller: whether a group listens is asked of the parent it
45805
+ // registers into. Read by useInteractiveProps below.
45806
+ controlInfo.readOnlyUncontrolled = useReadOnlyUncontrolled(props, controlInfo, uiStateController);
45781
45807
  // What the control draws is what it holds by itself, and this read is what
45782
45808
  // re-renders it when that changes. A button inheriting the value of the
45783
45809
  // control around it (a submit in a form, see ownUIStateSignal) draws none of
@@ -46773,16 +46799,21 @@ const INPUT_TYPE_SUPPORTING_READONLY_SET = new Set(["text", "date", "datetime-lo
46773
46799
  // warning below so a group can ask the same question a single control does: a
46774
46800
  // control with a `value` and nobody listening cannot be changed by hand, and
46775
46801
  // that is true whatever the control is.
46776
- const useIsControlListenedTo = props => {
46802
+ // The group is asked, not assumed from a controller being above: a control in
46803
+ // between may only pass registration along (a <Details> holds none of the
46804
+ // values it folds), and a group may hand the control further up or drop it.
46805
+ const useIsControlListenedTo = (props, uiStateController) => {
46777
46806
  const isProxy = Boolean(props["navi-control-proxy-for"]);
46778
46807
  const formContext = useContext(FormContext);
46779
- const parentUIStateController = useContext(ParentUIStateControllerContext);
46808
+ const {
46809
+ parentUIStateController
46810
+ } = uiStateController;
46780
46811
  return Boolean(props.signal ||
46781
46812
  // a bound signal is written back on uiAction → interactive
46782
- props.uiAction || props.action || formContext || parentUIStateController || isProxy || props.command);
46813
+ props.uiAction || props.action || formContext || parentUIStateController?.collectsChild(uiStateController) || isProxy || props.command);
46783
46814
  };
46784
- const useReadOnlyUncontrolled = (props, controlInfo) => {
46785
- const listenedTo = useIsControlListenedTo(props);
46815
+ const useReadOnlyUncontrolled = (props, controlInfo, uiStateController) => {
46816
+ const listenedTo = useIsControlListenedTo(props, uiStateController);
46786
46817
  useRef(false);
46787
46818
  if (!controlInfo.hasStateProp) {
46788
46819
  return false;
@@ -46841,7 +46872,7 @@ const useControlgroupProps = (props, {
46841
46872
  // makes the group read-only so children don't appear interactive when they
46842
46873
  // can't change. A form or a group around it IS someone listening — that is
46843
46874
  // what will send the value and hand a new one back.
46844
- const listenedTo = useIsControlListenedTo(props);
46875
+ const listenedTo = useIsControlListenedTo(props, uiGroupStateController);
46845
46876
  useRef(false);
46846
46877
  const implicitReadOnly = uiGroupStateController.hasValueProp && !listenedTo;
46847
46878
  if (implicitReadOnly && !props.readOnly) {
@@ -51664,6 +51695,9 @@ const LinkWithRoute = ({
51664
51695
  currentExcept,
51665
51696
  currentAlso,
51666
51697
  children,
51698
+ // Kept out of `rest`: spread after the route's url, an `href={undefined}`
51699
+ // riding along would erase it.
51700
+ href,
51667
51701
  ...rest
51668
51702
  }) => {
51669
51703
  const url = route.buildUrl(routeParams);
@@ -56836,11 +56870,14 @@ const ButtonRouteResolver = props => {
56836
56870
  };
56837
56871
  const ButtonWithRoute = props => {
56838
56872
  const Next = useNextResolver();
56873
+ // `href` is kept out of `rest`: spread after the route's url, an
56874
+ // `href={undefined}` riding along would erase it.
56839
56875
  const {
56840
56876
  route,
56841
56877
  routeParams,
56842
56878
  children,
56843
56879
  pseudoState,
56880
+ href,
56844
56881
  ...rest
56845
56882
  } = props;
56846
56883
  const url = route.buildUrl(routeParams);
@@ -73028,10 +73065,18 @@ const PickerCustom = props => {
73028
73065
  props.id = props.id || controlId || idDefault;
73029
73066
  // Given no value, the picker holds what the control in its popup holds (see
73030
73067
  // the `mount` decision below) — and a group around it collects that from the
73031
- // moment both exist.
73032
- const valueInPopup = !isControlValueGivenByProps(props);
73068
+ // moment both exist. A picker that picks nothing holds none: its popup's
73069
+ // controls are never adopted (see canRegisterAsFacadeChild).
73070
+ const valueInPopup = !props.picksNothing && !isControlValueGivenByProps(props);
73071
+ // Asked of the chain, not read off the context: a control above may only
73072
+ // pass registration along (a <Details> holds none of the values it folds),
73073
+ // and a group may hand a picker further up or drop it. "picker" is what the
73074
+ // picker's own controller registers as (see picker.jsx).
73033
73075
  const parentController = useContext(ParentUIStateControllerContext);
73034
- const answersToGroup = Boolean(parentController) && !props.standalone;
73076
+ const answersToGroup = !props.standalone && Boolean(parentController?.collectsChild({
73077
+ controlType: "picker",
73078
+ props
73079
+ }));
73035
73080
  // Same narrow-container/maxWidth-compact heuristic Popup itself uses (see
73036
73081
  // popup_mode.jsx's own useResolvedPopupMode) — frozen for the lifetime of an opening
73037
73082
  // (computed when closed, stable while open, so a screen resize mid-session
@@ -74211,12 +74256,7 @@ const PickerConfirmResolver = props => {
74211
74256
  mode: mode,
74212
74257
  focusCapture: focusCapture,
74213
74258
  openWhileReadOnly: openWhileReadOnly,
74214
- onConfirm: onConfirm
74215
- // A question holds no value to read before it is asked: the popup is
74216
- // built on the first open, like that of a picker told its value (see
74217
- // the `mount` resolution in picker_custom.jsx).
74218
- ,
74219
- mount: MOUNT_DEFAULT,
74259
+ onConfirm: onConfirm,
74220
74260
  message: undefined,
74221
74261
  confirmLabel: undefined,
74222
74262
  cancelLabel: undefined,