@jsenv/navi 0.29.443 → 0.29.444

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.
@@ -6281,6 +6281,10 @@ const CONTROL_PROP_SET = new Set([
6281
6281
  "charGuard",
6282
6282
  "maxLengthGuard",
6283
6283
 
6284
+ // A checkbox that is a yes/no: `true` checked, `false` unchecked, instead of
6285
+ // HTML's value-or-nothing (see resolveEmptyUIState).
6286
+ "boolean",
6287
+
6284
6288
  // This control answers for itself: it registers into no group, no form and no
6285
6289
  // picker around it (see useUIStateController). It belongs here rather than
6286
6290
  // being deleted off `props` where it is read: props is the vnode's own object
@@ -42149,6 +42153,14 @@ const useUIStateController = (
42149
42153
  return ownUIState || parentUIState;
42150
42154
  })
42151
42155
  : ownUIStateSignal;
42156
+ // What the control is worth, which its action is bound to: what it
42157
+ // holds, or its own empty when it holds nothing. Apart from
42158
+ // uiStateSignal because unchecked is `undefined` there (the DOM reads any
42159
+ // other value as checked), while a yes/no unchecked is worth `false`.
42160
+ const emptyUIStateSignal = signal(emptyUIState);
42161
+ const valueSignal = computed(() =>
42162
+ uiStateOrEmpty(uiStateSignal.value, emptyUIStateSignal.value),
42163
+ );
42152
42164
 
42153
42165
  // The two-way half of a bound `signal` prop: setting it re-renders and
42154
42166
  // re-syncs via state_prop_change, but with the same value → guarded as a
@@ -42197,6 +42209,7 @@ const useUIStateController = (
42197
42209
  // travel command ended up carrying a picker's selection: see
42198
42210
  // resolveCommandValue in commands.js.
42199
42211
  ownUIStateSignal,
42212
+ valueSignal,
42200
42213
  value: controlInfo.value,
42201
42214
  // The suggestion this control started on — what tells a field showing
42202
42215
  // its default from one carrying an answer (see isUIStateHeld).
@@ -42256,7 +42269,11 @@ const useUIStateController = (
42256
42269
  }
42257
42270
  s.uiActionInternal?.(currentUIState, e);
42258
42271
  if (s.uiAction) {
42259
- s.uiAction(currentUIState, e);
42272
+ const value = uiStateOrEmpty(
42273
+ currentUIState,
42274
+ controller.emptyUIState,
42275
+ );
42276
+ s.uiAction(value, e);
42260
42277
  }
42261
42278
  if (skipCommand) ; else {
42262
42279
  const command = controller.controlHostProps.command;
@@ -42743,6 +42760,7 @@ const useUIStateController = (
42743
42760
  return {
42744
42761
  controller,
42745
42762
  parentUiStateSignalHolder,
42763
+ emptyUIStateSignal,
42746
42764
  ...liveValues(),
42747
42765
  };
42748
42766
  },
@@ -42864,6 +42882,7 @@ const useUIStateController = (
42864
42882
  );
42865
42883
  scope.parentUiStateSignalHolder.value =
42866
42884
  parentUIStateController?.uiStateSignal ?? null;
42885
+ scope.emptyUIStateSignal.value = emptyUIState;
42867
42886
 
42868
42887
  const { controller } = scope;
42869
42888
  const controllerRef = controller.ref;
@@ -43074,14 +43093,11 @@ const keptAndNamedChildUIStates = (children, fallbackState, kept) => {
43074
43093
  const readNamedChildUIStates = (children, { warnNameless }) => {
43075
43094
  const values = {};
43076
43095
  for (const child of children) {
43077
- const { name, emptyUIState } = child;
43096
+ const { name } = child;
43078
43097
  // A control holding nothing writes its own empty, not a hole: the key is in
43079
43098
  // the object either way, and what is read from it keeps the shape the
43080
43099
  // reader was promised (see resolveEmptyUIState).
43081
- const uiState =
43082
- child.uiState === undefined && emptyUIState !== undefined
43083
- ? emptyUIState
43084
- : child.uiState;
43100
+ const uiState = uiStateOrEmpty(child.uiState, child.emptyUIState);
43085
43101
  if (!name) {
43086
43102
  if (isNamelessGrouping(child, uiState)) {
43087
43103
  Object.assign(
@@ -44413,11 +44429,10 @@ const useUIFacadeStateController = (props, realUIStateController) => {
44413
44429
  // whose last item was unselected holds [], the same thing clearing it
44414
44430
  // leaves — not a value that changes type on its owner the moment it
44415
44431
  // empties.
44416
- const { emptyUIState } = s.realUIStateController;
44417
- const uiStateToAdopt =
44418
- child.uiState === undefined && emptyUIState !== undefined
44419
- ? emptyUIState
44420
- : child.uiState;
44432
+ const uiStateToAdopt = uiStateOrEmpty(
44433
+ child.uiState,
44434
+ s.realUIStateController.emptyUIState,
44435
+ );
44421
44436
  s.realUIStateController.setUIState(uiStateToAdopt, propagateUpEvent);
44422
44437
  updatingRef.current = false;
44423
44438
  },
@@ -44657,8 +44672,9 @@ const resolveEmptyUIState = (props, controlType) => {
44657
44672
  if (controlType === "input" && props.type === "checkbox") {
44658
44673
  // A checkbox is a member of a set, the way HTML has it: checked it sends
44659
44674
  // its value ("on" by default), unchecked it sends nothing at all. Only one
44660
- // holding `true` is a yes/no, and a yes/no nobody said yes to is `false`.
44661
- return props.value === true ? false : undefined;
44675
+ // declared `boolean` is a yes/no, and a yes/no nobody said yes to is
44676
+ // `false`.
44677
+ return props.boolean ? false : undefined;
44662
44678
  }
44663
44679
  const stateShape = props["navi-state-shape"];
44664
44680
  if (stateShape === "array") {
@@ -44670,6 +44686,15 @@ const resolveEmptyUIState = (props, controlType) => {
44670
44686
  return undefined;
44671
44687
  };
44672
44688
 
44689
+ // What a control holding `uiState` is worth: its own empty when it holds
44690
+ // nothing, so the reader gets the shape it was promised.
44691
+ const uiStateOrEmpty = (uiState, emptyUIState) => {
44692
+ if (uiState === undefined && emptyUIState !== undefined) {
44693
+ return emptyUIState;
44694
+ }
44695
+ return uiState;
44696
+ };
44697
+
44673
44698
  // What a cleared control shows: its own empty, kept in the shape it was holding.
44674
44699
  const resolveClearedUIState = (controller) => {
44675
44700
  const { controlType, props } = controller;
@@ -44904,7 +44929,7 @@ const useControlProps = (props, {
44904
44929
  // of a form would re-render at each change of any of its fields.
44905
44930
  // eslint-disable-next-line no-unused-expressions
44906
44931
  uiStateController.ownUIStateSignal.value;
44907
- const boundAction = useAction(props.action, uiStateController.uiStateSignal);
44932
+ const boundAction = useAction(props.action, uiStateController.valueSignal);
44908
44933
  const [controlRootProps, controlHostProps] = useInteractiveProps(props, {
44909
44934
  uiStateController,
44910
44935
  boundAction,
@@ -56981,7 +57006,12 @@ const css$M = /* css */`@layer navi {
56981
57006
  const InputCheckbox = props => {
56982
57007
  const defaultRef = useRef();
56983
57008
  props.ref = props.ref || defaultRef;
56984
- props.value = props.value === undefined ? "on" : props.value;
57009
+ useRef(false);
57010
+ if (props.boolean) {
57011
+ props.value = true;
57012
+ } else if (props.value === undefined) {
57013
+ props.value = "on";
57014
+ }
56985
57015
  if (props.headless) {
56986
57016
  return jsx(InputCheckboxHeadless, {
56987
57017
  ...props,