@jsenv/navi 0.29.84 → 0.29.86
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 +486 -109
- package/dist/jsenv_navi.js.map +34 -14
- package/docs/control_object.md +34 -0
- package/docs/control_value.md +28 -1
- package/docs/form_changed.md +4 -1
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -15122,8 +15122,8 @@ const TIME_RANGE_CONSTRAINT = {
|
|
|
15122
15122
|
console.warn(`Time after constraint: no control with id "${after}"`);
|
|
15123
15123
|
return null;
|
|
15124
15124
|
}
|
|
15125
|
-
const timeBefore = minutesFromTime(otherController.uiState);
|
|
15126
|
-
const timeAfter = minutesFromTime(field.uiState);
|
|
15125
|
+
const timeBefore = minutesFromTime$1(otherController.uiState);
|
|
15126
|
+
const timeAfter = minutesFromTime$1(field.uiState);
|
|
15127
15127
|
if (timeBefore === null || timeAfter === null) {
|
|
15128
15128
|
return null;
|
|
15129
15129
|
}
|
|
@@ -15148,7 +15148,7 @@ CONSTRAINT_ATTRIBUTE_SET.add("data-time-min-duration");
|
|
|
15148
15148
|
|
|
15149
15149
|
// "HH:MM" as a number of minutes, which is what two times are compared and
|
|
15150
15150
|
// subtracted as. Anything else is a time nobody has finished writing.
|
|
15151
|
-
const minutesFromTime = (time) => {
|
|
15151
|
+
const minutesFromTime$1 = (time) => {
|
|
15152
15152
|
if (typeof time !== "string") {
|
|
15153
15153
|
return null;
|
|
15154
15154
|
}
|
|
@@ -22827,7 +22827,15 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
22827
22827
|
};
|
|
22828
22828
|
|
|
22829
22829
|
let abortController = null;
|
|
22830
|
-
const handleRoutingTask = (
|
|
22830
|
+
const handleRoutingTask = (target, options) => {
|
|
22831
|
+
// Everything below this line reasons on the URL as a whole: it is compared
|
|
22832
|
+
// to window.location.href, looked up in the history stack, written into the
|
|
22833
|
+
// document url signal and parsed there. A relative target ("/", "../x")
|
|
22834
|
+
// would silently lose every one of those — the browser would still resolve
|
|
22835
|
+
// it in pushState, but nothing else here would. So it is resolved once, at
|
|
22836
|
+
// the single door every navigation goes through, rather than by each caller
|
|
22837
|
+
// (navBack's fallback in particular arrives here raw).
|
|
22838
|
+
const url = new URL(target, window.location.href).href;
|
|
22831
22839
|
// Decided before anything is announced: an elided push IS the traversal it
|
|
22832
22840
|
// becomes, and the traversal will make its own announcements when the
|
|
22833
22841
|
// browser answers — a before/after cycle here would be about a navigation
|
|
@@ -24992,6 +25000,7 @@ const useUIStateController = (
|
|
|
24992
25000
|
const controlType = controlInfo.controlType;
|
|
24993
25001
|
const isRadio = controlType === "input" && props.type === "radio";
|
|
24994
25002
|
const isProxy = Boolean(props["navi-control-proxy-for"]);
|
|
25003
|
+
const emptyUIState = resolveEmptyUIState(props, controlType);
|
|
24995
25004
|
|
|
24996
25005
|
const scope = useRenderScope(
|
|
24997
25006
|
// ── init: runs once on mount ───────────────────────────────────────────
|
|
@@ -25042,6 +25051,7 @@ const useUIStateController = (
|
|
|
25042
25051
|
parentUiStateSignalHolder,
|
|
25043
25052
|
isProxy,
|
|
25044
25053
|
allowNameless,
|
|
25054
|
+
emptyUIState,
|
|
25045
25055
|
// Set here too, not only in `update` below: a control rendered once and
|
|
25046
25056
|
// never re-rendered would otherwise never say whether it was GIVEN a
|
|
25047
25057
|
// value (`value`, or a bound signal with no default of its own) or is
|
|
@@ -25234,6 +25244,9 @@ const useUIStateController = (
|
|
|
25234
25244
|
}
|
|
25235
25245
|
debugUIState(e, `publishUIState(${JSON.stringify(newUIState)})`);
|
|
25236
25246
|
publishUIState(newUIState, e);
|
|
25247
|
+
// A picker hands what it holds to the control in its popup — see
|
|
25248
|
+
// useUIFacadeStateController, which is what puts this here.
|
|
25249
|
+
controller.pushStateDownToFacadeChild?.(newUIState, e);
|
|
25237
25250
|
const el = controller.ref.current;
|
|
25238
25251
|
// Always notify the element that its UI state changed.
|
|
25239
25252
|
// Listeners use this to stay in sync (e.g. input_effect.js tracks currentState,
|
|
@@ -25425,12 +25438,7 @@ const useUIStateController = (
|
|
|
25425
25438
|
return true;
|
|
25426
25439
|
},
|
|
25427
25440
|
clearUIState: (e) => {
|
|
25428
|
-
|
|
25429
|
-
// Passing `""` would set checked=true because `"" !== undefined`.
|
|
25430
|
-
const isCheckable =
|
|
25431
|
-
controlType === "input" &&
|
|
25432
|
-
(props.type === "radio" || props.type === "checkbox");
|
|
25433
|
-
controller.setUIState(isCheckable ? undefined : "", e);
|
|
25441
|
+
controller.setUIState(resolveClearedUIState(controller), e);
|
|
25434
25442
|
},
|
|
25435
25443
|
resetUIState: (e) => {
|
|
25436
25444
|
controller.setUIState(controller.state, e);
|
|
@@ -25507,6 +25515,7 @@ const useUIStateController = (
|
|
|
25507
25515
|
controller.ref = props.ref;
|
|
25508
25516
|
controller.id = props.id; // never supposed to change, not supported for now
|
|
25509
25517
|
controller.name = props.name;
|
|
25518
|
+
controller.emptyUIState = emptyUIState;
|
|
25510
25519
|
controller.parentUIStateController = parentUIStateController;
|
|
25511
25520
|
const {
|
|
25512
25521
|
value,
|
|
@@ -25697,14 +25706,18 @@ const GROUP_DEFAULTS = {
|
|
|
25697
25706
|
single: {
|
|
25698
25707
|
// The same exclusions canRegisterAsFacadeChild already makes below (the
|
|
25699
25708
|
// picker façade asked the very same question: which child IS the value).
|
|
25700
|
-
// Buttons and links never hold one
|
|
25701
|
-
//
|
|
25702
|
-
//
|
|
25703
|
-
//
|
|
25709
|
+
// Buttons and links never hold one, and neither does a control that
|
|
25710
|
+
// declared itself nameless. A control *carrying* navi-list is the search
|
|
25711
|
+
// box driving some other list — not the list itself, which stays a
|
|
25712
|
+
// perfectly good single value here (one item, or the array a multiple list
|
|
25713
|
+
// exposes). Excluding the searcher is what leaves the list alone.
|
|
25704
25714
|
childControlFilter: (child) => {
|
|
25705
25715
|
if (child.controlType === "button" || child.controlType === "link") {
|
|
25706
25716
|
return false;
|
|
25707
25717
|
}
|
|
25718
|
+
if (child.allowNameless) {
|
|
25719
|
+
return false;
|
|
25720
|
+
}
|
|
25708
25721
|
if (child.props?.["navi-list"]) {
|
|
25709
25722
|
return false;
|
|
25710
25723
|
}
|
|
@@ -25737,7 +25750,14 @@ const GROUP_DEFAULTS = {
|
|
|
25737
25750
|
aggregateChildStates: (children) => {
|
|
25738
25751
|
const groupValues = {};
|
|
25739
25752
|
for (const child of children) {
|
|
25740
|
-
const { name,
|
|
25753
|
+
const { name, allowNameless, emptyUIState } = child;
|
|
25754
|
+
// A control holding nothing writes its own empty, not a hole: the key
|
|
25755
|
+
// is in the object either way, and what is read from it keeps the shape
|
|
25756
|
+
// the reader was promised (see resolveEmptyUIState).
|
|
25757
|
+
const uiState =
|
|
25758
|
+
child.uiState === undefined && emptyUIState !== undefined
|
|
25759
|
+
? emptyUIState
|
|
25760
|
+
: child.uiState;
|
|
25741
25761
|
if (!name) {
|
|
25742
25762
|
// A nameless GROUP is a grouping, not a value: it exists to hold its
|
|
25743
25763
|
// children together (a WheelGroup sharing navigation, a fieldset-ish
|
|
@@ -25985,6 +26005,25 @@ const useUIGroupStateController = (
|
|
|
25985
26005
|
ref,
|
|
25986
26006
|
getPropFromState: (uiState) => uiState,
|
|
25987
26007
|
distributeChildUIState: resolvedDistributeChildUIState,
|
|
26008
|
+
// Where the group puts a value on ONE child: the only place that knows
|
|
26009
|
+
// what each child gets, and the only one that sees a child it cannot
|
|
26010
|
+
// place — see warnChildAnswersForItself.
|
|
26011
|
+
placeChildUIState: (childUIStateController, groupUIState, e) => {
|
|
26012
|
+
if (!shouldPropagateStateToChild(childUIStateController)) {
|
|
26013
|
+
return;
|
|
26014
|
+
}
|
|
26015
|
+
if (childUIStateController.hasStateProp) {
|
|
26016
|
+
return;
|
|
26017
|
+
}
|
|
26018
|
+
const childNewState = resolvedDistributeChildUIState(
|
|
26019
|
+
groupUIState,
|
|
26020
|
+
childUIStateController,
|
|
26021
|
+
);
|
|
26022
|
+
if (childNewState === CANNOT_DERIVE) {
|
|
26023
|
+
return;
|
|
26024
|
+
}
|
|
26025
|
+
childUIStateController.setUIState(childNewState, e);
|
|
26026
|
+
},
|
|
25988
26027
|
setUIState: (newUIState, e) => {
|
|
25989
26028
|
if (
|
|
25990
26029
|
stateType === "object" &&
|
|
@@ -26017,14 +26056,9 @@ const useUIGroupStateController = (
|
|
|
26017
26056
|
});
|
|
26018
26057
|
chainEvent(propagateDownEvent, e);
|
|
26019
26058
|
for (const childUIStateController of childUIStateControllerArray) {
|
|
26020
|
-
|
|
26021
|
-
const childNewState = resolvedDistributeChildUIState(
|
|
26022
|
-
newUIState,
|
|
26059
|
+
controller.placeChildUIState(
|
|
26023
26060
|
childUIStateController,
|
|
26024
|
-
|
|
26025
|
-
if (childNewState === CANNOT_DERIVE) continue;
|
|
26026
|
-
childUIStateController.setUIState(
|
|
26027
|
-
childNewState,
|
|
26061
|
+
newUIState,
|
|
26028
26062
|
propagateDownEvent,
|
|
26029
26063
|
);
|
|
26030
26064
|
}
|
|
@@ -26083,27 +26117,17 @@ const useUIGroupStateController = (
|
|
|
26083
26117
|
debugUIGroup(
|
|
26084
26118
|
`${controlType}.registerChild("${childControlType}") -> registered (total: ${childUIStateControllerArray.length})`,
|
|
26085
26119
|
);
|
|
26086
|
-
if (
|
|
26120
|
+
if (controller.hasValueProp || controller.hasDefaultValueProp) {
|
|
26087
26121
|
const initialEvent = new CustomEvent("initial_state_push", {
|
|
26088
26122
|
detail: {},
|
|
26089
26123
|
});
|
|
26090
|
-
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
|
|
26094
|
-
|
|
26095
|
-
|
|
26096
|
-
|
|
26097
|
-
}
|
|
26098
|
-
} else if (controller.hasDefaultValueProp) {
|
|
26099
|
-
const childNewState = resolvedDistributeChildUIState(
|
|
26100
|
-
controller.defaultValue,
|
|
26101
|
-
childUIStateController,
|
|
26102
|
-
);
|
|
26103
|
-
if (childNewState !== CANNOT_DERIVE) {
|
|
26104
|
-
childUIStateController.setUIState(childNewState, initialEvent);
|
|
26105
|
-
}
|
|
26106
|
-
}
|
|
26124
|
+
controller.placeChildUIState(
|
|
26125
|
+
childUIStateController,
|
|
26126
|
+
controller.hasValueProp
|
|
26127
|
+
? controller.value
|
|
26128
|
+
: controller.defaultValue,
|
|
26129
|
+
initialEvent,
|
|
26130
|
+
);
|
|
26107
26131
|
}
|
|
26108
26132
|
onChange(new CustomEvent(`${childControlType}_mount`), {
|
|
26109
26133
|
notifyExternal: "silent",
|
|
@@ -26282,14 +26306,11 @@ const useUIGroupStateController = (
|
|
|
26282
26306
|
{ detail: {} },
|
|
26283
26307
|
);
|
|
26284
26308
|
for (const childUIStateController of childUIStateControllerArray) {
|
|
26285
|
-
|
|
26286
|
-
if (childUIStateController.hasStateProp) continue;
|
|
26287
|
-
const childNewState = controller.distributeChildUIState(
|
|
26288
|
-
value,
|
|
26309
|
+
controller.placeChildUIState(
|
|
26289
26310
|
childUIStateController,
|
|
26311
|
+
value,
|
|
26312
|
+
propagateDownEvent,
|
|
26290
26313
|
);
|
|
26291
|
-
if (childNewState === CANNOT_DERIVE) continue;
|
|
26292
|
-
childUIStateController.setUIState(childNewState, propagateDownEvent);
|
|
26293
26314
|
}
|
|
26294
26315
|
controller.syncInternalState(value);
|
|
26295
26316
|
}
|
|
@@ -26307,14 +26328,11 @@ const useUIGroupStateController = (
|
|
|
26307
26328
|
{ detail: {} },
|
|
26308
26329
|
);
|
|
26309
26330
|
for (const childUIStateController of childUIStateControllerArray) {
|
|
26310
|
-
|
|
26311
|
-
if (childUIStateController.hasStateProp) continue;
|
|
26312
|
-
const childNewState = controller.distributeChildUIState(
|
|
26313
|
-
defaultValue,
|
|
26331
|
+
controller.placeChildUIState(
|
|
26314
26332
|
childUIStateController,
|
|
26333
|
+
defaultValue,
|
|
26334
|
+
propagateDownEvent,
|
|
26315
26335
|
);
|
|
26316
|
-
if (childNewState === CANNOT_DERIVE) continue;
|
|
26317
|
-
childUIStateController.setUIState(childNewState, propagateDownEvent);
|
|
26318
26336
|
}
|
|
26319
26337
|
controller.syncInternalState(defaultValue);
|
|
26320
26338
|
}
|
|
@@ -26432,6 +26450,12 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
26432
26450
|
if (childController.controlType === "link") return false;
|
|
26433
26451
|
if (childController.controlType === "facade") return false;
|
|
26434
26452
|
if (childController.isProxy) return false;
|
|
26453
|
+
if (childController.allowNameless) {
|
|
26454
|
+
// A control saying it is not a field is not the one the picker talks
|
|
26455
|
+
// to: the search box above the list, the "select all" switch beside
|
|
26456
|
+
// it. It is there to help find the answer, not to be it.
|
|
26457
|
+
return false;
|
|
26458
|
+
}
|
|
26435
26459
|
if (childController.props["navi-list"]) {
|
|
26436
26460
|
// Controls with navi-list act as standalone list navigators and should
|
|
26437
26461
|
// not be treated as the picker's synced child.
|
|
@@ -26440,9 +26464,38 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
26440
26464
|
return true;
|
|
26441
26465
|
};
|
|
26442
26466
|
|
|
26467
|
+
// Picker → child. Handed to the real controller during THIS render so it
|
|
26468
|
+
// is in place before any layout effect runs: the value a parent form
|
|
26469
|
+
// distributes reaches its children from their registration effect, which
|
|
26470
|
+
// fires before the picker's own effects — a façade that only started
|
|
26471
|
+
// listening from an effect of its own was told nothing, and the popup
|
|
26472
|
+
// opened empty on a value the picker already held.
|
|
26473
|
+
const pushStateDownToChild = (newUIState, e) => {
|
|
26474
|
+
if (updatingRef.current) {
|
|
26475
|
+
return;
|
|
26476
|
+
}
|
|
26477
|
+
const child = firstChildControllerRef.current;
|
|
26478
|
+
if (!child) {
|
|
26479
|
+
return;
|
|
26480
|
+
}
|
|
26481
|
+
updatingRef.current = true;
|
|
26482
|
+
const propagateEventType =
|
|
26483
|
+
e.type === "initial_state_push"
|
|
26484
|
+
? "initial_state_push"
|
|
26485
|
+
: "propagate_down_set_ui_state";
|
|
26486
|
+
const propagateDownEvent = new CustomEvent(propagateEventType, {
|
|
26487
|
+
detail: {},
|
|
26488
|
+
});
|
|
26489
|
+
chainEvent(propagateDownEvent, e);
|
|
26490
|
+
child.setUIState(newUIState, propagateDownEvent);
|
|
26491
|
+
updatingRef.current = false;
|
|
26492
|
+
};
|
|
26493
|
+
realUIStateController.pushStateDownToFacadeChild = pushStateDownToChild;
|
|
26494
|
+
|
|
26443
26495
|
const facadeUIStateController = {
|
|
26444
26496
|
controlType: "facade",
|
|
26445
26497
|
props,
|
|
26498
|
+
pushStateDownToChild,
|
|
26446
26499
|
ref: realUIStateController.ref,
|
|
26447
26500
|
uiStateSignal: realUIStateController.uiStateSignal,
|
|
26448
26501
|
controlHostProps: realUIStateController.controlHostProps,
|
|
@@ -26456,7 +26509,8 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
26456
26509
|
`[navi] a second control ("${childType}"${child.name ? ` name="${child.name}"` : ""}) registered in the ${describePicker(props)} popup. ` +
|
|
26457
26510
|
`A picker talks to ONE control: the first one receives the picker's whole value and is the only one read back, ` +
|
|
26458
26511
|
`so this one is neither filled nor collected. ` +
|
|
26459
|
-
`A popup holding several values needs one group around them — wrap them in a <ControlGroup>, name each control inside it, and give the picker type="object"
|
|
26512
|
+
`A popup holding several values needs one group around them — wrap them in a <ControlGroup>, name each control inside it, and give the picker type="object". ` +
|
|
26513
|
+
`A control that is there to FIND the answer rather than be it (a search box, a "select all") says so with allowNameless and steps out of the way.`,
|
|
26460
26514
|
child,
|
|
26461
26515
|
);
|
|
26462
26516
|
} else {
|
|
@@ -26556,6 +26610,8 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
26556
26610
|
s.controller.ref = realUIStateController.ref;
|
|
26557
26611
|
s.controller.uiStateSignal = realUIStateController.uiStateSignal;
|
|
26558
26612
|
s.controller.controlHostProps = realUIStateController.controlHostProps;
|
|
26613
|
+
realUIStateController.pushStateDownToFacadeChild =
|
|
26614
|
+
s.controller.pushStateDownToChild;
|
|
26559
26615
|
|
|
26560
26616
|
return {
|
|
26561
26617
|
realUIStateController,
|
|
@@ -26563,26 +26619,6 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
26563
26619
|
},
|
|
26564
26620
|
);
|
|
26565
26621
|
|
|
26566
|
-
useLayoutEffect(() => {
|
|
26567
|
-
return realUIStateController.subscribe((newUIState, e) => {
|
|
26568
|
-
if (updatingRef.current) {
|
|
26569
|
-
return;
|
|
26570
|
-
}
|
|
26571
|
-
const child = firstChildControllerRef.current;
|
|
26572
|
-
if (!child) {
|
|
26573
|
-
return;
|
|
26574
|
-
}
|
|
26575
|
-
updatingRef.current = true;
|
|
26576
|
-
const propagateDownEvent = new CustomEvent(
|
|
26577
|
-
"propagate_down_set_ui_state",
|
|
26578
|
-
{ detail: {} },
|
|
26579
|
-
);
|
|
26580
|
-
chainEvent(propagateDownEvent, e);
|
|
26581
|
-
child.setUIState(newUIState, propagateDownEvent);
|
|
26582
|
-
updatingRef.current = false;
|
|
26583
|
-
});
|
|
26584
|
-
}, [realUIStateController]);
|
|
26585
|
-
|
|
26586
26622
|
return scope.controller;
|
|
26587
26623
|
};
|
|
26588
26624
|
|
|
@@ -26677,6 +26713,60 @@ const dispatchSyntheticInput = (el, inputEvent, causeEvent) => {
|
|
|
26677
26713
|
el.dispatchEvent(inputEvent);
|
|
26678
26714
|
};
|
|
26679
26715
|
|
|
26716
|
+
/**
|
|
26717
|
+
* What a control is worth when it holds nothing — nothing, in the shape of the
|
|
26718
|
+
* question it answers. A list of days nobody picked is an empty list, not an
|
|
26719
|
+
* empty string; a yes/no nobody said yes to is `false`. Without this the shape
|
|
26720
|
+
* changes under the reader as soon as the answer is empty, and the conversion
|
|
26721
|
+
* back gets written after the wrong value has already been sent.
|
|
26722
|
+
*
|
|
26723
|
+
* `undefined` means the control has no empty of its own — it is simply not
|
|
26724
|
+
* there, which is what an untouched date or an unchecked radio is.
|
|
26725
|
+
*/
|
|
26726
|
+
const resolveEmptyUIState = (props, controlType) => {
|
|
26727
|
+
if (controlType === "input" && props.type === "checkbox") {
|
|
26728
|
+
// A checkbox is a member of a set, the way HTML has it: checked it sends
|
|
26729
|
+
// its value ("on" by default), unchecked it sends nothing at all. Only one
|
|
26730
|
+
// holding `true` is a yes/no, and a yes/no nobody said yes to is `false`.
|
|
26731
|
+
return props.value === true ? false : undefined;
|
|
26732
|
+
}
|
|
26733
|
+
const stateShape = props["navi-state-shape"];
|
|
26734
|
+
if (stateShape === "array") {
|
|
26735
|
+
return EMPTY_ARRAY;
|
|
26736
|
+
}
|
|
26737
|
+
if (stateShape === "object") {
|
|
26738
|
+
return EMPTY_OBJECT;
|
|
26739
|
+
}
|
|
26740
|
+
return undefined;
|
|
26741
|
+
};
|
|
26742
|
+
|
|
26743
|
+
// What a cleared control shows: its own empty, kept in the shape it was holding.
|
|
26744
|
+
const resolveClearedUIState = (controller) => {
|
|
26745
|
+
const { controlType, props } = controller;
|
|
26746
|
+
if (
|
|
26747
|
+
controlType === "input" &&
|
|
26748
|
+
(props.type === "radio" || props.type === "checkbox")
|
|
26749
|
+
) {
|
|
26750
|
+
// Unchecked is `undefined`: any other value reads as checked, `false`
|
|
26751
|
+
// included (see the `checked` line in control_hooks' toDomProps). What such
|
|
26752
|
+
// a control is worth once unchecked is `emptyUIState`, read where the value
|
|
26753
|
+
// is collected rather than stored here.
|
|
26754
|
+
return undefined;
|
|
26755
|
+
}
|
|
26756
|
+
const { emptyUIState } = controller;
|
|
26757
|
+
if (emptyUIState !== undefined) {
|
|
26758
|
+
return emptyUIState;
|
|
26759
|
+
}
|
|
26760
|
+
const currentUIState = controller.uiState;
|
|
26761
|
+
if (Array.isArray(currentUIState)) {
|
|
26762
|
+
return EMPTY_ARRAY;
|
|
26763
|
+
}
|
|
26764
|
+
if (currentUIState !== null && typeof currentUIState === "object") {
|
|
26765
|
+
return EMPTY_OBJECT;
|
|
26766
|
+
}
|
|
26767
|
+
return "";
|
|
26768
|
+
};
|
|
26769
|
+
|
|
26680
26770
|
// What a control says when it has nothing to say: no value at all, or the empty
|
|
26681
26771
|
// array/object a group falls back to while it has no child to aggregate.
|
|
26682
26772
|
const uiStateHoldsNothing = (uiState) => {
|
|
@@ -50575,6 +50665,55 @@ const getNowHoursRoundedToStep = (stepMinutes, offsetMinutes = 0) => {
|
|
|
50575
50665
|
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
|
|
50576
50666
|
};
|
|
50577
50667
|
|
|
50668
|
+
/**
|
|
50669
|
+
* "HH:MM" and its two numbers, in both directions — what any control made of an
|
|
50670
|
+
* hour beside a minute (fields, wheels) aggregates to and is placed from. Held
|
|
50671
|
+
* as numbers, written on two digits: how they are shown is each control's own
|
|
50672
|
+
* business.
|
|
50673
|
+
*/
|
|
50674
|
+
const parseTimeParts = (time) => {
|
|
50675
|
+
if (typeof time !== "string") {
|
|
50676
|
+
return null;
|
|
50677
|
+
}
|
|
50678
|
+
const match = /^(\d{1,2}):(\d{1,2})/.exec(time);
|
|
50679
|
+
if (!match) {
|
|
50680
|
+
return null;
|
|
50681
|
+
}
|
|
50682
|
+
return { hour: Number(match[1]), minute: Number(match[2]) };
|
|
50683
|
+
};
|
|
50684
|
+
|
|
50685
|
+
// Half a time is not a time: a control holding one of the two and nothing in
|
|
50686
|
+
// the other has no value at all, and a form has nothing to send about it.
|
|
50687
|
+
const formatTimeParts = (hour, minute) => {
|
|
50688
|
+
if (
|
|
50689
|
+
hour === "" ||
|
|
50690
|
+
hour === undefined ||
|
|
50691
|
+
minute === "" ||
|
|
50692
|
+
minute === undefined
|
|
50693
|
+
) {
|
|
50694
|
+
return undefined;
|
|
50695
|
+
}
|
|
50696
|
+
return `${padTwo$1(hour)}:${padTwo$1(minute)}`;
|
|
50697
|
+
};
|
|
50698
|
+
|
|
50699
|
+
const minutesFromTime = (time) => {
|
|
50700
|
+
const parts = parseTimeParts(time);
|
|
50701
|
+
if (!parts) {
|
|
50702
|
+
return null;
|
|
50703
|
+
}
|
|
50704
|
+
return parts.hour * 60 + parts.minute;
|
|
50705
|
+
};
|
|
50706
|
+
|
|
50707
|
+
const timeFromMinutes = (minutes) => {
|
|
50708
|
+
const inDay =
|
|
50709
|
+
((minutes % MINUTES_PER_DAY) + MINUTES_PER_DAY) % MINUTES_PER_DAY;
|
|
50710
|
+
return `${padTwo$1(Math.floor(inDay / 60))}:${padTwo$1(inDay % 60)}`;
|
|
50711
|
+
};
|
|
50712
|
+
|
|
50713
|
+
const MINUTES_PER_DAY = 24 * 60;
|
|
50714
|
+
|
|
50715
|
+
const padTwo$1 = (value) => String(value).padStart(2, "0");
|
|
50716
|
+
|
|
50578
50717
|
// Maps validity type names → navi input type names.
|
|
50579
50718
|
// Numeric signal types must not fall through to the native type="number"
|
|
50580
50719
|
// (which adds spinner buttons and has poor UX) — they map to navi_number instead.
|
|
@@ -55408,7 +55547,12 @@ installImportMetaCssBuild(import.meta);const css$z = /* css */`
|
|
|
55408
55547
|
/* The list scrolls inside the popover */
|
|
55409
55548
|
.navi_list_container {
|
|
55410
55549
|
width: 100%;
|
|
55411
|
-
border-radius:
|
|
55550
|
+
/* The list's radius var, not border-radius itself: the longhands it
|
|
55551
|
+
feeds are what read the --x-corner-*-radius claims coming from
|
|
55552
|
+
outside (a header/footer covering a corner, a flush body — see
|
|
55553
|
+
box.jsx). Writing the shorthand here would flatten those four
|
|
55554
|
+
longhands back to one curve and square nothing. */
|
|
55555
|
+
--list-border-radius: max(
|
|
55412
55556
|
0px,
|
|
55413
55557
|
var(--picker-border-radius) - var(--picker-border-width)
|
|
55414
55558
|
);
|
|
@@ -55474,7 +55618,9 @@ installImportMetaCssBuild(import.meta);const css$z = /* css */`
|
|
|
55474
55618
|
|
|
55475
55619
|
.navi_list_container {
|
|
55476
55620
|
width: 100%;
|
|
55477
|
-
|
|
55621
|
+
/* See the popover block above: the var, not the shorthand, so the
|
|
55622
|
+
corner claims survive. */
|
|
55623
|
+
--list-border-radius: max(
|
|
55478
55624
|
0px,
|
|
55479
55625
|
var(--picker-border-radius) - var(--picker-border-width)
|
|
55480
55626
|
);
|
|
@@ -61992,8 +62138,7 @@ const PickerNaviTime = props => {
|
|
|
61992
62138
|
const {
|
|
61993
62139
|
min = "00:00",
|
|
61994
62140
|
max = "23:30",
|
|
61995
|
-
step
|
|
61996
|
-
value
|
|
62141
|
+
step
|
|
61997
62142
|
} = props;
|
|
61998
62143
|
const stepSeconds = timeStringToSeconds(step) ?? 1800;
|
|
61999
62144
|
const slots = useMemo(() => generateTimeSlots(min, max, stepSeconds), [min, max, stepSeconds]);
|
|
@@ -62008,7 +62153,6 @@ const PickerNaviTime = props => {
|
|
|
62008
62153
|
id: slot,
|
|
62009
62154
|
index: i,
|
|
62010
62155
|
value: slot,
|
|
62011
|
-
selected: value === slot,
|
|
62012
62156
|
children: jsx(Time, {
|
|
62013
62157
|
type: "time",
|
|
62014
62158
|
children: slot
|
|
@@ -62507,7 +62651,8 @@ const PickerObject = props => {
|
|
|
62507
62651
|
return jsx(Next, {
|
|
62508
62652
|
ui: jsx(PickerObjectUI, {}),
|
|
62509
62653
|
...props,
|
|
62510
|
-
type: "navi_js"
|
|
62654
|
+
type: "navi_js",
|
|
62655
|
+
"navi-state-shape": "object"
|
|
62511
62656
|
});
|
|
62512
62657
|
};
|
|
62513
62658
|
const PickerObjectUI = () => {
|
|
@@ -62542,7 +62687,8 @@ const PickerArray = props => {
|
|
|
62542
62687
|
maxLines: "3",
|
|
62543
62688
|
ui: jsx(PickerArrayUI, {}),
|
|
62544
62689
|
...props,
|
|
62545
|
-
type: "navi_js"
|
|
62690
|
+
type: "navi_js",
|
|
62691
|
+
"navi-state-shape": "array"
|
|
62546
62692
|
});
|
|
62547
62693
|
};
|
|
62548
62694
|
const PickerArrayUI = () => {
|
|
@@ -65016,8 +65162,8 @@ const TimeSpin = ({
|
|
|
65016
65162
|
minuteLabel = naviI18n("time.minute_label"),
|
|
65017
65163
|
...rest
|
|
65018
65164
|
}) => jsxs(SpinGroup, {
|
|
65019
|
-
aggregateChildStates: aggregateTime,
|
|
65020
|
-
distributeChildUIState: distributeTime,
|
|
65165
|
+
aggregateChildStates: aggregateTime$1,
|
|
65166
|
+
distributeChildUIState: distributeTime$1,
|
|
65021
65167
|
...rest,
|
|
65022
65168
|
children: [jsx(NumberSpin, {
|
|
65023
65169
|
name: "hour",
|
|
@@ -65043,9 +65189,8 @@ const TimeSpin = ({
|
|
|
65043
65189
|
})]
|
|
65044
65190
|
});
|
|
65045
65191
|
|
|
65046
|
-
// The two fields as one value, "HH:MM"
|
|
65047
|
-
|
|
65048
|
-
const aggregateTime = childUIStateControllers => {
|
|
65192
|
+
// The two fields as one value, "HH:MM".
|
|
65193
|
+
const aggregateTime$1 = childUIStateControllers => {
|
|
65049
65194
|
let hour = "";
|
|
65050
65195
|
let minute = "";
|
|
65051
65196
|
for (const child of childUIStateControllers) {
|
|
@@ -65056,37 +65201,18 @@ const aggregateTime = childUIStateControllers => {
|
|
|
65056
65201
|
minute = child.uiState ?? "";
|
|
65057
65202
|
}
|
|
65058
65203
|
}
|
|
65059
|
-
|
|
65060
|
-
return undefined;
|
|
65061
|
-
}
|
|
65062
|
-
return `${padTwo(hour)}:${padTwo(minute)}`;
|
|
65204
|
+
return formatTimeParts(hour, minute);
|
|
65063
65205
|
};
|
|
65064
65206
|
|
|
65065
65207
|
// The way back: what the group is set to (a picked value, a form being reset)
|
|
65066
65208
|
// lands on the field it belongs to.
|
|
65067
|
-
const distributeTime = (groupState, childUIStateController) => {
|
|
65068
|
-
const parts =
|
|
65209
|
+
const distributeTime$1 = (groupState, childUIStateController) => {
|
|
65210
|
+
const parts = parseTimeParts(groupState);
|
|
65069
65211
|
if (!parts) {
|
|
65070
65212
|
return undefined;
|
|
65071
65213
|
}
|
|
65072
65214
|
return parts[childUIStateController.name];
|
|
65073
65215
|
};
|
|
65074
|
-
const parseTime = time => {
|
|
65075
|
-
if (typeof time !== "string") {
|
|
65076
|
-
return null;
|
|
65077
|
-
}
|
|
65078
|
-
const match = /^(\d{1,2}):(\d{1,2})/.exec(time);
|
|
65079
|
-
if (!match) {
|
|
65080
|
-
return null;
|
|
65081
|
-
}
|
|
65082
|
-
// Numbers: what an hour and a minute are held as. How they are written —
|
|
65083
|
-
// "07" — is the field's business (see NumberSpin's `pad`).
|
|
65084
|
-
return {
|
|
65085
|
-
hour: Number(match[1]),
|
|
65086
|
-
minute: Number(match[2])
|
|
65087
|
-
};
|
|
65088
|
-
};
|
|
65089
|
-
const padTwo = value => String(value).padStart(2, "0");
|
|
65090
65216
|
|
|
65091
65217
|
/**
|
|
65092
65218
|
* @type {import("ignore:preact").FunctionComponent<{
|
|
@@ -66643,7 +66769,6 @@ const SplitButton = props => {
|
|
|
66643
66769
|
id: `${menuId}_${index}`,
|
|
66644
66770
|
index: index,
|
|
66645
66771
|
value: optionValue,
|
|
66646
|
-
selected: optionValue === valueResolved,
|
|
66647
66772
|
padding: "s",
|
|
66648
66773
|
spacing: "s",
|
|
66649
66774
|
...optionRest,
|
|
@@ -69337,6 +69462,258 @@ const WheelColon = props => {
|
|
|
69337
69462
|
};
|
|
69338
69463
|
Wheel.Colon = WheelColon;
|
|
69339
69464
|
|
|
69465
|
+
/**
|
|
69466
|
+
* A time of day, and a span between two of them, set by turning rather than by
|
|
69467
|
+
* typing. A wheel only ever shows values that exist: there is no half-written
|
|
69468
|
+
* hour to bound and correct under the fingers, which is what a time typed digit
|
|
69469
|
+
* by digit puts a field through ("1" on its way to "18").
|
|
69470
|
+
*
|
|
69471
|
+
* `TimeWheel` is a `WheelGroup` of two `Wheel`s and carries a single "HH:MM",
|
|
69472
|
+
* like `TimeSpin` — the two are interchangeable in a form. `TimeRangeWheel` is
|
|
69473
|
+
* two of those and carries `{ start, end }`, with the rule such a pair always
|
|
69474
|
+
* has: the end comes after the start. Here that rule is lived rather than
|
|
69475
|
+
* checked — the bounds push each other while they turn, so what the wheels show
|
|
69476
|
+
* is always a span. The send-time constraint stays underneath for what pushing
|
|
69477
|
+
* cannot fix (a start so late the span no longer fits in the day).
|
|
69478
|
+
*/
|
|
69479
|
+
|
|
69480
|
+
const HOUR_COUNT = 24;
|
|
69481
|
+
const MINUTES_PER_HOUR = 60;
|
|
69482
|
+
const LAST_MINUTE_OF_DAY = 23 * 60 + 59;
|
|
69483
|
+
|
|
69484
|
+
/**
|
|
69485
|
+
* @type {import("ignore:preact").FunctionComponent<{
|
|
69486
|
+
* name?: string,
|
|
69487
|
+
* value?: string,
|
|
69488
|
+
* defaultValue?: string,
|
|
69489
|
+
* signal?: import("@preact/signals").Signal<string>,
|
|
69490
|
+
* minuteStep?: number,
|
|
69491
|
+
* loop?: boolean,
|
|
69492
|
+
* separator?: import("ignore:preact").ComponentChildren,
|
|
69493
|
+
* hourLabel?: string,
|
|
69494
|
+
* minuteLabel?: string,
|
|
69495
|
+
* size?: string,
|
|
69496
|
+
* visibleCount?: number,
|
|
69497
|
+
* wheelProps?: object,
|
|
69498
|
+
* [key: string]: any,
|
|
69499
|
+
* }>}
|
|
69500
|
+
* @param {string} [value] The time shown, as "HH:MM".
|
|
69501
|
+
* @param {number} [minuteStep=1] How many minutes apart the values on the
|
|
69502
|
+
* minute wheel are — 15 for quarters of an hour.
|
|
69503
|
+
* @param {boolean} [loop=true] The wheels go round: 23h then 0h, 59 minutes
|
|
69504
|
+
* then 0. What a clock does. Say `loop={false}` for two ends one cannot turn
|
|
69505
|
+
* past.
|
|
69506
|
+
* @param {import("ignore:preact").ComponentChildren} [separator] What is written
|
|
69507
|
+
* between the hours and the minutes. "h" in French, ":" elsewhere.
|
|
69508
|
+
* @param {object} [wheelProps] Anything a `Wheel` takes, said once for both of
|
|
69509
|
+
* them — `visibleCount`, `itemWidth`, `glideSpeed`.
|
|
69510
|
+
*/
|
|
69511
|
+
const TimeWheel = ({
|
|
69512
|
+
minuteStep = 1,
|
|
69513
|
+
loop = true,
|
|
69514
|
+
separator = naviI18n("time.hour_separator"),
|
|
69515
|
+
hourLabel = naviI18n("time.hour_label"),
|
|
69516
|
+
minuteLabel = naviI18n("time.minute_label"),
|
|
69517
|
+
size,
|
|
69518
|
+
wheelProps,
|
|
69519
|
+
...rest
|
|
69520
|
+
}) => {
|
|
69521
|
+
const minutes = useMemo(() => {
|
|
69522
|
+
const minuteList = [];
|
|
69523
|
+
let minute = 0;
|
|
69524
|
+
while (minute < MINUTES_PER_HOUR) {
|
|
69525
|
+
minuteList.push(minute);
|
|
69526
|
+
minute += minuteStep;
|
|
69527
|
+
}
|
|
69528
|
+
return minuteList;
|
|
69529
|
+
}, [minuteStep]);
|
|
69530
|
+
return jsxs(WheelGroup, {
|
|
69531
|
+
aggregateChildStates: aggregateTime,
|
|
69532
|
+
distributeChildUIState: distributeTime,
|
|
69533
|
+
...rest,
|
|
69534
|
+
children: [jsx(Wheel, {
|
|
69535
|
+
name: "hour",
|
|
69536
|
+
type: "integer",
|
|
69537
|
+
bounded: !loop,
|
|
69538
|
+
size: size,
|
|
69539
|
+
"aria-label": hourLabel,
|
|
69540
|
+
...wheelProps,
|
|
69541
|
+
children: HOURS.map(hour => jsx(Wheel.Item, {
|
|
69542
|
+
value: hour,
|
|
69543
|
+
paddingX: "s",
|
|
69544
|
+
children: padTwo(hour)
|
|
69545
|
+
}, hour))
|
|
69546
|
+
}), jsx(WheelGroup.Separator, {
|
|
69547
|
+
size: size,
|
|
69548
|
+
children: separator
|
|
69549
|
+
}), jsx(Wheel, {
|
|
69550
|
+
name: "minute",
|
|
69551
|
+
type: "integer",
|
|
69552
|
+
bounded: !loop,
|
|
69553
|
+
size: size,
|
|
69554
|
+
"aria-label": minuteLabel,
|
|
69555
|
+
...wheelProps,
|
|
69556
|
+
children: minutes.map(minute => jsx(Wheel.Item, {
|
|
69557
|
+
value: minute,
|
|
69558
|
+
paddingX: "s",
|
|
69559
|
+
children: padTwo(minute)
|
|
69560
|
+
}, minute))
|
|
69561
|
+
})]
|
|
69562
|
+
});
|
|
69563
|
+
};
|
|
69564
|
+
|
|
69565
|
+
/**
|
|
69566
|
+
* @type {import("ignore:preact").FunctionComponent<{
|
|
69567
|
+
* name?: string,
|
|
69568
|
+
* value?: { start?: string, end?: string },
|
|
69569
|
+
* defaultValue?: { start?: string, end?: string },
|
|
69570
|
+
* signal?: import("@preact/signals").Signal<{ start?: string, end?: string }>,
|
|
69571
|
+
* minuteStep?: number,
|
|
69572
|
+
* minDuration?: number,
|
|
69573
|
+
* loop?: boolean,
|
|
69574
|
+
* size?: string,
|
|
69575
|
+
* startLabel?: import("ignore:preact").ComponentChildren,
|
|
69576
|
+
* endLabel?: import("ignore:preact").ComponentChildren,
|
|
69577
|
+
* timeProps?: object,
|
|
69578
|
+
* [key: string]: any,
|
|
69579
|
+
* }>}
|
|
69580
|
+
* @param {{ start?: string, end?: string }} [value] The span shown, as two
|
|
69581
|
+
* "HH:MM".
|
|
69582
|
+
* @param {import("ignore:preact").ComponentChildren} [startLabel] What is written
|
|
69583
|
+
* before the first time ("De"), and `endLabel` between the two ("à"). Say
|
|
69584
|
+
* `null` for neither.
|
|
69585
|
+
* @param {number} [minuteStep=1] How many minutes apart the values on both
|
|
69586
|
+
* minute wheels are.
|
|
69587
|
+
* @param {number} [minDuration=0] How long the span must last at least, in
|
|
69588
|
+
* minutes. Zero by default: a span of no length is a span all the same, only
|
|
69589
|
+
* one that goes backwards is not. It is what the bounds keep between them as
|
|
69590
|
+
* they turn — turn the start into the end and the end moves along, keeping
|
|
69591
|
+
* that much room.
|
|
69592
|
+
* @param {object} [timeProps] Anything a `TimeWheel` takes, said once for both
|
|
69593
|
+
* of them. `startTimeProps`/`endTimeProps` say it to one of the two, and win
|
|
69594
|
+
* over this one.
|
|
69595
|
+
*/
|
|
69596
|
+
const TimeRangeWheel = ({
|
|
69597
|
+
minuteStep = 1,
|
|
69598
|
+
minDuration = 0,
|
|
69599
|
+
loop = true,
|
|
69600
|
+
size,
|
|
69601
|
+
startLabel = naviI18n("time_range.from"),
|
|
69602
|
+
endLabel = naviI18n("time_range.to"),
|
|
69603
|
+
timeProps,
|
|
69604
|
+
startTimeProps,
|
|
69605
|
+
endTimeProps,
|
|
69606
|
+
...rest
|
|
69607
|
+
}) => {
|
|
69608
|
+
const startId = useId();
|
|
69609
|
+
const startRef = useRef(null);
|
|
69610
|
+
const endRef = useRef(null);
|
|
69611
|
+
|
|
69612
|
+
// What the pair does while it is being turned: the bound that just moved is
|
|
69613
|
+
// the one the user is holding, so it stays where it was put and the OTHER one
|
|
69614
|
+
// gives way. A refusal at the end of the gesture would leave the person to
|
|
69615
|
+
// undo what they just did.
|
|
69616
|
+
const keepBoundsApart = (movedSide, movedTime, e) => {
|
|
69617
|
+
const movedMinutes = minutesFromTime(movedTime);
|
|
69618
|
+
if (movedMinutes === null) {
|
|
69619
|
+
return;
|
|
69620
|
+
}
|
|
69621
|
+
const otherEl = movedSide === "start" ? endRef.current : startRef.current;
|
|
69622
|
+
if (!otherEl) {
|
|
69623
|
+
return;
|
|
69624
|
+
}
|
|
69625
|
+
const otherMinutes = minutesFromTime(getUIStateFromElement(otherEl));
|
|
69626
|
+
if (otherMinutes === null) {
|
|
69627
|
+
return;
|
|
69628
|
+
}
|
|
69629
|
+
const duration = movedSide === "start" ? otherMinutes - movedMinutes : movedMinutes - otherMinutes;
|
|
69630
|
+
if (duration >= minDuration) {
|
|
69631
|
+
return;
|
|
69632
|
+
}
|
|
69633
|
+
let pushedMinutes = movedSide === "start" ? movedMinutes + minDuration : movedMinutes - minDuration;
|
|
69634
|
+
// The day has ends the wheels do not: pushed past midnight, the other bound
|
|
69635
|
+
// would come back round on the wrong side of the one that pushed it. It
|
|
69636
|
+
// stops at the edge instead, and the span that no longer fits is what the
|
|
69637
|
+
// send-time constraint is there to say (see time_range_constraint.js).
|
|
69638
|
+
if (pushedMinutes < 0) {
|
|
69639
|
+
pushedMinutes = 0;
|
|
69640
|
+
} else if (pushedMinutes > LAST_MINUTE_OF_DAY) {
|
|
69641
|
+
pushedMinutes = LAST_MINUTE_OF_DAY;
|
|
69642
|
+
}
|
|
69643
|
+
dispatchRequestSetUIState(otherEl, timeFromMinutes(pushedMinutes), {
|
|
69644
|
+
event: e
|
|
69645
|
+
});
|
|
69646
|
+
};
|
|
69647
|
+
return jsxs(ControlGroup, {
|
|
69648
|
+
flex: true,
|
|
69649
|
+
alignY: "center",
|
|
69650
|
+
spacing: "s",
|
|
69651
|
+
size: size,
|
|
69652
|
+
...rest,
|
|
69653
|
+
children: [startLabel === null ? null : jsx(Text, {
|
|
69654
|
+
size: size,
|
|
69655
|
+
children: startLabel
|
|
69656
|
+
}), jsx(TimeWheel, {
|
|
69657
|
+
id: startId,
|
|
69658
|
+
ref: startRef,
|
|
69659
|
+
name: "start",
|
|
69660
|
+
minuteStep: minuteStep,
|
|
69661
|
+
loop: loop,
|
|
69662
|
+
size: size,
|
|
69663
|
+
uiAction: (value, e) => keepBoundsApart("start", value, e),
|
|
69664
|
+
...timeProps,
|
|
69665
|
+
...startTimeProps
|
|
69666
|
+
}), endLabel === null ? null : jsx(Text, {
|
|
69667
|
+
size: size,
|
|
69668
|
+
children: endLabel
|
|
69669
|
+
}), jsx(TimeWheel, {
|
|
69670
|
+
ref: endRef,
|
|
69671
|
+
name: "end",
|
|
69672
|
+
minuteStep: minuteStep,
|
|
69673
|
+
loop: loop,
|
|
69674
|
+
size: size,
|
|
69675
|
+
uiAction: (value, e) => keepBoundsApart("end", value, e)
|
|
69676
|
+
// Which time it comes after, and how much room there must be between
|
|
69677
|
+
// the two: said on the LATER of the two, so the answer is given where
|
|
69678
|
+
// the time one would have to move is (see time_range_constraint.js).
|
|
69679
|
+
,
|
|
69680
|
+
"data-time-after": startId,
|
|
69681
|
+
"data-time-min-duration": minDuration,
|
|
69682
|
+
...timeProps,
|
|
69683
|
+
...endTimeProps
|
|
69684
|
+
})]
|
|
69685
|
+
});
|
|
69686
|
+
};
|
|
69687
|
+
const HOURS = Array.from({
|
|
69688
|
+
length: HOUR_COUNT
|
|
69689
|
+
}, (_, hour) => hour);
|
|
69690
|
+
const padTwo = value => String(value).padStart(2, "0");
|
|
69691
|
+
|
|
69692
|
+
// The two wheels as one value, "HH:MM".
|
|
69693
|
+
const aggregateTime = childUIStateControllers => {
|
|
69694
|
+
let hour = "";
|
|
69695
|
+
let minute = "";
|
|
69696
|
+
for (const child of childUIStateControllers) {
|
|
69697
|
+
if (child.name === "hour") {
|
|
69698
|
+
hour = child.uiState ?? "";
|
|
69699
|
+
}
|
|
69700
|
+
if (child.name === "minute") {
|
|
69701
|
+
minute = child.uiState ?? "";
|
|
69702
|
+
}
|
|
69703
|
+
}
|
|
69704
|
+
return formatTimeParts(hour, minute);
|
|
69705
|
+
};
|
|
69706
|
+
|
|
69707
|
+
// The way back: what the group is set to (a value given to it, a form being
|
|
69708
|
+
// reset, the other bound pushing it) lands on the wheel it belongs to.
|
|
69709
|
+
const distributeTime = (groupState, childUIStateController) => {
|
|
69710
|
+
const parts = parseTimeParts(groupState);
|
|
69711
|
+
if (!parts) {
|
|
69712
|
+
return undefined;
|
|
69713
|
+
}
|
|
69714
|
+
return parts[childUIStateController.name];
|
|
69715
|
+
};
|
|
69716
|
+
|
|
69340
69717
|
const TableSelectionContext = createContext();
|
|
69341
69718
|
const useTableSelectionContextValue = (
|
|
69342
69719
|
selection,
|
|
@@ -75363,5 +75740,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
75363
75740
|
})
|
|
75364
75741
|
});
|
|
75365
75742
|
|
|
75366
|
-
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, RouteTransitionArea, 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, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, defineRouteDefaultTransition, defineRouteTransition, 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, useCanNavBack, useCanNavForward, 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 };
|
|
75743
|
+
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, RouteTransitionArea, 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, TimeRangeWheel, TimeSpin, TimeWheel, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, canNavBackSignal, canNavForwardSignal, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, defineRouteDefaultTransition, defineRouteTransition, 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, useCanNavBack, useCanNavForward, 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 };
|
|
75367
75744
|
//# sourceMappingURL=jsenv_navi.js.map
|