@jsenv/navi 0.27.46 → 0.27.48
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 +367 -230
- package/dist/jsenv_navi.js.map +42 -37
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -15447,7 +15447,12 @@ const asControlHostValue = (
|
|
|
15447
15447
|
if (type === "datetime-local") {
|
|
15448
15448
|
return asDatetimeLocalString(jsValue);
|
|
15449
15449
|
}
|
|
15450
|
-
if (
|
|
15450
|
+
if (
|
|
15451
|
+
type === "number" ||
|
|
15452
|
+
type === "range" ||
|
|
15453
|
+
inputMode === "numeric" ||
|
|
15454
|
+
inputMode === "decimal"
|
|
15455
|
+
) {
|
|
15451
15456
|
return asNumberString(jsValue);
|
|
15452
15457
|
}
|
|
15453
15458
|
if (type === "color") {
|
|
@@ -15521,7 +15526,8 @@ const readControlValue = (controlHost) => {
|
|
|
15521
15526
|
if (
|
|
15522
15527
|
type === "number" ||
|
|
15523
15528
|
type === "range" ||
|
|
15524
|
-
controlHost.inputMode === "numeric"
|
|
15529
|
+
controlHost.inputMode === "numeric" ||
|
|
15530
|
+
controlHost.inputMode === "decimal"
|
|
15525
15531
|
) {
|
|
15526
15532
|
return readNumberFromInput(controlHost);
|
|
15527
15533
|
}
|
|
@@ -19426,7 +19432,11 @@ const REQUIRED_CONSTRAINT = {
|
|
|
19426
19432
|
return naviI18n("constraint.required.time");
|
|
19427
19433
|
}
|
|
19428
19434
|
const inputMode = field.controlHostProps.inputMode;
|
|
19429
|
-
if (
|
|
19435
|
+
if (
|
|
19436
|
+
type === "number" ||
|
|
19437
|
+
inputMode === "numeric" ||
|
|
19438
|
+
inputMode === "decimal"
|
|
19439
|
+
) {
|
|
19430
19440
|
return naviI18n("constraint.required.number");
|
|
19431
19441
|
}
|
|
19432
19442
|
if (type === "datetime-local") {
|
|
@@ -19587,7 +19597,8 @@ const MAX_LENGTH_CONSTRAINT = {
|
|
|
19587
19597
|
} else if (!isTextarea) {
|
|
19588
19598
|
return null;
|
|
19589
19599
|
}
|
|
19590
|
-
const maxLength =
|
|
19600
|
+
const maxLength =
|
|
19601
|
+
field.controlHostProps.maxLength ?? field.props?.maxLengthGuard;
|
|
19591
19602
|
if (maxLength === undefined) {
|
|
19592
19603
|
return null;
|
|
19593
19604
|
}
|
|
@@ -19630,7 +19641,8 @@ const TYPE_NUMBER_CONSTRAINT = {
|
|
|
19630
19641
|
}
|
|
19631
19642
|
const type = field.controlHostProps.type;
|
|
19632
19643
|
const inputMode = field.controlHostProps.inputMode;
|
|
19633
|
-
const isNumber =
|
|
19644
|
+
const isNumber =
|
|
19645
|
+
type === "number" || inputMode === "numeric" || inputMode === "decimal";
|
|
19634
19646
|
if (!isNumber) {
|
|
19635
19647
|
return null;
|
|
19636
19648
|
}
|
|
@@ -19711,7 +19723,8 @@ const MIN_CONSTRAINT = {
|
|
|
19711
19723
|
if (!valueAsString) {
|
|
19712
19724
|
return null;
|
|
19713
19725
|
}
|
|
19714
|
-
const isNumber =
|
|
19726
|
+
const isNumber =
|
|
19727
|
+
type === "number" || inputMode === "numeric" || inputMode === "decimal";
|
|
19715
19728
|
if (isNumber) {
|
|
19716
19729
|
const minNumber = parseFloat(minString);
|
|
19717
19730
|
if (isNaN(minNumber)) {
|
|
@@ -19818,7 +19831,8 @@ const MAX_CONSTRAINT = {
|
|
|
19818
19831
|
if (!valueAsString) {
|
|
19819
19832
|
return null;
|
|
19820
19833
|
}
|
|
19821
|
-
const isNumber =
|
|
19834
|
+
const isNumber =
|
|
19835
|
+
type === "number" || inputMode === "numeric" || inputMode === "decimal";
|
|
19822
19836
|
if (isNumber) {
|
|
19823
19837
|
const maxNumber = parseFloat(maxString);
|
|
19824
19838
|
if (isNaN(maxNumber)) {
|
|
@@ -19940,7 +19954,8 @@ const STEP_CONSTRAINT = {
|
|
|
19940
19954
|
}
|
|
19941
19955
|
const type = field.controlHostProps.type;
|
|
19942
19956
|
const inputMode = field.controlHostProps.inputMode;
|
|
19943
|
-
const isNumericText =
|
|
19957
|
+
const isNumericText =
|
|
19958
|
+
type === "text" && (inputMode === "numeric" || inputMode === "decimal");
|
|
19944
19959
|
if (!isNumericText && !STEP_SUPPORTED_TYPE_SET.has(type)) {
|
|
19945
19960
|
return null;
|
|
19946
19961
|
}
|
|
@@ -22598,13 +22613,17 @@ const useUIStateController = (
|
|
|
22598
22613
|
);
|
|
22599
22614
|
}
|
|
22600
22615
|
}
|
|
22601
|
-
//
|
|
22602
|
-
//
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
|
|
22607
|
-
|
|
22616
|
+
// initial_state_push is pure initialization (equivalent to defaultValue on the
|
|
22617
|
+
// child itself): skip uiAction entirely so no side effects fire on mount.
|
|
22618
|
+
if (e.type !== "initial_state_push") {
|
|
22619
|
+
// Still fire uiAction so external listeners (e.g. signals) stay in
|
|
22620
|
+
// sync, but do NOT fire the command and do NOT notify the parent —
|
|
22621
|
+
// both would cause an infinite loop when a parent cascades state
|
|
22622
|
+
// down to its children (child command would re-trigger the cascade).
|
|
22623
|
+
uiStateController.onUIAction(e, {
|
|
22624
|
+
skipCommand: true,
|
|
22625
|
+
});
|
|
22626
|
+
}
|
|
22608
22627
|
// Exception: when the facade propagates a child state change up to the
|
|
22609
22628
|
// real picker input, also notify the parent group (e.g. Form) so it
|
|
22610
22629
|
// keeps its cached aggregated state in sync and fires its own uiAction.
|
|
@@ -22741,7 +22760,6 @@ const useUIStateController = (
|
|
|
22741
22760
|
uiStateController.rules = rules;
|
|
22742
22761
|
return uiStateController;
|
|
22743
22762
|
};
|
|
22744
|
-
|
|
22745
22763
|
const NO_PARENT = [() => {}, () => {}, () => {}];
|
|
22746
22764
|
const useParentControllerNotifiers = (
|
|
22747
22765
|
parentUIStateController,
|
|
@@ -22821,6 +22839,90 @@ const useParentControllerNotifiers = (
|
|
|
22821
22839
|
* **Filtering**: `childControlFilter` can exclude certain child types from aggregation
|
|
22822
22840
|
* (e.g. ignoring buttons inside a selectable list).
|
|
22823
22841
|
*/
|
|
22842
|
+
const CANNOT_DERIVE = Symbol("cannot_derive");
|
|
22843
|
+
|
|
22844
|
+
// Default aggregate/distribute implementations keyed by controlType or stateType.
|
|
22845
|
+
// Looked up in useUIGroupStateController to fill in omitted aggregateChildStates /
|
|
22846
|
+
// distributeChildUIState. If neither a default nor an explicit impl is found for a
|
|
22847
|
+
// group, creation throws so the caller knows it must supply them.
|
|
22848
|
+
const GROUP_DEFAULTS = {
|
|
22849
|
+
radio_group: {
|
|
22850
|
+
childControlFilter: (child) =>
|
|
22851
|
+
child.controlType === "input" && child.controlHostProps?.type === "radio",
|
|
22852
|
+
aggregateChildStates: (children) => {
|
|
22853
|
+
for (const child of children) {
|
|
22854
|
+
const childUIState = child.uiState;
|
|
22855
|
+
if (childUIState !== undefined) {
|
|
22856
|
+
return childUIState;
|
|
22857
|
+
}
|
|
22858
|
+
}
|
|
22859
|
+
return undefined;
|
|
22860
|
+
},
|
|
22861
|
+
distributeChildUIState: (newUIState, childUIStateController) => {
|
|
22862
|
+
const childSelected = childUIStateController.props.value === newUIState;
|
|
22863
|
+
if (childSelected) {
|
|
22864
|
+
return childUIStateController.props.value;
|
|
22865
|
+
}
|
|
22866
|
+
return undefined;
|
|
22867
|
+
},
|
|
22868
|
+
},
|
|
22869
|
+
checkbox_group: {
|
|
22870
|
+
childControlFilter: (child) =>
|
|
22871
|
+
child.controlType === "input" &&
|
|
22872
|
+
child.controlHostProps?.type === "checkbox",
|
|
22873
|
+
aggregateChildStates: (children) => {
|
|
22874
|
+
const values = [];
|
|
22875
|
+
for (const child of children) {
|
|
22876
|
+
const childUIState = child.uiState;
|
|
22877
|
+
if (childUIState !== undefined) {
|
|
22878
|
+
values.push(childUIState);
|
|
22879
|
+
}
|
|
22880
|
+
}
|
|
22881
|
+
return values.length === 0 ? undefined : values;
|
|
22882
|
+
},
|
|
22883
|
+
distributeChildUIState: (newUIState, childUIStateController) => {
|
|
22884
|
+
const childSelected =
|
|
22885
|
+
Array.isArray(newUIState) &&
|
|
22886
|
+
newUIState.includes(childUIStateController.props.value);
|
|
22887
|
+
if (childSelected) {
|
|
22888
|
+
return childUIStateController.props.value;
|
|
22889
|
+
}
|
|
22890
|
+
return undefined;
|
|
22891
|
+
},
|
|
22892
|
+
},
|
|
22893
|
+
object: {
|
|
22894
|
+
aggregateChildStates: (children) => {
|
|
22895
|
+
const groupValues = {};
|
|
22896
|
+
for (const child of children) {
|
|
22897
|
+
const { name, uiState, allowNameless } = child;
|
|
22898
|
+
if (!name) {
|
|
22899
|
+
if (!allowNameless) {
|
|
22900
|
+
console.warn(
|
|
22901
|
+
"A group child is missing a name property, its state won't be included in the group state",
|
|
22902
|
+
child,
|
|
22903
|
+
);
|
|
22904
|
+
}
|
|
22905
|
+
continue;
|
|
22906
|
+
}
|
|
22907
|
+
groupValues[name] = uiState;
|
|
22908
|
+
}
|
|
22909
|
+
return groupValues;
|
|
22910
|
+
},
|
|
22911
|
+
distributeChildUIState: (newUIState, child) => {
|
|
22912
|
+
const childName = child.name;
|
|
22913
|
+
if (
|
|
22914
|
+
childName &&
|
|
22915
|
+
newUIState !== null &&
|
|
22916
|
+
typeof newUIState === "object" &&
|
|
22917
|
+
Object.prototype.hasOwnProperty.call(newUIState, childName)
|
|
22918
|
+
) {
|
|
22919
|
+
return newUIState[childName];
|
|
22920
|
+
}
|
|
22921
|
+
return CANNOT_DERIVE;
|
|
22922
|
+
},
|
|
22923
|
+
},
|
|
22924
|
+
};
|
|
22925
|
+
|
|
22824
22926
|
const useUIGroupStateController = (
|
|
22825
22927
|
props,
|
|
22826
22928
|
controlType,
|
|
@@ -22840,11 +22942,26 @@ const useUIGroupStateController = (
|
|
|
22840
22942
|
const debugUIGroup = useDebugUIState();
|
|
22841
22943
|
const debugFocus = useDebugFocus();
|
|
22842
22944
|
|
|
22843
|
-
|
|
22844
|
-
|
|
22945
|
+
const defaults = GROUP_DEFAULTS[controlType] ?? GROUP_DEFAULTS[stateType];
|
|
22946
|
+
const resolvedChildControlFilter =
|
|
22947
|
+
childControlFilter ?? defaults?.childControlFilter ?? null;
|
|
22948
|
+
const resolvedAggregateChildStates =
|
|
22949
|
+
aggregateChildStates ?? defaults?.aggregateChildStates;
|
|
22950
|
+
const resolvedDistributeChildUIState =
|
|
22951
|
+
distributeChildUIState ?? defaults?.distributeChildUIState;
|
|
22952
|
+
if (
|
|
22953
|
+
typeof resolvedAggregateChildStates !== "function" ||
|
|
22954
|
+
typeof resolvedDistributeChildUIState !== "function"
|
|
22955
|
+
) {
|
|
22956
|
+
throw new Error(
|
|
22957
|
+
`No aggregate/distribute implementation found for controlType="${controlType}" stateType="${stateType}". ` +
|
|
22958
|
+
`Either use a known controlType/stateType or provide aggregateChildStates and distributeChildUIState explicitly.`,
|
|
22959
|
+
);
|
|
22845
22960
|
}
|
|
22846
22961
|
const parentUIStateController = useContext(ParentUIStateControllerContext);
|
|
22847
|
-
const
|
|
22962
|
+
const hasValueProp = Object.hasOwn(props, "value");
|
|
22963
|
+
const hasDefaultValueProp = Object.hasOwn(props, "defaultValue");
|
|
22964
|
+
const { id, name, value, defaultValue, uiAction, command } = props;
|
|
22848
22965
|
const ref = props.ref;
|
|
22849
22966
|
const uiActionRef = useRef(uiAction);
|
|
22850
22967
|
const fallbackState =
|
|
@@ -22893,7 +23010,7 @@ const useUIGroupStateController = (
|
|
|
22893
23010
|
pendingChangeRef.current = true;
|
|
22894
23011
|
return;
|
|
22895
23012
|
}
|
|
22896
|
-
const aggChildState =
|
|
23013
|
+
const aggChildState = resolvedAggregateChildStates(
|
|
22897
23014
|
childUIStateControllerArray,
|
|
22898
23015
|
fallbackState,
|
|
22899
23016
|
);
|
|
@@ -22958,13 +23075,68 @@ const useUIGroupStateController = (
|
|
|
22958
23075
|
}
|
|
22959
23076
|
});
|
|
22960
23077
|
|
|
23078
|
+
const isMonitoringChild = (childUIStateController) => {
|
|
23079
|
+
if (childUIStateController.isProxy) {
|
|
23080
|
+
return false;
|
|
23081
|
+
}
|
|
23082
|
+
if (
|
|
23083
|
+
resolvedChildControlFilter &&
|
|
23084
|
+
!resolvedChildControlFilter(childUIStateController)
|
|
23085
|
+
) {
|
|
23086
|
+
return false;
|
|
23087
|
+
}
|
|
23088
|
+
return true;
|
|
23089
|
+
};
|
|
23090
|
+
const shouldPropagateStateToChild = (childUIStateController) => {
|
|
23091
|
+
if (!isMonitoringChild(childUIStateController)) {
|
|
23092
|
+
return false;
|
|
23093
|
+
}
|
|
23094
|
+
if (childUIStateController.controlType === "button") {
|
|
23095
|
+
return false;
|
|
23096
|
+
}
|
|
23097
|
+
return true;
|
|
23098
|
+
};
|
|
23099
|
+
|
|
22961
23100
|
const existingController = controllerRef.current;
|
|
22962
23101
|
if (existingController) {
|
|
23102
|
+
const prevValue = existingController.value;
|
|
23103
|
+
const prevHasValueProp = existingController.hasValueProp;
|
|
22963
23104
|
existingController.props = props;
|
|
22964
23105
|
existingController.id = id;
|
|
22965
23106
|
existingController.name = name;
|
|
22966
23107
|
existingController.value = value;
|
|
23108
|
+
existingController.defaultValue = defaultValue;
|
|
23109
|
+
existingController.hasValueProp = hasValueProp;
|
|
23110
|
+
existingController.hasDefaultValueProp = hasDefaultValueProp;
|
|
22967
23111
|
uiActionRef.current = uiAction;
|
|
23112
|
+
// When the controlled value prop changes (or when becoming controlled for the
|
|
23113
|
+
// first time), silently cascade to children that have no individual state prop.
|
|
23114
|
+
if (
|
|
23115
|
+
hasValueProp &&
|
|
23116
|
+
(!prevHasValueProp || !compareTwoJsValues(value, prevValue))
|
|
23117
|
+
) {
|
|
23118
|
+
const propagateDownEvent = new CustomEvent(
|
|
23119
|
+
"propagate_down_set_ui_state",
|
|
23120
|
+
{ detail: {} },
|
|
23121
|
+
);
|
|
23122
|
+
for (const childUIStateController of childUIStateControllerArray) {
|
|
23123
|
+
if (!shouldPropagateStateToChild(childUIStateController)) {
|
|
23124
|
+
continue;
|
|
23125
|
+
}
|
|
23126
|
+
if (childUIStateController.hasStateProp) {
|
|
23127
|
+
continue;
|
|
23128
|
+
}
|
|
23129
|
+
const childNewState = existingController.distributeChildUIState(
|
|
23130
|
+
value,
|
|
23131
|
+
childUIStateController,
|
|
23132
|
+
);
|
|
23133
|
+
if (childNewState === CANNOT_DERIVE) {
|
|
23134
|
+
continue;
|
|
23135
|
+
}
|
|
23136
|
+
childUIStateController.setUIState(childNewState, propagateDownEvent);
|
|
23137
|
+
}
|
|
23138
|
+
existingController.syncInternalState(value);
|
|
23139
|
+
}
|
|
22968
23140
|
return existingController;
|
|
22969
23141
|
}
|
|
22970
23142
|
debugUIGroup(
|
|
@@ -22973,32 +23145,23 @@ const useUIGroupStateController = (
|
|
|
22973
23145
|
|
|
22974
23146
|
const [publishUIState, subscribeUIState] = createPubSub();
|
|
22975
23147
|
const uiStateSignal = signal(fallbackState);
|
|
22976
|
-
const isMonitoringChild = (childUIStateController) => {
|
|
22977
|
-
if (childUIStateController.isProxy) {
|
|
22978
|
-
return false;
|
|
22979
|
-
}
|
|
22980
|
-
if (childControlFilter && !childControlFilter(childUIStateController)) {
|
|
22981
|
-
return false;
|
|
22982
|
-
}
|
|
22983
|
-
return true;
|
|
22984
|
-
};
|
|
22985
23148
|
const groupUIStateController = {
|
|
22986
23149
|
controlType,
|
|
22987
23150
|
id,
|
|
22988
23151
|
name,
|
|
22989
23152
|
value,
|
|
23153
|
+
defaultValue,
|
|
23154
|
+
hasValueProp,
|
|
23155
|
+
hasDefaultValueProp,
|
|
22990
23156
|
props,
|
|
22991
23157
|
uiState: fallbackState,
|
|
22992
23158
|
uiStateSignal,
|
|
22993
23159
|
wantRequesterButtonState,
|
|
22994
23160
|
elementRef: ref,
|
|
22995
23161
|
getPropFromState: (uiState) => uiState,
|
|
22996
|
-
|
|
22997
|
-
//
|
|
22998
|
-
//
|
|
22999
|
-
// - "radio_group": child gets true/false based on whether its value matches the scalar state.
|
|
23000
|
-
// - "checkbox_group": child gets true/false based on whether its value is in the state array.
|
|
23001
|
-
// - default (ControlGroup): child gets the value at its named key in the state object.
|
|
23162
|
+
distributeChildUIState: resolvedDistributeChildUIState,
|
|
23163
|
+
// Cascades newUIState to each monitored child via resolvedDistributeChildUIState,
|
|
23164
|
+
// then re-aggregates and fires this group's own reactions.
|
|
23002
23165
|
setUIState: (newUIState, e) => {
|
|
23003
23166
|
if (
|
|
23004
23167
|
stateType === "object" &&
|
|
@@ -23017,73 +23180,42 @@ const useUIGroupStateController = (
|
|
|
23017
23180
|
);
|
|
23018
23181
|
return;
|
|
23019
23182
|
}
|
|
23020
|
-
|
|
23021
|
-
|
|
23022
|
-
|
|
23023
|
-
|
|
23183
|
+
// initial_state_push propagates silently (no uiAction anywhere in the chain);
|
|
23184
|
+
// regular updates use propagate_down_set_ui_state which fires uiAction on children.
|
|
23185
|
+
const propagateEventType =
|
|
23186
|
+
e.type === "initial_state_push"
|
|
23187
|
+
? "initial_state_push"
|
|
23188
|
+
: "propagate_down_set_ui_state";
|
|
23189
|
+
const propagateDownEvent = new CustomEvent(propagateEventType, {
|
|
23190
|
+
detail: {},
|
|
23191
|
+
});
|
|
23024
23192
|
chainEvent(propagateDownEvent, e);
|
|
23025
23193
|
for (const childUIStateController of childUIStateControllerArray) {
|
|
23026
|
-
if (!
|
|
23194
|
+
if (!shouldPropagateStateToChild(childUIStateController)) {
|
|
23027
23195
|
continue;
|
|
23028
23196
|
}
|
|
23029
|
-
|
|
23197
|
+
const childNewState = resolvedDistributeChildUIState(
|
|
23198
|
+
newUIState,
|
|
23199
|
+
childUIStateController,
|
|
23200
|
+
);
|
|
23201
|
+
if (childNewState === CANNOT_DERIVE) {
|
|
23030
23202
|
continue;
|
|
23031
23203
|
}
|
|
23032
|
-
|
|
23033
|
-
const childSelected =
|
|
23034
|
-
childUIStateController.props.value === newUIState;
|
|
23035
|
-
// Pass the child's own value (not `true`) when selected, `undefined` (not `false`) when not.
|
|
23036
|
-
// `toDomProps` uses `newUIState !== undefined` to determine `checked`, so passing `false`
|
|
23037
|
-
// would incorrectly render the radio as checked.
|
|
23038
|
-
const childNewState = childSelected
|
|
23039
|
-
? childUIStateController.props.value
|
|
23040
|
-
: undefined;
|
|
23041
|
-
childUIStateController.setUIState(childNewState, propagateDownEvent);
|
|
23042
|
-
} else if (controlType === "checkbox_group") {
|
|
23043
|
-
const childSelected =
|
|
23044
|
-
Array.isArray(newUIState) &&
|
|
23045
|
-
newUIState.includes(childUIStateController.props.value);
|
|
23046
|
-
const childNewState = childSelected
|
|
23047
|
-
? childUIStateController.props.value
|
|
23048
|
-
: undefined;
|
|
23049
|
-
childUIStateController.setUIState(childNewState, propagateDownEvent);
|
|
23050
|
-
} else if (distributeChildUIState) {
|
|
23051
|
-
const childrenStateMap = distributeChildUIState(newUIState);
|
|
23052
|
-
if (childrenStateMap && typeof childrenStateMap === "object") {
|
|
23053
|
-
const childName = childUIStateController.name;
|
|
23054
|
-
if (
|
|
23055
|
-
childName &&
|
|
23056
|
-
Object.prototype.hasOwnProperty.call(childrenStateMap, childName)
|
|
23057
|
-
) {
|
|
23058
|
-
childUIStateController.setUIState(
|
|
23059
|
-
childrenStateMap[childName],
|
|
23060
|
-
propagateDownEvent,
|
|
23061
|
-
);
|
|
23062
|
-
}
|
|
23063
|
-
}
|
|
23064
|
-
} else {
|
|
23065
|
-
const childName = childUIStateController.name;
|
|
23066
|
-
if (
|
|
23067
|
-
childName &&
|
|
23068
|
-
newUIState !== null &&
|
|
23069
|
-
typeof newUIState === "object" &&
|
|
23070
|
-
Object.prototype.hasOwnProperty.call(newUIState, childName)
|
|
23071
|
-
) {
|
|
23072
|
-
childUIStateController.setUIState(
|
|
23073
|
-
newUIState[childName],
|
|
23074
|
-
propagateDownEvent,
|
|
23075
|
-
);
|
|
23076
|
-
}
|
|
23077
|
-
}
|
|
23204
|
+
childUIStateController.setUIState(childNewState, propagateDownEvent);
|
|
23078
23205
|
}
|
|
23079
23206
|
// Re-aggregate from children and apply — do NOT call onChange to avoid a loop
|
|
23080
23207
|
// (onChange would call setUIState again, which would cascade again).
|
|
23081
|
-
const aggChildState =
|
|
23208
|
+
const aggChildState = resolvedAggregateChildStates(
|
|
23082
23209
|
childUIStateControllerArray,
|
|
23083
23210
|
fallbackState,
|
|
23084
23211
|
);
|
|
23085
23212
|
const groupUIState =
|
|
23086
23213
|
aggChildState === undefined ? fallbackState : aggChildState;
|
|
23214
|
+
if (e.type === "initial_state_push") {
|
|
23215
|
+
// Silent initialization: update state without firing uiAction or notifying parent.
|
|
23216
|
+
groupUIStateController.syncInternalState(groupUIState);
|
|
23217
|
+
return;
|
|
23218
|
+
}
|
|
23087
23219
|
applyState(groupUIState, e, { internalBehavior: true });
|
|
23088
23220
|
},
|
|
23089
23221
|
// Called on mount/unmount/render-batch: updates state silently with no external reactions.
|
|
@@ -23133,6 +23265,33 @@ const useUIGroupStateController = (
|
|
|
23133
23265
|
debugUIGroup(
|
|
23134
23266
|
`${controlType}.registerChild("${childControlType}") -> registered (total: ${childUIStateControllerArray.length})`,
|
|
23135
23267
|
);
|
|
23268
|
+
// Auto-derive child's initial state from the group's value/defaultValue
|
|
23269
|
+
// when the child has no individually-controlled state prop.
|
|
23270
|
+
// Use initial_state_push so uiAction does not fire during mount initialization.
|
|
23271
|
+
if (!childUIStateController.hasStateProp) {
|
|
23272
|
+
const initialEvent = new CustomEvent("initial_state_push", {
|
|
23273
|
+
detail: {},
|
|
23274
|
+
});
|
|
23275
|
+
if (groupUIStateController.hasValueProp) {
|
|
23276
|
+
// Controlled: always cascade current group value (even undefined = deselect all).
|
|
23277
|
+
const childNewState = resolvedDistributeChildUIState(
|
|
23278
|
+
groupUIStateController.value,
|
|
23279
|
+
childUIStateController,
|
|
23280
|
+
);
|
|
23281
|
+
if (childNewState !== CANNOT_DERIVE) {
|
|
23282
|
+
childUIStateController.setUIState(childNewState, initialEvent);
|
|
23283
|
+
}
|
|
23284
|
+
} else if (groupUIStateController.hasDefaultValueProp) {
|
|
23285
|
+
// Uncontrolled: set initial state from defaultValue on mount.
|
|
23286
|
+
const childNewState = resolvedDistributeChildUIState(
|
|
23287
|
+
groupUIStateController.defaultValue,
|
|
23288
|
+
childUIStateController,
|
|
23289
|
+
);
|
|
23290
|
+
if (childNewState !== CANNOT_DERIVE) {
|
|
23291
|
+
childUIStateController.setUIState(childNewState, initialEvent);
|
|
23292
|
+
}
|
|
23293
|
+
}
|
|
23294
|
+
}
|
|
23136
23295
|
onChange(new CustomEvent(`${childControlType}_mount`), {
|
|
23137
23296
|
notifyExternal: "silent",
|
|
23138
23297
|
// childUIStateController,
|
|
@@ -23211,10 +23370,7 @@ const useUIGroupStateController = (
|
|
|
23211
23370
|
);
|
|
23212
23371
|
chainEvent(propagateDownResetEvent, e);
|
|
23213
23372
|
for (const childUIStateController of childUIStateControllerArray) {
|
|
23214
|
-
if (!
|
|
23215
|
-
continue;
|
|
23216
|
-
}
|
|
23217
|
-
if (childUIStateController.controlType === "button") {
|
|
23373
|
+
if (!shouldPropagateStateToChild(childUIStateController)) {
|
|
23218
23374
|
continue;
|
|
23219
23375
|
}
|
|
23220
23376
|
childUIStateController.resetUIState(propagateDownResetEvent);
|
|
@@ -23387,6 +23543,18 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
23387
23543
|
);
|
|
23388
23544
|
firstChildControllerRef.current = child;
|
|
23389
23545
|
realUIStateController.facadeChild = child;
|
|
23546
|
+
// If the picker already has a meaningful state (from value or defaultValue),
|
|
23547
|
+
// push it to the child on registration so it reflects the pre-set value
|
|
23548
|
+
// without firing uiAction (equivalent to defaultValue on the child itself).
|
|
23549
|
+
const initialState = realUIStateController.uiState;
|
|
23550
|
+
if (initialState !== undefined) {
|
|
23551
|
+
updatingRef.current = true;
|
|
23552
|
+
const initialEvent = new CustomEvent("initial_state_push", {
|
|
23553
|
+
detail: {},
|
|
23554
|
+
});
|
|
23555
|
+
child.setUIState(initialState, initialEvent);
|
|
23556
|
+
updatingRef.current = false;
|
|
23557
|
+
}
|
|
23390
23558
|
}
|
|
23391
23559
|
},
|
|
23392
23560
|
unregisterChild: (child) => {
|
|
@@ -23479,6 +23647,10 @@ const INTERNAL_EVENT_SET = new Set([
|
|
|
23479
23647
|
// so the picker knows the child's current value (e.g. for "store value at open"),
|
|
23480
23648
|
// but no action pipeline, command, or synthetic input event should fire.
|
|
23481
23649
|
"facade_child_mount_sync",
|
|
23650
|
+
// Facade pushing its current state (from value/defaultValue) down to the first child
|
|
23651
|
+
// on registration, and group pushing value/defaultValue to children on registerChild.
|
|
23652
|
+
// Equivalent to defaultValue initialization: no uiAction, no commands, no parent notification.
|
|
23653
|
+
"initial_state_push",
|
|
23482
23654
|
]);
|
|
23483
23655
|
const isInternalEvent = (e) => {
|
|
23484
23656
|
return INTERNAL_EVENT_SET.has(e.type);
|
|
@@ -24282,6 +24454,12 @@ const useControlgroupProps = (props, {
|
|
|
24282
24454
|
cascadeValidationToChildren
|
|
24283
24455
|
});
|
|
24284
24456
|
const [boundAction] = useActionBoundToOneParam(action, uiGroupStateController.uiStateSignal);
|
|
24457
|
+
// Mirror single-input behaviour: a controlled value with no handler makes the
|
|
24458
|
+
// group read-only so children don't appear interactive when they can't change.
|
|
24459
|
+
const implicitReadOnly = uiGroupStateController.hasValueProp && !action && !props.uiAction;
|
|
24460
|
+
if (implicitReadOnly && !props.readOnly) {
|
|
24461
|
+
props.readOnly = true;
|
|
24462
|
+
}
|
|
24285
24463
|
const [actionRequester, setActionRequester] = useState();
|
|
24286
24464
|
const [controlRootProps, controlgroupProps] = useInteractiveProps(props, {
|
|
24287
24465
|
uiStateController: uiGroupStateController,
|
|
@@ -30480,25 +30658,7 @@ const ControlGroup = props => {
|
|
|
30480
30658
|
wantRequesterButtonState: true,
|
|
30481
30659
|
controlType: props.type || "control_group",
|
|
30482
30660
|
stateType: "object",
|
|
30483
|
-
cascadeValidationToChildren: true
|
|
30484
|
-
aggregateChildStates: childUIStateControllers => {
|
|
30485
|
-
const groupValues = {};
|
|
30486
|
-
for (const childUIStateController of childUIStateControllers) {
|
|
30487
|
-
const {
|
|
30488
|
-
name,
|
|
30489
|
-
uiState,
|
|
30490
|
-
allowNameless
|
|
30491
|
-
} = childUIStateController;
|
|
30492
|
-
if (!name) {
|
|
30493
|
-
if (!allowNameless) {
|
|
30494
|
-
console.warn("A ControlGroup child is missing a name property, its state won't be included in the group state", childUIStateController);
|
|
30495
|
-
}
|
|
30496
|
-
continue;
|
|
30497
|
-
}
|
|
30498
|
-
groupValues[name] = uiState;
|
|
30499
|
-
}
|
|
30500
|
-
return groupValues;
|
|
30501
|
-
}
|
|
30661
|
+
cascadeValidationToChildren: true
|
|
30502
30662
|
});
|
|
30503
30663
|
const {
|
|
30504
30664
|
children
|
|
@@ -31979,7 +32139,6 @@ const NAVI_TYPE_DEFAULTS = {
|
|
|
31979
32139
|
},
|
|
31980
32140
|
navi_number: {
|
|
31981
32141
|
type: "text",
|
|
31982
|
-
inputMode: "numeric",
|
|
31983
32142
|
autoCorrect: "off",
|
|
31984
32143
|
spellcheck: false,
|
|
31985
32144
|
autoComplete: "off",
|
|
@@ -32058,6 +32217,16 @@ const resolveInputProps = (props) => {
|
|
|
32058
32217
|
if (!currentTypeDefaults) {
|
|
32059
32218
|
return;
|
|
32060
32219
|
}
|
|
32220
|
+
// For navi_number: choose inputMode based on whether step/min/max suggest decimals.
|
|
32221
|
+
// inputMode="numeric" (integer keyboard) vs "decimal" (keyboard with decimal separator).
|
|
32222
|
+
if (currentType === "navi_number" && props.inputMode === undefined) {
|
|
32223
|
+
props.inputMode =
|
|
32224
|
+
hasDecimalPlaces(props.step) ||
|
|
32225
|
+
hasDecimalPlaces(props.min) ||
|
|
32226
|
+
hasDecimalPlaces(props.max)
|
|
32227
|
+
? "decimal"
|
|
32228
|
+
: "numeric";
|
|
32229
|
+
}
|
|
32061
32230
|
for (const key of Object.keys(currentTypeDefaults)) {
|
|
32062
32231
|
if (props[key] === undefined) {
|
|
32063
32232
|
props[key] = currentTypeDefaults[key];
|
|
@@ -32152,6 +32321,14 @@ const STEP_FORMATTER_BY_TYPE = {
|
|
|
32152
32321
|
"datetime": timeStringToSeconds,
|
|
32153
32322
|
};
|
|
32154
32323
|
|
|
32324
|
+
const hasDecimalPlaces = (value) => {
|
|
32325
|
+
if (value === undefined || value === null) {
|
|
32326
|
+
return false;
|
|
32327
|
+
}
|
|
32328
|
+
const num = Number(value);
|
|
32329
|
+
return !isNaN(num) && !Number.isInteger(num);
|
|
32330
|
+
};
|
|
32331
|
+
|
|
32155
32332
|
installImportMetaCssBuild(import.meta);const css$y = /* css */`
|
|
32156
32333
|
@layer navi {
|
|
32157
32334
|
.navi_input_range {
|
|
@@ -32422,7 +32599,8 @@ const InputRangeFieldInterface = props => {
|
|
|
32422
32599
|
};
|
|
32423
32600
|
resolveInputProps(props);
|
|
32424
32601
|
const [rangeRootProps, rangeHostProps] = useControlProps(props, {
|
|
32425
|
-
controlType: "input"
|
|
32602
|
+
controlType: "input"
|
|
32603
|
+
});
|
|
32426
32604
|
const {
|
|
32427
32605
|
basePseudoState
|
|
32428
32606
|
} = rangeHostProps;
|
|
@@ -32523,7 +32701,7 @@ const RangePseudoElements = ["::-navi-loader"];
|
|
|
32523
32701
|
|
|
32524
32702
|
const InputModeResolver = props => {
|
|
32525
32703
|
const Next = useNextResolver();
|
|
32526
|
-
if (props.inputMode === "numeric") {
|
|
32704
|
+
if (props.inputMode === "numeric" || props.inputMode === "decimal") {
|
|
32527
32705
|
return jsx(InputModeNumeric, {
|
|
32528
32706
|
...props
|
|
32529
32707
|
});
|
|
@@ -33078,6 +33256,38 @@ const InputTextualWithSuggestions = props => {
|
|
|
33078
33256
|
});
|
|
33079
33257
|
};
|
|
33080
33258
|
|
|
33259
|
+
// When readonly focus and mousedown should select input content
|
|
33260
|
+
// (the only relevant interaction to perform on readonly is copying the value)
|
|
33261
|
+
// Nice side effect is that input_group.jsx will see all input is selected
|
|
33262
|
+
// and arrow left/right will always nav between inputs.
|
|
33263
|
+
// (Otherwise we would prevent left/right + show calllout about readonly)
|
|
33264
|
+
const useAutoSelectReadOnly = (props) => {
|
|
33265
|
+
const onFocus = (e) => {
|
|
33266
|
+
props.onFocus(e);
|
|
33267
|
+
if (e.defaultPrevented) {
|
|
33268
|
+
return;
|
|
33269
|
+
}
|
|
33270
|
+
if (!e.target.readOnly) {
|
|
33271
|
+
return;
|
|
33272
|
+
}
|
|
33273
|
+
e.preventDefault();
|
|
33274
|
+
e.target.select();
|
|
33275
|
+
};
|
|
33276
|
+
const onMouseDown = (e) => {
|
|
33277
|
+
props.onMouseDown(e);
|
|
33278
|
+
if (e.defaultPrevented) {
|
|
33279
|
+
return;
|
|
33280
|
+
}
|
|
33281
|
+
if (!e.target.readOnly) {
|
|
33282
|
+
return;
|
|
33283
|
+
}
|
|
33284
|
+
e.preventDefault();
|
|
33285
|
+
e.target.select();
|
|
33286
|
+
};
|
|
33287
|
+
|
|
33288
|
+
return { onFocus, onMouseDown };
|
|
33289
|
+
};
|
|
33290
|
+
|
|
33081
33291
|
installImportMetaCssBuild(import.meta);/**
|
|
33082
33292
|
* Input component for all textual input types.
|
|
33083
33293
|
*
|
|
@@ -33513,35 +33723,14 @@ const InputTextualFirstResolver = props => {
|
|
|
33513
33723
|
};
|
|
33514
33724
|
const InputTextual = createComponentResolver([InputTextualFirstResolver, InputWithListResolver, InputWithSuggestionsResolver, InputTypeResolver, InputModeResolver, InputHeadlessResolver, InputTextualUI]);
|
|
33515
33725
|
const RealInput = props => {
|
|
33726
|
+
const autoSelectReadOnlyProps = useAutoSelectReadOnly(props);
|
|
33516
33727
|
return jsx(Box, {
|
|
33517
33728
|
...props,
|
|
33518
33729
|
as: "input",
|
|
33519
|
-
baseClassName: "navi_control_input"
|
|
33520
|
-
|
|
33521
|
-
// (the only relevant interaction to perform on readonly is copying the value)
|
|
33522
|
-
// Nice side effect is that input_group.jsx will see all input is selected
|
|
33523
|
-
// and arrow left/right will always nav between inputs.
|
|
33524
|
-
// (Otherwise we would prevent left/right + show calllout about readonly)
|
|
33525
|
-
,
|
|
33526
|
-
|
|
33527
|
-
onFocus: e => {
|
|
33528
|
-
props.onFocus(e);
|
|
33529
|
-
if (!e.defaultPrevented && e.target.readOnly) {
|
|
33530
|
-
e.preventDefault();
|
|
33531
|
-
e.target.select();
|
|
33532
|
-
}
|
|
33533
|
-
},
|
|
33534
|
-
onMouseDown: e => {
|
|
33535
|
-
props.onMouseDown(e);
|
|
33536
|
-
if (!e.defaultPrevented && e.target.readOnly) {
|
|
33537
|
-
e.preventDefault();
|
|
33538
|
-
e.target.select();
|
|
33539
|
-
}
|
|
33540
|
-
}
|
|
33730
|
+
baseClassName: "navi_control_input",
|
|
33731
|
+
...autoSelectReadOnlyProps,
|
|
33541
33732
|
// Never set native maxLength — our guard handles it. maxLength stays in
|
|
33542
33733
|
// inputControlHostProps so form validation constraints still read it.
|
|
33543
|
-
,
|
|
33544
|
-
|
|
33545
33734
|
maxLength: undefined
|
|
33546
33735
|
});
|
|
33547
33736
|
};
|
|
@@ -33856,25 +34045,7 @@ const FormControl = props => {
|
|
|
33856
34045
|
wantRequesterButtonState: true,
|
|
33857
34046
|
controlType: "form",
|
|
33858
34047
|
stateType: "object",
|
|
33859
|
-
cascadeValidationToChildren: true
|
|
33860
|
-
aggregateChildStates: childUIStateControllers => {
|
|
33861
|
-
const formValues = {};
|
|
33862
|
-
for (const childUIStateController of childUIStateControllers) {
|
|
33863
|
-
const {
|
|
33864
|
-
name,
|
|
33865
|
-
uiState,
|
|
33866
|
-
allowNameless
|
|
33867
|
-
} = childUIStateController;
|
|
33868
|
-
if (!name) {
|
|
33869
|
-
if (!allowNameless) {
|
|
33870
|
-
console.warn("A form child component is missing a name property, its state won't be included in the form state", childUIStateController);
|
|
33871
|
-
}
|
|
33872
|
-
continue;
|
|
33873
|
-
}
|
|
33874
|
-
formValues[name] = uiState;
|
|
33875
|
-
}
|
|
33876
|
-
return formValues;
|
|
33877
|
-
}
|
|
34048
|
+
cascadeValidationToChildren: true
|
|
33878
34049
|
});
|
|
33879
34050
|
const {
|
|
33880
34051
|
basePseudoState,
|
|
@@ -34100,19 +34271,7 @@ const CheckboxGroupInterface = props => {
|
|
|
34100
34271
|
...props
|
|
34101
34272
|
}, {
|
|
34102
34273
|
stateType: "array",
|
|
34103
|
-
controlType: "checkbox_group"
|
|
34104
|
-
childControlFilter: childUIStateController => {
|
|
34105
|
-
return childUIStateController.controlType === "input" && childUIStateController.controlHostProps.type === "checkbox";
|
|
34106
|
-
},
|
|
34107
|
-
aggregateChildStates: childUIStateControllers => {
|
|
34108
|
-
const values = [];
|
|
34109
|
-
for (const childUIStateController of childUIStateControllers) {
|
|
34110
|
-
if (childUIStateController.uiState) {
|
|
34111
|
-
values.push(childUIStateController.uiState);
|
|
34112
|
-
}
|
|
34113
|
-
}
|
|
34114
|
-
return values.length === 0 ? undefined : values;
|
|
34115
|
-
}
|
|
34274
|
+
controlType: "checkbox_group"
|
|
34116
34275
|
});
|
|
34117
34276
|
useFocusGroup(ref, {
|
|
34118
34277
|
wrap: "both"
|
|
@@ -34439,6 +34598,9 @@ const isTextInputElement = (el) => {
|
|
|
34439
34598
|
installImportMetaCssBuild(import.meta);const css$t = /* css */`
|
|
34440
34599
|
.navi_input_duration {
|
|
34441
34600
|
--duration-separator-spacing: 4px;
|
|
34601
|
+
--loader-color: var(--navi-loader-color);
|
|
34602
|
+
|
|
34603
|
+
position: relative; /* For loading outline */
|
|
34442
34604
|
|
|
34443
34605
|
.navi_label {
|
|
34444
34606
|
&[data-separator] {
|
|
@@ -34540,13 +34702,8 @@ const InputDuration = props => {
|
|
|
34540
34702
|
// Exception: if the current value already has minutes, show them read-only so
|
|
34541
34703
|
// the stored precision is faithfully displayed.
|
|
34542
34704
|
const showMinutes = maxSeconds >= 60 && (stepSeconds === undefined || stepSeconds % 3600 !== 0 || valueHasMinutes);
|
|
34543
|
-
|
|
34544
|
-
// Mirror the single-input behaviour: a controlled value with no handler makes the field read-only.
|
|
34545
|
-
const implicitReadOnly = hasValue && !uiAction && !action;
|
|
34546
|
-
if (implicitReadOnly && !props.readOnly && undefined) ;
|
|
34547
34705
|
const [groupRootProps, groupHostProps, childrenWrapperProps] = useControlgroupProps({
|
|
34548
34706
|
...props,
|
|
34549
|
-
readOnly: props.readOnly || implicitReadOnly,
|
|
34550
34707
|
uiAction: (groupState, event) => {
|
|
34551
34708
|
const hiddenInput = ref.current;
|
|
34552
34709
|
if (hiddenInput) {
|
|
@@ -34595,15 +34752,10 @@ const InputDuration = props => {
|
|
|
34595
34752
|
// sub-inputs are correctly reset to their original raw string values.
|
|
34596
34753
|
// ISO 8601 encodes milliseconds as fractional seconds (e.g. "PT0.5S" = 500ms),
|
|
34597
34754
|
// so fractional seconds are split back into whole seconds + ms.
|
|
34598
|
-
distributeChildUIState: groupState => {
|
|
34755
|
+
distributeChildUIState: (groupState, childUIStateController) => {
|
|
34599
34756
|
const components = parseDuration(groupState);
|
|
34600
34757
|
if (!components) {
|
|
34601
|
-
return
|
|
34602
|
-
hour: undefined,
|
|
34603
|
-
minute: undefined,
|
|
34604
|
-
second: undefined,
|
|
34605
|
-
millisecond: undefined
|
|
34606
|
-
};
|
|
34758
|
+
return undefined;
|
|
34607
34759
|
}
|
|
34608
34760
|
const rawSeconds = components.seconds;
|
|
34609
34761
|
let secondForField = rawSeconds;
|
|
@@ -34612,12 +34764,13 @@ const InputDuration = props => {
|
|
|
34612
34764
|
secondForField = Math.floor(rawSeconds);
|
|
34613
34765
|
millisecondForField = Math.round(rawSeconds % 1 * 1000);
|
|
34614
34766
|
}
|
|
34615
|
-
|
|
34767
|
+
const fieldMap = {
|
|
34616
34768
|
hour: components.hours,
|
|
34617
34769
|
minute: components.minutes,
|
|
34618
34770
|
second: secondForField,
|
|
34619
34771
|
millisecond: millisecondForField
|
|
34620
34772
|
};
|
|
34773
|
+
return fieldMap[childUIStateController.name];
|
|
34621
34774
|
}
|
|
34622
34775
|
});
|
|
34623
34776
|
const {
|
|
@@ -34639,6 +34792,8 @@ const InputDuration = props => {
|
|
|
34639
34792
|
secondValue = Math.floor(rawSecondValue);
|
|
34640
34793
|
millisecondValue = Math.round(rawSecondValue % 1 * 1000);
|
|
34641
34794
|
}
|
|
34795
|
+
const loading = basePseudoState[":-navi-loading"];
|
|
34796
|
+
delete basePseudoState[":-navi-loading"];
|
|
34642
34797
|
return jsxs(Box, {
|
|
34643
34798
|
ref: groupRef,
|
|
34644
34799
|
className: "navi_input_duration",
|
|
@@ -34648,7 +34803,11 @@ const InputDuration = props => {
|
|
|
34648
34803
|
unit: undefined,
|
|
34649
34804
|
unitHour: undefined,
|
|
34650
34805
|
...clipboardProps,
|
|
34651
|
-
children: [jsx(
|
|
34806
|
+
children: [jsx(LoadingOutline, {
|
|
34807
|
+
loading: loading,
|
|
34808
|
+
color: "var(--loader-color)",
|
|
34809
|
+
inset: -1
|
|
34810
|
+
}), jsx("input", {
|
|
34652
34811
|
...groupHostProps,
|
|
34653
34812
|
type: "hidden",
|
|
34654
34813
|
basePseudoState: undefined // eslint-disable-line react/no-unknown-property
|
|
@@ -34960,7 +35119,11 @@ const InputDurationPart = ({
|
|
|
34960
35119
|
return jsxs(Label, {
|
|
34961
35120
|
flex: "y",
|
|
34962
35121
|
"data-separator": separator || undefined,
|
|
34963
|
-
children: [jsx(Input
|
|
35122
|
+
children: [jsx(Input
|
|
35123
|
+
// When autofocused this field should be selected
|
|
35124
|
+
// this help to modify the value on mobile
|
|
35125
|
+
, {
|
|
35126
|
+
autoSelect: true,
|
|
34964
35127
|
type: "navi_number",
|
|
34965
35128
|
"navi-input-type": unit,
|
|
34966
35129
|
name: unit,
|
|
@@ -35006,20 +35169,7 @@ const RadioGroupInterface = props => {
|
|
|
35006
35169
|
resetOnError: true,
|
|
35007
35170
|
...props
|
|
35008
35171
|
}, {
|
|
35009
|
-
controlType: "radio_group"
|
|
35010
|
-
childControlFilter: childUIStateController => {
|
|
35011
|
-
return childUIStateController.controlType === "input" && childUIStateController.controlHostProps.type === "radio";
|
|
35012
|
-
},
|
|
35013
|
-
aggregateChildStates: childUIStateControllers => {
|
|
35014
|
-
let activeValue;
|
|
35015
|
-
for (const childUIStateController of childUIStateControllers) {
|
|
35016
|
-
if (childUIStateController.uiState) {
|
|
35017
|
-
activeValue = childUIStateController.uiState;
|
|
35018
|
-
break;
|
|
35019
|
-
}
|
|
35020
|
-
}
|
|
35021
|
-
return activeValue;
|
|
35022
|
-
}
|
|
35172
|
+
controlType: "radio_group"
|
|
35023
35173
|
});
|
|
35024
35174
|
useFocusGroup(ref, {
|
|
35025
35175
|
wrap: "both"
|
|
@@ -37547,30 +37697,7 @@ const ListSelectable = props => {
|
|
|
37547
37697
|
} = props;
|
|
37548
37698
|
const [listControlRootProps, listControlProps, childrenWrapperProps] = useControlgroupProps(props, {
|
|
37549
37699
|
stateType: multiple ? "array" : "",
|
|
37550
|
-
controlType: multiple ? "checkbox_group" : "radio_group"
|
|
37551
|
-
childControlFilter: multiple ? childUIStateController => {
|
|
37552
|
-
return childUIStateController.controlType === "input" && childUIStateController.controlHostProps.type === "checkbox";
|
|
37553
|
-
} : childUIStateController => {
|
|
37554
|
-
return childUIStateController.controlType === "input" && childUIStateController.controlHostProps.type === "radio";
|
|
37555
|
-
},
|
|
37556
|
-
aggregateChildStates: multiple ? childUIStateControllers => {
|
|
37557
|
-
const values = [];
|
|
37558
|
-
for (const childUIStateController of childUIStateControllers) {
|
|
37559
|
-
if (childUIStateController.uiState) {
|
|
37560
|
-
values.push(childUIStateController.uiState);
|
|
37561
|
-
}
|
|
37562
|
-
}
|
|
37563
|
-
return values.length === 0 ? undefined : values;
|
|
37564
|
-
} : childUIStateControllers => {
|
|
37565
|
-
let activeValue;
|
|
37566
|
-
for (const childUIStateController of childUIStateControllers) {
|
|
37567
|
-
if (childUIStateController.uiState) {
|
|
37568
|
-
activeValue = childUIStateController.uiState;
|
|
37569
|
-
break;
|
|
37570
|
-
}
|
|
37571
|
-
}
|
|
37572
|
-
return activeValue;
|
|
37573
|
-
}
|
|
37700
|
+
controlType: multiple ? "checkbox_group" : "radio_group"
|
|
37574
37701
|
});
|
|
37575
37702
|
const uiGroupStateController = getUIStateControllerById(listControlProps.id);
|
|
37576
37703
|
useFocusGroup(ref, {
|
|
@@ -37655,6 +37782,8 @@ const ListSelectable = props => {
|
|
|
37655
37782
|
...listControlRootProps,
|
|
37656
37783
|
...listControlProps,
|
|
37657
37784
|
name: undefined,
|
|
37785
|
+
value: undefined,
|
|
37786
|
+
defaultValue: undefined,
|
|
37658
37787
|
selectedIndicator: undefined,
|
|
37659
37788
|
selectable: undefined,
|
|
37660
37789
|
multiple: undefined
|
|
@@ -38727,7 +38856,13 @@ const useListScrollSync = ({
|
|
|
38727
38856
|
}
|
|
38728
38857
|
hasBeenDisplayedRef.current = true;
|
|
38729
38858
|
const items = tracker.itemsSignal.peek();
|
|
38730
|
-
const firstSelected = items.find(i =>
|
|
38859
|
+
const firstSelected = items.find(i => {
|
|
38860
|
+
if (i.selected) {
|
|
38861
|
+
return true;
|
|
38862
|
+
}
|
|
38863
|
+
const inputController = getUIStateControllerById(`${i.id}_input`);
|
|
38864
|
+
return inputController ? inputController.uiStateSignal.peek() : false;
|
|
38865
|
+
});
|
|
38731
38866
|
if (firstSelected) {
|
|
38732
38867
|
scrollToItem(firstSelected, {
|
|
38733
38868
|
event: new CustomEvent("navi_displayed", {
|
|
@@ -40605,19 +40740,20 @@ const isWithinPickerContent = (el, pickerEl) => {
|
|
|
40605
40740
|
};
|
|
40606
40741
|
const PickerInput = props => {
|
|
40607
40742
|
const {
|
|
40608
|
-
ui
|
|
40743
|
+
ui,
|
|
40744
|
+
readOnly
|
|
40609
40745
|
} = props;
|
|
40610
40746
|
|
|
40611
40747
|
// After type resolution: force readOnly when the input type would open the
|
|
40612
40748
|
// mobile keyboard. We also suppress the visual ":read-only" state so the
|
|
40613
40749
|
// picker still looks interactive (it is — just not keyboard-typeable).
|
|
40614
|
-
|
|
40615
|
-
|
|
40616
|
-
props["data-readonly-forced"] = "";
|
|
40617
|
-
}
|
|
40750
|
+
const readOnlyForced = readOnly ? false : MOBILE_KEYBOARD_TYPES.has(props.type || "text");
|
|
40751
|
+
const autoSelectReadOnlyProps = useAutoSelectReadOnly(props);
|
|
40618
40752
|
return jsx(Box, {
|
|
40619
40753
|
as: "input",
|
|
40620
40754
|
...props,
|
|
40755
|
+
readOnly: readOnlyForced ? true : readOnly,
|
|
40756
|
+
"data-readonly-forced": readOnlyForced ? "" : undefined,
|
|
40621
40757
|
ui: undefined,
|
|
40622
40758
|
className: "navi_picker_input",
|
|
40623
40759
|
pseudoClasses: PickerInputPseudoClasses,
|
|
@@ -40630,7 +40766,8 @@ const PickerInput = props => {
|
|
|
40630
40766
|
// Ensure tab does not tab through the browser picker elements (like in input date)
|
|
40631
40767
|
performTabNavigation(e);
|
|
40632
40768
|
}
|
|
40633
|
-
}
|
|
40769
|
+
},
|
|
40770
|
+
...autoSelectReadOnlyProps
|
|
40634
40771
|
});
|
|
40635
40772
|
};
|
|
40636
40773
|
// Input types that open the software keyboard on mobile.
|