@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.
@@ -43943,6 +43943,7 @@ const useRenderScope = (init, update) => {
43943
43943
  * registerChild(child): void; // Called on child mount
43944
43944
  * onChildUIAction(child, e, { stateChanged: boolean }): void; // Called when a child fires a UI action
43945
43945
  * unregisterChild(child): void; // Called on child unmount
43946
+ * collectsChild(child): boolean; // Whether registerChild(child) would put it in a value, here or above
43946
43947
  * props: Object;
43947
43948
  * ref: Ref; // Used to dispatch DOM events
43948
43949
  * getManagedControls(): UIStateController[]; // Returns controls whose validity is managed by this controller
@@ -44667,6 +44668,10 @@ const useUIStateController = (
44667
44668
  unregisterChild: (childUIStateController) => {
44668
44669
  s.parentUIStateController?.unregisterChild(childUIStateController);
44669
44670
  },
44671
+ collectsChild: (childUIStateController) =>
44672
+ Boolean(
44673
+ s.parentUIStateController?.collectsChild(childUIStateController),
44674
+ ),
44670
44675
  onChildUIAction: (childUIStateController, e, options) => {
44671
44676
  s.parentUIStateController?.onChildUIAction(
44672
44677
  childUIStateController,
@@ -45053,10 +45058,13 @@ const readNamedChildUIStates = (children, { warnNameless }) => {
45053
45058
  // Looked up in useUIGroupStateController to fill in omitted aggregateChildStates /
45054
45059
  // distributeChildUIState. If neither a default nor an explicit impl is found for a
45055
45060
  // group, creation throws so the caller knows it must supply them.
45061
+ // The radio/checkbox filters read `props.type`, which controlHostProps copies:
45062
+ // a child is also asked about while rendering, before its first render has
45063
+ // built controlHostProps (see collectsChild).
45056
45064
  const GROUP_DEFAULTS = {
45057
45065
  radio_group: {
45058
45066
  childControlFilter: (child) =>
45059
- child.controlType === "input" && child.controlHostProps?.type === "radio",
45067
+ child.controlType === "input" && child.props?.type === "radio",
45060
45068
  aggregateChildStates: keptOrFirstDefinedChildUIState,
45061
45069
  distributeChildUIState: (newUIState, childUIStateController) => {
45062
45070
  const childSelected = childUIStateController.props.value === newUIState;
@@ -45068,8 +45076,7 @@ const GROUP_DEFAULTS = {
45068
45076
  },
45069
45077
  checkbox_group: {
45070
45078
  childControlFilter: (child) =>
45071
- child.controlType === "input" &&
45072
- child.controlHostProps?.type === "checkbox",
45079
+ child.controlType === "input" && child.props?.type === "checkbox",
45073
45080
  aggregateChildStates: keptAndCheckedChildUIStates,
45074
45081
  distributeChildUIState: (newUIState, childUIStateController) => {
45075
45082
  const childSelected =
@@ -45614,6 +45621,9 @@ const useUIGroupStateController = (
45614
45621
 
45615
45622
  const controller = {
45616
45623
  controlType,
45624
+ // Published from the first render, like a leaf's: a group asks its
45625
+ // parent whether anyone listens before its first update.
45626
+ parentUIStateController,
45617
45627
  id,
45618
45628
  name,
45619
45629
  value,
@@ -45837,6 +45847,18 @@ const useUIGroupStateController = (
45837
45847
  notifyExternal: "silent",
45838
45848
  });
45839
45849
  },
45850
+ // registerChild above, asked ahead: monitored here, handed up, or
45851
+ // captured and dropped.
45852
+ collectsChild: (childUIStateController) => {
45853
+ if (isMonitoringChild(childUIStateController)) {
45854
+ return true;
45855
+ }
45856
+ const currentParent = s.parentUIStateController;
45857
+ if (allowCapture || !currentParent) {
45858
+ return false;
45859
+ }
45860
+ return currentParent.collectsChild(childUIStateController);
45861
+ },
45840
45862
  onChildUIAction: (
45841
45863
  childUIStateController,
45842
45864
  e,
@@ -46314,6 +46336,9 @@ const useUIFacadeStateController = (props, realUIStateController) => {
46314
46336
  s.realUIStateController.facadeChild = null;
46315
46337
  }
46316
46338
  },
46339
+ // A refused child is not handed further up: the popup's controls
46340
+ // answer to the picker or to nobody.
46341
+ collectsChild: (child) => canRegisterAsFacadeChild(child),
46317
46342
  getManagedControls: () => {
46318
46343
  const child = firstChildControllerRef.current;
46319
46344
  if (!child) {
@@ -46934,8 +46959,6 @@ const useControlProps = (props, {
46934
46959
  const controlInfo = createControlInfo(props, {
46935
46960
  controlType
46936
46961
  });
46937
- const readOnlyUncontrolled = useReadOnlyUncontrolled(props, controlInfo);
46938
- controlInfo.readOnlyUncontrolled = readOnlyUncontrolled;
46939
46962
  const uiStateController = useUIStateController(props, {
46940
46963
  controlInfo,
46941
46964
  syncDomState,
@@ -46943,6 +46966,9 @@ const useControlProps = (props, {
46943
46966
  persists,
46944
46967
  uiActionInternal
46945
46968
  });
46969
+ // After the controller: whether a group listens is asked of the parent it
46970
+ // registers into. Read by useInteractiveProps below.
46971
+ controlInfo.readOnlyUncontrolled = useReadOnlyUncontrolled(props, controlInfo, uiStateController);
46946
46972
  // What the control draws is what it holds by itself, and this read is what
46947
46973
  // re-renders it when that changes. A button inheriting the value of the
46948
46974
  // control around it (a submit in a form, see ownUIStateSignal) draws none of
@@ -47948,16 +47974,21 @@ const INPUT_TYPE_SUPPORTING_READONLY_SET = new Set(["text", "date", "datetime-lo
47948
47974
  // warning below so a group can ask the same question a single control does: a
47949
47975
  // control with a `value` and nobody listening cannot be changed by hand, and
47950
47976
  // that is true whatever the control is.
47951
- const useIsControlListenedTo = props => {
47977
+ // The group is asked, not assumed from a controller being above: a control in
47978
+ // between may only pass registration along (a <Details> holds none of the
47979
+ // values it folds), and a group may hand the control further up or drop it.
47980
+ const useIsControlListenedTo = (props, uiStateController) => {
47952
47981
  const isProxy = Boolean(props["navi-control-proxy-for"]);
47953
47982
  const formContext = useContext(FormContext);
47954
- const parentUIStateController = useContext(ParentUIStateControllerContext);
47983
+ const {
47984
+ parentUIStateController
47985
+ } = uiStateController;
47955
47986
  return Boolean(props.signal ||
47956
47987
  // a bound signal is written back on uiAction → interactive
47957
- props.uiAction || props.action || formContext || parentUIStateController || isProxy || props.command);
47988
+ props.uiAction || props.action || formContext || parentUIStateController?.collectsChild(uiStateController) || isProxy || props.command);
47958
47989
  };
47959
- const useReadOnlyUncontrolled = (props, controlInfo) => {
47960
- const listenedTo = useIsControlListenedTo(props);
47990
+ const useReadOnlyUncontrolled = (props, controlInfo, uiStateController) => {
47991
+ const listenedTo = useIsControlListenedTo(props, uiStateController);
47961
47992
  const warnedRef = useRef(false);
47962
47993
  if (!controlInfo.hasStateProp) {
47963
47994
  return false;
@@ -48025,7 +48056,7 @@ const useControlgroupProps = (props, {
48025
48056
  // makes the group read-only so children don't appear interactive when they
48026
48057
  // can't change. A form or a group around it IS someone listening — that is
48027
48058
  // what will send the value and hand a new one back.
48028
- const listenedTo = useIsControlListenedTo(props);
48059
+ const listenedTo = useIsControlListenedTo(props, uiGroupStateController);
48029
48060
  const implicitReadOnlyWarnedRef = useRef(false);
48030
48061
  const implicitReadOnly = uiGroupStateController.hasValueProp && !listenedTo;
48031
48062
  if (implicitReadOnly && !props.readOnly) {
@@ -74449,8 +74480,15 @@ const PickerCustom = props => {
74449
74480
  // moment both exist. A picker that picks nothing holds none: its popup's
74450
74481
  // controls are never adopted (see canRegisterAsFacadeChild).
74451
74482
  const valueInPopup = !props.picksNothing && !isControlValueGivenByProps(props);
74483
+ // Asked of the chain, not read off the context: a control above may only
74484
+ // pass registration along (a <Details> holds none of the values it folds),
74485
+ // and a group may hand a picker further up or drop it. "picker" is what the
74486
+ // picker's own controller registers as (see picker.jsx).
74452
74487
  const parentController = useContext(ParentUIStateControllerContext);
74453
- const answersToGroup = Boolean(parentController) && !props.standalone;
74488
+ const answersToGroup = !props.standalone && Boolean(parentController?.collectsChild({
74489
+ controlType: "picker",
74490
+ props
74491
+ }));
74454
74492
  // Same narrow-container/maxWidth-compact heuristic Popup itself uses (see
74455
74493
  // popup_mode.jsx's own useResolvedPopupMode) — frozen for the lifetime of an opening
74456
74494
  // (computed when closed, stable while open, so a screen resize mid-session