@jsenv/navi 0.29.53 → 0.29.55
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/jsenv_navi.js +741 -169
- package/dist/jsenv_navi.js.map +57 -26
- package/docs/error_handling.md +44 -14
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -38,7 +38,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
38
38
|
* any of these, and a number is the last resort, not the first tool.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
const css$
|
|
41
|
+
const css$11 = /* css */`
|
|
42
42
|
@layer navi {
|
|
43
43
|
:root {
|
|
44
44
|
/* A control that overlaps its neighbours (the members of a Group share
|
|
@@ -90,7 +90,7 @@ const css$10 = /* css */`
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
`;
|
|
93
|
-
import.meta.css = [css$
|
|
93
|
+
import.meta.css = [css$11, "@jsenv/navi/src/navi_z_indexes.js"];
|
|
94
94
|
|
|
95
95
|
const addIntoArray = (array, ...valuesToAdd) => {
|
|
96
96
|
if (valuesToAdd.length === 1) {
|
|
@@ -359,7 +359,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
359
359
|
* the very first render and the browser does everything on its own.
|
|
360
360
|
*/
|
|
361
361
|
const URL_TARGET_ATTRIBUTE = "data-url-target";
|
|
362
|
-
const css
|
|
362
|
+
const css$10 = /* css */`
|
|
363
363
|
@layer navi {
|
|
364
364
|
[${URL_TARGET_ATTRIBUTE}] {
|
|
365
365
|
animation: navi_url_target var(--navi-url-target-duration, 2000ms)
|
|
@@ -377,7 +377,7 @@ const css$$ = /* css */`
|
|
|
377
377
|
}
|
|
378
378
|
}
|
|
379
379
|
`;
|
|
380
|
-
import.meta.css = [css
|
|
380
|
+
import.meta.css = [css$10, "@jsenv/navi/src/nav/url_target/url_target.js"];
|
|
381
381
|
let urlTargetOptions = {
|
|
382
382
|
block: "center",
|
|
383
383
|
behavior: "smooth",
|
|
@@ -1299,6 +1299,10 @@ naviI18n.addAll({
|
|
|
1299
1299
|
en: "Confirm",
|
|
1300
1300
|
fr: "Confirmer",
|
|
1301
1301
|
},
|
|
1302
|
+
"button.more_actions": {
|
|
1303
|
+
en: "More actions",
|
|
1304
|
+
fr: "Autres actions",
|
|
1305
|
+
},
|
|
1302
1306
|
});
|
|
1303
1307
|
|
|
1304
1308
|
// Default built-in translations — apps can override any key via add()
|
|
@@ -7725,7 +7729,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
7725
7729
|
* - Arrow automatically shows when pointing at a valid anchor element
|
|
7726
7730
|
* - Centers in viewport when no anchor element provided or anchor is too big
|
|
7727
7731
|
*/
|
|
7728
|
-
const css
|
|
7732
|
+
const css$$ = /* css */`
|
|
7729
7733
|
@layer navi {
|
|
7730
7734
|
.navi_callout {
|
|
7731
7735
|
/* A callout is parented to what it explains, so it inherits from it — and
|
|
@@ -7964,7 +7968,7 @@ const openCallout = (message, {
|
|
|
7964
7968
|
skipFocus = false,
|
|
7965
7969
|
debug = () => {}
|
|
7966
7970
|
} = {}) => {
|
|
7967
|
-
import.meta.css = [css
|
|
7971
|
+
import.meta.css = [css$$, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
7968
7972
|
if (debug === true) {
|
|
7969
7973
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
7970
7974
|
}
|
|
@@ -13091,8 +13095,7 @@ const setActionPrivateProperties = (action, properties) => {
|
|
|
13091
13095
|
* the screen that will is often not even mounted — a route action runs before
|
|
13092
13096
|
* its page renders, which is precisely the case a guess made at failure time
|
|
13093
13097
|
* gets wrong. So nothing is guessed. The error is let go, and whoever displays
|
|
13094
|
-
* it SAYS so by marking it; what
|
|
13095
|
-
* chance was displayed by nobody, and only that is reported as unhandled.
|
|
13098
|
+
* it SAYS so by marking it; what nobody ever took is reported as unhandled.
|
|
13096
13099
|
*
|
|
13097
13100
|
* The mark is `__handled_by__`, the same one the jsenv supervisor reads to stay
|
|
13098
13101
|
* out of the way of an error the app is already showing — one mark, one meaning:
|
|
@@ -13112,22 +13115,58 @@ const errorIsDisplayed = (error) => {
|
|
|
13112
13115
|
};
|
|
13113
13116
|
|
|
13114
13117
|
/**
|
|
13115
|
-
*
|
|
13116
|
-
*
|
|
13117
|
-
*
|
|
13118
|
-
*
|
|
13119
|
-
* (
|
|
13120
|
-
*
|
|
13121
|
-
*
|
|
13122
|
-
*
|
|
13123
|
-
*
|
|
13118
|
+
* A render has read this error — it is now the render tree's business, not this
|
|
13119
|
+
* module's, and there is nothing left to report.
|
|
13120
|
+
*
|
|
13121
|
+
* Whatever the reader does with it is already covered without any deadline: it
|
|
13122
|
+
* displays it (and marks it), or it throws it, and a thrown error either finds a
|
|
13123
|
+
* boundary that displays it or reaches window on its own — `preact/debug`
|
|
13124
|
+
* re-throws every error a boundary caught, and an unbounded one aborts the
|
|
13125
|
+
* render loudly. Reporting it here as well would be a second voice saying the
|
|
13126
|
+
* same thing, always the wrong one, since this module cannot see which of those
|
|
13127
|
+
* happened.
|
|
13128
|
+
*/
|
|
13129
|
+
const errorTakenByRenderSet = new WeakSet();
|
|
13130
|
+
const markErrorAsTakenByRender = (error) => {
|
|
13131
|
+
if (error && typeof error === "object") {
|
|
13132
|
+
errorTakenByRenderSet.add(error);
|
|
13133
|
+
}
|
|
13134
|
+
};
|
|
13135
|
+
|
|
13136
|
+
/**
|
|
13137
|
+
* When the answer "nobody took it" is final.
|
|
13138
|
+
*
|
|
13139
|
+
* The floor is one macrotask: every render that could take the error — Preact's
|
|
13140
|
+
* queue, a Suspense boundary settling on the failure, the boundary above it —
|
|
13141
|
+
* happens in microtasks.
|
|
13142
|
+
*
|
|
13143
|
+
* That floor is enough for an action failing under a page that is already on
|
|
13144
|
+
* screen, and far too early for a route action: it fails ON the url change,
|
|
13145
|
+
* before its page exists, and that page cannot render until the routing that
|
|
13146
|
+
* asked for the data is over. Measured on an offline navigation, the screen
|
|
13147
|
+
* displaying the error arrived ~12ms after this deadline — so the app was told
|
|
13148
|
+
* it had displayed nothing while it was displaying it.
|
|
13149
|
+
*
|
|
13150
|
+
* The browser integration knows when the document has stopped moving and hands
|
|
13151
|
+
* that over here (see installReportDeadlineExtension); nothing else does, and
|
|
13152
|
+
* this module stays free of the DOM. Waiting longer costs nothing now that a
|
|
13153
|
+
* read is enough to call this off: what still reaches the report was read by no
|
|
13154
|
+
* render at all, and a late report about that is as good as a prompt one.
|
|
13155
|
+
*/
|
|
13156
|
+
let waitForDocumentSettled = null;
|
|
13157
|
+
const installReportDeadlineExtension = (fn) => {
|
|
13158
|
+
waitForDocumentSettled = fn;
|
|
13159
|
+
};
|
|
13160
|
+
|
|
13161
|
+
/**
|
|
13162
|
+
* Rethrown rather than logged: an error nobody took is an unhandled error, and
|
|
13124
13163
|
* the runtime already knows what to do with those (window "error" event, jsenv
|
|
13125
13164
|
* overlay in dev). Same trick preact/debug uses for the same reason.
|
|
13126
13165
|
*/
|
|
13127
13166
|
const errorReportedSet = new WeakSet();
|
|
13128
13167
|
const reportErrorIfNobodyDisplaysIt = (error, { action } = {}) => {
|
|
13129
|
-
|
|
13130
|
-
if (errorIsDisplayed(error)) {
|
|
13168
|
+
const decide = () => {
|
|
13169
|
+
if (errorIsDisplayed(error) || errorTakenByRenderSet.has(error)) {
|
|
13131
13170
|
return;
|
|
13132
13171
|
}
|
|
13133
13172
|
if (error && typeof error === "object") {
|
|
@@ -13143,6 +13182,19 @@ const reportErrorIfNobodyDisplaysIt = (error, { action } = {}) => {
|
|
|
13143
13182
|
error.action = action;
|
|
13144
13183
|
}
|
|
13145
13184
|
throw error;
|
|
13185
|
+
};
|
|
13186
|
+
|
|
13187
|
+
setTimeout(() => {
|
|
13188
|
+
if (errorIsDisplayed(error) || errorTakenByRenderSet.has(error)) {
|
|
13189
|
+
// Already taken within the microtasks that followed the failure: the
|
|
13190
|
+
// common case, and there is nothing to wait for.
|
|
13191
|
+
return;
|
|
13192
|
+
}
|
|
13193
|
+
if (waitForDocumentSettled) {
|
|
13194
|
+
waitForDocumentSettled(decide);
|
|
13195
|
+
return;
|
|
13196
|
+
}
|
|
13197
|
+
decide();
|
|
13146
13198
|
});
|
|
13147
13199
|
};
|
|
13148
13200
|
|
|
@@ -19983,7 +20035,7 @@ const setupNetworkMonitoring = () => {
|
|
|
19983
20035
|
};
|
|
19984
20036
|
setupNetworkMonitoring();
|
|
19985
20037
|
|
|
19986
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
20038
|
+
installImportMetaCssBuild(import.meta);const css$_ = /* css */`
|
|
19987
20039
|
.navi_loading_indicator_fluid_container {
|
|
19988
20040
|
position: relative;
|
|
19989
20041
|
display: flex;
|
|
@@ -20015,7 +20067,7 @@ const LoadingIndicatorFluid = ({
|
|
|
20015
20067
|
visuallyHidden,
|
|
20016
20068
|
...rest
|
|
20017
20069
|
}) => {
|
|
20018
|
-
import.meta.css = [css$
|
|
20070
|
+
import.meta.css = [css$_, "@jsenv/navi/src/graphic/loading/loading_indicator_fluid.jsx"];
|
|
20019
20071
|
const ref = useRef(null);
|
|
20020
20072
|
// The container dimensions can be deduced from the ref itself as the indicator is absolute inset 0
|
|
20021
20073
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
@@ -20220,7 +20272,7 @@ const LoadingRectangleSvg = ({
|
|
|
20220
20272
|
});
|
|
20221
20273
|
};
|
|
20222
20274
|
|
|
20223
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
20275
|
+
installImportMetaCssBuild(import.meta);const css$Z = /* css */`
|
|
20224
20276
|
.navi_loading_outline_wrapper {
|
|
20225
20277
|
position: absolute;
|
|
20226
20278
|
/* Controls place the outline slightly outside their box, right on top of
|
|
@@ -20257,7 +20309,7 @@ installImportMetaCssBuild(import.meta);const css$Y = /* css */`
|
|
|
20257
20309
|
}
|
|
20258
20310
|
`;
|
|
20259
20311
|
const LoadingOutline = props => {
|
|
20260
|
-
import.meta.css = [css$
|
|
20312
|
+
import.meta.css = [css$Z, "@jsenv/navi/src/graphic/loading/loading_outline.jsx"];
|
|
20261
20313
|
if (props.containerRef) {
|
|
20262
20314
|
const container = props.containerRef.current;
|
|
20263
20315
|
if (!container) {
|
|
@@ -20547,7 +20599,7 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
20547
20599
|
};
|
|
20548
20600
|
|
|
20549
20601
|
installImportMetaCssBuild(import.meta);// https://jsfiddle.net/v5xzJ/4/
|
|
20550
|
-
const css$
|
|
20602
|
+
const css$Y = /* css */`
|
|
20551
20603
|
@layer navi {
|
|
20552
20604
|
.navi_text {
|
|
20553
20605
|
&[data-skeleton] {
|
|
@@ -21053,7 +21105,7 @@ const TextShrinkWrap = props => {
|
|
|
21053
21105
|
});
|
|
21054
21106
|
};
|
|
21055
21107
|
const TextUI = props => {
|
|
21056
|
-
import.meta.css = [css$
|
|
21108
|
+
import.meta.css = [css$Y, "@jsenv/navi/src/text/text.jsx"];
|
|
21057
21109
|
let {
|
|
21058
21110
|
ref,
|
|
21059
21111
|
spacing,
|
|
@@ -22102,6 +22154,39 @@ const browserIntegration = setupBrowserIntegrationViaHistory({
|
|
|
22102
22154
|
isRouting: () => Boolean(updateRoutes),
|
|
22103
22155
|
});
|
|
22104
22156
|
|
|
22157
|
+
/**
|
|
22158
|
+
* How long an error that nothing displayed is given before it is called
|
|
22159
|
+
* unhandled (see action_error_report.js, which knows the rule but not the DOM).
|
|
22160
|
+
*
|
|
22161
|
+
* A route action fails ON the url change, before its page exists: it is the
|
|
22162
|
+
* routing itself that will bring what displays the error, so the answer is only
|
|
22163
|
+
* final once the document has stopped moving AND the frame that paints what it
|
|
22164
|
+
* brought has been through. Anything faster tells an app displaying "you are
|
|
22165
|
+
* offline" that it displayed nothing.
|
|
22166
|
+
*/
|
|
22167
|
+
installReportDeadlineExtension((decide) => {
|
|
22168
|
+
const whenPainted = () => {
|
|
22169
|
+
// The frame that paints what routing brought, then the microtasks after it:
|
|
22170
|
+
// a render claiming the error is on either side of that paint, never later.
|
|
22171
|
+
requestAnimationFrame(() => {
|
|
22172
|
+
setTimeout(decide);
|
|
22173
|
+
});
|
|
22174
|
+
};
|
|
22175
|
+
if (!documentIsBusySignal.peek()) {
|
|
22176
|
+
whenPainted();
|
|
22177
|
+
return;
|
|
22178
|
+
}
|
|
22179
|
+
// Busy right now, so the synchronous first callback of subscribe() says
|
|
22180
|
+
// "busy" and is skipped; what unsubscribes below is a later one.
|
|
22181
|
+
const unsubscribe = documentIsBusySignal.subscribe((documentIsBusy) => {
|
|
22182
|
+
if (documentIsBusy) {
|
|
22183
|
+
return;
|
|
22184
|
+
}
|
|
22185
|
+
unsubscribe();
|
|
22186
|
+
whenPainted();
|
|
22187
|
+
});
|
|
22188
|
+
});
|
|
22189
|
+
|
|
22105
22190
|
setOnAllRouteReady((v) => {
|
|
22106
22191
|
updateRoutes = v;
|
|
22107
22192
|
browserIntegration.init();
|
|
@@ -26901,7 +26986,7 @@ const getAssociatedLabels = element => {
|
|
|
26901
26986
|
return Array.from(element.labels);
|
|
26902
26987
|
};
|
|
26903
26988
|
|
|
26904
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
26989
|
+
installImportMetaCssBuild(import.meta);const css$X = /* css */`
|
|
26905
26990
|
@layer navi {
|
|
26906
26991
|
.navi_button {
|
|
26907
26992
|
--button-border-radius: var(--navi-control-border-radius);
|
|
@@ -27332,7 +27417,7 @@ installImportMetaCssBuild(import.meta);const css$W = /* css */`
|
|
|
27332
27417
|
}
|
|
27333
27418
|
`;
|
|
27334
27419
|
const ButtonUI = props => {
|
|
27335
|
-
import.meta.css = [css$
|
|
27420
|
+
import.meta.css = [css$X, "@jsenv/navi/src/control/input/button_ui.jsx"];
|
|
27336
27421
|
const {
|
|
27337
27422
|
ref,
|
|
27338
27423
|
// href/link
|
|
@@ -27344,7 +27429,11 @@ const ButtonUI = props => {
|
|
|
27344
27429
|
pressEffect,
|
|
27345
27430
|
icon,
|
|
27346
27431
|
cta,
|
|
27347
|
-
spacing
|
|
27432
|
+
spacing,
|
|
27433
|
+
// Whether the button draws the loading outline itself. A button that is
|
|
27434
|
+
// one half of a bigger control says no: what is busy is the control, and
|
|
27435
|
+
// the outline belongs around the whole of it (see split_button.jsx).
|
|
27436
|
+
loadingOutline = true
|
|
27348
27437
|
} = props;
|
|
27349
27438
|
const [buttonControlRootProps, buttonControlHostProps, controlChildrenWrapperProps] = useControlProps(props, {
|
|
27350
27439
|
controlType: "button",
|
|
@@ -27400,6 +27489,7 @@ const ButtonUI = props => {
|
|
|
27400
27489
|
spacing: undefined,
|
|
27401
27490
|
cta: undefined,
|
|
27402
27491
|
pressEffect: undefined,
|
|
27492
|
+
loadingOutline: undefined,
|
|
27403
27493
|
ref: ref,
|
|
27404
27494
|
as: as,
|
|
27405
27495
|
href: href,
|
|
@@ -27443,7 +27533,7 @@ const ButtonUI = props => {
|
|
|
27443
27533
|
visualSelector: visualSelector,
|
|
27444
27534
|
hasChildUsingForwardedProps: true,
|
|
27445
27535
|
children: [jsx(LoadingOutline, {
|
|
27446
|
-
loading: loading,
|
|
27536
|
+
loading: loadingOutline && loading,
|
|
27447
27537
|
inset: -1,
|
|
27448
27538
|
color: "var(--button-loader-color)"
|
|
27449
27539
|
}), jsx(ControlChildrenWrapper, {
|
|
@@ -29166,7 +29256,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
29166
29256
|
* reaches the real container.
|
|
29167
29257
|
*/
|
|
29168
29258
|
let openLocalDialogCount = 0;
|
|
29169
|
-
const css$
|
|
29259
|
+
const css$W = /* css */`
|
|
29170
29260
|
@layer navi {
|
|
29171
29261
|
.navi_dialog {
|
|
29172
29262
|
/* Min gap between the dialog and the edges of its container. Written
|
|
@@ -29675,7 +29765,7 @@ const css$V = /* css */`
|
|
|
29675
29765
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
29676
29766
|
*/
|
|
29677
29767
|
const Dialog = props => {
|
|
29678
|
-
import.meta.css = [css$
|
|
29768
|
+
import.meta.css = [css$W, "@jsenv/navi/src/layout/dialog.jsx"];
|
|
29679
29769
|
if (props.openController) {
|
|
29680
29770
|
return jsx(ControlledDialog, {
|
|
29681
29771
|
...props
|
|
@@ -30589,7 +30679,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
30589
30679
|
* and applied.
|
|
30590
30680
|
*/
|
|
30591
30681
|
let openLocalPopoverCount = 0;
|
|
30592
|
-
const css$
|
|
30682
|
+
const css$V = /* css */`
|
|
30593
30683
|
@layer navi {
|
|
30594
30684
|
.navi_popover {
|
|
30595
30685
|
/* soft: user-configurable preferred max-height. Kept as a *default*
|
|
@@ -31022,7 +31112,7 @@ const css$U = /* css */`
|
|
|
31022
31112
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
31023
31113
|
*/
|
|
31024
31114
|
const Popover = props => {
|
|
31025
|
-
import.meta.css = [css$
|
|
31115
|
+
import.meta.css = [css$V, "@jsenv/navi/src/layout/popover.jsx"];
|
|
31026
31116
|
if (props.openController) {
|
|
31027
31117
|
return jsx(ControlledPopover, {
|
|
31028
31118
|
...props
|
|
@@ -32041,7 +32131,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
32041
32131
|
* event, and a caller replacing the body entirely then has one protocol to
|
|
32042
32132
|
* follow — `--navi-confirm` for yes, anything that closes for no.
|
|
32043
32133
|
*/
|
|
32044
|
-
const css$
|
|
32134
|
+
const css$U = /* css */`
|
|
32045
32135
|
/* The width lives on the body rather than on the popup, so that custom
|
|
32046
32136
|
content (which replaces this body entirely) sizes itself instead of
|
|
32047
32137
|
inheriting a ceiling meant for a sentence-long question. */
|
|
@@ -32178,7 +32268,7 @@ const ConfirmPopup = ({
|
|
|
32178
32268
|
onAnswer,
|
|
32179
32269
|
onClosed
|
|
32180
32270
|
}) => {
|
|
32181
|
-
import.meta.css = [css$
|
|
32271
|
+
import.meta.css = [css$U, "@jsenv/navi/src/action/confirm_popup.jsx"];
|
|
32182
32272
|
const {
|
|
32183
32273
|
mode,
|
|
32184
32274
|
confirmLabel,
|
|
@@ -32262,7 +32352,7 @@ const defaultBody = (message, {
|
|
|
32262
32352
|
});
|
|
32263
32353
|
};
|
|
32264
32354
|
|
|
32265
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
32355
|
+
installImportMetaCssBuild(import.meta);const css$T = /* css */`
|
|
32266
32356
|
.action_error {
|
|
32267
32357
|
margin-top: 0;
|
|
32268
32358
|
margin-bottom: 20px;
|
|
@@ -32287,7 +32377,7 @@ const ActionRenderer = ({
|
|
|
32287
32377
|
children,
|
|
32288
32378
|
disabled
|
|
32289
32379
|
}) => {
|
|
32290
|
-
import.meta.css = [css$
|
|
32380
|
+
import.meta.css = [css$T, "@jsenv/navi/src/action/action_renderer.jsx"];
|
|
32291
32381
|
if (action === undefined) {
|
|
32292
32382
|
throw new Error("ActionRenderer requires an action to render, but none was provided.");
|
|
32293
32383
|
}
|
|
@@ -35861,6 +35951,10 @@ const useActionAsyncData = (action, {
|
|
|
35861
35951
|
throw dismissedPromise;
|
|
35862
35952
|
}
|
|
35863
35953
|
const actionError = action.errorSignal.peek();
|
|
35954
|
+
// A render has it now, whichever way it goes from here (see
|
|
35955
|
+
// action_error_report.js): displayed below, or thrown to a boundary that
|
|
35956
|
+
// displays it — and if none does, the throw reaches window on its own.
|
|
35957
|
+
markErrorAsTakenByRender(actionError);
|
|
35864
35958
|
if (errorEffect === "use") {
|
|
35865
35959
|
// Handed to the component, which is what displays it from here on
|
|
35866
35960
|
// (see action_error_report.js)
|
|
@@ -37842,7 +37936,7 @@ const TRAVEL_HEIGHT_PROPERTY = "--navi-route-travel-height";
|
|
|
37842
37936
|
const TRAVEL_OLD_TOP_PROPERTY = "--navi-route-travel-old-top";
|
|
37843
37937
|
const TRAVEL_OLD_LEFT_PROPERTY = "--navi-route-travel-old-left";
|
|
37844
37938
|
const TRAVEL_GEOMETRY_PROPERTIES = [TRAVEL_TOP_PROPERTY, TRAVEL_LEFT_PROPERTY, TRAVEL_WIDTH_PROPERTY, TRAVEL_HEIGHT_PROPERTY, TRAVEL_OLD_TOP_PROPERTY, TRAVEL_OLD_LEFT_PROPERTY];
|
|
37845
|
-
const css$
|
|
37939
|
+
const css$S = /* css */`
|
|
37846
37940
|
/* The name that makes the page inside this box a picture of its own during a
|
|
37847
37941
|
transition — rather than part of the one big picture the document takes, so
|
|
37848
37942
|
the two pages can move past each other while everything else stays where it
|
|
@@ -38224,7 +38318,7 @@ const RouteTravel = ({
|
|
|
38224
38318
|
children,
|
|
38225
38319
|
...rest
|
|
38226
38320
|
}) => {
|
|
38227
|
-
import.meta.css = [css$
|
|
38321
|
+
import.meta.css = [css$S, "@jsenv/navi/src/nav/route_travel.jsx"];
|
|
38228
38322
|
const elementRef = useRef();
|
|
38229
38323
|
const gestureRef = useRef(null);
|
|
38230
38324
|
// The travel in hand: the transition keeping the picture of the page being
|
|
@@ -40884,7 +40978,7 @@ const PhoneSvg = () => {
|
|
|
40884
40978
|
};
|
|
40885
40979
|
|
|
40886
40980
|
installImportMetaCssBuild(import.meta);// # TextAnchor — how it works
|
|
40887
|
-
const css$
|
|
40981
|
+
const css$R = /* css */`
|
|
40888
40982
|
.navi_text_anchor {
|
|
40889
40983
|
vertical-align: baseline;
|
|
40890
40984
|
user-select: none;
|
|
@@ -40919,7 +41013,7 @@ const TextAnchor = ({
|
|
|
40919
41013
|
textSize,
|
|
40920
41014
|
lineLayout
|
|
40921
41015
|
}) => {
|
|
40922
|
-
import.meta.css = [css$
|
|
41016
|
+
import.meta.css = [css$R, "@jsenv/navi/src/text/text_anchor.jsx"];
|
|
40923
41017
|
const anchorRef = useRef();
|
|
40924
41018
|
|
|
40925
41019
|
// Plain useLayoutEffect would also fire while an ancestor dialog/popover
|
|
@@ -41034,7 +41128,7 @@ const computeTopOffset = ({
|
|
|
41034
41128
|
};
|
|
41035
41129
|
const charTopCanvas = document.createElement("canvas");
|
|
41036
41130
|
|
|
41037
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
41131
|
+
installImportMetaCssBuild(import.meta);const css$Q = /* css */`
|
|
41038
41132
|
@layer navi {
|
|
41039
41133
|
/* Ensure data attributes from box.jsx can win to update display */
|
|
41040
41134
|
.navi_icon {
|
|
@@ -41192,7 +41286,7 @@ const Icon = ({
|
|
|
41192
41286
|
fillLine,
|
|
41193
41287
|
...props
|
|
41194
41288
|
}) => {
|
|
41195
|
-
import.meta.css = [css$
|
|
41289
|
+
import.meta.css = [css$Q, "@jsenv/navi/src/text/icon.jsx"];
|
|
41196
41290
|
const innerChildren = href ? jsx("svg", {
|
|
41197
41291
|
width: "100%",
|
|
41198
41292
|
height: "100%",
|
|
@@ -41354,7 +41448,7 @@ const useDimColorWhen = (elementRef, shouldDim) => {
|
|
|
41354
41448
|
});
|
|
41355
41449
|
};
|
|
41356
41450
|
|
|
41357
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
41451
|
+
installImportMetaCssBuild(import.meta);const css$P = /* css */`
|
|
41358
41452
|
@layer navi {
|
|
41359
41453
|
.navi_link {
|
|
41360
41454
|
--link-border-radius: unset;
|
|
@@ -41802,7 +41896,7 @@ Object.assign(PSEUDO_CLASSES, {
|
|
|
41802
41896
|
* @param {boolean} [props.readOnly]
|
|
41803
41897
|
*/
|
|
41804
41898
|
const Link = props => {
|
|
41805
|
-
import.meta.css = [css$
|
|
41899
|
+
import.meta.css = [css$P, "@jsenv/navi/src/nav/link/link.jsx"];
|
|
41806
41900
|
if (props.route) {
|
|
41807
41901
|
return jsx(LinkWithRoute, {
|
|
41808
41902
|
...props
|
|
@@ -42105,7 +42199,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
42105
42199
|
* https://dribbble.com/search/tabs
|
|
42106
42200
|
*/
|
|
42107
42201
|
let navCount = 0;
|
|
42108
|
-
const css$
|
|
42202
|
+
const css$O = /* css */`
|
|
42109
42203
|
@layer navi {
|
|
42110
42204
|
.navi_nav {
|
|
42111
42205
|
--nav-border: none;
|
|
@@ -42388,7 +42482,7 @@ const Nav = ({
|
|
|
42388
42482
|
slideContainer,
|
|
42389
42483
|
...props
|
|
42390
42484
|
}) => {
|
|
42391
|
-
import.meta.css = [css$
|
|
42485
|
+
import.meta.css = [css$O, "@jsenv/navi/src/nav/link/nav.jsx"];
|
|
42392
42486
|
const defaultRef = useRef();
|
|
42393
42487
|
props.ref = props.ref || defaultRef;
|
|
42394
42488
|
const navRef = props.ref;
|
|
@@ -42915,7 +43009,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
42915
43009
|
* Border width participates in layout (it is added to the tab and page
|
|
42916
43010
|
* padding): a thick border grows the binder rather than eating into the text.
|
|
42917
43011
|
*/
|
|
42918
|
-
const css$
|
|
43012
|
+
const css$N = /* css */`
|
|
42919
43013
|
@layer navi {
|
|
42920
43014
|
.navi_binder {
|
|
42921
43015
|
--binder-border-width: var(--navi-control-border-width);
|
|
@@ -43228,7 +43322,7 @@ const Binder = ({
|
|
|
43228
43322
|
pagePadding,
|
|
43229
43323
|
...props
|
|
43230
43324
|
}) => {
|
|
43231
|
-
import.meta.css = [css$
|
|
43325
|
+
import.meta.css = [css$N, "@jsenv/navi/src/nav/binder/binder.jsx"];
|
|
43232
43326
|
const items = toChildArray(children).map((child, index) => {
|
|
43233
43327
|
const {
|
|
43234
43328
|
value: itemValue,
|
|
@@ -43712,7 +43806,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
43712
43806
|
* into the size; a box-shadow draws the identical line and stays out of
|
|
43713
43807
|
* layout.
|
|
43714
43808
|
*/
|
|
43715
|
-
const css$
|
|
43809
|
+
const css$M = /* css */`
|
|
43716
43810
|
@layer navi {
|
|
43717
43811
|
:root {
|
|
43718
43812
|
--navi-fixed-bar-width: 56px;
|
|
@@ -43848,7 +43942,7 @@ const FixedBar = ({
|
|
|
43848
43942
|
border = true,
|
|
43849
43943
|
...props
|
|
43850
43944
|
}) => {
|
|
43851
|
-
import.meta.css = [css$
|
|
43945
|
+
import.meta.css = [css$M, "@jsenv/navi/src/layout/fixed_bar/fixed_bar.jsx"];
|
|
43852
43946
|
const defaultRef = useRef();
|
|
43853
43947
|
props.ref = props.ref || defaultRef;
|
|
43854
43948
|
// Whichever of width/height crosses the edge the bar sits on is what the
|
|
@@ -43936,7 +44030,7 @@ const FixedBar = ({
|
|
|
43936
44030
|
// Subpixel layout rounds rectangles up on boxes that fit exactly.
|
|
43937
44031
|
const OVERFLOW_TOLERANCE = 1;
|
|
43938
44032
|
|
|
43939
|
-
const css$
|
|
44033
|
+
const css$L = /* css */ `
|
|
43940
44034
|
[data-navi-overflow-x] {
|
|
43941
44035
|
outline: 2px dashed #e74c3c;
|
|
43942
44036
|
outline-offset: -2px;
|
|
@@ -43960,7 +44054,7 @@ const detectHorizontalOverflow = ({
|
|
|
43960
44054
|
let styleEl = null;
|
|
43961
44055
|
if (highlight) {
|
|
43962
44056
|
styleEl = document.createElement("style");
|
|
43963
|
-
styleEl.textContent = css$
|
|
44057
|
+
styleEl.textContent = css$L;
|
|
43964
44058
|
document.head.appendChild(styleEl);
|
|
43965
44059
|
}
|
|
43966
44060
|
|
|
@@ -44116,7 +44210,7 @@ const useFocusGroup = (
|
|
|
44116
44210
|
|
|
44117
44211
|
installImportMetaCssBuild(import.meta);const rightArrowPath = "M680-480L360-160l-80-80 240-240-240-240 80-80 320 320z";
|
|
44118
44212
|
const downArrowPath = "M480-280L160-600l80-80 240 240 240-240 80 80-320 320z";
|
|
44119
|
-
const css$
|
|
44213
|
+
const css$K = /* css */`
|
|
44120
44214
|
.navi_summary_marker {
|
|
44121
44215
|
width: 1em;
|
|
44122
44216
|
height: 1em;
|
|
@@ -44201,7 +44295,7 @@ const SummaryMarker = ({
|
|
|
44201
44295
|
open,
|
|
44202
44296
|
loading
|
|
44203
44297
|
}) => {
|
|
44204
|
-
import.meta.css = [css$
|
|
44298
|
+
import.meta.css = [css$K, "@jsenv/navi/src/control/details/summary_marker.jsx"];
|
|
44205
44299
|
const showLoading = useDebounceTrue(loading, 300);
|
|
44206
44300
|
const mountedRef = useRef(false);
|
|
44207
44301
|
const prevOpenRef = useRef(open);
|
|
@@ -44255,7 +44349,7 @@ const SummaryMarker = ({
|
|
|
44255
44349
|
});
|
|
44256
44350
|
};
|
|
44257
44351
|
|
|
44258
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
44352
|
+
installImportMetaCssBuild(import.meta);const css$J = /* css */`
|
|
44259
44353
|
.navi_details {
|
|
44260
44354
|
position: relative;
|
|
44261
44355
|
z-index: 1;
|
|
@@ -44301,7 +44395,7 @@ const Details = props => {
|
|
|
44301
44395
|
return details;
|
|
44302
44396
|
};
|
|
44303
44397
|
const DetailsField = props => {
|
|
44304
|
-
import.meta.css = [css$
|
|
44398
|
+
import.meta.css = [css$J, "@jsenv/navi/src/control/details/details.jsx"];
|
|
44305
44399
|
const {
|
|
44306
44400
|
ref,
|
|
44307
44401
|
persists,
|
|
@@ -44557,7 +44651,7 @@ const ControlGroup = props => {
|
|
|
44557
44651
|
};
|
|
44558
44652
|
const CONTROL_GROUP_PSEUDO_CLASSES = [":hover", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
|
|
44559
44653
|
|
|
44560
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
44654
|
+
installImportMetaCssBuild(import.meta);const css$I = /* css */`
|
|
44561
44655
|
@layer navi {
|
|
44562
44656
|
.navi_checkbox {
|
|
44563
44657
|
--switch-margin: 0; /* Useful to reserve space for outline */
|
|
@@ -44635,7 +44729,7 @@ installImportMetaCssBuild(import.meta);const css$H = /* css */`
|
|
|
44635
44729
|
}
|
|
44636
44730
|
`;
|
|
44637
44731
|
const SwitchUI = () => {
|
|
44638
|
-
import.meta.css = [css$
|
|
44732
|
+
import.meta.css = [css$I, "@jsenv/navi/src/control/input/switch_ui.jsx"];
|
|
44639
44733
|
return jsx(Box, {
|
|
44640
44734
|
className: "navi_switch",
|
|
44641
44735
|
as: "svg",
|
|
@@ -44677,7 +44771,7 @@ const useCheckableProps = (props, options) => {
|
|
|
44677
44771
|
return result;
|
|
44678
44772
|
};
|
|
44679
44773
|
|
|
44680
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
44774
|
+
installImportMetaCssBuild(import.meta);const css$H = /* css */`
|
|
44681
44775
|
@layer navi {
|
|
44682
44776
|
.navi_checkbox {
|
|
44683
44777
|
--border-radius: var(--navi-checkbox-border-radius);
|
|
@@ -45004,7 +45098,7 @@ const InputCheckboxHeadless = props => {
|
|
|
45004
45098
|
});
|
|
45005
45099
|
};
|
|
45006
45100
|
const InputCheckboxFieldInterface = props => {
|
|
45007
|
-
import.meta.css = [css$
|
|
45101
|
+
import.meta.css = [css$H, "@jsenv/navi/src/control/input/input_checkbox.jsx"];
|
|
45008
45102
|
const [checkboxRootProps, checkboxHostProps] = useCheckableProps(props);
|
|
45009
45103
|
const {
|
|
45010
45104
|
icon,
|
|
@@ -45126,7 +45220,7 @@ const CheckboxButtonStyleCSSVars = {
|
|
|
45126
45220
|
const CheckboxPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":checked", ":-navi-loading"];
|
|
45127
45221
|
const CheckboxPseudoElements = ["::-navi-loader", "::-navi-checkmark"];
|
|
45128
45222
|
|
|
45129
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
45223
|
+
installImportMetaCssBuild(import.meta);const css$G = /* css */`
|
|
45130
45224
|
@layer navi {
|
|
45131
45225
|
.navi_label {
|
|
45132
45226
|
--label-required-indicator-color: var(--navi-color-danger, #b42318);
|
|
@@ -45206,7 +45300,7 @@ installImportMetaCssBuild(import.meta);const css$F = /* css */`
|
|
|
45206
45300
|
* </Field>
|
|
45207
45301
|
*/
|
|
45208
45302
|
const Field = props => {
|
|
45209
|
-
import.meta.css = [css$
|
|
45303
|
+
import.meta.css = [css$G, "@jsenv/navi/src/control/field.jsx"];
|
|
45210
45304
|
const refDefault = useRef();
|
|
45211
45305
|
props.ref = props.ref || refDefault;
|
|
45212
45306
|
const {
|
|
@@ -45241,7 +45335,7 @@ const FieldCSSVars = {
|
|
|
45241
45335
|
spacingWithControl: "--spacing-with-control"
|
|
45242
45336
|
};
|
|
45243
45337
|
const FieldAsContainer = props => {
|
|
45244
|
-
import.meta.css = [css$
|
|
45338
|
+
import.meta.css = [css$G, "@jsenv/navi/src/control/field.jsx"];
|
|
45245
45339
|
const {
|
|
45246
45340
|
children
|
|
45247
45341
|
} = props;
|
|
@@ -45273,7 +45367,7 @@ const FieldAsContainer = props => {
|
|
|
45273
45367
|
};
|
|
45274
45368
|
const FIELD_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
|
|
45275
45369
|
const Label = props => {
|
|
45276
|
-
import.meta.css = [css$
|
|
45370
|
+
import.meta.css = [css$G, "@jsenv/navi/src/control/field.jsx"];
|
|
45277
45371
|
const {
|
|
45278
45372
|
children,
|
|
45279
45373
|
// Marks the label when its control is required. Takes what to show, or
|
|
@@ -45435,7 +45529,7 @@ const InputSlot = ({
|
|
|
45435
45529
|
});
|
|
45436
45530
|
};
|
|
45437
45531
|
|
|
45438
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
45532
|
+
installImportMetaCssBuild(import.meta);const css$F = /* css */`
|
|
45439
45533
|
@layer navi {
|
|
45440
45534
|
.navi_radio {
|
|
45441
45535
|
--margin: 3px 3px 3px 5px;
|
|
@@ -45798,7 +45892,7 @@ const InputRadioHeadless = props => {
|
|
|
45798
45892
|
};
|
|
45799
45893
|
const VARIANT_SET = new Set(["icon", "button", "radio"]);
|
|
45800
45894
|
const InputRadioFieldInterface = props => {
|
|
45801
|
-
import.meta.css = [css$
|
|
45895
|
+
import.meta.css = [css$F, "@jsenv/navi/src/control/input/input_radio.jsx"];
|
|
45802
45896
|
const [radioRootProps, radioHostProps] = useCheckableProps(props);
|
|
45803
45897
|
const {
|
|
45804
45898
|
icon,
|
|
@@ -45944,7 +46038,7 @@ const RadioButtonStyleCSSVars = {
|
|
|
45944
46038
|
const RadioPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":checked", ":-navi-loading"];
|
|
45945
46039
|
const RadioPseudoElements = ["::-navi-loader", "::-navi-radiomark"];
|
|
45946
46040
|
|
|
45947
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
46041
|
+
installImportMetaCssBuild(import.meta);const css$E = /* css */`
|
|
45948
46042
|
@layer navi {
|
|
45949
46043
|
.navi_input_range {
|
|
45950
46044
|
--border-radius: 6px;
|
|
@@ -46199,7 +46293,7 @@ const InputRange = props => {
|
|
|
46199
46293
|
});
|
|
46200
46294
|
};
|
|
46201
46295
|
const InputRangeFieldInterface = props => {
|
|
46202
|
-
import.meta.css = [css$
|
|
46296
|
+
import.meta.css = [css$E, "@jsenv/navi/src/control/input/input_range.jsx"];
|
|
46203
46297
|
const {
|
|
46204
46298
|
ref
|
|
46205
46299
|
} = props;
|
|
@@ -46326,24 +46420,68 @@ const InputModeResolver = props => {
|
|
|
46326
46420
|
};
|
|
46327
46421
|
const InputModeNumericOrDecimal = props => {
|
|
46328
46422
|
const Next = useNextResolver();
|
|
46423
|
+
// Where a finger landed, to tell a tap that placed the caret from a drag that
|
|
46424
|
+
// selected a part of the number (see onPointerUp below).
|
|
46425
|
+
const pointerDownRef = useRef(null);
|
|
46329
46426
|
return jsx(Next, {
|
|
46330
46427
|
...props,
|
|
46428
|
+
// A field with no room for one more digit is a field one can only
|
|
46429
|
+
// REPLACE: typing into it does nothing at all, so taking the caret
|
|
46430
|
+
// selects what is there and the first digit typed starts a new number.
|
|
46431
|
+
// The same handover navi_input_full does once a field fills up, asked at
|
|
46432
|
+
// the other end — when the field is entered rather than when it is
|
|
46433
|
+
// filled.
|
|
46434
|
+
onFocus: e => {
|
|
46435
|
+
props.onFocus?.(e);
|
|
46436
|
+
if (e.defaultPrevented) {
|
|
46437
|
+
return;
|
|
46438
|
+
}
|
|
46439
|
+
selectIfFull(e.currentTarget);
|
|
46440
|
+
},
|
|
46441
|
+
onPointerDown: e => {
|
|
46442
|
+
props.onPointerDown?.(e);
|
|
46443
|
+
pointerDownRef.current = {
|
|
46444
|
+
x: e.clientX,
|
|
46445
|
+
y: e.clientY
|
|
46446
|
+
};
|
|
46447
|
+
}
|
|
46448
|
+
// Once more on release, for a finger: a phone places the caret where it
|
|
46449
|
+
// was tapped AFTER the focus above, which undoes the selection made
|
|
46450
|
+
// there — and a field one cannot type into is then back to being a field
|
|
46451
|
+
// one cannot type into. Not for a mouse, which is how one selects a
|
|
46452
|
+
// single digit by dragging across it; and not for a finger that
|
|
46453
|
+
// travelled, which was scrolling the page rather than aiming at the
|
|
46454
|
+
// number.
|
|
46455
|
+
,
|
|
46456
|
+
onPointerUp: e => {
|
|
46457
|
+
props.onPointerUp?.(e);
|
|
46458
|
+
if (e.defaultPrevented) {
|
|
46459
|
+
return;
|
|
46460
|
+
}
|
|
46461
|
+
if (e.pointerType !== "touch") {
|
|
46462
|
+
return;
|
|
46463
|
+
}
|
|
46464
|
+
const pointerDown = pointerDownRef.current;
|
|
46465
|
+
if (pointerDown) {
|
|
46466
|
+
const dx = e.clientX - pointerDown.x;
|
|
46467
|
+
const dy = e.clientY - pointerDown.y;
|
|
46468
|
+
if (dx * dx + dy * dy > TAP_SLOP * TAP_SLOP) {
|
|
46469
|
+
return;
|
|
46470
|
+
}
|
|
46471
|
+
}
|
|
46472
|
+
selectIfFull(e.currentTarget);
|
|
46473
|
+
},
|
|
46331
46474
|
onInput: e => {
|
|
46332
46475
|
props.onInput?.(e);
|
|
46333
46476
|
if (e.defaultPrevented) {
|
|
46334
46477
|
return;
|
|
46335
46478
|
}
|
|
46336
46479
|
const input = e.currentTarget;
|
|
46337
|
-
let maxLength = input.maxLength;
|
|
46338
|
-
if (maxLength === -1) {
|
|
46339
|
-
const naviMaxLengthAttr = input.getAttribute("navi-max-length");
|
|
46340
|
-
maxLength = naviMaxLengthAttr === null ? undefined : Number(naviMaxLengthAttr);
|
|
46341
|
-
}
|
|
46342
46480
|
const caretAtEnd = input.selectionStart === input.value.length;
|
|
46343
46481
|
if (!caretAtEnd) {
|
|
46344
46482
|
return;
|
|
46345
46483
|
}
|
|
46346
|
-
if (!isFull(input,
|
|
46484
|
+
if (!isFull(input, readMaxLength(input))) {
|
|
46347
46485
|
return;
|
|
46348
46486
|
}
|
|
46349
46487
|
// Field is full and caret is at the end: notify listeners then
|
|
@@ -46373,6 +46511,37 @@ const InputModeNumericOrDecimal = props => {
|
|
|
46373
46511
|
});
|
|
46374
46512
|
};
|
|
46375
46513
|
|
|
46514
|
+
// How far a finger may travel and still be aiming at what it landed on, in
|
|
46515
|
+
// pixels: past that it was scrolling the page.
|
|
46516
|
+
const TAP_SLOP = 10;
|
|
46517
|
+
|
|
46518
|
+
// Everything already in the field, selected — but only when nothing more can be
|
|
46519
|
+
// typed after it, which is when replacing is the only edit left. A field one is
|
|
46520
|
+
// merely half-way through filling keeps its caret where it was put.
|
|
46521
|
+
const selectIfFull = input => {
|
|
46522
|
+
if (input.readOnly || input.disabled) {
|
|
46523
|
+
return;
|
|
46524
|
+
}
|
|
46525
|
+
if (!isFull(input, readMaxLength(input))) {
|
|
46526
|
+
return;
|
|
46527
|
+
}
|
|
46528
|
+
if (input.selectionStart === 0 && input.selectionEnd === input.value.length) {
|
|
46529
|
+
return;
|
|
46530
|
+
}
|
|
46531
|
+
input.select();
|
|
46532
|
+
};
|
|
46533
|
+
|
|
46534
|
+
// What the field accepts at most: our own attribute first, since the native
|
|
46535
|
+
// maxLength is deliberately left unset (see RealInput in input_textual.jsx).
|
|
46536
|
+
const readMaxLength = input => {
|
|
46537
|
+
const maxLength = input.maxLength;
|
|
46538
|
+
if (maxLength !== -1) {
|
|
46539
|
+
return maxLength;
|
|
46540
|
+
}
|
|
46541
|
+
const naviMaxLengthAttr = input.getAttribute("navi-max-length");
|
|
46542
|
+
return naviMaxLengthAttr === null ? undefined : Number(naviMaxLengthAttr);
|
|
46543
|
+
};
|
|
46544
|
+
|
|
46376
46545
|
// A field is full when there is no room for another digit — and room is not
|
|
46377
46546
|
// only a number of characters: an hour of two digits at most is also full at
|
|
46378
46547
|
// "7", because 7 followed by anything is past the 23 it accepts. Both are the
|
|
@@ -48164,7 +48333,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
48164
48333
|
* This means an editable thing MUST have a parent with position relative that wraps the content and the eventual editable input
|
|
48165
48334
|
*
|
|
48166
48335
|
*/
|
|
48167
|
-
const css$
|
|
48336
|
+
const css$D = /* css */`
|
|
48168
48337
|
.navi_editable_wrapper {
|
|
48169
48338
|
--inset-top: 0px;
|
|
48170
48339
|
--inset-right: 0px;
|
|
@@ -48213,7 +48382,7 @@ const useEditionController = () => {
|
|
|
48213
48382
|
};
|
|
48214
48383
|
};
|
|
48215
48384
|
const Editable = props => {
|
|
48216
|
-
import.meta.css = [css$
|
|
48385
|
+
import.meta.css = [css$D, "@jsenv/navi/src/control/edition/editable.jsx"];
|
|
48217
48386
|
let {
|
|
48218
48387
|
children,
|
|
48219
48388
|
action,
|
|
@@ -48762,7 +48931,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
48762
48931
|
* meet are drawn once instead of twice, and only the outer corners stay
|
|
48763
48932
|
* rounded. See docs/control_group.md.
|
|
48764
48933
|
*/
|
|
48765
|
-
const css$
|
|
48934
|
+
const css$C = /* css */`
|
|
48766
48935
|
.navi_group {
|
|
48767
48936
|
--group-border-width: var(--navi-control-border-width);
|
|
48768
48937
|
|
|
@@ -48878,7 +49047,7 @@ const Group = ({
|
|
|
48878
49047
|
vertical = row,
|
|
48879
49048
|
...props
|
|
48880
49049
|
}) => {
|
|
48881
|
-
import.meta.css = [css$
|
|
49050
|
+
import.meta.css = [css$C, "@jsenv/navi/src/control/group.jsx"];
|
|
48882
49051
|
return jsx(Box, {
|
|
48883
49052
|
baseClassName: "navi_group",
|
|
48884
49053
|
"data-vertical": vertical ? "" : undefined
|
|
@@ -49044,7 +49213,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
49044
49213
|
* So: nothing scrollable between the cap and the slides (a shared [data-body]
|
|
49045
49214
|
* around them IS a scroller, see box.jsx), and `overflow="auto"` on each Slide.
|
|
49046
49215
|
*/
|
|
49047
|
-
const css$
|
|
49216
|
+
const css$B = /* css */`
|
|
49048
49217
|
/* Where the picture stands relative to the slide that is current, in boxes
|
|
49049
49218
|
(see paintTravelProgress). Declared, so that it is a NUMBER the browser can
|
|
49050
49219
|
interpolate: the trait an indicator draws has to travel with the slides,
|
|
@@ -49528,7 +49697,7 @@ const SlideContainer = ({
|
|
|
49528
49697
|
children,
|
|
49529
49698
|
...rest
|
|
49530
49699
|
}) => {
|
|
49531
|
-
import.meta.css = [css$
|
|
49700
|
+
import.meta.css = [css$B, "@jsenv/navi/src/layout/slide_container.jsx"];
|
|
49532
49701
|
const debugFocus = useDebugFocus();
|
|
49533
49702
|
const trackRef = useRef();
|
|
49534
49703
|
// The box itself: it is what takes the keyboard when what is on screen holds
|
|
@@ -52349,7 +52518,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
52349
52518
|
* pass through untouched via `...rest` to whichever of Popover/Dialog
|
|
52350
52519
|
* actually renders.
|
|
52351
52520
|
*/
|
|
52352
|
-
const css$
|
|
52521
|
+
const css$A = /* css */`
|
|
52353
52522
|
@layer navi {
|
|
52354
52523
|
.navi_popup {
|
|
52355
52524
|
--popup-border-radius: var(--navi-popup-border-radius);
|
|
@@ -52462,7 +52631,7 @@ const css$z = /* css */`
|
|
|
52462
52631
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
52463
52632
|
*/
|
|
52464
52633
|
const Popup = props => {
|
|
52465
|
-
import.meta.css = [css$
|
|
52634
|
+
import.meta.css = [css$A, "@jsenv/navi/src/layout/popup.jsx"];
|
|
52466
52635
|
const {
|
|
52467
52636
|
mode: modeProp,
|
|
52468
52637
|
maxWidth,
|
|
@@ -52525,7 +52694,7 @@ const Popup = props => {
|
|
|
52525
52694
|
});
|
|
52526
52695
|
};
|
|
52527
52696
|
|
|
52528
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
52697
|
+
installImportMetaCssBuild(import.meta);const css$z = /* css */`
|
|
52529
52698
|
.navi_picker {
|
|
52530
52699
|
/* Sizing ceilings (maxmax), background, box-shadow, outline, padding,
|
|
52531
52700
|
overflow... are already handled correctly by Popup/Popover/Dialog
|
|
@@ -52643,7 +52812,7 @@ installImportMetaCssBuild(import.meta);const css$y = /* css */`
|
|
|
52643
52812
|
}
|
|
52644
52813
|
`;
|
|
52645
52814
|
const PickerCustomResolver = props => {
|
|
52646
|
-
import.meta.css = [css$
|
|
52815
|
+
import.meta.css = [css$z, "@jsenv/navi/src/control/picker/picker_custom.jsx"];
|
|
52647
52816
|
if (props.children === undefined) {
|
|
52648
52817
|
return jsx(PickerNative, {
|
|
52649
52818
|
...props
|
|
@@ -52755,6 +52924,9 @@ const PickerCustom = props => {
|
|
|
52755
52924
|
delete pickerProps.open;
|
|
52756
52925
|
delete pickerProps.defaultOpen;
|
|
52757
52926
|
delete pickerProps.escapeEffect;
|
|
52927
|
+
// Read below for the popup alone; on the trigger it would land on the DOM as
|
|
52928
|
+
// an unknown attribute holding a ref object.
|
|
52929
|
+
delete pickerProps.anchor;
|
|
52758
52930
|
const popupProps = {};
|
|
52759
52931
|
Object.assign(pickerProps, {
|
|
52760
52932
|
popupProps,
|
|
@@ -52930,7 +53102,10 @@ const PickerCustom = props => {
|
|
|
52930
53102
|
children
|
|
52931
53103
|
});
|
|
52932
53104
|
Object.assign(popupProps, {
|
|
52933
|
-
|
|
53105
|
+
// The trigger, unless the caller names something else: a picker whose
|
|
53106
|
+
// trigger is a piece of a bigger control (the chevron half of a split
|
|
53107
|
+
// button) hangs its popup off the whole control instead.
|
|
53108
|
+
anchor: props.anchor || props.ref,
|
|
52934
53109
|
openController,
|
|
52935
53110
|
// A picker whose value was never given to it reads it off the control in
|
|
52936
53111
|
// its popup (see useUIFacadeStateController): the trigger shows what the
|
|
@@ -53417,7 +53592,7 @@ const LoadingIndicator = ({
|
|
|
53417
53592
|
});
|
|
53418
53593
|
};
|
|
53419
53594
|
|
|
53420
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
53595
|
+
installImportMetaCssBuild(import.meta);const css$y = /* css */`
|
|
53421
53596
|
@layer navi {
|
|
53422
53597
|
.navi_separator {
|
|
53423
53598
|
--size: 1px;
|
|
@@ -53495,7 +53670,7 @@ const Separator = ({
|
|
|
53495
53670
|
style,
|
|
53496
53671
|
...props
|
|
53497
53672
|
}) => {
|
|
53498
|
-
import.meta.css = [css$
|
|
53673
|
+
import.meta.css = [css$y, "@jsenv/navi/src/layout/separator.jsx"];
|
|
53499
53674
|
return jsx(Box, {
|
|
53500
53675
|
as: vertical ? "span" : "hr",
|
|
53501
53676
|
...props,
|
|
@@ -53988,7 +54163,7 @@ const ListItemFooter = props => {
|
|
|
53988
54163
|
});
|
|
53989
54164
|
};
|
|
53990
54165
|
|
|
53991
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
54166
|
+
installImportMetaCssBuild(import.meta);const css$x = /* css */`
|
|
53992
54167
|
@layer navi {
|
|
53993
54168
|
.navi_list_container[navi-selectable] {
|
|
53994
54169
|
/* Focus outline */
|
|
@@ -54200,7 +54375,7 @@ const ListSelectableResolver = props => {
|
|
|
54200
54375
|
};
|
|
54201
54376
|
const ListSelectable = props => {
|
|
54202
54377
|
const Next = useNextResolver();
|
|
54203
|
-
import.meta.css = [css$
|
|
54378
|
+
import.meta.css = [css$x, "@jsenv/navi/src/control/list/list_selectable.jsx"];
|
|
54204
54379
|
// we allow ourselves to auto-generate a name
|
|
54205
54380
|
const defaultName = useId();
|
|
54206
54381
|
props.name = props.name || `listbox_${defaultName}`;
|
|
@@ -54820,7 +54995,7 @@ const ListVirtualContext = createContext(null);
|
|
|
54820
54995
|
// that returning a component of one's own — instead of a bare <List.Item> —
|
|
54821
54996
|
// works the same way.
|
|
54822
54997
|
const ListRowContext = createContext(null);
|
|
54823
|
-
const css$
|
|
54998
|
+
const css$w = /* css */`
|
|
54824
54999
|
@layer navi {
|
|
54825
55000
|
.navi_list_container {
|
|
54826
55001
|
--list-outline-width: 1px;
|
|
@@ -55461,7 +55636,7 @@ const css$v = /* css */`
|
|
|
55461
55636
|
}
|
|
55462
55637
|
`;
|
|
55463
55638
|
const ListUI = props => {
|
|
55464
|
-
import.meta.css = [css$
|
|
55639
|
+
import.meta.css = [css$w, "@jsenv/navi/src/control/list/list.jsx"];
|
|
55465
55640
|
const {
|
|
55466
55641
|
ref,
|
|
55467
55642
|
renderBudget: renderBudgetProp = RENDER_BUDGET_DEFAULT,
|
|
@@ -58957,7 +59132,7 @@ const PickerPresetResolver = props => {
|
|
|
58957
59132
|
});
|
|
58958
59133
|
};
|
|
58959
59134
|
|
|
58960
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
59135
|
+
installImportMetaCssBuild(import.meta);const css$v = /* css */`
|
|
58961
59136
|
@layer navi {
|
|
58962
59137
|
}
|
|
58963
59138
|
.navi_badge {
|
|
@@ -59069,7 +59244,7 @@ const Badge = ({
|
|
|
59069
59244
|
className,
|
|
59070
59245
|
...props
|
|
59071
59246
|
}) => {
|
|
59072
|
-
import.meta.css = [css$
|
|
59247
|
+
import.meta.css = [css$v, "@jsenv/navi/src/text/badge.jsx"];
|
|
59073
59248
|
const defaultRef = useRef();
|
|
59074
59249
|
props.ref = props.ref || defaultRef;
|
|
59075
59250
|
const {
|
|
@@ -59121,7 +59296,7 @@ const BadgeButton = props => {
|
|
|
59121
59296
|
};
|
|
59122
59297
|
Badge.Button = BadgeButton;
|
|
59123
59298
|
|
|
59124
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
59299
|
+
installImportMetaCssBuild(import.meta);const css$u = /* css */`
|
|
59125
59300
|
@layer navi {
|
|
59126
59301
|
}
|
|
59127
59302
|
.navi_badge_list {
|
|
@@ -59146,7 +59321,7 @@ const BadgeList = ({
|
|
|
59146
59321
|
max,
|
|
59147
59322
|
...props
|
|
59148
59323
|
}) => {
|
|
59149
|
-
import.meta.css = [css$
|
|
59324
|
+
import.meta.css = [css$u, "@jsenv/navi/src/text/badge_list.jsx"];
|
|
59150
59325
|
const measureRef = useRef();
|
|
59151
59326
|
const visibleRef = useRef();
|
|
59152
59327
|
useLayoutEffect(() => {
|
|
@@ -59221,7 +59396,7 @@ const BadgeList = ({
|
|
|
59221
59396
|
});
|
|
59222
59397
|
};
|
|
59223
59398
|
|
|
59224
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
59399
|
+
installImportMetaCssBuild(import.meta);const css$t = /* css */`
|
|
59225
59400
|
.navi_color {
|
|
59226
59401
|
display: block;
|
|
59227
59402
|
aspect-ratio: 1/1;
|
|
@@ -59252,7 +59427,7 @@ const Color = ({
|
|
|
59252
59427
|
children,
|
|
59253
59428
|
...rest
|
|
59254
59429
|
}) => {
|
|
59255
|
-
import.meta.css = [css$
|
|
59430
|
+
import.meta.css = [css$t, "@jsenv/navi/src/text/color.jsx"];
|
|
59256
59431
|
const color = children || undefined;
|
|
59257
59432
|
return jsx(Box, {
|
|
59258
59433
|
as: "span",
|
|
@@ -59707,7 +59882,7 @@ const PickerFileUI = () => {
|
|
|
59707
59882
|
return String(value);
|
|
59708
59883
|
};
|
|
59709
59884
|
|
|
59710
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
59885
|
+
installImportMetaCssBuild(import.meta);const css$s = /* css */`
|
|
59711
59886
|
@layer navi {
|
|
59712
59887
|
.navi_picker {
|
|
59713
59888
|
--picker-border-radius: var(--navi-control-border-radius);
|
|
@@ -60096,7 +60271,7 @@ installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
|
60096
60271
|
}
|
|
60097
60272
|
`;
|
|
60098
60273
|
const PickerButton = props => {
|
|
60099
|
-
import.meta.css = [css$
|
|
60274
|
+
import.meta.css = [css$s, "@jsenv/navi/src/control/picker/picker.jsx"];
|
|
60100
60275
|
if (typeof props.maxLines === "string") {
|
|
60101
60276
|
props.maxLines = parseInt(props.maxLines);
|
|
60102
60277
|
}
|
|
@@ -60507,7 +60682,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
60507
60682
|
* refuse it on purpose, which is what keeps the focus where the travel happens
|
|
60508
60683
|
* instead of moving it into a slide that is about to leave.
|
|
60509
60684
|
*/
|
|
60510
|
-
const css$
|
|
60685
|
+
const css$r = /* css */`
|
|
60511
60686
|
@layer navi {
|
|
60512
60687
|
.navi_picker_spin {
|
|
60513
60688
|
/* A picker one steps through is still a picker: what themes every picker
|
|
@@ -60718,6 +60893,12 @@ const css$q = /* css */`
|
|
|
60718
60893
|
a radius there would show as a notch in the pressed background. */
|
|
60719
60894
|
border-radius: 0;
|
|
60720
60895
|
cursor: pointer;
|
|
60896
|
+
/* Pressed with a finger as readily as with a mouse: no double-tap zoom to
|
|
60897
|
+
wait for between two presses, and no word of the value getting selected
|
|
60898
|
+
under the finger of someone holding one down. */
|
|
60899
|
+
touch-action: manipulation;
|
|
60900
|
+
user-select: none;
|
|
60901
|
+
-webkit-user-select: none;
|
|
60721
60902
|
}
|
|
60722
60903
|
/* Said as data-hover rather than :hover: a touch browser synthesizes the
|
|
60723
60904
|
enter and never the leave, so a CSS :hover would stay grey under the last
|
|
@@ -60970,7 +61151,7 @@ const Spin = ({
|
|
|
60970
61151
|
nextLabel,
|
|
60971
61152
|
...rest
|
|
60972
61153
|
}) => {
|
|
60973
|
-
import.meta.css = [css$
|
|
61154
|
+
import.meta.css = [css$r, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
|
|
60974
61155
|
const id = useId();
|
|
60975
61156
|
// What the group around it says, when there is one: how big the whole thing
|
|
60976
61157
|
// is written is said once, on the group, and every spin in it follows.
|
|
@@ -61329,55 +61510,11 @@ const WayOut = ({
|
|
|
61329
61510
|
onPress,
|
|
61330
61511
|
onPointerDown,
|
|
61331
61512
|
children
|
|
61332
|
-
}) =>
|
|
61333
|
-
|
|
61334
|
-
|
|
61335
|
-
|
|
61336
|
-
|
|
61337
|
-
// rounded by are keyed on (see the CSS above).
|
|
61338
|
-
,
|
|
61339
|
-
|
|
61340
|
-
"data-way-out": atStart ? "start" : "end"
|
|
61341
|
-
// Tracked rather than left to CSS :hover, which stays on after a tap on a
|
|
61342
|
-
// touch device (see the CSS above).
|
|
61343
|
-
,
|
|
61344
|
-
|
|
61345
|
-
pseudoClasses: WAY_OUT_PSEUDO_CLASSES
|
|
61346
|
-
// Announced as a button because that is what it is to whoever cannot see
|
|
61347
|
-
// the chevron — and marked unavailable rather than removed when there is
|
|
61348
|
-
// nothing that way, so it keeps its place.
|
|
61349
|
-
,
|
|
61350
|
-
|
|
61351
|
-
role: "button",
|
|
61352
|
-
"aria-label": label,
|
|
61353
|
-
"aria-disabled": unavailableMessage ? "true" : undefined,
|
|
61354
|
-
"data-unavailable": unavailableMessage ? "" : undefined
|
|
61355
|
-
// At the chevron, not beside it: a callout aims its arrow at where the
|
|
61356
|
-
// anchor's text starts, and there is no text here — only a glyph in the
|
|
61357
|
-
// middle of the box, which is what one pressed and what the answer is
|
|
61358
|
-
// about.
|
|
61359
|
-
,
|
|
61360
|
-
|
|
61361
|
-
"data-callout-arrow-x": "center"
|
|
61362
|
-
// Read by triggerNaviCommand below the same way it reads a button's own.
|
|
61363
|
-
,
|
|
61364
|
-
|
|
61365
|
-
commandfor: commandFor,
|
|
61366
|
-
flex: true,
|
|
61367
|
-
align: "center"
|
|
61368
|
-
// A press, answered where it starts: mousedown rather than click, which is
|
|
61369
|
-
// what makes holding one feel immediate — and the click after it is stopped
|
|
61370
|
-
// below, so a <Label> wrapping the whole control does not forward it to the
|
|
61371
|
-
// control and open the calendar on the way past.
|
|
61372
|
-
,
|
|
61373
|
-
|
|
61374
|
-
onClick: e => {
|
|
61375
|
-
e.preventDefault();
|
|
61376
|
-
},
|
|
61377
|
-
onPointerDown: onPointerDown,
|
|
61378
|
-
onMouseDown: e => {
|
|
61379
|
-
// No focus, no text selection: the keyboard is put on the middle below.
|
|
61380
|
-
e.preventDefault();
|
|
61513
|
+
}) => {
|
|
61514
|
+
// Where the finger landed, to tell a press from the start of a scroll (see
|
|
61515
|
+
// onPointerUp below).
|
|
61516
|
+
const pointerDownRef = useRef(null);
|
|
61517
|
+
const press = e => {
|
|
61381
61518
|
if (unavailableMessage) {
|
|
61382
61519
|
// Why it does nothing, said where one pressed: a control would have
|
|
61383
61520
|
// done this through its own interaction gate, and this one has none.
|
|
@@ -61389,11 +61526,108 @@ const WayOut = ({
|
|
|
61389
61526
|
return;
|
|
61390
61527
|
}
|
|
61391
61528
|
onPress(e);
|
|
61392
|
-
}
|
|
61393
|
-
|
|
61394
|
-
|
|
61395
|
-
|
|
61396
|
-
|
|
61529
|
+
};
|
|
61530
|
+
return jsx(Box, {
|
|
61531
|
+
as: "span",
|
|
61532
|
+
baseClassName: "navi_picker_spin_way_out"
|
|
61533
|
+
// Which end of the box it sits in, said by the chevron rather than read
|
|
61534
|
+
// from its place among its siblings: that is what the corners it is
|
|
61535
|
+
// rounded by are keyed on (see the CSS above).
|
|
61536
|
+
,
|
|
61537
|
+
|
|
61538
|
+
"data-way-out": atStart ? "start" : "end"
|
|
61539
|
+
// Tracked rather than left to CSS :hover, which stays on after a tap on a
|
|
61540
|
+
// touch device (see the CSS above).
|
|
61541
|
+
,
|
|
61542
|
+
|
|
61543
|
+
pseudoClasses: WAY_OUT_PSEUDO_CLASSES
|
|
61544
|
+
// Announced as a button because that is what it is to whoever cannot see
|
|
61545
|
+
// the chevron — and marked unavailable rather than removed when there is
|
|
61546
|
+
// nothing that way, so it keeps its place.
|
|
61547
|
+
,
|
|
61548
|
+
|
|
61549
|
+
role: "button",
|
|
61550
|
+
"aria-label": label,
|
|
61551
|
+
"aria-disabled": unavailableMessage ? "true" : undefined,
|
|
61552
|
+
"data-unavailable": unavailableMessage ? "" : undefined
|
|
61553
|
+
// At the chevron, not beside it: a callout aims its arrow at where the
|
|
61554
|
+
// anchor's text starts, and there is no text here — only a glyph in the
|
|
61555
|
+
// middle of the box, which is what one pressed and what the answer is
|
|
61556
|
+
// about.
|
|
61557
|
+
,
|
|
61558
|
+
|
|
61559
|
+
"data-callout-arrow-x": "center"
|
|
61560
|
+
// Read by triggerNaviCommand below the same way it reads a button's own.
|
|
61561
|
+
,
|
|
61562
|
+
|
|
61563
|
+
commandfor: commandFor,
|
|
61564
|
+
flex: true,
|
|
61565
|
+
align: "center"
|
|
61566
|
+
// The press is answered on the pointer events below, and the click that
|
|
61567
|
+
// follows them is stopped here: a <Label> wrapping the whole control
|
|
61568
|
+
// forwards a click to what it labels, which would open the calendar on
|
|
61569
|
+
// the way past.
|
|
61570
|
+
,
|
|
61571
|
+
|
|
61572
|
+
onClick: e => {
|
|
61573
|
+
e.preventDefault();
|
|
61574
|
+
},
|
|
61575
|
+
onPointerDown: e => {
|
|
61576
|
+
onPointerDown(e);
|
|
61577
|
+
pointerDownRef.current = {
|
|
61578
|
+
x: e.clientX,
|
|
61579
|
+
y: e.clientY
|
|
61580
|
+
};
|
|
61581
|
+
if (e.pointerType === "touch") {
|
|
61582
|
+
return;
|
|
61583
|
+
}
|
|
61584
|
+
// No focus, no text selection: the keyboard is put on the middle below.
|
|
61585
|
+
// preventDefault on the pointer event rather than on the mouse one it is
|
|
61586
|
+
// followed by, since the press is answered here.
|
|
61587
|
+
e.preventDefault();
|
|
61588
|
+
press(e);
|
|
61589
|
+
}
|
|
61590
|
+
// A finger is answered when it lifts, not when it lands: what starts on a
|
|
61591
|
+
// chevron may be a press or may be the beginning of a scroll, and only the
|
|
61592
|
+
// release tells the two apart. A mouse has no such doubt and is answered
|
|
61593
|
+
// above, as it goes down, which is what makes holding one feel immediate.
|
|
61594
|
+
,
|
|
61595
|
+
|
|
61596
|
+
onPointerUp: e => {
|
|
61597
|
+
if (e.pointerType !== "touch") {
|
|
61598
|
+
return;
|
|
61599
|
+
}
|
|
61600
|
+
const pointerDown = pointerDownRef.current;
|
|
61601
|
+
if (pointerDown) {
|
|
61602
|
+
const dx = e.clientX - pointerDown.x;
|
|
61603
|
+
const dy = e.clientY - pointerDown.y;
|
|
61604
|
+
if (dx * dx + dy * dy > WAY_OUT_TAP_SLOP * WAY_OUT_TAP_SLOP) {
|
|
61605
|
+
return;
|
|
61606
|
+
}
|
|
61607
|
+
}
|
|
61608
|
+
press(e);
|
|
61609
|
+
}
|
|
61610
|
+
// A chevron has nothing to copy, look up or share: the menu a long press
|
|
61611
|
+
// opens on a phone would only get in the way of someone stepping through
|
|
61612
|
+
// values, and the pressed state under it would stay on once it opened.
|
|
61613
|
+
,
|
|
61614
|
+
|
|
61615
|
+
onContextMenu: e => {
|
|
61616
|
+
if (e.pointerType !== "touch") {
|
|
61617
|
+
// right click is allowed
|
|
61618
|
+
return;
|
|
61619
|
+
}
|
|
61620
|
+
e.preventDefault();
|
|
61621
|
+
},
|
|
61622
|
+
children: jsx(Icon, {
|
|
61623
|
+
children: children
|
|
61624
|
+
})
|
|
61625
|
+
});
|
|
61626
|
+
};
|
|
61627
|
+
|
|
61628
|
+
// How far a finger may travel and still be pressing what it landed on, in
|
|
61629
|
+
// pixels: past that it was scrolling the page.
|
|
61630
|
+
const WAY_OUT_TAP_SLOP = 10;
|
|
61397
61631
|
const PICKER_SPIN_PSEUDO_CLASSES = [":hover", ":focus-visible"];
|
|
61398
61632
|
const WAY_OUT_PSEUDO_CLASSES = [":hover"];
|
|
61399
61633
|
|
|
@@ -61453,7 +61687,7 @@ const renderValueDefault = value => String(value ?? "");
|
|
|
61453
61687
|
* are passed on to the spins sitting in them.
|
|
61454
61688
|
*/
|
|
61455
61689
|
const SpinGroup = props => {
|
|
61456
|
-
import.meta.css = [css$
|
|
61690
|
+
import.meta.css = [css$r, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
|
|
61457
61691
|
const {
|
|
61458
61692
|
size
|
|
61459
61693
|
} = props;
|
|
@@ -61984,7 +62218,7 @@ const TimeRangeSpin = ({
|
|
|
61984
62218
|
};
|
|
61985
62219
|
|
|
61986
62220
|
installImportMetaCssBuild(import.meta);// TOFIX: select in data then reset, it reset to red/blue instead of red/blue/green
|
|
61987
|
-
const css$
|
|
62221
|
+
const css$q = /* css */`
|
|
61988
62222
|
.navi_checkbox_group {
|
|
61989
62223
|
border-style: solid;
|
|
61990
62224
|
|
|
@@ -62025,7 +62259,7 @@ const CheckboxGroup = props => {
|
|
|
62025
62259
|
return checkboxGroup;
|
|
62026
62260
|
};
|
|
62027
62261
|
const CheckboxGroupInterface = props => {
|
|
62028
|
-
import.meta.css = [css$
|
|
62262
|
+
import.meta.css = [css$q, "@jsenv/navi/src/control/input/checkbox_group.jsx"];
|
|
62029
62263
|
const {
|
|
62030
62264
|
ref
|
|
62031
62265
|
} = props;
|
|
@@ -62074,7 +62308,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
62074
62308
|
* shared sheet is registered here too — a page may render a Textarea without
|
|
62075
62309
|
* any Input.
|
|
62076
62310
|
*/
|
|
62077
|
-
const css$
|
|
62311
|
+
const css$p = /* css */`
|
|
62078
62312
|
.navi_input.navi_textarea {
|
|
62079
62313
|
.navi_control_input {
|
|
62080
62314
|
min-height: calc(var(--textarea-min-rows, 1.5) * 1lh);
|
|
@@ -62161,7 +62395,7 @@ const Textarea = ({
|
|
|
62161
62395
|
width = "35ch",
|
|
62162
62396
|
...props
|
|
62163
62397
|
}) => {
|
|
62164
|
-
import.meta.css = [inputCss + css$
|
|
62398
|
+
import.meta.css = [inputCss + css$p, "@jsenv/navi/src/control/input/textarea.jsx"];
|
|
62165
62399
|
const defaultRef = useRef(null);
|
|
62166
62400
|
props.ref = props.ref || defaultRef;
|
|
62167
62401
|
usePlaceholderHeight(props.ref, props.placeholder);
|
|
@@ -62235,7 +62469,7 @@ const TextareaCharCount = ({
|
|
|
62235
62469
|
maxLength,
|
|
62236
62470
|
...rest
|
|
62237
62471
|
}) => {
|
|
62238
|
-
import.meta.css = [css$
|
|
62472
|
+
import.meta.css = [css$p, "@jsenv/navi/src/control/input/textarea.jsx"];
|
|
62239
62473
|
const resolvedValue = signal ? signal.value : value;
|
|
62240
62474
|
const length = typeof resolvedValue === "string" ? resolvedValue.length : 0;
|
|
62241
62475
|
return jsx(Box, {
|
|
@@ -62420,7 +62654,7 @@ const formatIntlUnit = (unit, {
|
|
|
62420
62654
|
}
|
|
62421
62655
|
};
|
|
62422
62656
|
|
|
62423
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
62657
|
+
installImportMetaCssBuild(import.meta);const css$o = /* css */`
|
|
62424
62658
|
.navi_input_duration {
|
|
62425
62659
|
--duration-separator-spacing: 4px;
|
|
62426
62660
|
--loader-color: var(--navi-loader-color);
|
|
@@ -62487,7 +62721,7 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
62487
62721
|
* "auto" aligns each field toward its neighbouring separator (first→right, last→left, middle/solo→center).
|
|
62488
62722
|
*/
|
|
62489
62723
|
const InputDuration = props => {
|
|
62490
|
-
import.meta.css = [css$
|
|
62724
|
+
import.meta.css = [css$o, "@jsenv/navi/src/control/input/input_duration.jsx"];
|
|
62491
62725
|
const defaultRef = useRef();
|
|
62492
62726
|
props.ref = props.ref || defaultRef;
|
|
62493
62727
|
props.max = props.max || "23h59";
|
|
@@ -62989,7 +63223,7 @@ const InputDurationPart = ({
|
|
|
62989
63223
|
});
|
|
62990
63224
|
};
|
|
62991
63225
|
|
|
62992
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
63226
|
+
installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
62993
63227
|
.navi_radio_group {
|
|
62994
63228
|
border-style: solid;
|
|
62995
63229
|
|
|
@@ -63009,7 +63243,7 @@ const RadioGroup = props => {
|
|
|
63009
63243
|
return radioGroup;
|
|
63010
63244
|
};
|
|
63011
63245
|
const RadioGroupInterface = props => {
|
|
63012
|
-
import.meta.css = [css$
|
|
63246
|
+
import.meta.css = [css$n, "@jsenv/navi/src/control/input/radio_group.jsx"];
|
|
63013
63247
|
const {
|
|
63014
63248
|
ref
|
|
63015
63249
|
} = props;
|
|
@@ -63067,7 +63301,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
63067
63301
|
* control is drawn — the list it opens stays the platform's own, which is the
|
|
63068
63302
|
* whole point of using a select.
|
|
63069
63303
|
*/
|
|
63070
|
-
const css$
|
|
63304
|
+
const css$m = /* css */`
|
|
63071
63305
|
.navi_input.navi_select {
|
|
63072
63306
|
.navi_control_input {
|
|
63073
63307
|
/* Room for the chevron, which sits over the padding rather than beside
|
|
@@ -63122,7 +63356,7 @@ const Select = ({
|
|
|
63122
63356
|
multiple,
|
|
63123
63357
|
...props
|
|
63124
63358
|
}) => {
|
|
63125
|
-
import.meta.css = [inputCss + css$
|
|
63359
|
+
import.meta.css = [inputCss + css$m, "@jsenv/navi/src/control/input/select.jsx"];
|
|
63126
63360
|
const defaultRef = useRef(null);
|
|
63127
63361
|
props.ref = props.ref || defaultRef;
|
|
63128
63362
|
seedDefaultValueFromSignal(props);
|
|
@@ -63171,6 +63405,344 @@ const Select = ({
|
|
|
63171
63405
|
});
|
|
63172
63406
|
};
|
|
63173
63407
|
|
|
63408
|
+
installImportMetaCssBuild(import.meta);/**
|
|
63409
|
+
* SplitButton — one action, several variants of it.
|
|
63410
|
+
*
|
|
63411
|
+
* The left half does the thing; the right half opens the list of the other
|
|
63412
|
+
* ways of doing it. What choosing means is the caller's call (`chooseEffect`):
|
|
63413
|
+
* the chosen way becomes what the button does, or it is done right away.
|
|
63414
|
+
*
|
|
63415
|
+
* Both halves are `<Button>`s, deliberately: the chevron half has to wear the
|
|
63416
|
+
* exact same paint as the label half for any `cta`/`variant`/`color` the
|
|
63417
|
+
* caller passes, and a `<Picker variant="icon">` next to a button paints from
|
|
63418
|
+
* its own set of custom properties — matching them would mean restating the
|
|
63419
|
+
* button's palette here and keeping the copy in step forever. So the popup
|
|
63420
|
+
* comes from a headless `<Picker>` sitting behind the chevron button, hung off
|
|
63421
|
+
* the whole split button (`anchor`) so the menu lines up with it.
|
|
63422
|
+
*
|
|
63423
|
+
* The busy state is worn by the control, not by the half that happens to be
|
|
63424
|
+
* running: one loading outline around both, which is why each half is told
|
|
63425
|
+
* `loadingOutline={false}`.
|
|
63426
|
+
*/
|
|
63427
|
+
const css$l = /* css */`
|
|
63428
|
+
/* Around the pair rather than in it: the outline is drawn against this
|
|
63429
|
+
element and follows its corners, and a Group counts its own children to
|
|
63430
|
+
know which corners to square — an outline among them would be one of the
|
|
63431
|
+
halves. Hence a box holding a group, rather than the group itself. */
|
|
63432
|
+
.navi_split_button {
|
|
63433
|
+
position: relative;
|
|
63434
|
+
display: inline-flex;
|
|
63435
|
+
border-radius: var(--navi-control-border-radius);
|
|
63436
|
+
|
|
63437
|
+
> .navi_group {
|
|
63438
|
+
flex: 1 1 auto;
|
|
63439
|
+
}
|
|
63440
|
+
}
|
|
63441
|
+
/* The chevron half is a button with a headless picker behind it: the picker
|
|
63442
|
+
box is inset: 0 of whatever is positioned above it, and that must be the
|
|
63443
|
+
chevron alone, not the whole split button. */
|
|
63444
|
+
.navi_split_button_menu {
|
|
63445
|
+
position: relative;
|
|
63446
|
+
display: flex;
|
|
63447
|
+
}
|
|
63448
|
+
`;
|
|
63449
|
+
|
|
63450
|
+
/**
|
|
63451
|
+
* @type {import("ignore:preact").FunctionComponent<{
|
|
63452
|
+
* options: Array<{value: any, label: import("ignore:preact").ComponentChildren, [key: string]: any}>,
|
|
63453
|
+
* value?: any,
|
|
63454
|
+
* defaultValue?: any,
|
|
63455
|
+
* label?: import("ignore:preact").ComponentChildren,
|
|
63456
|
+
* action?: (value: any, event: Event) => void | Promise<void>,
|
|
63457
|
+
* onValueChange?: (value: any, event: Event) => void,
|
|
63458
|
+
* chooseEffect?: "select" | "run",
|
|
63459
|
+
* menuLabel?: string,
|
|
63460
|
+
* menuIcon?: import("ignore:preact").ComponentChildren,
|
|
63461
|
+
* menuIconSize?: number | string,
|
|
63462
|
+
* loading?: boolean,
|
|
63463
|
+
* readOnly?: boolean,
|
|
63464
|
+
* disabled?: boolean,
|
|
63465
|
+
* cta?: boolean,
|
|
63466
|
+
* variant?: string,
|
|
63467
|
+
* color?: string,
|
|
63468
|
+
* backgroundColor?: string,
|
|
63469
|
+
* borderColor?: string,
|
|
63470
|
+
* borderWidth?: number | string,
|
|
63471
|
+
* borderRadius?: number | string,
|
|
63472
|
+
* pressEffect?: "none" | string,
|
|
63473
|
+
* mode?: "popover" | "dialog",
|
|
63474
|
+
* popoverMode?: "nearby" | "overlay",
|
|
63475
|
+
* positionArea?: string,
|
|
63476
|
+
* popupWidthFitContent?: boolean,
|
|
63477
|
+
* popoverMaxHeight?: number | string,
|
|
63478
|
+
* dialogMaxWidth?: number | string,
|
|
63479
|
+
* dialogMaxHeight?: number | string,
|
|
63480
|
+
* dialogExpand?: boolean,
|
|
63481
|
+
* dialogExpandX?: boolean,
|
|
63482
|
+
* dialogExpandY?: boolean,
|
|
63483
|
+
* dockedOnSmallTouchScreen?: boolean,
|
|
63484
|
+
* marginWithContainer?: number | string,
|
|
63485
|
+
* backdropVariant?: "auto" | "discrete" | "invisible",
|
|
63486
|
+
* pointerInteractionOutsideEffect?: "close" | "cancel" | "capture",
|
|
63487
|
+
* escapeEffect?: "cancel" | "close",
|
|
63488
|
+
* popupLayer?: "top" | "local",
|
|
63489
|
+
* [key: string]: any,
|
|
63490
|
+
* }>}
|
|
63491
|
+
* @param {Array<{value: any, label: import("ignore:preact").ComponentChildren}>} options
|
|
63492
|
+
* The ways of doing the thing, in the order the menu lists them. Anything
|
|
63493
|
+
* else on an entry (`disabled`, `icon`, `padding`…) reaches its row.
|
|
63494
|
+
* @param {any} [value] Which entry the button stands for — the one it runs, and
|
|
63495
|
+
* the one it is named after. `defaultValue` is the uncontrolled form; without
|
|
63496
|
+
* either, the first entry.
|
|
63497
|
+
* @param {import("ignore:preact").ComponentChildren} [label] What the button says, when
|
|
63498
|
+
* that is not the name of the entry it stands for — a button that keeps one
|
|
63499
|
+
* name whatever the menu does.
|
|
63500
|
+
* @param {(value: any, event: Event) => void} [action] Do the thing, for the
|
|
63501
|
+
* entry given. Pressed on the button it is the entry the button stands for;
|
|
63502
|
+
* with `chooseEffect="run"` it is also the entry just chosen. Awaited: the
|
|
63503
|
+
* whole split button is busy until it settles.
|
|
63504
|
+
* @param {"select"|"run"} [chooseEffect="select"] What choosing an entry does.
|
|
63505
|
+
* "select" hands the button that entry, to be run by a press on it — for
|
|
63506
|
+
* something one does again and again, where the last choice is the likely
|
|
63507
|
+
* next one. "run" does it there and then and leaves the button alone — for a
|
|
63508
|
+
* menu of one-offs around a primary action.
|
|
63509
|
+
* @param {string} [menuLabel] What the chevron half is called, for a screen
|
|
63510
|
+
* reader and for the tooltip a long press raises.
|
|
63511
|
+
* @param {import("ignore:preact").ComponentChildren} [menuIcon] What the chevron half
|
|
63512
|
+
* draws, in place of the chevron. It is the whole of what that half says, so
|
|
63513
|
+
* pass something that still reads as "there is more here" — and say what,
|
|
63514
|
+
* through `menuLabel`.
|
|
63515
|
+
* @param {number|string} [menuIconSize] How big that icon is drawn. Defaults to
|
|
63516
|
+
* the button's own font size.
|
|
63517
|
+
* @param {"popover"|"dialog"} [mode="popover"] What the menu is drawn as. A
|
|
63518
|
+
* picker left to itself turns into a dialog on a small screen; a split
|
|
63519
|
+
* button's menu stays hung off the button there, so this says popover unless
|
|
63520
|
+
* the caller asks for the dialog back.
|
|
63521
|
+
* @param {string} [positionArea="bottom-end"] Where the menu goes — relative to
|
|
63522
|
+
* the whole split button in popover mode, relative to the viewport in dialog
|
|
63523
|
+
* mode. Same grammar as Picker/Popover. The default only applies to a
|
|
63524
|
+
* popover; a dialog keeps Dialog's own "center".
|
|
63525
|
+
*
|
|
63526
|
+
* Every other prop the Picker's popup answers to is forwarded as-is —
|
|
63527
|
+
* `dockedOnSmallTouchScreen`, `dialogExpand*`, `dialogMaxWidth`/`Height`,
|
|
63528
|
+
* `marginWithContainer`, `popoverMode`, `popoverSpacing`, `popupLayer`,
|
|
63529
|
+
* `popupWidthFitContent`, `popoverMaxHeight`, `backdropVariant`,
|
|
63530
|
+
* `pointerInteractionOutsideEffect`, `escapeEffect`, `closeOnFocusOut`,
|
|
63531
|
+
* `scrollCapture`, `focusCapture`, `popupBackgroundColor`,
|
|
63532
|
+
* `popupBorderRadius`, `animation`. See picker.jsx for what each one says.
|
|
63533
|
+
* Anything else lands on the split button's own box.
|
|
63534
|
+
*/
|
|
63535
|
+
const SplitButton = props => {
|
|
63536
|
+
import.meta.css = [css$l, "@jsenv/navi/src/control/input/split_button.jsx"];
|
|
63537
|
+
const {
|
|
63538
|
+
options = [],
|
|
63539
|
+
value,
|
|
63540
|
+
defaultValue,
|
|
63541
|
+
label,
|
|
63542
|
+
action,
|
|
63543
|
+
onValueChange,
|
|
63544
|
+
chooseEffect = "select",
|
|
63545
|
+
menuLabel = naviI18n("button.more_actions"),
|
|
63546
|
+
menuIcon = jsx(ChevronDownSvg$1, {}),
|
|
63547
|
+
menuIconSize,
|
|
63548
|
+
loading,
|
|
63549
|
+
readOnly,
|
|
63550
|
+
disabled,
|
|
63551
|
+
// Shared paint: whatever the caller says about how the button looks is said
|
|
63552
|
+
// about both halves, or the seam shows.
|
|
63553
|
+
cta,
|
|
63554
|
+
variant,
|
|
63555
|
+
color,
|
|
63556
|
+
backgroundColor,
|
|
63557
|
+
borderColor,
|
|
63558
|
+
borderWidth,
|
|
63559
|
+
borderRadius,
|
|
63560
|
+
// A split button is a frame the user's eye reads as one box; the left half
|
|
63561
|
+
// shrinking under the finger while the right half stays put breaks it.
|
|
63562
|
+
pressEffect = "none",
|
|
63563
|
+
id,
|
|
63564
|
+
...rest
|
|
63565
|
+
} = props;
|
|
63566
|
+
// Everything the popup answers to travels to the Picker; everything else is
|
|
63567
|
+
// the split button's own box (margins, width, data-*). Sorted by name rather
|
|
63568
|
+
// than named one by one so a Picker popup prop is forwarded by adding it to
|
|
63569
|
+
// that list, not by threading it through here.
|
|
63570
|
+
const [popupProps, boxProps] = splitPopupProps(rest);
|
|
63571
|
+
// A split button is a control on the page, not a place one goes: its menu
|
|
63572
|
+
// hangs off it even on a phone, where a picker left to itself would decide a
|
|
63573
|
+
// small screen means a dialog. Passing mode="dialog" asks for that back.
|
|
63574
|
+
const mode = popupProps.mode === undefined ? "popover" : popupProps.mode;
|
|
63575
|
+
popupProps.mode = mode;
|
|
63576
|
+
// Read against the trigger in popover mode and against the VIEWPORT in
|
|
63577
|
+
// dialog mode, so this default only holds for the former — under the chevron
|
|
63578
|
+
// half, growing leftwards when the menu is wider than the button. A dialog
|
|
63579
|
+
// keeps Dialog's own "center".
|
|
63580
|
+
if (popupProps.positionArea === undefined && mode === "popover") {
|
|
63581
|
+
popupProps.positionArea = "bottom-end";
|
|
63582
|
+
}
|
|
63583
|
+
const idDefault = useId();
|
|
63584
|
+
const idResolved = id || idDefault;
|
|
63585
|
+
const menuId = `${idResolved}_menu`;
|
|
63586
|
+
const rootRef = useRef(null);
|
|
63587
|
+
const [valueState, setValueState] = useState(defaultValue === undefined ? options[0]?.value : defaultValue);
|
|
63588
|
+
const valueResolved = value === undefined ? valueState : value;
|
|
63589
|
+
const optionShown = options.find(option => option.value === valueResolved) || options[0];
|
|
63590
|
+
|
|
63591
|
+
// Busy is the control's, not the half that happens to be running: an action
|
|
63592
|
+
// given as a plain function is watched by running it from here, one given as
|
|
63593
|
+
// a navi action carries its own running state and is handed over untouched.
|
|
63594
|
+
const actionIsNaviAction = typeof action === "function" && action.isAction;
|
|
63595
|
+
const naviActionStatus = useActionStatus(actionIsNaviAction ? action : undefined);
|
|
63596
|
+
const [actionRunning, setActionRunning] = useState(false);
|
|
63597
|
+
const runAction = async (...args) => {
|
|
63598
|
+
setActionRunning(true);
|
|
63599
|
+
try {
|
|
63600
|
+
return await action(...args);
|
|
63601
|
+
} finally {
|
|
63602
|
+
setActionRunning(false);
|
|
63603
|
+
}
|
|
63604
|
+
};
|
|
63605
|
+
const actionResolved = actionIsNaviAction ? action : action ? runAction : undefined;
|
|
63606
|
+
const loadingResolved = Boolean(loading || actionRunning || naviActionStatus.loading);
|
|
63607
|
+
const halfProps = {
|
|
63608
|
+
cta,
|
|
63609
|
+
variant,
|
|
63610
|
+
color,
|
|
63611
|
+
backgroundColor,
|
|
63612
|
+
borderColor,
|
|
63613
|
+
borderWidth,
|
|
63614
|
+
borderRadius,
|
|
63615
|
+
pressEffect,
|
|
63616
|
+
loading: loadingResolved,
|
|
63617
|
+
readOnly,
|
|
63618
|
+
disabled,
|
|
63619
|
+
// The outline belongs around the pair, drawn below.
|
|
63620
|
+
loadingOutline: false
|
|
63621
|
+
};
|
|
63622
|
+
return jsxs(Box, {
|
|
63623
|
+
ref: rootRef,
|
|
63624
|
+
className: "navi_split_button",
|
|
63625
|
+
borderRadius: borderRadius,
|
|
63626
|
+
...boxProps,
|
|
63627
|
+
children: [jsx(LoadingOutline, {
|
|
63628
|
+
loading: loadingResolved,
|
|
63629
|
+
inset: -1,
|
|
63630
|
+
color: "var(--button-loader-color)"
|
|
63631
|
+
}), jsxs(Group, {
|
|
63632
|
+
children: [jsx(Button, {
|
|
63633
|
+
id: idResolved,
|
|
63634
|
+
value: valueResolved,
|
|
63635
|
+
action: actionResolved,
|
|
63636
|
+
...halfProps,
|
|
63637
|
+
children: label === undefined ? optionShown?.label : label
|
|
63638
|
+
}), jsxs("div", {
|
|
63639
|
+
className: "navi_split_button_menu",
|
|
63640
|
+
children: [jsx(Button, {
|
|
63641
|
+
icon: true,
|
|
63642
|
+
paddingX: "s",
|
|
63643
|
+
expandY: true
|
|
63644
|
+
// flex + align: the icon is the whole content, so it is laid out as
|
|
63645
|
+
// its own box in the middle of the button. Left to flow inline it
|
|
63646
|
+
// would sit on the baseline of a line of text that is not there,
|
|
63647
|
+
// with the descender space still kept below it.
|
|
63648
|
+
,
|
|
63649
|
+
|
|
63650
|
+
flex: true,
|
|
63651
|
+
align: "center",
|
|
63652
|
+
"aria-label": menuLabel,
|
|
63653
|
+
command: "--navi-open",
|
|
63654
|
+
commandFor: menuId,
|
|
63655
|
+
...halfProps,
|
|
63656
|
+
children: jsx(Icon, {
|
|
63657
|
+
size: menuIconSize,
|
|
63658
|
+
lineOverflow: "allow",
|
|
63659
|
+
children: menuIcon
|
|
63660
|
+
})
|
|
63661
|
+
}), jsx(Picker, {
|
|
63662
|
+
id: menuId,
|
|
63663
|
+
variant: "headless",
|
|
63664
|
+
allowNameless: true,
|
|
63665
|
+
anchor: rootRef,
|
|
63666
|
+
...popupProps,
|
|
63667
|
+
readOnly: readOnly,
|
|
63668
|
+
disabled: disabled,
|
|
63669
|
+
loading: loadingResolved,
|
|
63670
|
+
value: chooseEffect === "select" ? valueResolved : undefined,
|
|
63671
|
+
action: chooseEffect === "select" ? (chosenValue, event) => {
|
|
63672
|
+
setValueState(chosenValue);
|
|
63673
|
+
onValueChange?.(chosenValue, event);
|
|
63674
|
+
} : undefined,
|
|
63675
|
+
children: chooseEffect === "select" ? jsx(List, {
|
|
63676
|
+
selectable: true,
|
|
63677
|
+
command: "--navi-send",
|
|
63678
|
+
children: options.map((option, index) => {
|
|
63679
|
+
const {
|
|
63680
|
+
value: optionValue,
|
|
63681
|
+
label: optionLabel,
|
|
63682
|
+
...optionRest
|
|
63683
|
+
} = option;
|
|
63684
|
+
return jsx(List.Item, {
|
|
63685
|
+
selectable: true,
|
|
63686
|
+
id: `${menuId}_${index}`,
|
|
63687
|
+
index: index,
|
|
63688
|
+
value: optionValue,
|
|
63689
|
+
selected: optionValue === valueResolved,
|
|
63690
|
+
padding: "s",
|
|
63691
|
+
spacing: "s",
|
|
63692
|
+
...optionRest,
|
|
63693
|
+
children: optionLabel
|
|
63694
|
+
}, optionValue);
|
|
63695
|
+
})
|
|
63696
|
+
}) : jsx(List, {
|
|
63697
|
+
children: options.map((option, index) => {
|
|
63698
|
+
const {
|
|
63699
|
+
value: optionValue,
|
|
63700
|
+
label: optionLabel,
|
|
63701
|
+
...optionRest
|
|
63702
|
+
} = option;
|
|
63703
|
+
return jsx(List.Item, {
|
|
63704
|
+
id: `${menuId}_${index}`,
|
|
63705
|
+
index: index,
|
|
63706
|
+
padding: "0",
|
|
63707
|
+
children: jsx(Button, {
|
|
63708
|
+
variant: "bare",
|
|
63709
|
+
expandX: true,
|
|
63710
|
+
alignX: "start",
|
|
63711
|
+
padding: "s",
|
|
63712
|
+
pressEffect: "none",
|
|
63713
|
+
command: "--navi-close",
|
|
63714
|
+
commandFor: menuId,
|
|
63715
|
+
value: optionValue,
|
|
63716
|
+
action: actionResolved,
|
|
63717
|
+
...optionRest,
|
|
63718
|
+
children: optionLabel
|
|
63719
|
+
})
|
|
63720
|
+
}, optionValue);
|
|
63721
|
+
})
|
|
63722
|
+
})
|
|
63723
|
+
})]
|
|
63724
|
+
})]
|
|
63725
|
+
})]
|
|
63726
|
+
});
|
|
63727
|
+
};
|
|
63728
|
+
|
|
63729
|
+
// What the Picker's popup answers to — Picker's own popup props, named here so
|
|
63730
|
+
// a caller reaches all of them through the split button (see picker.jsx's JSDoc
|
|
63731
|
+
// for what each one says).
|
|
63732
|
+
const POPUP_PROP_SET = new Set(["mode", "popupLayer", "positionArea", "popoverMode", "popoverSpacing", "popupWidthFitContent", "popoverMaxHeight", "dialogMaxWidth", "dialogMaxHeight", "dialogExpand", "dialogExpandX", "dialogExpandY", "dockedOnSmallTouchScreen", "marginWithContainer", "backdropVariant", "pointerInteractionOutsideEffect", "escapeEffect", "closeOnFocusOut", "scrollCapture", "focusCapture", "popupBackgroundColor", "popupBorderRadius", "animation"]);
|
|
63733
|
+
const splitPopupProps = props => {
|
|
63734
|
+
const popupProps = {};
|
|
63735
|
+
const boxProps = {};
|
|
63736
|
+
for (const key of Object.keys(props)) {
|
|
63737
|
+
if (POPUP_PROP_SET.has(key)) {
|
|
63738
|
+
popupProps[key] = props[key];
|
|
63739
|
+
} else {
|
|
63740
|
+
boxProps[key] = props[key];
|
|
63741
|
+
}
|
|
63742
|
+
}
|
|
63743
|
+
return [popupProps, boxProps];
|
|
63744
|
+
};
|
|
63745
|
+
|
|
63174
63746
|
/**
|
|
63175
63747
|
* applySearch — matches value against searchText.
|
|
63176
63748
|
*
|
|
@@ -71839,5 +72411,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
71839
72411
|
})
|
|
71840
72412
|
});
|
|
71841
72413
|
|
|
71842
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
72414
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, SplitButton, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, errorIsDisplayed, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, markErrorAsDisplayedBy, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, smallTouchScreenSignal, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
|
|
71843
72415
|
//# sourceMappingURL=jsenv_navi.js.map
|