@jsenv/navi 0.29.472 → 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) {
@@ -73037,8 +73068,15 @@ const PickerCustom = props => {
73037
73068
  // moment both exist. A picker that picks nothing holds none: its popup's
73038
73069
  // controls are never adopted (see canRegisterAsFacadeChild).
73039
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).
73040
73075
  const parentController = useContext(ParentUIStateControllerContext);
73041
- const answersToGroup = Boolean(parentController) && !props.standalone;
73076
+ const answersToGroup = !props.standalone && Boolean(parentController?.collectsChild({
73077
+ controlType: "picker",
73078
+ props
73079
+ }));
73042
73080
  // Same narrow-container/maxWidth-compact heuristic Popup itself uses (see
73043
73081
  // popup_mode.jsx's own useResolvedPopupMode) — frozen for the lifetime of an opening
73044
73082
  // (computed when closed, stable while open, so a screen resize mid-session