@jsenv/navi 0.29.230 → 0.29.232
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 +385 -101
- package/dist/dev/jsenv_navi.js.map +16 -14
- package/dist/dev/jsenv_navi_side_effects.js +3 -0
- package/dist/dev/jsenv_navi_side_effects.js.map +2 -2
- package/dist/jsenv_navi.js +360 -98
- package/dist/jsenv_navi.js.map +16 -14
- package/dist/jsenv_navi_side_effects.js +3 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/actions.md +27 -0
- package/docs/css_architecture.md +20 -0
- package/docs/form_changed.md +12 -3
- package/docs/i18n.md +9 -0
- package/docs/navigation.md +81 -0
- package/docs/resource.md +5 -0
- package/docs/typography.md +18 -0
- package/package.json +2 -2
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -80,7 +80,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
80
80
|
* any of these, and a number is the last resort, not the first tool.
|
|
81
81
|
*/
|
|
82
82
|
|
|
83
|
-
const css$
|
|
83
|
+
const css$17 = /* css */ `@layer navi {
|
|
84
84
|
:root {
|
|
85
85
|
--navi-z-index-control-hovered: 1;
|
|
86
86
|
--navi-z-index-control-focused: 2;
|
|
@@ -92,7 +92,7 @@ const css$16 = /* css */ `@layer navi {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
`;
|
|
95
|
-
import.meta.css = [css$
|
|
95
|
+
import.meta.css = [css$17, "@jsenv/navi/src/navi_z_indexes.js"];
|
|
96
96
|
|
|
97
97
|
const addIntoArray = (array, ...valuesToAdd) => {
|
|
98
98
|
if (valuesToAdd.length === 1) {
|
|
@@ -328,7 +328,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
328
328
|
|
|
329
329
|
const URL_TARGET_ATTRIBUTE = "data-url-target";
|
|
330
330
|
|
|
331
|
-
const css$
|
|
331
|
+
const css$16 = /* css */ `@layer navi {
|
|
332
332
|
[data-url-target] {
|
|
333
333
|
animation: navi_url_target var(--navi-url-target-duration, 2s)
|
|
334
334
|
ease-out;
|
|
@@ -346,7 +346,7 @@ const css$15 = /* css */ `@layer navi {
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
`;
|
|
349
|
-
import.meta.css = [css$
|
|
349
|
+
import.meta.css = [css$16, "@jsenv/navi/src/nav/url_target/url_target.js"];
|
|
350
350
|
|
|
351
351
|
let urlTargetOptions = {
|
|
352
352
|
block: "start",
|
|
@@ -1683,7 +1683,7 @@ const useActionStatus = (action) => {
|
|
|
1683
1683
|
};
|
|
1684
1684
|
|
|
1685
1685
|
installImportMetaCssBuild(import.meta);
|
|
1686
|
-
const css$
|
|
1686
|
+
const css$15 = /* css */`.action_error {
|
|
1687
1687
|
background: #fdd;
|
|
1688
1688
|
border: 1px solid red;
|
|
1689
1689
|
margin-top: 0;
|
|
@@ -1707,7 +1707,7 @@ const ActionRenderer = ({
|
|
|
1707
1707
|
children,
|
|
1708
1708
|
disabled
|
|
1709
1709
|
}) => {
|
|
1710
|
-
import.meta.css = [css$
|
|
1710
|
+
import.meta.css = [css$15, "@jsenv/navi/src/action/action_renderer.jsx"];
|
|
1711
1711
|
if (action === undefined) {
|
|
1712
1712
|
throw new Error("ActionRenderer requires an action to render, but none was provided.");
|
|
1713
1713
|
}
|
|
@@ -2902,6 +2902,9 @@ ${lines.join("\n")}`,
|
|
|
2902
2902
|
const willPrerunSet = new Set();
|
|
2903
2903
|
const willRunSet = new Set();
|
|
2904
2904
|
const willPromoteSet = new Set(); // prerun -> run requested
|
|
2905
|
+
// Asked for, already under way: the request adds no work, and answers with
|
|
2906
|
+
// the run that is already carrying it (see join_active_runs below).
|
|
2907
|
+
const willJoinSet = new Set();
|
|
2905
2908
|
const staysRunningSet = new Set();
|
|
2906
2909
|
const staysAbortedSet = new Set();
|
|
2907
2910
|
const staysFailedSet = new Set();
|
|
@@ -2953,8 +2956,11 @@ ${lines.join("\n")}`,
|
|
|
2953
2956
|
} else {
|
|
2954
2957
|
willRunSet.add(action);
|
|
2955
2958
|
}
|
|
2959
|
+
} else {
|
|
2960
|
+
// The request performs nothing, and still has to be answered: what
|
|
2961
|
+
// the caller asked for is the answer, not the running.
|
|
2962
|
+
willJoinSet.add(action);
|
|
2956
2963
|
}
|
|
2957
|
-
// Otherwise, ignore the request (action stays as-is)
|
|
2958
2964
|
} else if (isPrerun) {
|
|
2959
2965
|
willPrerunSet.add(action);
|
|
2960
2966
|
} else {
|
|
@@ -2981,6 +2987,7 @@ ${lines.join("\n")}`,
|
|
|
2981
2987
|
// Special case: action was prerun but not yet requested to run
|
|
2982
2988
|
// Just promote it to "run requested" without rerunning
|
|
2983
2989
|
willPromoteSet.add(actionToRun);
|
|
2990
|
+
willJoinSet.add(actionToRun);
|
|
2984
2991
|
continue;
|
|
2985
2992
|
}
|
|
2986
2993
|
handleActionRequest(actionToRun, "run");
|
|
@@ -3134,6 +3141,27 @@ ${lines.join("\n")}`);
|
|
|
3134
3141
|
actionToPromote.isPrerunSignal.value = false;
|
|
3135
3142
|
}
|
|
3136
3143
|
}
|
|
3144
|
+
|
|
3145
|
+
// A run asked for an action that is already running, or that already holds
|
|
3146
|
+
// the answer, performs nothing — and answers all the same, with the run
|
|
3147
|
+
// that IS carrying it. `await action.run()` then means the same thing to
|
|
3148
|
+
// every caller: the second one waits for the first one's request and reads
|
|
3149
|
+
// the same data, instead of being handed `undefined` before the network has
|
|
3150
|
+
// said anything. See the run() JSDoc.
|
|
3151
|
+
{
|
|
3152
|
+
for (const actionToJoin of willJoinSet) {
|
|
3153
|
+
const runningPromise = actionPromiseMap.get(actionToJoin);
|
|
3154
|
+
if (runningPromise) {
|
|
3155
|
+
hasAsync = true;
|
|
3156
|
+
resultArray.push({ type: "async", promise: runningPromise });
|
|
3157
|
+
} else {
|
|
3158
|
+
resultArray.push({
|
|
3159
|
+
type: "sync",
|
|
3160
|
+
result: actionToJoin.dataSignal.peek(),
|
|
3161
|
+
});
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3137
3165
|
if (DEBUG$2) {
|
|
3138
3166
|
console.groupEnd();
|
|
3139
3167
|
}
|
|
@@ -3288,10 +3316,15 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
3288
3316
|
return dispatchSingleAction(action, "prerun", options);
|
|
3289
3317
|
};
|
|
3290
3318
|
/**
|
|
3291
|
-
* Requests the action's data
|
|
3292
|
-
*
|
|
3293
|
-
*
|
|
3294
|
-
*
|
|
3319
|
+
* Requests the action's data, and resolves with it.
|
|
3320
|
+
*
|
|
3321
|
+
* An action that is already RUNNING or COMPLETED already has it, so the
|
|
3322
|
+
* request starts nothing — it JOINS: the caller waits for the run that is
|
|
3323
|
+
* already carrying the question and reads the same answer. Which is what
|
|
3324
|
+
* makes `await action.run()` say one thing to every caller, whether it is
|
|
3325
|
+
* the one that asked first or the one that arrived while the request was
|
|
3326
|
+
* out. Use `rerun()` to force a fresh run ("refresh", "check now", any
|
|
3327
|
+
* explicit user intent to go back to the network).
|
|
3295
3328
|
*/
|
|
3296
3329
|
const run = (options) => {
|
|
3297
3330
|
action.debug(`${action}.run(${stringifyForDisplay(options)})`);
|
|
@@ -4854,6 +4887,25 @@ const warnSignalCollision = (props, controlType, stateProp) => {
|
|
|
4854
4887
|
}
|
|
4855
4888
|
};
|
|
4856
4889
|
|
|
4890
|
+
/**
|
|
4891
|
+
* A signal handed to a prop that holds a plain value is the `signal` binding
|
|
4892
|
+
* written in the wrong place. Nothing throws: the signal object becomes the
|
|
4893
|
+
* value, and a value that cannot be written to the DOM is replaced by a
|
|
4894
|
+
* `window.__navi_js('…')` reference string — which is then what the field
|
|
4895
|
+
* shows on screen (see toDomValue in controller_registry.js).
|
|
4896
|
+
*/
|
|
4897
|
+
const warnSignalAsState = (props, controlType, stateProps) => {
|
|
4898
|
+
for (const stateProp of stateProps) {
|
|
4899
|
+
if (isSignal(props[stateProp])) {
|
|
4900
|
+
console.warn(
|
|
4901
|
+
`[navi] "${controlType}" got a signal as "${stateProp}". ` +
|
|
4902
|
+
`A signal binds a control through "signal" — did you mean signal={...}? ` +
|
|
4903
|
+
`As "${stateProp}" it is taken for the value itself.`,
|
|
4904
|
+
);
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
};
|
|
4908
|
+
|
|
4857
4909
|
// In-memory registry of all mounted ui state controllers keyed by their id.
|
|
4858
4910
|
// Allows direct controller access without dispatching DOM events — used by external
|
|
4859
4911
|
// callers (e.g. selectable_list) to call setUIState by id instead of via the DOM.
|
|
@@ -5864,7 +5916,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
5864
5916
|
* (see calloutTemplate in callout.js).
|
|
5865
5917
|
*/
|
|
5866
5918
|
|
|
5867
|
-
const css$
|
|
5919
|
+
const css$14 = /* css */`.navi_callout_status_icon {
|
|
5868
5920
|
--x-callout-status-icon-color: var(--navi-callout-neutral-color);
|
|
5869
5921
|
box-sizing: border-box;
|
|
5870
5922
|
aspect-ratio: 1;
|
|
@@ -5935,7 +5987,7 @@ const CalloutStatusIcon = ({
|
|
|
5935
5987
|
status = "info",
|
|
5936
5988
|
shape = "square"
|
|
5937
5989
|
}) => {
|
|
5938
|
-
import.meta.css = [css$
|
|
5990
|
+
import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
|
|
5939
5991
|
return jsx("span", {
|
|
5940
5992
|
className: "navi_callout_status_icon",
|
|
5941
5993
|
"data-status": status === "none" ? undefined : status,
|
|
@@ -5957,7 +6009,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
5957
6009
|
// Unique for the page's lifetime: a caller may write the id in a commandfor.
|
|
5958
6010
|
let calloutCount = 0;
|
|
5959
6011
|
|
|
5960
|
-
const css$
|
|
6012
|
+
const css$13 = /* css */ `
|
|
5961
6013
|
@layer navi {
|
|
5962
6014
|
.navi_callout {
|
|
5963
6015
|
/* A callout is parented to what it explains, so it inherits from it — and
|
|
@@ -6305,7 +6357,7 @@ const openCallout = (
|
|
|
6305
6357
|
debug = () => {},
|
|
6306
6358
|
} = {},
|
|
6307
6359
|
) => {
|
|
6308
|
-
import.meta.css = [css$
|
|
6360
|
+
import.meta.css = [css$13, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
6309
6361
|
if (debug === true) {
|
|
6310
6362
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
6311
6363
|
}
|
|
@@ -18515,8 +18567,12 @@ const VISUAL_PROPS = {
|
|
|
18515
18567
|
outlineWidth: PASS_THROUGH,
|
|
18516
18568
|
boxDecorationBreak: PASS_THROUGH,
|
|
18517
18569
|
boxShadow: PASS_THROUGH,
|
|
18518
|
-
background:
|
|
18519
|
-
|
|
18570
|
+
background: (value) => {
|
|
18571
|
+
return { background: resolveSurfaceKeyword(value) };
|
|
18572
|
+
},
|
|
18573
|
+
backgroundColor: (value) => {
|
|
18574
|
+
return { backgroundColor: resolveSurfaceKeyword(value) };
|
|
18575
|
+
},
|
|
18520
18576
|
backgroundImage: PASS_THROUGH,
|
|
18521
18577
|
backgroundSize: PASS_THROUGH,
|
|
18522
18578
|
border: PASS_THROUGH,
|
|
@@ -18881,6 +18937,18 @@ const resolveColorKeyword = (value) => {
|
|
|
18881
18937
|
return COLOR_KEYWORD_MAP[value] || value;
|
|
18882
18938
|
};
|
|
18883
18939
|
|
|
18940
|
+
// The two planes an app paints: the paper its content sits on, and the frame
|
|
18941
|
+
// around it (see navi_css_vars.js). Named rather than derived at each call
|
|
18942
|
+
// site, so a top bar and a side menu are the same plane without agreeing on a
|
|
18943
|
+
// formula.
|
|
18944
|
+
const SURFACE_KEYWORD_MAP = {
|
|
18945
|
+
surface: "var(--navi-surface-color)",
|
|
18946
|
+
chrome: "var(--navi-chrome-color)",
|
|
18947
|
+
};
|
|
18948
|
+
const resolveSurfaceKeyword = (value) => {
|
|
18949
|
+
return SURFACE_KEYWORD_MAP[value] || value;
|
|
18950
|
+
};
|
|
18951
|
+
|
|
18884
18952
|
const DEFAULT_DISPLAY_BY_TAG_NAME = {
|
|
18885
18953
|
"inline": new Set([
|
|
18886
18954
|
"a",
|
|
@@ -20810,6 +20878,16 @@ const PSEUDO_STATE_CHILD_PROP_SET = new Set(["tabIndex", "tabindex"]);
|
|
|
20810
20878
|
* children?: import("ignore:preact").ComponentChildren,
|
|
20811
20879
|
* [key: string]: any,
|
|
20812
20880
|
* }>}
|
|
20881
|
+
* @param {string} [background] Any CSS background, plus two keywords for the
|
|
20882
|
+
* planes an app paints: `"surface"` is the paper content sits on,
|
|
20883
|
+
* `"chrome"` the frame around a screen — a top bar, a side menu, a toolbar.
|
|
20884
|
+
* Both follow the theme, so a bar stays a bar in dark mode
|
|
20885
|
+
* (`--navi-surface-color` / `--navi-chrome-color`). `backgroundColor` takes
|
|
20886
|
+
* the same two.
|
|
20887
|
+
* @param {string} [color] Any CSS color, plus the five ink keywords —
|
|
20888
|
+
* `"primary"`, `"secondary"`, `"emphasis"`, `"discrete"`, `"hint"` — which
|
|
20889
|
+
* say how loud a text is rather than which color it is (see
|
|
20890
|
+
* docs/typography.md).
|
|
20813
20891
|
* @param {object} [interactions] What this box answers, by interaction name
|
|
20814
20892
|
* (see docs/interactions.md). A plain box has no wiring of its own: it does
|
|
20815
20893
|
* nothing with `action` (that is a control's prop), so a click on it is
|
|
@@ -22721,6 +22799,20 @@ const debug$2 = (...args) => {
|
|
|
22721
22799
|
// Base URL management
|
|
22722
22800
|
let baseFileUrl;
|
|
22723
22801
|
let baseUrl;
|
|
22802
|
+
/**
|
|
22803
|
+
* Where this document's addresses start, for a document that is not at the
|
|
22804
|
+
* root: a panel served from "/admin/admin.html" declares
|
|
22805
|
+
* `setBaseUrl("/admin/admin.html")` and then writes its routes as "/places",
|
|
22806
|
+
* "/places/:placeId". The url "/admin/places/42" matches, and `buildUrl` puts
|
|
22807
|
+
* the prefix back.
|
|
22808
|
+
*
|
|
22809
|
+
* Call it in the routes module, above the `route()` calls it governs — they
|
|
22810
|
+
* read the base url as they are created, so an entry point calling it after
|
|
22811
|
+
* importing them is too late. The server must serve the document for every
|
|
22812
|
+
* address below it (see docs/navigation.md).
|
|
22813
|
+
*
|
|
22814
|
+
* @param {string} value - the document's own url, absolute or relative.
|
|
22815
|
+
*/
|
|
22724
22816
|
const setBaseUrl = (value) => {
|
|
22725
22817
|
baseFileUrl = new URL(
|
|
22726
22818
|
value,
|
|
@@ -28563,7 +28655,7 @@ const ROUTE_TRAVEL_ATTRIBUTE = "data-navi-route-travel";
|
|
|
28563
28655
|
// the root pictures must NOT move (they carry the whole viewport, blank bands
|
|
28564
28656
|
// included).
|
|
28565
28657
|
|
|
28566
|
-
const css$
|
|
28658
|
+
const css$12 = /* css */`:root[data-navi-route-transition] [data-navi-route-transition-area] {
|
|
28567
28659
|
view-transition-name: navi-route-transition;
|
|
28568
28660
|
}
|
|
28569
28661
|
|
|
@@ -28884,7 +28976,7 @@ const RouteTransitionArea = ({
|
|
|
28884
28976
|
children,
|
|
28885
28977
|
...rest
|
|
28886
28978
|
}) => {
|
|
28887
|
-
import.meta.css = [css$
|
|
28979
|
+
import.meta.css = [css$12, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
28888
28980
|
installTransitionWindowCss();
|
|
28889
28981
|
const props = {
|
|
28890
28982
|
...rest,
|
|
@@ -28954,7 +29046,7 @@ const RouteTransitionArea = ({
|
|
|
28954
29046
|
* @returns {() => void} remove this relation.
|
|
28955
29047
|
*/
|
|
28956
29048
|
const defineRouteTransition = (from, to, transition) => {
|
|
28957
|
-
import.meta.css = [css$
|
|
29049
|
+
import.meta.css = [css$12, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
28958
29050
|
installTransitionWindowCss();
|
|
28959
29051
|
if (!to) {
|
|
28960
29052
|
throw new TypeError(`defineRouteTransition needs a destination: "to" is ${to}. The page reached from anywhere is written defineRouteTransition(null, THAT_PAGE, ...) — there is no relation the other way round, a page LEFT for anywhere being the back half of that one.`);
|
|
@@ -28995,7 +29087,7 @@ const defineRouteTransition = (from, to, transition) => {
|
|
|
28995
29087
|
* @returns {() => void} remove this default.
|
|
28996
29088
|
*/
|
|
28997
29089
|
const defineRouteDefaultTransition = transition => {
|
|
28998
|
-
import.meta.css = [css$
|
|
29090
|
+
import.meta.css = [css$12, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
28999
29091
|
installTransitionWindowCss();
|
|
29000
29092
|
const value = normalizeTransition(transition);
|
|
29001
29093
|
defaultTransition = value;
|
|
@@ -29817,7 +29909,7 @@ const DRAGGED_ATTRIBUTE = "data-navi-route-travel-dragged";
|
|
|
29817
29909
|
const TURNED_ATTRIBUTE = "data-navi-route-travel-turned";
|
|
29818
29910
|
// The name the box wears while it travels, and only then (see nameForTravel).
|
|
29819
29911
|
const TRAVEL_NAME = "navi-route-travel";
|
|
29820
|
-
const css$
|
|
29912
|
+
const css$11 = /* css */`.navi_route_travel {
|
|
29821
29913
|
position: relative;
|
|
29822
29914
|
|
|
29823
29915
|
&[data-travel-by-drag="x"] {
|
|
@@ -30106,7 +30198,7 @@ const RouteTravel = ({
|
|
|
30106
30198
|
children,
|
|
30107
30199
|
...rest
|
|
30108
30200
|
}) => {
|
|
30109
|
-
import.meta.css = [css$
|
|
30201
|
+
import.meta.css = [css$11, "@jsenv/navi/src/nav/route_travel.jsx"];
|
|
30110
30202
|
installTransitionWindowCss();
|
|
30111
30203
|
const elementRef = useRef();
|
|
30112
30204
|
const gestureRef = useRef(null);
|
|
@@ -37154,9 +37246,11 @@ const useControlProps = (props, {
|
|
|
37154
37246
|
* props, which is how `signal` came to be forgotten.
|
|
37155
37247
|
*/
|
|
37156
37248
|
const isControlValueGivenByProps = props => Object.hasOwn(props, "value") || Object.hasOwn(props, "defaultValue") || Object.hasOwn(props, "signal");
|
|
37249
|
+
const CONTROL_STATE_PROPS = ["value", "defaultValue", "checked", "defaultChecked", "open", "defaultOpen"];
|
|
37157
37250
|
const createControlInfo = (props, {
|
|
37158
37251
|
controlType
|
|
37159
37252
|
}) => {
|
|
37253
|
+
warnSignalAsState(props, controlType, CONTROL_STATE_PROPS);
|
|
37160
37254
|
let statePropName;
|
|
37161
37255
|
let defaultStatePropName;
|
|
37162
37256
|
let stateInitial;
|
|
@@ -39792,7 +39886,7 @@ const setupNetworkMonitoring = () => {
|
|
|
39792
39886
|
setupNetworkMonitoring();
|
|
39793
39887
|
|
|
39794
39888
|
installImportMetaCssBuild(import.meta);
|
|
39795
|
-
const css
|
|
39889
|
+
const css$10 = /* css */`.navi_loading_indicator_fluid_container {
|
|
39796
39890
|
border-radius: inherit;
|
|
39797
39891
|
opacity: 1;
|
|
39798
39892
|
width: 100%;
|
|
@@ -39823,7 +39917,7 @@ const LoadingIndicatorFluid = ({
|
|
|
39823
39917
|
visuallyHidden,
|
|
39824
39918
|
...rest
|
|
39825
39919
|
}) => {
|
|
39826
|
-
import.meta.css = [css
|
|
39920
|
+
import.meta.css = [css$10, "@jsenv/navi/src/graphic/loading/loading_indicator_fluid.jsx"];
|
|
39827
39921
|
const ref = useRef(null);
|
|
39828
39922
|
// The container dimensions can be deduced from the ref itself as the indicator is absolute inset 0
|
|
39829
39923
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
@@ -40029,7 +40123,7 @@ const LoadingRectangleSvg = ({
|
|
|
40029
40123
|
};
|
|
40030
40124
|
|
|
40031
40125
|
installImportMetaCssBuild(import.meta);
|
|
40032
|
-
const css
|
|
40126
|
+
const css$$ = /* css */`.navi_loading_outline_wrapper {
|
|
40033
40127
|
top: max(var(--loading-outline-min-inset, -100vh),
|
|
40034
40128
|
var(--loading-rectangle-top, 0px));
|
|
40035
40129
|
right: max(var(--loading-outline-min-inset, -100vh),
|
|
@@ -40050,7 +40144,7 @@ const css$_ = /* css */`.navi_loading_outline_wrapper {
|
|
|
40050
40144
|
}
|
|
40051
40145
|
`;
|
|
40052
40146
|
const LoadingOutline = props => {
|
|
40053
|
-
import.meta.css = [css
|
|
40147
|
+
import.meta.css = [css$$, "@jsenv/navi/src/graphic/loading/loading_outline.jsx"];
|
|
40054
40148
|
if (props.containerRef) {
|
|
40055
40149
|
const container = props.containerRef.current;
|
|
40056
40150
|
if (!container) {
|
|
@@ -40185,7 +40279,7 @@ const LoadingOutlineWithPortal = props => {
|
|
|
40185
40279
|
};
|
|
40186
40280
|
|
|
40187
40281
|
installImportMetaCssBuild(import.meta);
|
|
40188
|
-
const css$
|
|
40282
|
+
const css$_ = /* css */`.navi_text_anchor {
|
|
40189
40283
|
vertical-align: baseline;
|
|
40190
40284
|
user-select: none;
|
|
40191
40285
|
overflow: hidden;
|
|
@@ -40223,7 +40317,7 @@ const TextAnchor = ({
|
|
|
40223
40317
|
textSize,
|
|
40224
40318
|
lineLayout
|
|
40225
40319
|
}) => {
|
|
40226
|
-
import.meta.css = [css$
|
|
40320
|
+
import.meta.css = [css$_, "@jsenv/navi/src/text/text_anchor.jsx"];
|
|
40227
40321
|
const anchorRef = useRef();
|
|
40228
40322
|
|
|
40229
40323
|
// Plain useLayoutEffect would also fire while an ancestor dialog/popover
|
|
@@ -40451,7 +40545,7 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
40451
40545
|
};
|
|
40452
40546
|
|
|
40453
40547
|
installImportMetaCssBuild(import.meta);
|
|
40454
|
-
const css$
|
|
40548
|
+
const css$Z = /* css */`@layer navi {
|
|
40455
40549
|
.navi_text {
|
|
40456
40550
|
&[data-skeleton] {
|
|
40457
40551
|
border-radius: .2em;
|
|
@@ -41020,7 +41114,7 @@ const TextShrinkWrap = props => {
|
|
|
41020
41114
|
});
|
|
41021
41115
|
};
|
|
41022
41116
|
const TextUI = props => {
|
|
41023
|
-
import.meta.css = [css$
|
|
41117
|
+
import.meta.css = [css$Z, "@jsenv/navi/src/text/text.jsx"];
|
|
41024
41118
|
let {
|
|
41025
41119
|
ref,
|
|
41026
41120
|
spacing,
|
|
@@ -41244,7 +41338,7 @@ const Icon = ({
|
|
|
41244
41338
|
fillLine,
|
|
41245
41339
|
...props
|
|
41246
41340
|
}) => {
|
|
41247
|
-
import.meta.css = [css$
|
|
41341
|
+
import.meta.css = [css$Z, "@jsenv/navi/src/text/text.jsx"];
|
|
41248
41342
|
const innerChildren = href ? jsx("svg", {
|
|
41249
41343
|
width: "100%",
|
|
41250
41344
|
height: "100%",
|
|
@@ -41434,7 +41528,7 @@ const useDimColorWhen = (elementRef, shouldDim) => {
|
|
|
41434
41528
|
};
|
|
41435
41529
|
|
|
41436
41530
|
installImportMetaCssBuild(import.meta);
|
|
41437
|
-
const css$
|
|
41531
|
+
const css$Y = /* css */`@layer navi {
|
|
41438
41532
|
.navi_link {
|
|
41439
41533
|
--link-border-radius: unset;
|
|
41440
41534
|
--link-outline-color: var(--navi-focus-outline-color);
|
|
@@ -41892,7 +41986,7 @@ Object.assign(PSEUDO_CLASSES, {
|
|
|
41892
41986
|
* @param {boolean} [props.readOnly]
|
|
41893
41987
|
*/
|
|
41894
41988
|
const Link = props => {
|
|
41895
|
-
import.meta.css = [css$
|
|
41989
|
+
import.meta.css = [css$Y, "@jsenv/navi/src/nav/link/link.jsx"];
|
|
41896
41990
|
if (props.route) {
|
|
41897
41991
|
return jsx(LinkWithRoute, {
|
|
41898
41992
|
...props
|
|
@@ -42460,7 +42554,7 @@ const rememberProgressAnimation = (navElement, travel) => {
|
|
|
42460
42554
|
travel.progressAnimation = animation;
|
|
42461
42555
|
travel.progressFrom = animation ? parseFloat(animation.effect.getKeyframes()[0]["--slide-travel-progress"]) || 0 : 0;
|
|
42462
42556
|
};
|
|
42463
|
-
const css$
|
|
42557
|
+
const css$X = /* css */`@layer navi {
|
|
42464
42558
|
.navi_nav {
|
|
42465
42559
|
--nav-border: none;
|
|
42466
42560
|
--nav-padding: 0px;
|
|
@@ -42724,7 +42818,7 @@ const Nav = ({
|
|
|
42724
42818
|
slideContainer,
|
|
42725
42819
|
...props
|
|
42726
42820
|
}) => {
|
|
42727
|
-
import.meta.css = [css$
|
|
42821
|
+
import.meta.css = [css$X, "@jsenv/navi/src/nav/link/nav.jsx"];
|
|
42728
42822
|
const defaultRef = useRef();
|
|
42729
42823
|
props.ref = props.ref || defaultRef;
|
|
42730
42824
|
const navRef = props.ref;
|
|
@@ -43600,7 +43694,7 @@ const buildBinderCenterlinePathBelow = ({
|
|
|
43600
43694
|
};
|
|
43601
43695
|
|
|
43602
43696
|
installImportMetaCssBuild(import.meta);
|
|
43603
|
-
const css$
|
|
43697
|
+
const css$W = /* css */`@layer navi {
|
|
43604
43698
|
.navi_binder {
|
|
43605
43699
|
--binder-border-width: var(--navi-control-border-width);
|
|
43606
43700
|
--binder-border-radius: var(--navi-control-border-radius);
|
|
@@ -43885,7 +43979,7 @@ const Binder = ({
|
|
|
43885
43979
|
pagePadding,
|
|
43886
43980
|
...props
|
|
43887
43981
|
}) => {
|
|
43888
|
-
import.meta.css = [css$
|
|
43982
|
+
import.meta.css = [css$W, "@jsenv/navi/src/nav/binder/binder.jsx"];
|
|
43889
43983
|
const items = toChildArray(children).map((child, index) => {
|
|
43890
43984
|
const {
|
|
43891
43985
|
value: itemValue,
|
|
@@ -44332,7 +44426,7 @@ const writeSpace = (area) => {
|
|
|
44332
44426
|
};
|
|
44333
44427
|
|
|
44334
44428
|
installImportMetaCssBuild(import.meta);
|
|
44335
|
-
const css$
|
|
44429
|
+
const css$V = /* css */`@layer navi {
|
|
44336
44430
|
:root {
|
|
44337
44431
|
--navi-fixed-bar-width: 56px;
|
|
44338
44432
|
--navi-fixed-bar-height: 56px;
|
|
@@ -44452,7 +44546,7 @@ const FixedBar = ({
|
|
|
44452
44546
|
border = true,
|
|
44453
44547
|
...props
|
|
44454
44548
|
}) => {
|
|
44455
|
-
import.meta.css = [css$
|
|
44549
|
+
import.meta.css = [css$V, "@jsenv/navi/src/layout/fixed_bar/fixed_bar.jsx"];
|
|
44456
44550
|
const defaultRef = useRef();
|
|
44457
44551
|
props.ref = props.ref || defaultRef;
|
|
44458
44552
|
// Said with the width the border rule reads rather than with an attribute of
|
|
@@ -44546,7 +44640,7 @@ const FixedBar = ({
|
|
|
44546
44640
|
// Subpixel layout rounds rectangles up on boxes that fit exactly.
|
|
44547
44641
|
const OVERFLOW_TOLERANCE = 1;
|
|
44548
44642
|
|
|
44549
|
-
const css$
|
|
44643
|
+
const css$U = /* css */ `
|
|
44550
44644
|
[data-navi-overflow-x] {
|
|
44551
44645
|
outline: 2px dashed #e74c3c;
|
|
44552
44646
|
outline-offset: -2px;
|
|
@@ -44570,7 +44664,7 @@ const detectHorizontalOverflow = ({
|
|
|
44570
44664
|
let styleEl = null;
|
|
44571
44665
|
if (highlight) {
|
|
44572
44666
|
styleEl = document.createElement("style");
|
|
44573
|
-
styleEl.textContent = css$
|
|
44667
|
+
styleEl.textContent = css$U;
|
|
44574
44668
|
document.head.appendChild(styleEl);
|
|
44575
44669
|
}
|
|
44576
44670
|
|
|
@@ -44726,7 +44820,7 @@ const useFocusGroup = (
|
|
|
44726
44820
|
|
|
44727
44821
|
installImportMetaCssBuild(import.meta);
|
|
44728
44822
|
const rightArrowPath = "M680-480L360-160l-80-80 240-240-240-240 80-80 320 320z";
|
|
44729
|
-
const css$
|
|
44823
|
+
const css$T = /* css */`.navi_summary_marker {
|
|
44730
44824
|
flex-shrink: 0;
|
|
44731
44825
|
width: 1em;
|
|
44732
44826
|
height: 1em;
|
|
@@ -44818,7 +44912,7 @@ const SummaryMarker = ({
|
|
|
44818
44912
|
loading,
|
|
44819
44913
|
openDirection = "down"
|
|
44820
44914
|
}) => {
|
|
44821
|
-
import.meta.css = [css$
|
|
44915
|
+
import.meta.css = [css$T, "@jsenv/navi/src/control/details/summary_marker.jsx"];
|
|
44822
44916
|
const showLoading = useDebounceTrue(loading, 300);
|
|
44823
44917
|
return jsx("span", {
|
|
44824
44918
|
className: "navi_summary_marker",
|
|
@@ -44864,7 +44958,7 @@ const SummaryMarker = ({
|
|
|
44864
44958
|
};
|
|
44865
44959
|
|
|
44866
44960
|
installImportMetaCssBuild(import.meta);
|
|
44867
|
-
const css$
|
|
44961
|
+
const css$S = /* css */`.navi_details {
|
|
44868
44962
|
z-index: 1;
|
|
44869
44963
|
flex-direction: column;
|
|
44870
44964
|
flex-shrink: 0;
|
|
@@ -44909,7 +45003,7 @@ const Details = props => {
|
|
|
44909
45003
|
return details;
|
|
44910
45004
|
};
|
|
44911
45005
|
const DetailsField = props => {
|
|
44912
|
-
import.meta.css = [css$
|
|
45006
|
+
import.meta.css = [css$S, "@jsenv/navi/src/control/details/details.jsx"];
|
|
44913
45007
|
const {
|
|
44914
45008
|
ref,
|
|
44915
45009
|
persists,
|
|
@@ -45825,6 +45919,7 @@ const useOpenPropsEffectOnOpenController = (
|
|
|
45825
45919
|
name = "popup",
|
|
45826
45920
|
) => {
|
|
45827
45921
|
const { signal, value, defaultOpen, navState } = props;
|
|
45922
|
+
warnSignalAsState(props, name, ["open", "defaultOpen"]);
|
|
45828
45923
|
const { id: navStateId, type: navStateType } = resolveNavStateProp(
|
|
45829
45924
|
navState,
|
|
45830
45925
|
props.id,
|
|
@@ -46709,7 +46804,7 @@ const usePopupContentMount = (
|
|
|
46709
46804
|
};
|
|
46710
46805
|
|
|
46711
46806
|
installImportMetaCssBuild(import.meta);
|
|
46712
|
-
const css$
|
|
46807
|
+
const css$R = /* css */`.navi_expandable {
|
|
46713
46808
|
flex-direction: column;
|
|
46714
46809
|
flex-shrink: 0;
|
|
46715
46810
|
display: flex;
|
|
@@ -46939,7 +47034,7 @@ const useExpandableContext = partName => {
|
|
|
46939
47034
|
* builds the content ahead of the click.
|
|
46940
47035
|
*/
|
|
46941
47036
|
const Expandable = props => {
|
|
46942
|
-
import.meta.css = [css$
|
|
47037
|
+
import.meta.css = [css$R, "@jsenv/navi/src/control/expandable/expandable.jsx"];
|
|
46943
47038
|
/* The open props are read from `props` by the open controller below; they
|
|
46944
47039
|
are named here only to keep them out of `rest`, and so out of the DOM. */
|
|
46945
47040
|
/* eslint-disable no-unused-vars */
|
|
@@ -47626,49 +47721,6 @@ const shallowDiffers = (a, b) => {
|
|
|
47626
47721
|
return false;
|
|
47627
47722
|
};
|
|
47628
47723
|
|
|
47629
|
-
const ButtonRouteResolver = props => {
|
|
47630
|
-
const Next = useNextResolver();
|
|
47631
|
-
if (props.route) {
|
|
47632
|
-
return jsx(ButtonWithRoute, {
|
|
47633
|
-
...props
|
|
47634
|
-
});
|
|
47635
|
-
}
|
|
47636
|
-
return jsx(Next, {
|
|
47637
|
-
...props
|
|
47638
|
-
});
|
|
47639
|
-
};
|
|
47640
|
-
const ButtonWithRoute = props => {
|
|
47641
|
-
const Next = useNextResolver();
|
|
47642
|
-
const {
|
|
47643
|
-
route,
|
|
47644
|
-
routeParams,
|
|
47645
|
-
children,
|
|
47646
|
-
pseudoState,
|
|
47647
|
-
...rest
|
|
47648
|
-
} = props;
|
|
47649
|
-
{
|
|
47650
|
-
assertRoute(route);
|
|
47651
|
-
}
|
|
47652
|
-
const url = route.buildUrl(routeParams);
|
|
47653
|
-
const {
|
|
47654
|
-
matching
|
|
47655
|
-
} = useRouteStatus(route);
|
|
47656
|
-
const paramsAreMatching = route.matchesParams(routeParams);
|
|
47657
|
-
const linkMatching = matching && paramsAreMatching;
|
|
47658
|
-
|
|
47659
|
-
// Merged into whatever the caller already holds: a button can be forced into
|
|
47660
|
-
// a state for a demo and still learn its own current-ness from its route.
|
|
47661
|
-
return jsx(Next, {
|
|
47662
|
-
href: url,
|
|
47663
|
-
pseudoState: {
|
|
47664
|
-
...pseudoState,
|
|
47665
|
-
":-navi-href-current": linkMatching
|
|
47666
|
-
},
|
|
47667
|
-
...rest,
|
|
47668
|
-
children: children || route.buildRelativeUrl(routeParams)
|
|
47669
|
-
});
|
|
47670
|
-
};
|
|
47671
|
-
|
|
47672
47724
|
const LIGHT_ACCENT_ATTRIBUTE = "data-accent-light";
|
|
47673
47725
|
const VERY_LIGHT_ACCENT_ATTRIBUTE = "data-accent-very-light";
|
|
47674
47726
|
const DARK_CONTRAST_ATTRIBUTE = "data-accent-needs-dark-fg";
|
|
@@ -47769,7 +47821,7 @@ const useAccentColorAttributes = (
|
|
|
47769
47821
|
};
|
|
47770
47822
|
|
|
47771
47823
|
installImportMetaCssBuild(import.meta);
|
|
47772
|
-
const css$
|
|
47824
|
+
const css$Q = /* css */`@layer navi {
|
|
47773
47825
|
.navi_button {
|
|
47774
47826
|
--button-border-radius: var(--navi-control-border-radius);
|
|
47775
47827
|
--button-border-width: var(--navi-control-border-width);
|
|
@@ -48070,7 +48122,7 @@ a.navi_button {
|
|
|
48070
48122
|
}
|
|
48071
48123
|
`;
|
|
48072
48124
|
const ButtonUI = props => {
|
|
48073
|
-
import.meta.css = [css$
|
|
48125
|
+
import.meta.css = [css$Q, "@jsenv/navi/src/control/input/button_ui.jsx"];
|
|
48074
48126
|
const {
|
|
48075
48127
|
ref,
|
|
48076
48128
|
// href/link
|
|
@@ -48285,6 +48337,189 @@ const ButtonShadow = () => {
|
|
|
48285
48337
|
};
|
|
48286
48338
|
markAsOutsideTextFlow(ButtonShadow);
|
|
48287
48339
|
|
|
48340
|
+
installImportMetaCssBuild(import.meta);
|
|
48341
|
+
const css$P = /* css */`.navi_button_confirm {
|
|
48342
|
+
align-items: center;
|
|
48343
|
+
gap: var(--navi-s);
|
|
48344
|
+
flex-wrap: wrap;
|
|
48345
|
+
display: inline-flex;
|
|
48346
|
+
}
|
|
48347
|
+
`;
|
|
48348
|
+
const ButtonConfirmResolver = props => {
|
|
48349
|
+
const Next = useNextResolver();
|
|
48350
|
+
if (!props.confirm) {
|
|
48351
|
+
return jsx(Next, {
|
|
48352
|
+
...props
|
|
48353
|
+
});
|
|
48354
|
+
}
|
|
48355
|
+
return jsx(ButtonConfirm, {
|
|
48356
|
+
...props,
|
|
48357
|
+
Next: Next
|
|
48358
|
+
});
|
|
48359
|
+
};
|
|
48360
|
+
const ButtonConfirm = ({
|
|
48361
|
+
Next,
|
|
48362
|
+
confirm,
|
|
48363
|
+
confirmLabel = naviI18n("button.confirm"),
|
|
48364
|
+
cancelLabel = naviI18n("button.cancel"),
|
|
48365
|
+
confirmTestId,
|
|
48366
|
+
cancelTestId,
|
|
48367
|
+
...props
|
|
48368
|
+
}) => {
|
|
48369
|
+
import.meta.css = [css$P, "@jsenv/navi/src/control/input/button_confirm.jsx"];
|
|
48370
|
+
const [asking, setAsking] = useState(false);
|
|
48371
|
+
const askingRef = useRef(null);
|
|
48372
|
+
const cancelRef = useRef(null);
|
|
48373
|
+
// Whether going back to the resting label owes the focus a place to land: a
|
|
48374
|
+
// press on "Annuler" or an Escape does, a focus that already left does not —
|
|
48375
|
+
// it is elsewhere by then, and pulling it back would take it from whatever
|
|
48376
|
+
// the person moved to.
|
|
48377
|
+
const restoreFocusRef = useRef(false);
|
|
48378
|
+
const questionId = `${useId()}_question`;
|
|
48379
|
+
const cancel = restoreFocus => {
|
|
48380
|
+
restoreFocusRef.current = restoreFocus;
|
|
48381
|
+
setAsking(false);
|
|
48382
|
+
};
|
|
48383
|
+
useLayoutEffect(() => {
|
|
48384
|
+
if (asking) {
|
|
48385
|
+
// The press was aimed at this button, and the button that answers now is
|
|
48386
|
+
// where it should land: navi's own ladder inside the question, which
|
|
48387
|
+
// finds "Confirmer" (first in the row) unless the caller marked
|
|
48388
|
+
// something else.
|
|
48389
|
+
const found = findFocusTarget(askingRef.current, {
|
|
48390
|
+
restoreMayClaim: true
|
|
48391
|
+
});
|
|
48392
|
+
if (found) {
|
|
48393
|
+
moveFocusTo(found.target);
|
|
48394
|
+
}
|
|
48395
|
+
return;
|
|
48396
|
+
}
|
|
48397
|
+
if (restoreFocusRef.current) {
|
|
48398
|
+
restoreFocusRef.current = false;
|
|
48399
|
+
// The answer may have taken the button with it — a row deleting itself
|
|
48400
|
+
// is the whole point of asking — and there is then nothing to give the
|
|
48401
|
+
// focus back to.
|
|
48402
|
+
const buttonEl = props.ref.current;
|
|
48403
|
+
if (buttonEl) {
|
|
48404
|
+
moveFocusTo(buttonEl);
|
|
48405
|
+
}
|
|
48406
|
+
}
|
|
48407
|
+
}, [asking]);
|
|
48408
|
+
if (!asking) {
|
|
48409
|
+
return jsx(Next, {
|
|
48410
|
+
...props,
|
|
48411
|
+
// The first press is the question, not the act — so nothing that acts
|
|
48412
|
+
// is handed to the resolvers below. `type` with them: a submit button
|
|
48413
|
+
// would be given `--navi-send` back.
|
|
48414
|
+
type: "button",
|
|
48415
|
+
action: undefined,
|
|
48416
|
+
command: undefined,
|
|
48417
|
+
href: undefined,
|
|
48418
|
+
route: undefined,
|
|
48419
|
+
onClick: () => {
|
|
48420
|
+
setAsking(true);
|
|
48421
|
+
}
|
|
48422
|
+
});
|
|
48423
|
+
}
|
|
48424
|
+
const {
|
|
48425
|
+
action,
|
|
48426
|
+
onActionEnd,
|
|
48427
|
+
onClick
|
|
48428
|
+
} = props;
|
|
48429
|
+
return jsxs("span", {
|
|
48430
|
+
className: "navi_button_confirm",
|
|
48431
|
+
ref: askingRef,
|
|
48432
|
+
onKeyDown: e => {
|
|
48433
|
+
if (e.key === "Escape") {
|
|
48434
|
+
cancel(true);
|
|
48435
|
+
}
|
|
48436
|
+
},
|
|
48437
|
+
onFocusOut: e => {
|
|
48438
|
+
if (!askingRef.current.contains(e.relatedTarget)) {
|
|
48439
|
+
cancel(false);
|
|
48440
|
+
}
|
|
48441
|
+
},
|
|
48442
|
+
children: [jsx("span", {
|
|
48443
|
+
id: questionId,
|
|
48444
|
+
children: confirm === true ? naviI18n("confirm.message") : confirm
|
|
48445
|
+
}), jsx(Next, {
|
|
48446
|
+
...props,
|
|
48447
|
+
"aria-describedby": questionId
|
|
48448
|
+
// The button confirming is the button that was pressed: it keeps the
|
|
48449
|
+
// caller's own testid unless the question names one of its own.
|
|
48450
|
+
,
|
|
48451
|
+
"data-testid": confirmTestId || props["data-testid"],
|
|
48452
|
+
onClick: e => {
|
|
48453
|
+
onClick?.(e);
|
|
48454
|
+
if (!action) {
|
|
48455
|
+
// A command runs on the press itself: there is nothing to wait for,
|
|
48456
|
+
// the question is answered as soon as it is pressed.
|
|
48457
|
+
cancel(true);
|
|
48458
|
+
}
|
|
48459
|
+
}
|
|
48460
|
+
// A run that failed keeps the question up, with its callout on the
|
|
48461
|
+
// button that raised it: the retry is then one press away.
|
|
48462
|
+
,
|
|
48463
|
+
onActionEnd: (data, e) => {
|
|
48464
|
+
onActionEnd?.(data, e);
|
|
48465
|
+
cancel(true);
|
|
48466
|
+
},
|
|
48467
|
+
children: confirmLabel
|
|
48468
|
+
}), jsx(ButtonUI, {
|
|
48469
|
+
ref: cancelRef,
|
|
48470
|
+
type: "button",
|
|
48471
|
+
"data-testid": cancelTestId,
|
|
48472
|
+
onClick: () => {
|
|
48473
|
+
cancel(true);
|
|
48474
|
+
},
|
|
48475
|
+
children: cancelLabel
|
|
48476
|
+
})]
|
|
48477
|
+
});
|
|
48478
|
+
};
|
|
48479
|
+
|
|
48480
|
+
const ButtonRouteResolver = props => {
|
|
48481
|
+
const Next = useNextResolver();
|
|
48482
|
+
if (props.route) {
|
|
48483
|
+
return jsx(ButtonWithRoute, {
|
|
48484
|
+
...props
|
|
48485
|
+
});
|
|
48486
|
+
}
|
|
48487
|
+
return jsx(Next, {
|
|
48488
|
+
...props
|
|
48489
|
+
});
|
|
48490
|
+
};
|
|
48491
|
+
const ButtonWithRoute = props => {
|
|
48492
|
+
const Next = useNextResolver();
|
|
48493
|
+
const {
|
|
48494
|
+
route,
|
|
48495
|
+
routeParams,
|
|
48496
|
+
children,
|
|
48497
|
+
pseudoState,
|
|
48498
|
+
...rest
|
|
48499
|
+
} = props;
|
|
48500
|
+
{
|
|
48501
|
+
assertRoute(route);
|
|
48502
|
+
}
|
|
48503
|
+
const url = route.buildUrl(routeParams);
|
|
48504
|
+
const {
|
|
48505
|
+
matching
|
|
48506
|
+
} = useRouteStatus(route);
|
|
48507
|
+
const paramsAreMatching = route.matchesParams(routeParams);
|
|
48508
|
+
const linkMatching = matching && paramsAreMatching;
|
|
48509
|
+
|
|
48510
|
+
// Merged into whatever the caller already holds: a button can be forced into
|
|
48511
|
+
// a state for a demo and still learn its own current-ness from its route.
|
|
48512
|
+
return jsx(Next, {
|
|
48513
|
+
href: url,
|
|
48514
|
+
pseudoState: {
|
|
48515
|
+
...pseudoState,
|
|
48516
|
+
":-navi-href-current": linkMatching
|
|
48517
|
+
},
|
|
48518
|
+
...rest,
|
|
48519
|
+
children: children || route.buildRelativeUrl(routeParams)
|
|
48520
|
+
});
|
|
48521
|
+
};
|
|
48522
|
+
|
|
48288
48523
|
const ButtonFirstResolver = props => {
|
|
48289
48524
|
const Next = useNextResolver();
|
|
48290
48525
|
const defaultRef = useRef(null);
|
|
@@ -48446,6 +48681,20 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
48446
48681
|
* more: the work may already have happened on the other side (see
|
|
48447
48682
|
* docs/actions.md#aborting-saves-resources-it-does-not-undo), so say it only
|
|
48448
48683
|
* where the screen can be re-opened on what is actually there.
|
|
48684
|
+
* @param {boolean|import("ignore:preact").ComponentChildren} [confirm] Ask before
|
|
48685
|
+
* doing it, in the place the button stands: the first press replaces the
|
|
48686
|
+
* button with this question and a "Confirmer"/"Annuler" pair, the second one
|
|
48687
|
+
* does what the button was for. `true` asks navi's default question. The
|
|
48688
|
+
* press stays under the finger and there is nothing to dismiss — "Annuler",
|
|
48689
|
+
* Escape, or the focus leaving puts the button back. Use `<Picker
|
|
48690
|
+
* type="confirm">` instead when the question is long enough to want a popup,
|
|
48691
|
+
* or when the row has no space for it.
|
|
48692
|
+
* @param {import("ignore:preact").ComponentChildren} [confirmLabel] Label of the
|
|
48693
|
+
* second press.
|
|
48694
|
+
* @param {import("ignore:preact").ComponentChildren} [cancelLabel] Label of the way
|
|
48695
|
+
* out of the question.
|
|
48696
|
+
* @param {string} [confirmTestId] `data-testid` of the second press.
|
|
48697
|
+
* @param {string} [cancelTestId] `data-testid` of the way out.
|
|
48449
48698
|
* @param {string} [contentDisplay] The display of the frame the button draws
|
|
48450
48699
|
* around its children. It follows the button's own by default — its display
|
|
48451
48700
|
* and, a display alone saying nothing about direction, the rest of its flow
|
|
@@ -48460,7 +48709,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
48460
48709
|
* (it wears it itself) and loses the shrink under the finger, which has
|
|
48461
48710
|
* nothing left to scale but the interactive area itself.
|
|
48462
48711
|
*/
|
|
48463
|
-
const Button = createComponentResolver([ButtonFirstResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonUI]);
|
|
48712
|
+
const Button = createComponentResolver([ButtonFirstResolver, ButtonConfirmResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonUI]);
|
|
48464
48713
|
|
|
48465
48714
|
installImportMetaCssBuild(import.meta);
|
|
48466
48715
|
const css$O = /* css */`.navi_control_swap {
|
|
@@ -56505,13 +56754,7 @@ const TimeDate = ({
|
|
|
56505
56754
|
})
|
|
56506
56755
|
});
|
|
56507
56756
|
}
|
|
56508
|
-
const date = toDate(children
|
|
56509
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
56510
|
-
const d = new Date(`${value}T00:00:00`);
|
|
56511
|
-
return isNaN(d.getTime()) ? null : d;
|
|
56512
|
-
}
|
|
56513
|
-
return null;
|
|
56514
|
-
});
|
|
56757
|
+
const date = toDate(children);
|
|
56515
56758
|
if (!date) {
|
|
56516
56759
|
return jsx(TimeText, {
|
|
56517
56760
|
...props,
|
|
@@ -73491,6 +73734,26 @@ const css$r = /* css */`.navi_checkbox_group {
|
|
|
73491
73734
|
`;
|
|
73492
73735
|
|
|
73493
73736
|
/**
|
|
73737
|
+
* A set of checkboxes answering one question, holding the array of the values
|
|
73738
|
+
* that are checked.
|
|
73739
|
+
*
|
|
73740
|
+
* It renders a `<fieldset>` and hands its `name` down, so the boxes inside are
|
|
73741
|
+
* plain `<Input type="checkbox" value="…">` with nothing else to declare; each
|
|
73742
|
+
* value that is checked appears in the array, in the order the boxes are
|
|
73743
|
+
* written. What it holds is said the way every control says it — `value` /
|
|
73744
|
+
* `defaultValue` (an array), or a bound `signal` — and inside a `<Form>` the
|
|
73745
|
+
* group is one entry of the submitted params, an array.
|
|
73746
|
+
*
|
|
73747
|
+
* ```jsx
|
|
73748
|
+
* <CheckboxGroup name="permissions" defaultValue={["places.moderate"]} action={save}>
|
|
73749
|
+
* <legend>Permissions</legend>
|
|
73750
|
+
* <Field as="label"><Input type="checkbox" value="places.moderate" /> Modérer les lieux</Field>
|
|
73751
|
+
* <Field as="label"><Input type="checkbox" value="games.list" /> Lister les parties</Field>
|
|
73752
|
+
* </CheckboxGroup>
|
|
73753
|
+
* ```
|
|
73754
|
+
*
|
|
73755
|
+
* See `<RadioGroup>` for the same shape holding a single value.
|
|
73756
|
+
*
|
|
73494
73757
|
* @type {import("ignore:preact").FunctionComponent<{
|
|
73495
73758
|
* maxLength?: number,
|
|
73496
73759
|
* maxLengthGuard?: number,
|
|
@@ -74212,6 +74475,27 @@ const css$p = /* css */`.navi_radio_group {
|
|
|
74212
74475
|
}
|
|
74213
74476
|
}
|
|
74214
74477
|
`;
|
|
74478
|
+
|
|
74479
|
+
/**
|
|
74480
|
+
* A set of radios answering one question, holding the value of the one that is
|
|
74481
|
+
* checked.
|
|
74482
|
+
*
|
|
74483
|
+
* It renders a `<fieldset>` and hands its `name` down, so the radios inside are
|
|
74484
|
+
* plain `<Input type="radio" value="…">` with nothing else to declare — sharing
|
|
74485
|
+
* a name is what makes them exclusive. What it holds is said the way every
|
|
74486
|
+
* control says it — `value` / `defaultValue`, or a bound `signal` — and inside a
|
|
74487
|
+
* `<Form>` the group is one entry of the submitted params.
|
|
74488
|
+
*
|
|
74489
|
+
* ```jsx
|
|
74490
|
+
* <RadioGroup name="role" defaultValue="viewer" action={save}>
|
|
74491
|
+
* <legend>Rôle</legend>
|
|
74492
|
+
* <Field as="label"><Input type="radio" value="viewer" /> Lecteur</Field>
|
|
74493
|
+
* <Field as="label"><Input type="radio" value="admin" /> Administrateur</Field>
|
|
74494
|
+
* </RadioGroup>
|
|
74495
|
+
* ```
|
|
74496
|
+
*
|
|
74497
|
+
* See `<CheckboxGroup>` for the same shape holding an array.
|
|
74498
|
+
*/
|
|
74215
74499
|
const RadioGroup = props => {
|
|
74216
74500
|
const refDefault = useRef(null);
|
|
74217
74501
|
props.ref = props.ref || refDefault;
|