@jsenv/navi 0.29.230 → 0.29.231
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 +347 -96
- package/dist/dev/jsenv_navi.js.map +15 -13
- 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 +322 -93
- package/dist/jsenv_navi.js.map +15 -13
- 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/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
|
}
|
|
@@ -5749,7 +5749,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
5749
5749
|
* (see calloutTemplate in callout.js).
|
|
5750
5750
|
*/
|
|
5751
5751
|
|
|
5752
|
-
const css$
|
|
5752
|
+
const css$14 = /* css */`.navi_callout_status_icon {
|
|
5753
5753
|
--x-callout-status-icon-color: var(--navi-callout-neutral-color);
|
|
5754
5754
|
box-sizing: border-box;
|
|
5755
5755
|
aspect-ratio: 1;
|
|
@@ -5820,7 +5820,7 @@ const CalloutStatusIcon = ({
|
|
|
5820
5820
|
status = "info",
|
|
5821
5821
|
shape = "square"
|
|
5822
5822
|
}) => {
|
|
5823
|
-
import.meta.css = [css$
|
|
5823
|
+
import.meta.css = [css$14, "@jsenv/navi/src/control/rules/callout/callout_status_icon.jsx"];
|
|
5824
5824
|
return jsx("span", {
|
|
5825
5825
|
className: "navi_callout_status_icon",
|
|
5826
5826
|
"data-status": status === "none" ? undefined : status,
|
|
@@ -5842,7 +5842,7 @@ installImportMetaCssBuild(import.meta);
|
|
|
5842
5842
|
// Unique for the page's lifetime: a caller may write the id in a commandfor.
|
|
5843
5843
|
let calloutCount = 0;
|
|
5844
5844
|
|
|
5845
|
-
const css$
|
|
5845
|
+
const css$13 = /* css */ `
|
|
5846
5846
|
@layer navi {
|
|
5847
5847
|
.navi_callout {
|
|
5848
5848
|
/* A callout is parented to what it explains, so it inherits from it — and
|
|
@@ -6190,7 +6190,7 @@ const openCallout = (
|
|
|
6190
6190
|
debug = () => {},
|
|
6191
6191
|
} = {},
|
|
6192
6192
|
) => {
|
|
6193
|
-
import.meta.css = [css$
|
|
6193
|
+
import.meta.css = [css$13, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
6194
6194
|
if (debug === true) {
|
|
6195
6195
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
6196
6196
|
}
|
|
@@ -18238,8 +18238,12 @@ const VISUAL_PROPS = {
|
|
|
18238
18238
|
outlineWidth: PASS_THROUGH,
|
|
18239
18239
|
boxDecorationBreak: PASS_THROUGH,
|
|
18240
18240
|
boxShadow: PASS_THROUGH,
|
|
18241
|
-
background:
|
|
18242
|
-
|
|
18241
|
+
background: (value) => {
|
|
18242
|
+
return { background: resolveSurfaceKeyword(value) };
|
|
18243
|
+
},
|
|
18244
|
+
backgroundColor: (value) => {
|
|
18245
|
+
return { backgroundColor: resolveSurfaceKeyword(value) };
|
|
18246
|
+
},
|
|
18243
18247
|
backgroundImage: PASS_THROUGH,
|
|
18244
18248
|
backgroundSize: PASS_THROUGH,
|
|
18245
18249
|
border: PASS_THROUGH,
|
|
@@ -18604,6 +18608,18 @@ const resolveColorKeyword = (value) => {
|
|
|
18604
18608
|
return COLOR_KEYWORD_MAP[value] || value;
|
|
18605
18609
|
};
|
|
18606
18610
|
|
|
18611
|
+
// The two planes an app paints: the paper its content sits on, and the frame
|
|
18612
|
+
// around it (see navi_css_vars.js). Named rather than derived at each call
|
|
18613
|
+
// site, so a top bar and a side menu are the same plane without agreeing on a
|
|
18614
|
+
// formula.
|
|
18615
|
+
const SURFACE_KEYWORD_MAP = {
|
|
18616
|
+
surface: "var(--navi-surface-color)",
|
|
18617
|
+
chrome: "var(--navi-chrome-color)",
|
|
18618
|
+
};
|
|
18619
|
+
const resolveSurfaceKeyword = (value) => {
|
|
18620
|
+
return SURFACE_KEYWORD_MAP[value] || value;
|
|
18621
|
+
};
|
|
18622
|
+
|
|
18607
18623
|
const DEFAULT_DISPLAY_BY_TAG_NAME = {
|
|
18608
18624
|
"inline": new Set([
|
|
18609
18625
|
"a",
|
|
@@ -20505,6 +20521,16 @@ const PSEUDO_STATE_CHILD_PROP_SET = new Set(["tabIndex", "tabindex"]);
|
|
|
20505
20521
|
* children?: import("ignore:preact").ComponentChildren,
|
|
20506
20522
|
* [key: string]: any,
|
|
20507
20523
|
* }>}
|
|
20524
|
+
* @param {string} [background] Any CSS background, plus two keywords for the
|
|
20525
|
+
* planes an app paints: `"surface"` is the paper content sits on,
|
|
20526
|
+
* `"chrome"` the frame around a screen — a top bar, a side menu, a toolbar.
|
|
20527
|
+
* Both follow the theme, so a bar stays a bar in dark mode
|
|
20528
|
+
* (`--navi-surface-color` / `--navi-chrome-color`). `backgroundColor` takes
|
|
20529
|
+
* the same two.
|
|
20530
|
+
* @param {string} [color] Any CSS color, plus the five ink keywords —
|
|
20531
|
+
* `"primary"`, `"secondary"`, `"emphasis"`, `"discrete"`, `"hint"` — which
|
|
20532
|
+
* say how loud a text is rather than which color it is (see
|
|
20533
|
+
* docs/typography.md).
|
|
20508
20534
|
* @param {object} [interactions] What this box answers, by interaction name
|
|
20509
20535
|
* (see docs/interactions.md). A plain box has no wiring of its own: it does
|
|
20510
20536
|
* nothing with `action` (that is a control's prop), so a click on it is
|
|
@@ -22408,6 +22434,20 @@ const debug$2 = (...args) => {
|
|
|
22408
22434
|
// Base URL management
|
|
22409
22435
|
let baseFileUrl;
|
|
22410
22436
|
let baseUrl;
|
|
22437
|
+
/**
|
|
22438
|
+
* Where this document's addresses start, for a document that is not at the
|
|
22439
|
+
* root: a panel served from "/admin/admin.html" declares
|
|
22440
|
+
* `setBaseUrl("/admin/admin.html")` and then writes its routes as "/places",
|
|
22441
|
+
* "/places/:placeId". The url "/admin/places/42" matches, and `buildUrl` puts
|
|
22442
|
+
* the prefix back.
|
|
22443
|
+
*
|
|
22444
|
+
* Call it in the routes module, above the `route()` calls it governs — they
|
|
22445
|
+
* read the base url as they are created, so an entry point calling it after
|
|
22446
|
+
* importing them is too late. The server must serve the document for every
|
|
22447
|
+
* address below it (see docs/navigation.md).
|
|
22448
|
+
*
|
|
22449
|
+
* @param {string} value - the document's own url, absolute or relative.
|
|
22450
|
+
*/
|
|
22411
22451
|
const setBaseUrl = (value) => {
|
|
22412
22452
|
baseFileUrl = new URL(
|
|
22413
22453
|
value,
|
|
@@ -28120,7 +28160,7 @@ const ROUTE_TRAVEL_ATTRIBUTE = "data-navi-route-travel";
|
|
|
28120
28160
|
// the root pictures must NOT move (they carry the whole viewport, blank bands
|
|
28121
28161
|
// included).
|
|
28122
28162
|
|
|
28123
|
-
const css$
|
|
28163
|
+
const css$12 = /* css */`:root[data-navi-route-transition] [data-navi-route-transition-area] {
|
|
28124
28164
|
view-transition-name: navi-route-transition;
|
|
28125
28165
|
}
|
|
28126
28166
|
|
|
@@ -28441,7 +28481,7 @@ const RouteTransitionArea = ({
|
|
|
28441
28481
|
children,
|
|
28442
28482
|
...rest
|
|
28443
28483
|
}) => {
|
|
28444
|
-
import.meta.css = [css$
|
|
28484
|
+
import.meta.css = [css$12, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
28445
28485
|
installTransitionWindowCss();
|
|
28446
28486
|
const props = {
|
|
28447
28487
|
...rest,
|
|
@@ -28511,7 +28551,7 @@ const RouteTransitionArea = ({
|
|
|
28511
28551
|
* @returns {() => void} remove this relation.
|
|
28512
28552
|
*/
|
|
28513
28553
|
const defineRouteTransition = (from, to, transition) => {
|
|
28514
|
-
import.meta.css = [css$
|
|
28554
|
+
import.meta.css = [css$12, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
28515
28555
|
installTransitionWindowCss();
|
|
28516
28556
|
if (!to) {
|
|
28517
28557
|
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.`);
|
|
@@ -28552,7 +28592,7 @@ const defineRouteTransition = (from, to, transition) => {
|
|
|
28552
28592
|
* @returns {() => void} remove this default.
|
|
28553
28593
|
*/
|
|
28554
28594
|
const defineRouteDefaultTransition = transition => {
|
|
28555
|
-
import.meta.css = [css$
|
|
28595
|
+
import.meta.css = [css$12, "@jsenv/navi/src/nav/route_transition.jsx"];
|
|
28556
28596
|
installTransitionWindowCss();
|
|
28557
28597
|
const value = normalizeTransition(transition);
|
|
28558
28598
|
defaultTransition = value;
|
|
@@ -29374,7 +29414,7 @@ const DRAGGED_ATTRIBUTE = "data-navi-route-travel-dragged";
|
|
|
29374
29414
|
const TURNED_ATTRIBUTE = "data-navi-route-travel-turned";
|
|
29375
29415
|
// The name the box wears while it travels, and only then (see nameForTravel).
|
|
29376
29416
|
const TRAVEL_NAME = "navi-route-travel";
|
|
29377
|
-
const css$
|
|
29417
|
+
const css$11 = /* css */`.navi_route_travel {
|
|
29378
29418
|
position: relative;
|
|
29379
29419
|
|
|
29380
29420
|
&[data-travel-by-drag="x"] {
|
|
@@ -29663,7 +29703,7 @@ const RouteTravel = ({
|
|
|
29663
29703
|
children,
|
|
29664
29704
|
...rest
|
|
29665
29705
|
}) => {
|
|
29666
|
-
import.meta.css = [css$
|
|
29706
|
+
import.meta.css = [css$11, "@jsenv/navi/src/nav/route_travel.jsx"];
|
|
29667
29707
|
installTransitionWindowCss();
|
|
29668
29708
|
const elementRef = useRef();
|
|
29669
29709
|
const gestureRef = useRef(null);
|
|
@@ -39227,7 +39267,7 @@ const setupNetworkMonitoring = () => {
|
|
|
39227
39267
|
setupNetworkMonitoring();
|
|
39228
39268
|
|
|
39229
39269
|
installImportMetaCssBuild(import.meta);
|
|
39230
|
-
const css
|
|
39270
|
+
const css$10 = /* css */`.navi_loading_indicator_fluid_container {
|
|
39231
39271
|
border-radius: inherit;
|
|
39232
39272
|
opacity: 1;
|
|
39233
39273
|
width: 100%;
|
|
@@ -39258,7 +39298,7 @@ const LoadingIndicatorFluid = ({
|
|
|
39258
39298
|
visuallyHidden,
|
|
39259
39299
|
...rest
|
|
39260
39300
|
}) => {
|
|
39261
|
-
import.meta.css = [css
|
|
39301
|
+
import.meta.css = [css$10, "@jsenv/navi/src/graphic/loading/loading_indicator_fluid.jsx"];
|
|
39262
39302
|
const ref = useRef(null);
|
|
39263
39303
|
// The container dimensions can be deduced from the ref itself as the indicator is absolute inset 0
|
|
39264
39304
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
@@ -39464,7 +39504,7 @@ const LoadingRectangleSvg = ({
|
|
|
39464
39504
|
};
|
|
39465
39505
|
|
|
39466
39506
|
installImportMetaCssBuild(import.meta);
|
|
39467
|
-
const css
|
|
39507
|
+
const css$$ = /* css */`.navi_loading_outline_wrapper {
|
|
39468
39508
|
top: max(var(--loading-outline-min-inset, -100vh),
|
|
39469
39509
|
var(--loading-rectangle-top, 0px));
|
|
39470
39510
|
right: max(var(--loading-outline-min-inset, -100vh),
|
|
@@ -39485,7 +39525,7 @@ const css$_ = /* css */`.navi_loading_outline_wrapper {
|
|
|
39485
39525
|
}
|
|
39486
39526
|
`;
|
|
39487
39527
|
const LoadingOutline = props => {
|
|
39488
|
-
import.meta.css = [css
|
|
39528
|
+
import.meta.css = [css$$, "@jsenv/navi/src/graphic/loading/loading_outline.jsx"];
|
|
39489
39529
|
if (props.containerRef) {
|
|
39490
39530
|
const container = props.containerRef.current;
|
|
39491
39531
|
if (!container) {
|
|
@@ -39620,7 +39660,7 @@ const LoadingOutlineWithPortal = props => {
|
|
|
39620
39660
|
};
|
|
39621
39661
|
|
|
39622
39662
|
installImportMetaCssBuild(import.meta);
|
|
39623
|
-
const css$
|
|
39663
|
+
const css$_ = /* css */`.navi_text_anchor {
|
|
39624
39664
|
vertical-align: baseline;
|
|
39625
39665
|
user-select: none;
|
|
39626
39666
|
overflow: hidden;
|
|
@@ -39658,7 +39698,7 @@ const TextAnchor = ({
|
|
|
39658
39698
|
textSize,
|
|
39659
39699
|
lineLayout
|
|
39660
39700
|
}) => {
|
|
39661
|
-
import.meta.css = [css$
|
|
39701
|
+
import.meta.css = [css$_, "@jsenv/navi/src/text/text_anchor.jsx"];
|
|
39662
39702
|
const anchorRef = useRef();
|
|
39663
39703
|
|
|
39664
39704
|
// Plain useLayoutEffect would also fire while an ancestor dialog/popover
|
|
@@ -39886,7 +39926,7 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
39886
39926
|
};
|
|
39887
39927
|
|
|
39888
39928
|
installImportMetaCssBuild(import.meta);
|
|
39889
|
-
const css$
|
|
39929
|
+
const css$Z = /* css */`@layer navi {
|
|
39890
39930
|
.navi_text {
|
|
39891
39931
|
&[data-skeleton] {
|
|
39892
39932
|
border-radius: .2em;
|
|
@@ -40455,7 +40495,7 @@ const TextShrinkWrap = props => {
|
|
|
40455
40495
|
});
|
|
40456
40496
|
};
|
|
40457
40497
|
const TextUI = props => {
|
|
40458
|
-
import.meta.css = [css$
|
|
40498
|
+
import.meta.css = [css$Z, "@jsenv/navi/src/text/text.jsx"];
|
|
40459
40499
|
let {
|
|
40460
40500
|
ref,
|
|
40461
40501
|
spacing,
|
|
@@ -40679,7 +40719,7 @@ const Icon = ({
|
|
|
40679
40719
|
fillLine,
|
|
40680
40720
|
...props
|
|
40681
40721
|
}) => {
|
|
40682
|
-
import.meta.css = [css$
|
|
40722
|
+
import.meta.css = [css$Z, "@jsenv/navi/src/text/text.jsx"];
|
|
40683
40723
|
const innerChildren = href ? jsx("svg", {
|
|
40684
40724
|
width: "100%",
|
|
40685
40725
|
height: "100%",
|
|
@@ -40869,7 +40909,7 @@ const useDimColorWhen = (elementRef, shouldDim) => {
|
|
|
40869
40909
|
};
|
|
40870
40910
|
|
|
40871
40911
|
installImportMetaCssBuild(import.meta);
|
|
40872
|
-
const css$
|
|
40912
|
+
const css$Y = /* css */`@layer navi {
|
|
40873
40913
|
.navi_link {
|
|
40874
40914
|
--link-border-radius: unset;
|
|
40875
40915
|
--link-outline-color: var(--navi-focus-outline-color);
|
|
@@ -41327,7 +41367,7 @@ Object.assign(PSEUDO_CLASSES, {
|
|
|
41327
41367
|
* @param {boolean} [props.readOnly]
|
|
41328
41368
|
*/
|
|
41329
41369
|
const Link = props => {
|
|
41330
|
-
import.meta.css = [css$
|
|
41370
|
+
import.meta.css = [css$Y, "@jsenv/navi/src/nav/link/link.jsx"];
|
|
41331
41371
|
if (props.route) {
|
|
41332
41372
|
return jsx(LinkWithRoute, {
|
|
41333
41373
|
...props
|
|
@@ -41886,7 +41926,7 @@ const rememberProgressAnimation = (navElement, travel) => {
|
|
|
41886
41926
|
travel.progressAnimation = animation;
|
|
41887
41927
|
travel.progressFrom = animation ? parseFloat(animation.effect.getKeyframes()[0]["--slide-travel-progress"]) || 0 : 0;
|
|
41888
41928
|
};
|
|
41889
|
-
const css$
|
|
41929
|
+
const css$X = /* css */`@layer navi {
|
|
41890
41930
|
.navi_nav {
|
|
41891
41931
|
--nav-border: none;
|
|
41892
41932
|
--nav-padding: 0px;
|
|
@@ -42150,7 +42190,7 @@ const Nav = ({
|
|
|
42150
42190
|
slideContainer,
|
|
42151
42191
|
...props
|
|
42152
42192
|
}) => {
|
|
42153
|
-
import.meta.css = [css$
|
|
42193
|
+
import.meta.css = [css$X, "@jsenv/navi/src/nav/link/nav.jsx"];
|
|
42154
42194
|
const defaultRef = useRef();
|
|
42155
42195
|
props.ref = props.ref || defaultRef;
|
|
42156
42196
|
const navRef = props.ref;
|
|
@@ -43026,7 +43066,7 @@ const buildBinderCenterlinePathBelow = ({
|
|
|
43026
43066
|
};
|
|
43027
43067
|
|
|
43028
43068
|
installImportMetaCssBuild(import.meta);
|
|
43029
|
-
const css$
|
|
43069
|
+
const css$W = /* css */`@layer navi {
|
|
43030
43070
|
.navi_binder {
|
|
43031
43071
|
--binder-border-width: var(--navi-control-border-width);
|
|
43032
43072
|
--binder-border-radius: var(--navi-control-border-radius);
|
|
@@ -43311,7 +43351,7 @@ const Binder = ({
|
|
|
43311
43351
|
pagePadding,
|
|
43312
43352
|
...props
|
|
43313
43353
|
}) => {
|
|
43314
|
-
import.meta.css = [css$
|
|
43354
|
+
import.meta.css = [css$W, "@jsenv/navi/src/nav/binder/binder.jsx"];
|
|
43315
43355
|
const items = toChildArray(children).map((child, index) => {
|
|
43316
43356
|
const {
|
|
43317
43357
|
value: itemValue,
|
|
@@ -43758,7 +43798,7 @@ const writeSpace = (area) => {
|
|
|
43758
43798
|
};
|
|
43759
43799
|
|
|
43760
43800
|
installImportMetaCssBuild(import.meta);
|
|
43761
|
-
const css$
|
|
43801
|
+
const css$V = /* css */`@layer navi {
|
|
43762
43802
|
:root {
|
|
43763
43803
|
--navi-fixed-bar-width: 56px;
|
|
43764
43804
|
--navi-fixed-bar-height: 56px;
|
|
@@ -43878,7 +43918,7 @@ const FixedBar = ({
|
|
|
43878
43918
|
border = true,
|
|
43879
43919
|
...props
|
|
43880
43920
|
}) => {
|
|
43881
|
-
import.meta.css = [css$
|
|
43921
|
+
import.meta.css = [css$V, "@jsenv/navi/src/layout/fixed_bar/fixed_bar.jsx"];
|
|
43882
43922
|
const defaultRef = useRef();
|
|
43883
43923
|
props.ref = props.ref || defaultRef;
|
|
43884
43924
|
// Said with the width the border rule reads rather than with an attribute of
|
|
@@ -43972,7 +44012,7 @@ const FixedBar = ({
|
|
|
43972
44012
|
// Subpixel layout rounds rectangles up on boxes that fit exactly.
|
|
43973
44013
|
const OVERFLOW_TOLERANCE = 1;
|
|
43974
44014
|
|
|
43975
|
-
const css$
|
|
44015
|
+
const css$U = /* css */ `
|
|
43976
44016
|
[data-navi-overflow-x] {
|
|
43977
44017
|
outline: 2px dashed #e74c3c;
|
|
43978
44018
|
outline-offset: -2px;
|
|
@@ -43996,7 +44036,7 @@ const detectHorizontalOverflow = ({
|
|
|
43996
44036
|
let styleEl = null;
|
|
43997
44037
|
if (highlight) {
|
|
43998
44038
|
styleEl = document.createElement("style");
|
|
43999
|
-
styleEl.textContent = css$
|
|
44039
|
+
styleEl.textContent = css$U;
|
|
44000
44040
|
document.head.appendChild(styleEl);
|
|
44001
44041
|
}
|
|
44002
44042
|
|
|
@@ -44152,7 +44192,7 @@ const useFocusGroup = (
|
|
|
44152
44192
|
|
|
44153
44193
|
installImportMetaCssBuild(import.meta);
|
|
44154
44194
|
const rightArrowPath = "M680-480L360-160l-80-80 240-240-240-240 80-80 320 320z";
|
|
44155
|
-
const css$
|
|
44195
|
+
const css$T = /* css */`.navi_summary_marker {
|
|
44156
44196
|
flex-shrink: 0;
|
|
44157
44197
|
width: 1em;
|
|
44158
44198
|
height: 1em;
|
|
@@ -44244,7 +44284,7 @@ const SummaryMarker = ({
|
|
|
44244
44284
|
loading,
|
|
44245
44285
|
openDirection = "down"
|
|
44246
44286
|
}) => {
|
|
44247
|
-
import.meta.css = [css$
|
|
44287
|
+
import.meta.css = [css$T, "@jsenv/navi/src/control/details/summary_marker.jsx"];
|
|
44248
44288
|
const showLoading = useDebounceTrue(loading, 300);
|
|
44249
44289
|
return jsx("span", {
|
|
44250
44290
|
className: "navi_summary_marker",
|
|
@@ -44290,7 +44330,7 @@ const SummaryMarker = ({
|
|
|
44290
44330
|
};
|
|
44291
44331
|
|
|
44292
44332
|
installImportMetaCssBuild(import.meta);
|
|
44293
|
-
const css$
|
|
44333
|
+
const css$S = /* css */`.navi_details {
|
|
44294
44334
|
z-index: 1;
|
|
44295
44335
|
flex-direction: column;
|
|
44296
44336
|
flex-shrink: 0;
|
|
@@ -44335,7 +44375,7 @@ const Details = props => {
|
|
|
44335
44375
|
return details;
|
|
44336
44376
|
};
|
|
44337
44377
|
const DetailsField = props => {
|
|
44338
|
-
import.meta.css = [css$
|
|
44378
|
+
import.meta.css = [css$S, "@jsenv/navi/src/control/details/details.jsx"];
|
|
44339
44379
|
const {
|
|
44340
44380
|
ref,
|
|
44341
44381
|
persists,
|
|
@@ -46080,7 +46120,7 @@ const usePopupContentMount = (
|
|
|
46080
46120
|
};
|
|
46081
46121
|
|
|
46082
46122
|
installImportMetaCssBuild(import.meta);
|
|
46083
|
-
const css$
|
|
46123
|
+
const css$R = /* css */`.navi_expandable {
|
|
46084
46124
|
flex-direction: column;
|
|
46085
46125
|
flex-shrink: 0;
|
|
46086
46126
|
display: flex;
|
|
@@ -46310,7 +46350,7 @@ const useExpandableContext = partName => {
|
|
|
46310
46350
|
* builds the content ahead of the click.
|
|
46311
46351
|
*/
|
|
46312
46352
|
const Expandable = props => {
|
|
46313
|
-
import.meta.css = [css$
|
|
46353
|
+
import.meta.css = [css$R, "@jsenv/navi/src/control/expandable/expandable.jsx"];
|
|
46314
46354
|
/* The open props are read from `props` by the open controller below; they
|
|
46315
46355
|
are named here only to keep them out of `rest`, and so out of the DOM. */
|
|
46316
46356
|
/* eslint-disable no-unused-vars */
|
|
@@ -46997,46 +47037,6 @@ const shallowDiffers = (a, b) => {
|
|
|
46997
47037
|
return false;
|
|
46998
47038
|
};
|
|
46999
47039
|
|
|
47000
|
-
const ButtonRouteResolver = props => {
|
|
47001
|
-
const Next = useNextResolver();
|
|
47002
|
-
if (props.route) {
|
|
47003
|
-
return jsx(ButtonWithRoute, {
|
|
47004
|
-
...props
|
|
47005
|
-
});
|
|
47006
|
-
}
|
|
47007
|
-
return jsx(Next, {
|
|
47008
|
-
...props
|
|
47009
|
-
});
|
|
47010
|
-
};
|
|
47011
|
-
const ButtonWithRoute = props => {
|
|
47012
|
-
const Next = useNextResolver();
|
|
47013
|
-
const {
|
|
47014
|
-
route,
|
|
47015
|
-
routeParams,
|
|
47016
|
-
children,
|
|
47017
|
-
pseudoState,
|
|
47018
|
-
...rest
|
|
47019
|
-
} = props;
|
|
47020
|
-
const url = route.buildUrl(routeParams);
|
|
47021
|
-
const {
|
|
47022
|
-
matching
|
|
47023
|
-
} = useRouteStatus(route);
|
|
47024
|
-
const paramsAreMatching = route.matchesParams(routeParams);
|
|
47025
|
-
const linkMatching = matching && paramsAreMatching;
|
|
47026
|
-
|
|
47027
|
-
// Merged into whatever the caller already holds: a button can be forced into
|
|
47028
|
-
// a state for a demo and still learn its own current-ness from its route.
|
|
47029
|
-
return jsx(Next, {
|
|
47030
|
-
href: url,
|
|
47031
|
-
pseudoState: {
|
|
47032
|
-
...pseudoState,
|
|
47033
|
-
":-navi-href-current": linkMatching
|
|
47034
|
-
},
|
|
47035
|
-
...rest,
|
|
47036
|
-
children: children || route.buildRelativeUrl(routeParams)
|
|
47037
|
-
});
|
|
47038
|
-
};
|
|
47039
|
-
|
|
47040
47040
|
const LIGHT_ACCENT_ATTRIBUTE = "data-accent-light";
|
|
47041
47041
|
const VERY_LIGHT_ACCENT_ATTRIBUTE = "data-accent-very-light";
|
|
47042
47042
|
const DARK_CONTRAST_ATTRIBUTE = "data-accent-needs-dark-fg";
|
|
@@ -47137,7 +47137,7 @@ const useAccentColorAttributes = (
|
|
|
47137
47137
|
};
|
|
47138
47138
|
|
|
47139
47139
|
installImportMetaCssBuild(import.meta);
|
|
47140
|
-
const css$
|
|
47140
|
+
const css$Q = /* css */`@layer navi {
|
|
47141
47141
|
.navi_button {
|
|
47142
47142
|
--button-border-radius: var(--navi-control-border-radius);
|
|
47143
47143
|
--button-border-width: var(--navi-control-border-width);
|
|
@@ -47438,7 +47438,7 @@ a.navi_button {
|
|
|
47438
47438
|
}
|
|
47439
47439
|
`;
|
|
47440
47440
|
const ButtonUI = props => {
|
|
47441
|
-
import.meta.css = [css$
|
|
47441
|
+
import.meta.css = [css$Q, "@jsenv/navi/src/control/input/button_ui.jsx"];
|
|
47442
47442
|
const {
|
|
47443
47443
|
ref,
|
|
47444
47444
|
// href/link
|
|
@@ -47653,6 +47653,186 @@ const ButtonShadow = () => {
|
|
|
47653
47653
|
};
|
|
47654
47654
|
markAsOutsideTextFlow(ButtonShadow);
|
|
47655
47655
|
|
|
47656
|
+
installImportMetaCssBuild(import.meta);
|
|
47657
|
+
const css$P = /* css */`.navi_button_confirm {
|
|
47658
|
+
align-items: center;
|
|
47659
|
+
gap: var(--navi-s);
|
|
47660
|
+
flex-wrap: wrap;
|
|
47661
|
+
display: inline-flex;
|
|
47662
|
+
}
|
|
47663
|
+
`;
|
|
47664
|
+
const ButtonConfirmResolver = props => {
|
|
47665
|
+
const Next = useNextResolver();
|
|
47666
|
+
if (!props.confirm) {
|
|
47667
|
+
return jsx(Next, {
|
|
47668
|
+
...props
|
|
47669
|
+
});
|
|
47670
|
+
}
|
|
47671
|
+
return jsx(ButtonConfirm, {
|
|
47672
|
+
...props,
|
|
47673
|
+
Next: Next
|
|
47674
|
+
});
|
|
47675
|
+
};
|
|
47676
|
+
const ButtonConfirm = ({
|
|
47677
|
+
Next,
|
|
47678
|
+
confirm,
|
|
47679
|
+
confirmLabel = naviI18n("button.confirm"),
|
|
47680
|
+
cancelLabel = naviI18n("button.cancel"),
|
|
47681
|
+
confirmTestId,
|
|
47682
|
+
cancelTestId,
|
|
47683
|
+
...props
|
|
47684
|
+
}) => {
|
|
47685
|
+
import.meta.css = [css$P, "@jsenv/navi/src/control/input/button_confirm.jsx"];
|
|
47686
|
+
const [asking, setAsking] = useState(false);
|
|
47687
|
+
const askingRef = useRef(null);
|
|
47688
|
+
const cancelRef = useRef(null);
|
|
47689
|
+
// Whether going back to the resting label owes the focus a place to land: a
|
|
47690
|
+
// press on "Annuler" or an Escape does, a focus that already left does not —
|
|
47691
|
+
// it is elsewhere by then, and pulling it back would take it from whatever
|
|
47692
|
+
// the person moved to.
|
|
47693
|
+
const restoreFocusRef = useRef(false);
|
|
47694
|
+
const questionId = `${useId()}_question`;
|
|
47695
|
+
const cancel = restoreFocus => {
|
|
47696
|
+
restoreFocusRef.current = restoreFocus;
|
|
47697
|
+
setAsking(false);
|
|
47698
|
+
};
|
|
47699
|
+
useLayoutEffect(() => {
|
|
47700
|
+
if (asking) {
|
|
47701
|
+
// The press was aimed at this button, and the button that answers now is
|
|
47702
|
+
// where it should land: navi's own ladder inside the question, which
|
|
47703
|
+
// finds "Confirmer" (first in the row) unless the caller marked
|
|
47704
|
+
// something else.
|
|
47705
|
+
const found = findFocusTarget(askingRef.current, {
|
|
47706
|
+
restoreMayClaim: true
|
|
47707
|
+
});
|
|
47708
|
+
if (found) {
|
|
47709
|
+
moveFocusTo(found.target);
|
|
47710
|
+
}
|
|
47711
|
+
return;
|
|
47712
|
+
}
|
|
47713
|
+
if (restoreFocusRef.current) {
|
|
47714
|
+
restoreFocusRef.current = false;
|
|
47715
|
+
// The answer may have taken the button with it — a row deleting itself
|
|
47716
|
+
// is the whole point of asking — and there is then nothing to give the
|
|
47717
|
+
// focus back to.
|
|
47718
|
+
const buttonEl = props.ref.current;
|
|
47719
|
+
if (buttonEl) {
|
|
47720
|
+
moveFocusTo(buttonEl);
|
|
47721
|
+
}
|
|
47722
|
+
}
|
|
47723
|
+
}, [asking]);
|
|
47724
|
+
if (!asking) {
|
|
47725
|
+
return jsx(Next, {
|
|
47726
|
+
...props,
|
|
47727
|
+
// The first press is the question, not the act — so nothing that acts
|
|
47728
|
+
// is handed to the resolvers below. `type` with them: a submit button
|
|
47729
|
+
// would be given `--navi-send` back.
|
|
47730
|
+
type: "button",
|
|
47731
|
+
action: undefined,
|
|
47732
|
+
command: undefined,
|
|
47733
|
+
href: undefined,
|
|
47734
|
+
route: undefined,
|
|
47735
|
+
onClick: () => {
|
|
47736
|
+
setAsking(true);
|
|
47737
|
+
}
|
|
47738
|
+
});
|
|
47739
|
+
}
|
|
47740
|
+
const {
|
|
47741
|
+
action,
|
|
47742
|
+
onActionEnd,
|
|
47743
|
+
onClick
|
|
47744
|
+
} = props;
|
|
47745
|
+
return jsxs("span", {
|
|
47746
|
+
className: "navi_button_confirm",
|
|
47747
|
+
ref: askingRef,
|
|
47748
|
+
onKeyDown: e => {
|
|
47749
|
+
if (e.key === "Escape") {
|
|
47750
|
+
cancel(true);
|
|
47751
|
+
}
|
|
47752
|
+
},
|
|
47753
|
+
onFocusOut: e => {
|
|
47754
|
+
if (!askingRef.current.contains(e.relatedTarget)) {
|
|
47755
|
+
cancel(false);
|
|
47756
|
+
}
|
|
47757
|
+
},
|
|
47758
|
+
children: [jsx("span", {
|
|
47759
|
+
id: questionId,
|
|
47760
|
+
children: confirm === true ? naviI18n("confirm.message") : confirm
|
|
47761
|
+
}), jsx(Next, {
|
|
47762
|
+
...props,
|
|
47763
|
+
"aria-describedby": questionId
|
|
47764
|
+
// The button confirming is the button that was pressed: it keeps the
|
|
47765
|
+
// caller's own testid unless the question names one of its own.
|
|
47766
|
+
,
|
|
47767
|
+
"data-testid": confirmTestId || props["data-testid"],
|
|
47768
|
+
onClick: e => {
|
|
47769
|
+
onClick?.(e);
|
|
47770
|
+
if (!action) {
|
|
47771
|
+
// A command runs on the press itself: there is nothing to wait for,
|
|
47772
|
+
// the question is answered as soon as it is pressed.
|
|
47773
|
+
cancel(true);
|
|
47774
|
+
}
|
|
47775
|
+
}
|
|
47776
|
+
// A run that failed keeps the question up, with its callout on the
|
|
47777
|
+
// button that raised it: the retry is then one press away.
|
|
47778
|
+
,
|
|
47779
|
+
onActionEnd: (data, e) => {
|
|
47780
|
+
onActionEnd?.(data, e);
|
|
47781
|
+
cancel(true);
|
|
47782
|
+
},
|
|
47783
|
+
children: confirmLabel
|
|
47784
|
+
}), jsx(ButtonUI, {
|
|
47785
|
+
ref: cancelRef,
|
|
47786
|
+
type: "button",
|
|
47787
|
+
"data-testid": cancelTestId,
|
|
47788
|
+
onClick: () => {
|
|
47789
|
+
cancel(true);
|
|
47790
|
+
},
|
|
47791
|
+
children: cancelLabel
|
|
47792
|
+
})]
|
|
47793
|
+
});
|
|
47794
|
+
};
|
|
47795
|
+
|
|
47796
|
+
const ButtonRouteResolver = props => {
|
|
47797
|
+
const Next = useNextResolver();
|
|
47798
|
+
if (props.route) {
|
|
47799
|
+
return jsx(ButtonWithRoute, {
|
|
47800
|
+
...props
|
|
47801
|
+
});
|
|
47802
|
+
}
|
|
47803
|
+
return jsx(Next, {
|
|
47804
|
+
...props
|
|
47805
|
+
});
|
|
47806
|
+
};
|
|
47807
|
+
const ButtonWithRoute = props => {
|
|
47808
|
+
const Next = useNextResolver();
|
|
47809
|
+
const {
|
|
47810
|
+
route,
|
|
47811
|
+
routeParams,
|
|
47812
|
+
children,
|
|
47813
|
+
pseudoState,
|
|
47814
|
+
...rest
|
|
47815
|
+
} = props;
|
|
47816
|
+
const url = route.buildUrl(routeParams);
|
|
47817
|
+
const {
|
|
47818
|
+
matching
|
|
47819
|
+
} = useRouteStatus(route);
|
|
47820
|
+
const paramsAreMatching = route.matchesParams(routeParams);
|
|
47821
|
+
const linkMatching = matching && paramsAreMatching;
|
|
47822
|
+
|
|
47823
|
+
// Merged into whatever the caller already holds: a button can be forced into
|
|
47824
|
+
// a state for a demo and still learn its own current-ness from its route.
|
|
47825
|
+
return jsx(Next, {
|
|
47826
|
+
href: url,
|
|
47827
|
+
pseudoState: {
|
|
47828
|
+
...pseudoState,
|
|
47829
|
+
":-navi-href-current": linkMatching
|
|
47830
|
+
},
|
|
47831
|
+
...rest,
|
|
47832
|
+
children: children || route.buildRelativeUrl(routeParams)
|
|
47833
|
+
});
|
|
47834
|
+
};
|
|
47835
|
+
|
|
47656
47836
|
const ButtonFirstResolver = props => {
|
|
47657
47837
|
const Next = useNextResolver();
|
|
47658
47838
|
const defaultRef = useRef(null);
|
|
@@ -47814,6 +47994,20 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
47814
47994
|
* more: the work may already have happened on the other side (see
|
|
47815
47995
|
* docs/actions.md#aborting-saves-resources-it-does-not-undo), so say it only
|
|
47816
47996
|
* where the screen can be re-opened on what is actually there.
|
|
47997
|
+
* @param {boolean|import("ignore:preact").ComponentChildren} [confirm] Ask before
|
|
47998
|
+
* doing it, in the place the button stands: the first press replaces the
|
|
47999
|
+
* button with this question and a "Confirmer"/"Annuler" pair, the second one
|
|
48000
|
+
* does what the button was for. `true` asks navi's default question. The
|
|
48001
|
+
* press stays under the finger and there is nothing to dismiss — "Annuler",
|
|
48002
|
+
* Escape, or the focus leaving puts the button back. Use `<Picker
|
|
48003
|
+
* type="confirm">` instead when the question is long enough to want a popup,
|
|
48004
|
+
* or when the row has no space for it.
|
|
48005
|
+
* @param {import("ignore:preact").ComponentChildren} [confirmLabel] Label of the
|
|
48006
|
+
* second press.
|
|
48007
|
+
* @param {import("ignore:preact").ComponentChildren} [cancelLabel] Label of the way
|
|
48008
|
+
* out of the question.
|
|
48009
|
+
* @param {string} [confirmTestId] `data-testid` of the second press.
|
|
48010
|
+
* @param {string} [cancelTestId] `data-testid` of the way out.
|
|
47817
48011
|
* @param {string} [contentDisplay] The display of the frame the button draws
|
|
47818
48012
|
* around its children. It follows the button's own by default — its display
|
|
47819
48013
|
* and, a display alone saying nothing about direction, the rest of its flow
|
|
@@ -47828,7 +48022,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
|
|
|
47828
48022
|
* (it wears it itself) and loses the shrink under the finger, which has
|
|
47829
48023
|
* nothing left to scale but the interactive area itself.
|
|
47830
48024
|
*/
|
|
47831
|
-
const Button = createComponentResolver([ButtonFirstResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonUI]);
|
|
48025
|
+
const Button = createComponentResolver([ButtonFirstResolver, ButtonConfirmResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonUI]);
|
|
47832
48026
|
|
|
47833
48027
|
installImportMetaCssBuild(import.meta);
|
|
47834
48028
|
const css$O = /* css */`.navi_control_swap {
|
|
@@ -55820,13 +56014,7 @@ const TimeDate = ({
|
|
|
55820
56014
|
})
|
|
55821
56015
|
});
|
|
55822
56016
|
}
|
|
55823
|
-
const date = toDate(children
|
|
55824
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
55825
|
-
const d = new Date(`${value}T00:00:00`);
|
|
55826
|
-
return isNaN(d.getTime()) ? null : d;
|
|
55827
|
-
}
|
|
55828
|
-
return null;
|
|
55829
|
-
});
|
|
56017
|
+
const date = toDate(children);
|
|
55830
56018
|
if (!date) {
|
|
55831
56019
|
return jsx(TimeText, {
|
|
55832
56020
|
...props,
|
|
@@ -72269,6 +72457,26 @@ const css$r = /* css */`.navi_checkbox_group {
|
|
|
72269
72457
|
`;
|
|
72270
72458
|
|
|
72271
72459
|
/**
|
|
72460
|
+
* A set of checkboxes answering one question, holding the array of the values
|
|
72461
|
+
* that are checked.
|
|
72462
|
+
*
|
|
72463
|
+
* It renders a `<fieldset>` and hands its `name` down, so the boxes inside are
|
|
72464
|
+
* plain `<Input type="checkbox" value="…">` with nothing else to declare; each
|
|
72465
|
+
* value that is checked appears in the array, in the order the boxes are
|
|
72466
|
+
* written. What it holds is said the way every control says it — `value` /
|
|
72467
|
+
* `defaultValue` (an array), or a bound `signal` — and inside a `<Form>` the
|
|
72468
|
+
* group is one entry of the submitted params, an array.
|
|
72469
|
+
*
|
|
72470
|
+
* ```jsx
|
|
72471
|
+
* <CheckboxGroup name="permissions" defaultValue={["places.moderate"]} action={save}>
|
|
72472
|
+
* <legend>Permissions</legend>
|
|
72473
|
+
* <Field as="label"><Input type="checkbox" value="places.moderate" /> Modérer les lieux</Field>
|
|
72474
|
+
* <Field as="label"><Input type="checkbox" value="games.list" /> Lister les parties</Field>
|
|
72475
|
+
* </CheckboxGroup>
|
|
72476
|
+
* ```
|
|
72477
|
+
*
|
|
72478
|
+
* See `<RadioGroup>` for the same shape holding a single value.
|
|
72479
|
+
*
|
|
72272
72480
|
* @type {import("ignore:preact").FunctionComponent<{
|
|
72273
72481
|
* maxLength?: number,
|
|
72274
72482
|
* maxLengthGuard?: number,
|
|
@@ -72990,6 +73198,27 @@ const css$p = /* css */`.navi_radio_group {
|
|
|
72990
73198
|
}
|
|
72991
73199
|
}
|
|
72992
73200
|
`;
|
|
73201
|
+
|
|
73202
|
+
/**
|
|
73203
|
+
* A set of radios answering one question, holding the value of the one that is
|
|
73204
|
+
* checked.
|
|
73205
|
+
*
|
|
73206
|
+
* It renders a `<fieldset>` and hands its `name` down, so the radios inside are
|
|
73207
|
+
* plain `<Input type="radio" value="…">` with nothing else to declare — sharing
|
|
73208
|
+
* a name is what makes them exclusive. What it holds is said the way every
|
|
73209
|
+
* control says it — `value` / `defaultValue`, or a bound `signal` — and inside a
|
|
73210
|
+
* `<Form>` the group is one entry of the submitted params.
|
|
73211
|
+
*
|
|
73212
|
+
* ```jsx
|
|
73213
|
+
* <RadioGroup name="role" defaultValue="viewer" action={save}>
|
|
73214
|
+
* <legend>Rôle</legend>
|
|
73215
|
+
* <Field as="label"><Input type="radio" value="viewer" /> Lecteur</Field>
|
|
73216
|
+
* <Field as="label"><Input type="radio" value="admin" /> Administrateur</Field>
|
|
73217
|
+
* </RadioGroup>
|
|
73218
|
+
* ```
|
|
73219
|
+
*
|
|
73220
|
+
* See `<CheckboxGroup>` for the same shape holding an array.
|
|
73221
|
+
*/
|
|
72993
73222
|
const RadioGroup = props => {
|
|
72994
73223
|
const refDefault = useRef(null);
|
|
72995
73224
|
props.ref = props.ref || refDefault;
|