@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.
- package/dist/dev/jsenv_navi.js +57 -16
- package/dist/dev/jsenv_navi.js.map +6 -6
- package/dist/jsenv_navi.js +45 -15
- package/dist/jsenv_navi.js.map +6 -6
- package/docs/AI_INSTRUCTIONS.md +6 -4
- package/docs/actions.md +6 -2
- package/docs/control_value.md +28 -8
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -6502,6 +6502,10 @@ const CONTROL_PROP_SET = new Set([
|
|
|
6502
6502
|
"charGuard",
|
|
6503
6503
|
"maxLengthGuard",
|
|
6504
6504
|
|
|
6505
|
+
// A checkbox that is a yes/no: `true` checked, `false` unchecked, instead of
|
|
6506
|
+
// HTML's value-or-nothing (see resolveEmptyUIState).
|
|
6507
|
+
"boolean",
|
|
6508
|
+
|
|
6505
6509
|
// This control answers for itself: it registers into no group, no form and no
|
|
6506
6510
|
// picker around it (see useUIStateController). It belongs here rather than
|
|
6507
6511
|
// being deleted off `props` where it is read: props is the vnode's own object
|
|
@@ -43198,6 +43202,14 @@ const useUIStateController = (
|
|
|
43198
43202
|
return ownUIState || parentUIState;
|
|
43199
43203
|
})
|
|
43200
43204
|
: ownUIStateSignal;
|
|
43205
|
+
// What the control is worth, which its action is bound to: what it
|
|
43206
|
+
// holds, or its own empty when it holds nothing. Apart from
|
|
43207
|
+
// uiStateSignal because unchecked is `undefined` there (the DOM reads any
|
|
43208
|
+
// other value as checked), while a yes/no unchecked is worth `false`.
|
|
43209
|
+
const emptyUIStateSignal = signal(emptyUIState);
|
|
43210
|
+
const valueSignal = computed(() =>
|
|
43211
|
+
uiStateOrEmpty(uiStateSignal.value, emptyUIStateSignal.value),
|
|
43212
|
+
);
|
|
43201
43213
|
|
|
43202
43214
|
// The two-way half of a bound `signal` prop: setting it re-renders and
|
|
43203
43215
|
// re-syncs via state_prop_change, but with the same value → guarded as a
|
|
@@ -43246,6 +43258,7 @@ const useUIStateController = (
|
|
|
43246
43258
|
// travel command ended up carrying a picker's selection: see
|
|
43247
43259
|
// resolveCommandValue in commands.js.
|
|
43248
43260
|
ownUIStateSignal,
|
|
43261
|
+
valueSignal,
|
|
43249
43262
|
value: controlInfo.value,
|
|
43250
43263
|
// The suggestion this control started on — what tells a field showing
|
|
43251
43264
|
// its default from one carrying an answer (see isUIStateHeld).
|
|
@@ -43309,8 +43322,12 @@ const useUIStateController = (
|
|
|
43309
43322
|
}
|
|
43310
43323
|
s.uiActionInternal?.(currentUIState, e);
|
|
43311
43324
|
if (s.uiAction) {
|
|
43312
|
-
|
|
43313
|
-
|
|
43325
|
+
const value = uiStateOrEmpty(
|
|
43326
|
+
currentUIState,
|
|
43327
|
+
controller.emptyUIState,
|
|
43328
|
+
);
|
|
43329
|
+
debugUIState(`calling uiAction for ${controlType}`, value);
|
|
43330
|
+
s.uiAction(value, e);
|
|
43314
43331
|
}
|
|
43315
43332
|
if (skipCommand) ; else {
|
|
43316
43333
|
const command = controller.controlHostProps.command;
|
|
@@ -43814,6 +43831,7 @@ const useUIStateController = (
|
|
|
43814
43831
|
return {
|
|
43815
43832
|
controller,
|
|
43816
43833
|
parentUiStateSignalHolder,
|
|
43834
|
+
emptyUIStateSignal,
|
|
43817
43835
|
...liveValues(),
|
|
43818
43836
|
};
|
|
43819
43837
|
},
|
|
@@ -43935,6 +43953,7 @@ const useUIStateController = (
|
|
|
43935
43953
|
);
|
|
43936
43954
|
scope.parentUiStateSignalHolder.value =
|
|
43937
43955
|
parentUIStateController?.uiStateSignal ?? null;
|
|
43956
|
+
scope.emptyUIStateSignal.value = emptyUIState;
|
|
43938
43957
|
|
|
43939
43958
|
const { controller } = scope;
|
|
43940
43959
|
const controllerRef = controller.ref;
|
|
@@ -44145,14 +44164,11 @@ const keptAndNamedChildUIStates = (children, fallbackState, kept) => {
|
|
|
44145
44164
|
const readNamedChildUIStates = (children, { warnNameless }) => {
|
|
44146
44165
|
const values = {};
|
|
44147
44166
|
for (const child of children) {
|
|
44148
|
-
const { name
|
|
44167
|
+
const { name } = child;
|
|
44149
44168
|
// A control holding nothing writes its own empty, not a hole: the key is in
|
|
44150
44169
|
// the object either way, and what is read from it keeps the shape the
|
|
44151
44170
|
// reader was promised (see resolveEmptyUIState).
|
|
44152
|
-
const uiState =
|
|
44153
|
-
child.uiState === undefined && emptyUIState !== undefined
|
|
44154
|
-
? emptyUIState
|
|
44155
|
-
: child.uiState;
|
|
44171
|
+
const uiState = uiStateOrEmpty(child.uiState, child.emptyUIState);
|
|
44156
44172
|
if (!name) {
|
|
44157
44173
|
if (isNamelessGrouping(child, uiState)) {
|
|
44158
44174
|
Object.assign(
|
|
@@ -45499,11 +45515,10 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
45499
45515
|
// whose last item was unselected holds [], the same thing clearing it
|
|
45500
45516
|
// leaves — not a value that changes type on its owner the moment it
|
|
45501
45517
|
// empties.
|
|
45502
|
-
const
|
|
45503
|
-
|
|
45504
|
-
|
|
45505
|
-
|
|
45506
|
-
: child.uiState;
|
|
45518
|
+
const uiStateToAdopt = uiStateOrEmpty(
|
|
45519
|
+
child.uiState,
|
|
45520
|
+
s.realUIStateController.emptyUIState,
|
|
45521
|
+
);
|
|
45507
45522
|
s.realUIStateController.setUIState(uiStateToAdopt, propagateUpEvent);
|
|
45508
45523
|
updatingRef.current = false;
|
|
45509
45524
|
},
|
|
@@ -45821,8 +45836,9 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
45821
45836
|
if (controlType === "input" && props.type === "checkbox") {
|
|
45822
45837
|
// A checkbox is a member of a set, the way HTML has it: checked it sends
|
|
45823
45838
|
// its value ("on" by default), unchecked it sends nothing at all. Only one
|
|
45824
|
-
//
|
|
45825
|
-
|
|
45839
|
+
// declared `boolean` is a yes/no, and a yes/no nobody said yes to is
|
|
45840
|
+
// `false`.
|
|
45841
|
+
return props.boolean ? false : undefined;
|
|
45826
45842
|
}
|
|
45827
45843
|
const stateShape = props["navi-state-shape"];
|
|
45828
45844
|
if (stateShape === "array") {
|
|
@@ -45834,6 +45850,15 @@ const resolveEmptyUIState = (props, controlType) => {
|
|
|
45834
45850
|
return undefined;
|
|
45835
45851
|
};
|
|
45836
45852
|
|
|
45853
|
+
// What a control holding `uiState` is worth: its own empty when it holds
|
|
45854
|
+
// nothing, so the reader gets the shape it was promised.
|
|
45855
|
+
const uiStateOrEmpty = (uiState, emptyUIState) => {
|
|
45856
|
+
if (uiState === undefined && emptyUIState !== undefined) {
|
|
45857
|
+
return emptyUIState;
|
|
45858
|
+
}
|
|
45859
|
+
return uiState;
|
|
45860
|
+
};
|
|
45861
|
+
|
|
45837
45862
|
// What a cleared control shows: its own empty, kept in the shape it was holding.
|
|
45838
45863
|
const resolveClearedUIState = (controller) => {
|
|
45839
45864
|
const { controlType, props } = controller;
|
|
@@ -46069,7 +46094,7 @@ const useControlProps = (props, {
|
|
|
46069
46094
|
// of a form would re-render at each change of any of its fields.
|
|
46070
46095
|
// eslint-disable-next-line no-unused-expressions
|
|
46071
46096
|
uiStateController.ownUIStateSignal.value;
|
|
46072
|
-
const boundAction = useAction(props.action, uiStateController.
|
|
46097
|
+
const boundAction = useAction(props.action, uiStateController.valueSignal);
|
|
46073
46098
|
const [controlRootProps, controlHostProps] = useInteractiveProps(props, {
|
|
46074
46099
|
uiStateController,
|
|
46075
46100
|
boundAction,
|
|
@@ -58190,7 +58215,23 @@ const css$M = /* css */`@layer navi {
|
|
|
58190
58215
|
const InputCheckbox = props => {
|
|
58191
58216
|
const defaultRef = useRef();
|
|
58192
58217
|
props.ref = props.ref || defaultRef;
|
|
58193
|
-
|
|
58218
|
+
const valueWarnedRef = useRef(false);
|
|
58219
|
+
if (!valueWarnedRef.current) {
|
|
58220
|
+
// `true` is what `boolean` writes into `value` below, and Preact hands the
|
|
58221
|
+
// same props object back on a re-render the component triggers itself.
|
|
58222
|
+
if (props.boolean && props.value !== undefined && props.value !== true) {
|
|
58223
|
+
valueWarnedRef.current = true;
|
|
58224
|
+
console.warn(`[checkbox] "boolean" is true or false, its "value" is ignored: remove value={${JSON.stringify(props.value)}}.`);
|
|
58225
|
+
} else if (!props.boolean && props.value === true) {
|
|
58226
|
+
valueWarnedRef.current = true;
|
|
58227
|
+
console.warn(`[checkbox] value={true} is a value like any other: unchecked is undefined. For a yes/no (true or false), use the "boolean" prop.`);
|
|
58228
|
+
}
|
|
58229
|
+
}
|
|
58230
|
+
if (props.boolean) {
|
|
58231
|
+
props.value = true;
|
|
58232
|
+
} else if (props.value === undefined) {
|
|
58233
|
+
props.value = "on";
|
|
58234
|
+
}
|
|
58194
58235
|
if (props.headless) {
|
|
58195
58236
|
return jsx(InputCheckboxHeadless, {
|
|
58196
58237
|
...props,
|