@jsenv/navi 0.29.381 → 0.29.383
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 +60 -4
- package/dist/dev/jsenv_navi.js.map +4 -4
- package/dist/jsenv_navi.js +60 -4
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -17917,6 +17917,11 @@ const generateSignalId = () => {
|
|
|
17917
17917
|
* `?level=3,4` becomes `[3, 4]` instead of `["3", "4"]`. Without it items stay strings.
|
|
17918
17918
|
* @param {number} [options.step] - For number type: step size for precision. Values will be rounded to nearest multiple of step.
|
|
17919
17919
|
* @param {Array} [options.oneOf] - Array of valid values for validation. Signal will be marked invalid if value is not in this array
|
|
17920
|
+
* @param {boolean} [options.autoFix=false] - Hold the repaired value instead of the refused one: a number
|
|
17921
|
+
* clamped to `min`/`max` and rounded to `step`, a value outside `oneOf` replaced by the signal's
|
|
17922
|
+
* default value (an enumeration has no nearest value, and its order says nothing). Without it the
|
|
17923
|
+
* refused value is held as-is, marked invalid, and `validSignal` reads `undefined`. When the value
|
|
17924
|
+
* came from the address, the address is rewritten with the repaired one (a replace).
|
|
17920
17925
|
* @param {boolean} [options.weak=false] - The param qualifies one visit, not the screen: it is written into a
|
|
17921
17926
|
* url only when explicitly named (`routeParams={{ edit: id }}`), never inherited from the signal's current
|
|
17922
17927
|
* value, and it goes back to the default value when the route stops matching. Use it for params like an
|
|
@@ -17951,11 +17956,11 @@ const generateSignalId = () => {
|
|
|
17951
17956
|
* });
|
|
17952
17957
|
*
|
|
17953
17958
|
* @example
|
|
17954
|
-
* // Signal with validation and auto-fix
|
|
17959
|
+
* // Signal with validation and auto-fix: an unknown tab falls back to "overview"
|
|
17955
17960
|
* const tab = stateSignal("overview", {
|
|
17956
17961
|
* id: "current-tab",
|
|
17957
17962
|
* oneOf: ["overview", "details", "settings"],
|
|
17958
|
-
* autoFix:
|
|
17963
|
+
* autoFix: true,
|
|
17959
17964
|
* persists: true
|
|
17960
17965
|
* });
|
|
17961
17966
|
*
|
|
@@ -18052,6 +18057,10 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
18052
18057
|
oneOf,
|
|
18053
18058
|
localStorageRepresentation,
|
|
18054
18059
|
autoFix,
|
|
18060
|
+
// What a value no rule can repair falls back to: the default is what the
|
|
18061
|
+
// state is when nothing says otherwise, read at repair time (from
|
|
18062
|
+
// processValue, below) so a dynamic default is read where it stands.
|
|
18063
|
+
fallback: () => getDefaultValue(false),
|
|
18055
18064
|
});
|
|
18056
18065
|
const readFromLocalStorage = persists
|
|
18057
18066
|
? () => {
|
|
@@ -27169,6 +27178,14 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
27169
27178
|
// Apply all signal updates in a batch
|
|
27170
27179
|
const matchingRouteSet = new Set();
|
|
27171
27180
|
const routeUpdateSet = new Set();
|
|
27181
|
+
// Params the address gave and the state could not hold as written
|
|
27182
|
+
// (autoFix): the address is rewritten with what the state holds, once
|
|
27183
|
+
// the sync is over, so the repaired value is not left in the url to be
|
|
27184
|
+
// replayed by a reload or a copied link.
|
|
27185
|
+
const repairedParamsByRoute = new Map();
|
|
27186
|
+
// A query connection is inherited by the child routes of the one
|
|
27187
|
+
// declaring it: the first route to record a param answers for all.
|
|
27188
|
+
const repairedParamNameSet = new Set();
|
|
27172
27189
|
batch(() => {
|
|
27173
27190
|
for (const {
|
|
27174
27191
|
route,
|
|
@@ -27351,6 +27368,29 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
27351
27368
|
);
|
|
27352
27369
|
}
|
|
27353
27370
|
paramSignal.value = urlParamValue;
|
|
27371
|
+
if (
|
|
27372
|
+
paramSignal.validity?.autoFixed &&
|
|
27373
|
+
!repairedParamNameSet.has(paramName)
|
|
27374
|
+
) {
|
|
27375
|
+
repairedParamNameSet.add(paramName);
|
|
27376
|
+
const repairedValue = paramSignal.peek();
|
|
27377
|
+
if (debug) {
|
|
27378
|
+
console.debug(
|
|
27379
|
+
`[route] URL->Signal: ${paramName}=${urlParamValue} repaired to ${repairedValue}, the url will be rewritten`,
|
|
27380
|
+
);
|
|
27381
|
+
}
|
|
27382
|
+
let repairedParams = repairedParamsByRoute.get(route);
|
|
27383
|
+
if (!repairedParams) {
|
|
27384
|
+
repairedParams = {};
|
|
27385
|
+
repairedParamsByRoute.set(route, repairedParams);
|
|
27386
|
+
}
|
|
27387
|
+
// A default is not written in an address (see the signal -> url sync)
|
|
27388
|
+
repairedParams[paramName] = connection.isDefaultValue(
|
|
27389
|
+
repairedValue,
|
|
27390
|
+
)
|
|
27391
|
+
? undefined
|
|
27392
|
+
: repairedValue;
|
|
27393
|
+
}
|
|
27354
27394
|
continue;
|
|
27355
27395
|
}
|
|
27356
27396
|
}
|
|
@@ -27361,6 +27401,12 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
27361
27401
|
// Reset flag after URL -> Signal synchronization is complete
|
|
27362
27402
|
isUpdatingRoutesFromUrl = false;
|
|
27363
27403
|
Object.assign(returnValue, { matchingRouteSet });
|
|
27404
|
+
for (const [route, repairedParams] of repairedParamsByRoute) {
|
|
27405
|
+
route.replaceParams(repairedParams, {
|
|
27406
|
+
callReason: `url params repaired on ${route}: ${Object.keys(repairedParams).join(", ")}`,
|
|
27407
|
+
history: "replace",
|
|
27408
|
+
});
|
|
27409
|
+
}
|
|
27364
27410
|
}
|
|
27365
27411
|
|
|
27366
27412
|
return returnValue;
|
|
@@ -40917,6 +40963,11 @@ const uiStateHoldsNothing = (uiState) => {
|
|
|
40917
40963
|
*/
|
|
40918
40964
|
const NO_ACTION_YET = Symbol("no_action_yet");
|
|
40919
40965
|
|
|
40966
|
+
// The control types rendered as an element `<label for>` can name: HTML's
|
|
40967
|
+
// labelable elements are button, input, select, textarea, output, meter and
|
|
40968
|
+
// progress — a picker's trigger is one of them, a link or a details is not.
|
|
40969
|
+
const CONTROL_TYPES_A_LABEL_CAN_POINT_AT = new Set(["button", "input", "select", "picker"]);
|
|
40970
|
+
|
|
40920
40971
|
// Resets field-specific contexts so nested fields inside this component
|
|
40921
40972
|
// don't inherit the current field's id, message props, or interface reporting.
|
|
40922
40973
|
// Sets ParentUIStateControllerContext to the leaf's own uiStateController so
|
|
@@ -41014,8 +41065,13 @@ const useControlProps = (props, {
|
|
|
41014
41065
|
standalone
|
|
41015
41066
|
} = props;
|
|
41016
41067
|
const idDefault = useId();
|
|
41017
|
-
|
|
41018
|
-
|
|
41068
|
+
// A Field's id is what its Label points at, so only a control a label can
|
|
41069
|
+
// point at (see the set) takes it. A link in the label's own text is a
|
|
41070
|
+
// control too, but never that target: taking the id would make two elements
|
|
41071
|
+
// answer for it, and the label would read the link's state as its control's.
|
|
41072
|
+
const fieldId = useContext(ControlIdContext);
|
|
41073
|
+
const labelable = CONTROL_TYPES_A_LABEL_CAN_POINT_AT.has(controlType);
|
|
41074
|
+
props.id = props.id || (labelable ? fieldId : undefined) || idDefault;
|
|
41019
41075
|
const controlName = useContext(ControlNameContext);
|
|
41020
41076
|
props.name = props.name || controlName;
|
|
41021
41077
|
const isCheckable = isCheckableInput(controlType, props.type);
|