@jsenv/navi 0.27.36 → 0.27.37
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 +679 -612
- package/dist/jsenv_navi.js.map +69 -67
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -20761,11 +20761,9 @@ const checkConstraints = ({
|
|
|
20761
20761
|
fromRequestAction,
|
|
20762
20762
|
} = {}) => {
|
|
20763
20763
|
let elementToValidate = event.currentTarget;
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
20767
|
-
elementToValidate = controlHost;
|
|
20768
|
-
}
|
|
20764
|
+
const controlHost = findControlHost(elementToValidate);
|
|
20765
|
+
if (controlHost) {
|
|
20766
|
+
elementToValidate = controlHost;
|
|
20769
20767
|
}
|
|
20770
20768
|
|
|
20771
20769
|
let validationInterface = elementToValidate.__validationInterface__;
|
|
@@ -22858,7 +22856,7 @@ const createOnKeyDownForShortcutArray = (shortcuts, busyRef) => {
|
|
|
22858
22856
|
}
|
|
22859
22857
|
|
|
22860
22858
|
return (keyboardEvent) => {
|
|
22861
|
-
applyKeyboardShortcuts(shortcutsCopy, keyboardEvent);
|
|
22859
|
+
return applyKeyboardShortcuts(shortcutsCopy, keyboardEvent);
|
|
22862
22860
|
};
|
|
22863
22861
|
};
|
|
22864
22862
|
const createOnKeyDownForShortcuts = (shortcuts) => {
|
|
@@ -22943,7 +22941,7 @@ const applyKeyboardShortcuts = (shortcuts, keyboardEvent) => {
|
|
|
22943
22941
|
if (returnValue === false) {
|
|
22944
22942
|
keyboardEvent.preventDefault();
|
|
22945
22943
|
}
|
|
22946
|
-
return
|
|
22944
|
+
return returnValue;
|
|
22947
22945
|
}
|
|
22948
22946
|
return null;
|
|
22949
22947
|
};
|
|
@@ -23173,8 +23171,15 @@ const css$L = /* css */`
|
|
|
23173
23171
|
&[data-skeleton] {
|
|
23174
23172
|
border-radius: 0.2em;
|
|
23175
23173
|
}
|
|
23174
|
+
|
|
23176
23175
|
&[data-capitalize] {
|
|
23177
|
-
|
|
23176
|
+
text-transform: capitalize;
|
|
23177
|
+
|
|
23178
|
+
.navi_text_sizer {
|
|
23179
|
+
.navi_text {
|
|
23180
|
+
display: inline-block; /* We need inline-block to match the pseudo element */
|
|
23181
|
+
}
|
|
23182
|
+
}
|
|
23178
23183
|
}
|
|
23179
23184
|
&[data-shrinkwrap] {
|
|
23180
23185
|
display: inline-block;
|
|
@@ -23220,25 +23225,11 @@ const css$L = /* css */`
|
|
|
23220
23225
|
}
|
|
23221
23226
|
|
|
23222
23227
|
&[data-text-overflow] {
|
|
23228
|
+
display: block;
|
|
23223
23229
|
min-width: 0;
|
|
23224
|
-
flex-wrap: wrap;
|
|
23225
23230
|
text-overflow: ellipsis;
|
|
23226
23231
|
overflow: hidden;
|
|
23227
23232
|
overflow-wrap: normal;
|
|
23228
|
-
|
|
23229
|
-
.navi_text_overflow_wrapper {
|
|
23230
|
-
display: flex;
|
|
23231
|
-
width: 100%;
|
|
23232
|
-
flex-grow: 1;
|
|
23233
|
-
justify-content: inherit;
|
|
23234
|
-
gap: 0.3em;
|
|
23235
|
-
|
|
23236
|
-
.navi_text_overflow_text {
|
|
23237
|
-
max-width: 100%;
|
|
23238
|
-
text-overflow: ellipsis;
|
|
23239
|
-
overflow: hidden;
|
|
23240
|
-
}
|
|
23241
|
-
}
|
|
23242
23233
|
}
|
|
23243
23234
|
|
|
23244
23235
|
&[data-skeleton] {
|
|
@@ -23469,9 +23460,6 @@ const shouldInjectSpacingBetween = (left, right) => {
|
|
|
23469
23460
|
if (rightIsNode && isMarkedAsOutsideTextFlow(right)) {
|
|
23470
23461
|
return false;
|
|
23471
23462
|
}
|
|
23472
|
-
if (rightIsNode && right.props && right.props.overflowPinned) {
|
|
23473
|
-
return false;
|
|
23474
|
-
}
|
|
23475
23463
|
if (typeof left === "string" && /\s$/.test(left)) {
|
|
23476
23464
|
return false;
|
|
23477
23465
|
}
|
|
@@ -23480,7 +23468,7 @@ const shouldInjectSpacingBetween = (left, right) => {
|
|
|
23480
23468
|
}
|
|
23481
23469
|
return true;
|
|
23482
23470
|
};
|
|
23483
|
-
|
|
23471
|
+
|
|
23484
23472
|
/**
|
|
23485
23473
|
* Text component for rendering inline or block text with layout-stable style changes.
|
|
23486
23474
|
*
|
|
@@ -23494,10 +23482,6 @@ const OverflowPinnedContext = createContext(null);
|
|
|
23494
23482
|
* `maxLines={1}` truncates after one line (single-line ellipsis).
|
|
23495
23483
|
* `maxLines={n}` (n > 1) truncates after n lines (multi-line clamp).
|
|
23496
23484
|
*
|
|
23497
|
-
* @param {boolean} [props.overflowPinned]
|
|
23498
|
-
* Must be used inside a `<Text maxLines>` parent.
|
|
23499
|
-
* Pins this element outside the truncated text flow (e.g. a badge or icon).
|
|
23500
|
-
*
|
|
23501
23485
|
* @param {string} [props.spacing]
|
|
23502
23486
|
* Controls the separator injected between child nodes.
|
|
23503
23487
|
* Accepts a size/spacing scale key, a CSS length, or `"pre"` / `0` to disable.
|
|
@@ -23562,11 +23546,6 @@ const TextDispatcher = props => {
|
|
|
23562
23546
|
...props
|
|
23563
23547
|
});
|
|
23564
23548
|
}
|
|
23565
|
-
if (props.overflowPinned) {
|
|
23566
|
-
return jsx(TextOverflowPinned, {
|
|
23567
|
-
...props
|
|
23568
|
-
});
|
|
23569
|
-
}
|
|
23570
23549
|
if (props.selectRange) {
|
|
23571
23550
|
return jsx(TextWithSelectRange, {
|
|
23572
23551
|
...props
|
|
@@ -23740,10 +23719,7 @@ const TextOverflow = ({
|
|
|
23740
23719
|
children,
|
|
23741
23720
|
...rest
|
|
23742
23721
|
}) => {
|
|
23743
|
-
const [overflowPinned, setOverflowPinned] = useState(null);
|
|
23744
23722
|
return jsx(TextDispatcher, {
|
|
23745
|
-
inline: true,
|
|
23746
|
-
flex: true,
|
|
23747
23723
|
block: true,
|
|
23748
23724
|
as: "div",
|
|
23749
23725
|
pre: noWrap === undefined ? true : undefined
|
|
@@ -23755,44 +23731,10 @@ const TextOverflow = ({
|
|
|
23755
23731
|
...rest,
|
|
23756
23732
|
maxLines: undefined,
|
|
23757
23733
|
"data-text-overflow": "",
|
|
23758
|
-
spacing:
|
|
23759
|
-
|
|
23760
|
-
|
|
23761
|
-
children: [overflowPinned && overflowPinned.position === "start" ? overflowPinned.vnode : null, jsx(OverflowPinnedContext.Provider, {
|
|
23762
|
-
value: setOverflowPinned,
|
|
23763
|
-
children: jsx(Text, {
|
|
23764
|
-
className: "navi_text_overflow_text",
|
|
23765
|
-
spacing: spacing,
|
|
23766
|
-
capitalize: capitalize,
|
|
23767
|
-
children: children
|
|
23768
|
-
})
|
|
23769
|
-
}), overflowPinned && overflowPinned.position === "end" ? overflowPinned.vnode : null]
|
|
23770
|
-
})
|
|
23771
|
-
});
|
|
23772
|
-
};
|
|
23773
|
-
const TextOverflowPinned = props => {
|
|
23774
|
-
const {
|
|
23775
|
-
overflowPinned
|
|
23776
|
-
} = props;
|
|
23777
|
-
const setOverflowPinned = useContext(OverflowPinnedContext);
|
|
23778
|
-
const text = jsx(Text, {
|
|
23779
|
-
...props,
|
|
23780
|
-
"data-overflow-pinned": "",
|
|
23781
|
-
overflowPinned: undefined
|
|
23734
|
+
spacing: spacing,
|
|
23735
|
+
capitalize: capitalize,
|
|
23736
|
+
children: children
|
|
23782
23737
|
});
|
|
23783
|
-
if (!setOverflowPinned) {
|
|
23784
|
-
console.warn(`<Text overflowPinned> declared outside a <Text maxLines="1">`);
|
|
23785
|
-
return text;
|
|
23786
|
-
}
|
|
23787
|
-
if (overflowPinned) {
|
|
23788
|
-
setOverflowPinned({
|
|
23789
|
-
vnode: text,
|
|
23790
|
-
position: overflowPinned === true ? "end" : overflowPinned
|
|
23791
|
-
});
|
|
23792
|
-
return null;
|
|
23793
|
-
}
|
|
23794
|
-
setOverflowPinned(null);
|
|
23795
|
-
return text;
|
|
23796
23738
|
};
|
|
23797
23739
|
const TextWithSelectRange = ({
|
|
23798
23740
|
ref,
|
|
@@ -24553,7 +24495,15 @@ const useAutoFocus = (
|
|
|
24553
24495
|
// }
|
|
24554
24496
|
// }, []);
|
|
24555
24497
|
|
|
24556
|
-
return
|
|
24498
|
+
return {
|
|
24499
|
+
"autoFocus": undefined,
|
|
24500
|
+
"navi-autofocus": autoFocus
|
|
24501
|
+
? autoFocus === true
|
|
24502
|
+
? ""
|
|
24503
|
+
: autoFocus
|
|
24504
|
+
: undefined,
|
|
24505
|
+
"navi-autofocus-select": autoFocus && autoSelect ? "" : undefined,
|
|
24506
|
+
};
|
|
24557
24507
|
};
|
|
24558
24508
|
|
|
24559
24509
|
let blurEvent = null;
|
|
@@ -24577,7 +24527,189 @@ document.body.addEventListener(
|
|
|
24577
24527
|
{ capture: true },
|
|
24578
24528
|
);
|
|
24579
24529
|
|
|
24580
|
-
|
|
24530
|
+
/**
|
|
24531
|
+
* Converts a JS value into the form expected by the browser DOM property for a
|
|
24532
|
+
* given control type/input type combination.
|
|
24533
|
+
*
|
|
24534
|
+
* For example:
|
|
24535
|
+
* - `datetime-local` inputs expect a local datetime string without timezone
|
|
24536
|
+
* - `number`/`range` inputs expect a numeric string or number
|
|
24537
|
+
* - `color` inputs require a non-empty hex string (falls back to `#000000`)
|
|
24538
|
+
* - All other inputs receive the value as-is (undefined → "")
|
|
24539
|
+
*
|
|
24540
|
+
* Returns either the converted value directly, or a converter function when the
|
|
24541
|
+
* conversion depends on the runtime value (e.g. plain inputs return `asInputValue`).
|
|
24542
|
+
*
|
|
24543
|
+
* @param {any} value - The JS value to convert.
|
|
24544
|
+
* @param {{ controlType: string, type: string }} options
|
|
24545
|
+
* @returns {any} The DOM-compatible value or a converter function.
|
|
24546
|
+
*/
|
|
24547
|
+
const asControlHostValue = (
|
|
24548
|
+
jsValue,
|
|
24549
|
+
{ controlType, type, inputMode },
|
|
24550
|
+
) => {
|
|
24551
|
+
if (controlType === "input" || controlType === "picker") {
|
|
24552
|
+
if (type === "datetime-local") {
|
|
24553
|
+
return asDatetimeLocalString(jsValue);
|
|
24554
|
+
}
|
|
24555
|
+
if (type === "number" || type === "range" || inputMode === "numeric") {
|
|
24556
|
+
return asNumberString(jsValue);
|
|
24557
|
+
}
|
|
24558
|
+
if (type === "color") {
|
|
24559
|
+
return asColorString(jsValue);
|
|
24560
|
+
}
|
|
24561
|
+
return asInputValue(jsValue);
|
|
24562
|
+
}
|
|
24563
|
+
return jsValue;
|
|
24564
|
+
};
|
|
24565
|
+
// As explained in https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/datetime-local#setting_timezones
|
|
24566
|
+
// datetime-local does not support timezones
|
|
24567
|
+
const asDatetimeLocalString = (dateTimeString) => {
|
|
24568
|
+
const date = new Date(dateTimeString);
|
|
24569
|
+
if (isNaN(date.getTime())) {
|
|
24570
|
+
return dateTimeString;
|
|
24571
|
+
}
|
|
24572
|
+
const year = date.getFullYear();
|
|
24573
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
24574
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
24575
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
24576
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
24577
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
24578
|
+
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
|
|
24579
|
+
};
|
|
24580
|
+
const asNumberString = (jsValue) => {
|
|
24581
|
+
if (jsValue === undefined) {
|
|
24582
|
+
return "";
|
|
24583
|
+
}
|
|
24584
|
+
return jsValue;
|
|
24585
|
+
};
|
|
24586
|
+
// Browser requires a non-empty value for <input type="color">.
|
|
24587
|
+
// When our logical value is empty we give it #000000 so it doesn't choke.
|
|
24588
|
+
// The UI uses the original (possibly empty) value to show the checkerboard.
|
|
24589
|
+
const asColorString = (jsValue) => {
|
|
24590
|
+
return jsValue || "#000000";
|
|
24591
|
+
};
|
|
24592
|
+
const asInputValue = (jsValue) => {
|
|
24593
|
+
if (jsValue === undefined) {
|
|
24594
|
+
return "";
|
|
24595
|
+
}
|
|
24596
|
+
return jsValue;
|
|
24597
|
+
};
|
|
24598
|
+
|
|
24599
|
+
/**
|
|
24600
|
+
* Reads the current logical JS value from a control host DOM element.
|
|
24601
|
+
*
|
|
24602
|
+
* Handles all navi control host element types:
|
|
24603
|
+
* - `<button>` — reads via `navi_get_value` custom event, falls back to `button.value`
|
|
24604
|
+
* - `<input type="number|range">` — parses as a number, returns `undefined` when empty
|
|
24605
|
+
* - `<input type="checkbox|radio">` — returns `undefined` when unchecked, otherwise reads
|
|
24606
|
+
* via `navi_get_value` custom event (to preserve the original JS type of the value prop)
|
|
24607
|
+
* - `<input type="datetime-local">` — converts the local datetime string to an ISO 8601 string
|
|
24608
|
+
* - `<input type="navi_picker">` — delegates to the controller via `navi_get_ui_state`
|
|
24609
|
+
* - All other inputs — returns `input.value` as a string
|
|
24610
|
+
*
|
|
24611
|
+
* @param {HTMLElement} controlHost - The control host DOM element to read from.
|
|
24612
|
+
* @returns {any} The current logical value of the control.
|
|
24613
|
+
*/
|
|
24614
|
+
const readControlValue = (controlHost) => {
|
|
24615
|
+
if (
|
|
24616
|
+
controlHost.tagName === "BUTTON" ||
|
|
24617
|
+
controlHost.getAttribute("role") === "button"
|
|
24618
|
+
) {
|
|
24619
|
+
return readValueFromButton(controlHost);
|
|
24620
|
+
}
|
|
24621
|
+
if (controlHost.tagName === "INPUT") {
|
|
24622
|
+
// important: input.type = "navi_js"; followed by input.type; returns "text"
|
|
24623
|
+
// so use getAttribute
|
|
24624
|
+
const type = controlHost.getAttribute("type");
|
|
24625
|
+
|
|
24626
|
+
if (
|
|
24627
|
+
type === "number" ||
|
|
24628
|
+
type === "range" ||
|
|
24629
|
+
controlHost.inputMode === "numeric"
|
|
24630
|
+
) {
|
|
24631
|
+
return readNumberFromInput(controlHost);
|
|
24632
|
+
}
|
|
24633
|
+
if (type === "color") {
|
|
24634
|
+
return readValueFromControlHost(controlHost);
|
|
24635
|
+
}
|
|
24636
|
+
if (type === "checkbox" || type === "radio") {
|
|
24637
|
+
return readValueFromCheckableInput(controlHost);
|
|
24638
|
+
}
|
|
24639
|
+
if (type === "datetime-local") {
|
|
24640
|
+
return readDatetimeLocalFromInput(controlHost);
|
|
24641
|
+
}
|
|
24642
|
+
if (type === "navi_js") {
|
|
24643
|
+
return getUIStateFromElement(controlHost);
|
|
24644
|
+
}
|
|
24645
|
+
return readValueFromInput(controlHost);
|
|
24646
|
+
}
|
|
24647
|
+
if (controlHost.hasAttribute("navi-control-host")) {
|
|
24648
|
+
// Non-button, non-input navi controls (e.g. Badge.Button rendered as span)
|
|
24649
|
+
return readValueFromControlHost(controlHost);
|
|
24650
|
+
}
|
|
24651
|
+
return readValueFromElement(controlHost);
|
|
24652
|
+
};
|
|
24653
|
+
const readValueFromControlHost = (controlHost) => {
|
|
24654
|
+
return readValueFromNaviCustomEvent(controlHost, controlHost.value);
|
|
24655
|
+
};
|
|
24656
|
+
const readValueFromButton = (button) => {
|
|
24657
|
+
return readValueFromControlHost(button);
|
|
24658
|
+
};
|
|
24659
|
+
const readDatetimeLocalFromInput = (input) => {
|
|
24660
|
+
const localDateTimeString = input.value;
|
|
24661
|
+
if (localDateTimeString === "") {
|
|
24662
|
+
return "";
|
|
24663
|
+
}
|
|
24664
|
+
const localDate = new Date(localDateTimeString);
|
|
24665
|
+
if (isNaN(localDate.getTime())) {
|
|
24666
|
+
return localDateTimeString;
|
|
24667
|
+
}
|
|
24668
|
+
return localDate.toISOString();
|
|
24669
|
+
};
|
|
24670
|
+
const readNumberFromInput = (input) => {
|
|
24671
|
+
const numberString = input.value;
|
|
24672
|
+
if (numberString === "") {
|
|
24673
|
+
return "";
|
|
24674
|
+
}
|
|
24675
|
+
const asNumber = Number(numberString);
|
|
24676
|
+
if (isNaN(asNumber)) {
|
|
24677
|
+
return numberString;
|
|
24678
|
+
}
|
|
24679
|
+
return asNumber;
|
|
24680
|
+
};
|
|
24681
|
+
const readValueFromCheckableInput = (input) => {
|
|
24682
|
+
const checked = input.checked;
|
|
24683
|
+
if (!checked) {
|
|
24684
|
+
return undefined;
|
|
24685
|
+
}
|
|
24686
|
+
return readValueFromControlHost(input);
|
|
24687
|
+
};
|
|
24688
|
+
const readValueFromInput = (input) => {
|
|
24689
|
+
const value = input.value;
|
|
24690
|
+
return value;
|
|
24691
|
+
};
|
|
24692
|
+
const readValueFromElement = (element) => {
|
|
24693
|
+
const value = element.value;
|
|
24694
|
+
return value;
|
|
24695
|
+
};
|
|
24696
|
+
const readValueFromNaviCustomEvent = (field, fallback) => {
|
|
24697
|
+
// prefer the value given as prop (respect original type, browser would convert to string)
|
|
24698
|
+
let responded;
|
|
24699
|
+
let value;
|
|
24700
|
+
dispatchCustomEvent(field, "navi_get_value", {
|
|
24701
|
+
respondWith: (jsValue) => {
|
|
24702
|
+
responded = true;
|
|
24703
|
+
value = jsValue;
|
|
24704
|
+
},
|
|
24705
|
+
});
|
|
24706
|
+
if (responded) {
|
|
24707
|
+
return value;
|
|
24708
|
+
}
|
|
24709
|
+
return fallback;
|
|
24710
|
+
};
|
|
24711
|
+
|
|
24712
|
+
const triggerNaviCommand = (
|
|
24581
24713
|
element,
|
|
24582
24714
|
command,
|
|
24583
24715
|
event,
|
|
@@ -24616,6 +24748,7 @@ const dispatchNaviCommand = (
|
|
|
24616
24748
|
value,
|
|
24617
24749
|
});
|
|
24618
24750
|
};
|
|
24751
|
+
|
|
24619
24752
|
// Returns the target explicitly declared via HTML attributes (commandfor / navi-command-target),
|
|
24620
24753
|
// or undefined when no such attribute is present.
|
|
24621
24754
|
// Returns null when the attribute is present but the target element was not found (already warned).
|
|
@@ -24681,6 +24814,13 @@ const resolveCommandValue = (source, event) => {
|
|
|
24681
24814
|
if (source.hasAttribute("command-value")) {
|
|
24682
24815
|
return source.getAttribute("command-value");
|
|
24683
24816
|
}
|
|
24817
|
+
if (source.type === "radio" || source.type === "checkbox") {
|
|
24818
|
+
// Use readControlValue so that radio/checkbox sources return their `value`
|
|
24819
|
+
// attribute (e.g. "Cherry") rather than the boolean checked state (true).
|
|
24820
|
+
// getUIStateFromElement would return true for a checked radio, which is
|
|
24821
|
+
// wrong when the command needs to propagate the selected item's identity.
|
|
24822
|
+
return readControlValue(source);
|
|
24823
|
+
}
|
|
24684
24824
|
return getUIStateFromElement(source);
|
|
24685
24825
|
};
|
|
24686
24826
|
|
|
@@ -24773,7 +24913,7 @@ registerNaviCommand("--navi-clear", (source, event) => {
|
|
|
24773
24913
|
target,
|
|
24774
24914
|
implementation: () => {
|
|
24775
24915
|
if (fromInput) ; else {
|
|
24776
|
-
|
|
24916
|
+
triggerNaviCommand(source, "--navi-close", event, {
|
|
24777
24917
|
optional: true,
|
|
24778
24918
|
});
|
|
24779
24919
|
}
|
|
@@ -24928,11 +25068,11 @@ const executeNaviDefine = (source, event, target) => {
|
|
|
24928
25068
|
// send button's undefined value would corrupt the picker state.
|
|
24929
25069
|
const skipUpdate = resolvePickerInnerControl(target) !== null;
|
|
24930
25070
|
if (!skipUpdate) {
|
|
24931
|
-
|
|
25071
|
+
triggerNaviCommand(source, "--navi-update", event);
|
|
24932
25072
|
}
|
|
24933
25073
|
// The picker's onClose already dispatches the action with the final value.
|
|
24934
25074
|
// Dispatching again here would fire the action twice.
|
|
24935
|
-
return
|
|
25075
|
+
return triggerNaviCommand(target, "--navi-close", event);
|
|
24936
25076
|
};
|
|
24937
25077
|
|
|
24938
25078
|
registerNaviCommand("--navi-scroll", (source, event) => {
|
|
@@ -24951,6 +25091,36 @@ registerNaviCommand("--navi-scroll", (source, event) => {
|
|
|
24951
25091
|
},
|
|
24952
25092
|
};
|
|
24953
25093
|
});
|
|
25094
|
+
registerNaviCommand("--navi-check", (source, event) => {
|
|
25095
|
+
const target =
|
|
25096
|
+
resolveExplicitTarget(source) || resolveFirstParentControl(source);
|
|
25097
|
+
if (!target) {
|
|
25098
|
+
return undefined;
|
|
25099
|
+
}
|
|
25100
|
+
return {
|
|
25101
|
+
target,
|
|
25102
|
+
implementation: () => {
|
|
25103
|
+
return dispatchCustomEvent(target, "navi_request_check", {
|
|
25104
|
+
event,
|
|
25105
|
+
});
|
|
25106
|
+
},
|
|
25107
|
+
};
|
|
25108
|
+
});
|
|
25109
|
+
registerNaviCommand("--navi-uncheck", (source, event) => {
|
|
25110
|
+
const target =
|
|
25111
|
+
resolveExplicitTarget(source) || resolveFirstParentControl(source);
|
|
25112
|
+
if (!target) {
|
|
25113
|
+
return undefined;
|
|
25114
|
+
}
|
|
25115
|
+
return {
|
|
25116
|
+
target,
|
|
25117
|
+
implementation: () => {
|
|
25118
|
+
return dispatchCustomEvent(target, "navi_request_uncheck", {
|
|
25119
|
+
event,
|
|
25120
|
+
});
|
|
25121
|
+
},
|
|
25122
|
+
};
|
|
25123
|
+
});
|
|
24954
25124
|
registerNaviCommand("--navi-select", (source, event) => {
|
|
24955
25125
|
const target =
|
|
24956
25126
|
resolveExplicitTarget(source) || resolveFirstParentControl(source);
|
|
@@ -25041,223 +25211,42 @@ const CONTROL_PROP_SET = new Set([
|
|
|
25041
25211
|
"autoFocusVisible",
|
|
25042
25212
|
"autoSelect",
|
|
25043
25213
|
|
|
25044
|
-
"onMouseDown",
|
|
25045
|
-
"onClick",
|
|
25046
|
-
"onKeyDown",
|
|
25047
|
-
"onPaste",
|
|
25048
|
-
"onInput",
|
|
25049
|
-
|
|
25050
|
-
|
|
25051
|
-
"
|
|
25052
|
-
"
|
|
25053
|
-
"
|
|
25054
|
-
"
|
|
25055
|
-
"
|
|
25056
|
-
"
|
|
25057
|
-
"
|
|
25058
|
-
"
|
|
25059
|
-
"
|
|
25060
|
-
|
|
25061
|
-
|
|
25062
|
-
"
|
|
25063
|
-
"
|
|
25064
|
-
|
|
25065
|
-
|
|
25066
|
-
|
|
25067
|
-
const
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
const
|
|
25071
|
-
const
|
|
25072
|
-
const
|
|
25073
|
-
const
|
|
25074
|
-
createContext();
|
|
25075
|
-
|
|
25076
|
-
|
|
25077
|
-
const
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
* Converts a JS value into the form expected by the browser DOM property for a
|
|
25081
|
-
* given control type/input type combination.
|
|
25082
|
-
*
|
|
25083
|
-
* For example:
|
|
25084
|
-
* - `datetime-local` inputs expect a local datetime string without timezone
|
|
25085
|
-
* - `number`/`range` inputs expect a numeric string or number
|
|
25086
|
-
* - `color` inputs require a non-empty hex string (falls back to `#000000`)
|
|
25087
|
-
* - All other inputs receive the value as-is (undefined → "")
|
|
25088
|
-
*
|
|
25089
|
-
* Returns either the converted value directly, or a converter function when the
|
|
25090
|
-
* conversion depends on the runtime value (e.g. plain inputs return `asInputValue`).
|
|
25091
|
-
*
|
|
25092
|
-
* @param {any} value - The JS value to convert.
|
|
25093
|
-
* @param {{ controlType: string, type: string }} options
|
|
25094
|
-
* @returns {any} The DOM-compatible value or a converter function.
|
|
25095
|
-
*/
|
|
25096
|
-
const asControlHostValue = (
|
|
25097
|
-
jsValue,
|
|
25098
|
-
{ controlType, type, inputMode },
|
|
25099
|
-
) => {
|
|
25100
|
-
if (controlType === "input" || controlType === "picker") {
|
|
25101
|
-
if (type === "datetime-local") {
|
|
25102
|
-
return asDatetimeLocalString(jsValue);
|
|
25103
|
-
}
|
|
25104
|
-
if (type === "number" || type === "range" || inputMode === "numeric") {
|
|
25105
|
-
return asNumberString(jsValue);
|
|
25106
|
-
}
|
|
25107
|
-
if (type === "color") {
|
|
25108
|
-
return asColorString(jsValue);
|
|
25109
|
-
}
|
|
25110
|
-
return asInputValue(jsValue);
|
|
25111
|
-
}
|
|
25112
|
-
return jsValue;
|
|
25113
|
-
};
|
|
25114
|
-
// As explained in https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/datetime-local#setting_timezones
|
|
25115
|
-
// datetime-local does not support timezones
|
|
25116
|
-
const asDatetimeLocalString = (dateTimeString) => {
|
|
25117
|
-
const date = new Date(dateTimeString);
|
|
25118
|
-
if (isNaN(date.getTime())) {
|
|
25119
|
-
return dateTimeString;
|
|
25120
|
-
}
|
|
25121
|
-
const year = date.getFullYear();
|
|
25122
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
25123
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
25124
|
-
const hours = String(date.getHours()).padStart(2, "0");
|
|
25125
|
-
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
25126
|
-
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
25127
|
-
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
|
|
25128
|
-
};
|
|
25129
|
-
const asNumberString = (jsValue) => {
|
|
25130
|
-
if (jsValue === undefined) {
|
|
25131
|
-
return "";
|
|
25132
|
-
}
|
|
25133
|
-
return jsValue;
|
|
25134
|
-
};
|
|
25135
|
-
// Browser requires a non-empty value for <input type="color">.
|
|
25136
|
-
// When our logical value is empty we give it #000000 so it doesn't choke.
|
|
25137
|
-
// The UI uses the original (possibly empty) value to show the checkerboard.
|
|
25138
|
-
const asColorString = (jsValue) => {
|
|
25139
|
-
return jsValue || "#000000";
|
|
25140
|
-
};
|
|
25141
|
-
const asInputValue = (jsValue) => {
|
|
25142
|
-
if (jsValue === undefined) {
|
|
25143
|
-
return "";
|
|
25144
|
-
}
|
|
25145
|
-
return jsValue;
|
|
25146
|
-
};
|
|
25147
|
-
|
|
25148
|
-
/**
|
|
25149
|
-
* Reads the current logical JS value from a control host DOM element.
|
|
25150
|
-
*
|
|
25151
|
-
* Handles all navi control host element types:
|
|
25152
|
-
* - `<button>` — reads via `navi_get_value` custom event, falls back to `button.value`
|
|
25153
|
-
* - `<input type="number|range">` — parses as a number, returns `undefined` when empty
|
|
25154
|
-
* - `<input type="checkbox|radio">` — returns `undefined` when unchecked, otherwise reads
|
|
25155
|
-
* via `navi_get_value` custom event (to preserve the original JS type of the value prop)
|
|
25156
|
-
* - `<input type="datetime-local">` — converts the local datetime string to an ISO 8601 string
|
|
25157
|
-
* - `<input type="navi_picker">` — delegates to the controller via `navi_get_ui_state`
|
|
25158
|
-
* - All other inputs — returns `input.value` as a string
|
|
25159
|
-
*
|
|
25160
|
-
* @param {HTMLElement} controlHost - The control host DOM element to read from.
|
|
25161
|
-
* @returns {any} The current logical value of the control.
|
|
25162
|
-
*/
|
|
25163
|
-
const readControlValue = (controlHost) => {
|
|
25164
|
-
if (
|
|
25165
|
-
controlHost.tagName === "BUTTON" ||
|
|
25166
|
-
controlHost.getAttribute("role") === "button"
|
|
25167
|
-
) {
|
|
25168
|
-
return readValueFromButton(controlHost);
|
|
25169
|
-
}
|
|
25170
|
-
if (controlHost.tagName === "INPUT") {
|
|
25171
|
-
// important: input.type = "navi_js"; followed by input.type; returns "text"
|
|
25172
|
-
// so use getAttribute
|
|
25173
|
-
const type = controlHost.getAttribute("type");
|
|
25174
|
-
|
|
25175
|
-
if (
|
|
25176
|
-
type === "number" ||
|
|
25177
|
-
type === "range" ||
|
|
25178
|
-
controlHost.inputMode === "numeric"
|
|
25179
|
-
) {
|
|
25180
|
-
return readNumberFromInput(controlHost);
|
|
25181
|
-
}
|
|
25182
|
-
if (type === "color") {
|
|
25183
|
-
return readValueFromControlHost(controlHost);
|
|
25184
|
-
}
|
|
25185
|
-
if (type === "checkbox" || type === "radio") {
|
|
25186
|
-
return readValueFromCheckableInput(controlHost);
|
|
25187
|
-
}
|
|
25188
|
-
if (type === "datetime-local") {
|
|
25189
|
-
return readDatetimeLocalFromInput(controlHost);
|
|
25190
|
-
}
|
|
25191
|
-
if (type === "navi_js") {
|
|
25192
|
-
return getUIStateFromElement(controlHost);
|
|
25193
|
-
}
|
|
25194
|
-
return readValueFromInput(controlHost);
|
|
25195
|
-
}
|
|
25196
|
-
if (controlHost.hasAttribute("navi-control-host")) {
|
|
25197
|
-
// Non-button, non-input navi controls (e.g. Badge.Button rendered as span)
|
|
25198
|
-
return readValueFromControlHost(controlHost);
|
|
25199
|
-
}
|
|
25200
|
-
return readValueFromElement(controlHost);
|
|
25201
|
-
};
|
|
25202
|
-
const readValueFromControlHost = (controlHost) => {
|
|
25203
|
-
return readValueFromNaviCustomEvent(controlHost, controlHost.value);
|
|
25204
|
-
};
|
|
25205
|
-
const readValueFromButton = (button) => {
|
|
25206
|
-
return readValueFromControlHost(button);
|
|
25207
|
-
};
|
|
25208
|
-
const readDatetimeLocalFromInput = (input) => {
|
|
25209
|
-
const localDateTimeString = input.value;
|
|
25210
|
-
if (localDateTimeString === "") {
|
|
25211
|
-
return "";
|
|
25212
|
-
}
|
|
25213
|
-
const localDate = new Date(localDateTimeString);
|
|
25214
|
-
if (isNaN(localDate.getTime())) {
|
|
25215
|
-
return localDateTimeString;
|
|
25216
|
-
}
|
|
25217
|
-
return localDate.toISOString();
|
|
25218
|
-
};
|
|
25219
|
-
const readNumberFromInput = (input) => {
|
|
25220
|
-
const numberString = input.value;
|
|
25221
|
-
if (numberString === "") {
|
|
25222
|
-
return "";
|
|
25223
|
-
}
|
|
25224
|
-
const asNumber = Number(numberString);
|
|
25225
|
-
if (isNaN(asNumber)) {
|
|
25226
|
-
return numberString;
|
|
25227
|
-
}
|
|
25228
|
-
return asNumber;
|
|
25229
|
-
};
|
|
25230
|
-
const readValueFromCheckableInput = (input) => {
|
|
25231
|
-
const checked = input.checked;
|
|
25232
|
-
if (!checked) {
|
|
25233
|
-
return undefined;
|
|
25234
|
-
}
|
|
25235
|
-
return readValueFromControlHost(input);
|
|
25236
|
-
};
|
|
25237
|
-
const readValueFromInput = (input) => {
|
|
25238
|
-
const value = input.value;
|
|
25239
|
-
return value;
|
|
25240
|
-
};
|
|
25241
|
-
const readValueFromElement = (element) => {
|
|
25242
|
-
const value = element.value;
|
|
25243
|
-
return value;
|
|
25244
|
-
};
|
|
25245
|
-
const readValueFromNaviCustomEvent = (field, fallback) => {
|
|
25246
|
-
// prefer the value given as prop (respect original type, browser would convert to string)
|
|
25247
|
-
let responded;
|
|
25248
|
-
let value;
|
|
25249
|
-
dispatchCustomEvent(field, "navi_get_value", {
|
|
25250
|
-
respondWith: (jsValue) => {
|
|
25251
|
-
responded = true;
|
|
25252
|
-
value = jsValue;
|
|
25253
|
-
},
|
|
25254
|
-
});
|
|
25255
|
-
if (responded) {
|
|
25256
|
-
return value;
|
|
25257
|
-
}
|
|
25258
|
-
return fallback;
|
|
25259
|
-
};
|
|
25260
|
-
|
|
25214
|
+
"onMouseDown",
|
|
25215
|
+
"onClick",
|
|
25216
|
+
"onKeyDown",
|
|
25217
|
+
"onPaste",
|
|
25218
|
+
"onInput",
|
|
25219
|
+
"interactionDefinitions",
|
|
25220
|
+
|
|
25221
|
+
"onCancel",
|
|
25222
|
+
"cancelOnBlurInvalid",
|
|
25223
|
+
"cancelOnEscape",
|
|
25224
|
+
"onActionPrevented",
|
|
25225
|
+
"onActionStart",
|
|
25226
|
+
"onActionAborted",
|
|
25227
|
+
"onActionError",
|
|
25228
|
+
"actionErrorEffect",
|
|
25229
|
+
"errorMapping",
|
|
25230
|
+
"onActionEnd",
|
|
25231
|
+
|
|
25232
|
+
"resetOnCancel",
|
|
25233
|
+
"resetOnAbort",
|
|
25234
|
+
"resetOnError",
|
|
25235
|
+
]);
|
|
25236
|
+
|
|
25237
|
+
const ControlToInterfaceContext = createContext(null);
|
|
25238
|
+
const MessagePropsRefContext = createContext();
|
|
25239
|
+
|
|
25240
|
+
const ControlNameContext = createContext();
|
|
25241
|
+
const DisabledContext = createContext();
|
|
25242
|
+
const ReadOnlyContext = createContext();
|
|
25243
|
+
const RequiredContext = createContext();
|
|
25244
|
+
const LoadingContext = createContext();
|
|
25245
|
+
createContext();
|
|
25246
|
+
|
|
25247
|
+
const ActionContext = createContext();
|
|
25248
|
+
const ActionRequesterContext = createContext();
|
|
25249
|
+
|
|
25261
25250
|
// In-memory registry of all mounted ui state controllers keyed by their id.
|
|
25262
25251
|
// Allows direct controller access without dispatching DOM events — used by external
|
|
25263
25252
|
// callers (e.g. selectable_list) to call setUIState by id instead of via the DOM.
|
|
@@ -25353,6 +25342,7 @@ const onUIStateControllerDestroyed = (uiStateController) => {
|
|
|
25353
25342
|
* registerChild(child): void; // Called on child mount
|
|
25354
25343
|
* onChildInteraction(child, e, { stateChanged: boolean }): void; // Called on user interaction
|
|
25355
25344
|
* unregisterChild(child): void; // Called on child unmount
|
|
25345
|
+
* props: Object;
|
|
25356
25346
|
* }
|
|
25357
25347
|
* ```
|
|
25358
25348
|
*/
|
|
@@ -25576,7 +25566,7 @@ const useUIStateController = (
|
|
|
25576
25566
|
if (command) {
|
|
25577
25567
|
const element = uiStateController.elementRef.current;
|
|
25578
25568
|
if (element) {
|
|
25579
|
-
|
|
25569
|
+
triggerNaviCommand(element, command, e);
|
|
25580
25570
|
}
|
|
25581
25571
|
}
|
|
25582
25572
|
};
|
|
@@ -25616,8 +25606,8 @@ const useUIStateController = (
|
|
|
25616
25606
|
uiStateController.props.type === "radio" &&
|
|
25617
25607
|
shouldNotifyParent(e)
|
|
25618
25608
|
) {
|
|
25619
|
-
onUIAction();
|
|
25620
25609
|
notifyParentAboutChildInteraction(e, { stateChanged: false });
|
|
25610
|
+
onUIAction();
|
|
25621
25611
|
}
|
|
25622
25612
|
return false;
|
|
25623
25613
|
}
|
|
@@ -25942,6 +25932,10 @@ const useUIGroupStateController = (
|
|
|
25942
25932
|
`${controlType}.applyState(${JSON.stringify(newUIState)}, "${e.type}") -> updates from ${JSON.stringify(currentUIState)} to ${JSON.stringify(newUIState)}`,
|
|
25943
25933
|
);
|
|
25944
25934
|
publishUIState(newUIState);
|
|
25935
|
+
// Notify the parent (facade) BEFORE firing the command so that when a
|
|
25936
|
+
// command like --navi-send closes the picker, the picker input already
|
|
25937
|
+
// holds the new value.
|
|
25938
|
+
notifyParentAboutChildInteraction(e, { stateChanged: true });
|
|
25945
25939
|
if (internalBehavior) {
|
|
25946
25940
|
// Fire uiAction only — skip command to avoid re-triggering the same command
|
|
25947
25941
|
// that caused this setUIState call in the first place.
|
|
@@ -25958,7 +25952,6 @@ const useUIGroupStateController = (
|
|
|
25958
25952
|
value: newUIState,
|
|
25959
25953
|
});
|
|
25960
25954
|
}
|
|
25961
|
-
notifyParentAboutChildInteraction(e, { stateChanged: true });
|
|
25962
25955
|
};
|
|
25963
25956
|
|
|
25964
25957
|
useLayoutEffect(() => {
|
|
@@ -25976,6 +25969,7 @@ const useUIGroupStateController = (
|
|
|
25976
25969
|
existingUIStateController.id = id;
|
|
25977
25970
|
existingUIStateController.name = name;
|
|
25978
25971
|
existingUIStateController.value = value;
|
|
25972
|
+
existingUIStateController.props = props;
|
|
25979
25973
|
uiActionRef.current = uiAction;
|
|
25980
25974
|
return existingUIStateController;
|
|
25981
25975
|
}
|
|
@@ -25999,6 +25993,7 @@ const useUIGroupStateController = (
|
|
|
25999
25993
|
id,
|
|
26000
25994
|
name,
|
|
26001
25995
|
value,
|
|
25996
|
+
props,
|
|
26002
25997
|
uiState: fallbackState,
|
|
26003
25998
|
uiStateSignal,
|
|
26004
25999
|
wantRequesterButtonState,
|
|
@@ -26094,7 +26089,7 @@ const useUIGroupStateController = (
|
|
|
26094
26089
|
if (command) {
|
|
26095
26090
|
const el = ref.current;
|
|
26096
26091
|
if (el) {
|
|
26097
|
-
|
|
26092
|
+
triggerNaviCommand(el, command, e);
|
|
26098
26093
|
}
|
|
26099
26094
|
}
|
|
26100
26095
|
},
|
|
@@ -26235,7 +26230,7 @@ const EMPTY_OBJECT = {};
|
|
|
26235
26230
|
* inside the picker popup. It also means `commands.js` no longer has to
|
|
26236
26231
|
* manually re-dispatch to inner controls.
|
|
26237
26232
|
*/
|
|
26238
|
-
const useUIFacadeStateController = (uiStateController) => {
|
|
26233
|
+
const useUIFacadeStateController = (props, uiStateController) => {
|
|
26239
26234
|
const firstChildControllerRef = useRef(null);
|
|
26240
26235
|
const updatingRef = useRef(false);
|
|
26241
26236
|
|
|
@@ -26259,11 +26254,24 @@ const useUIFacadeStateController = (uiStateController) => {
|
|
|
26259
26254
|
});
|
|
26260
26255
|
}, []);
|
|
26261
26256
|
|
|
26257
|
+
const includeChildController = (childController) => {
|
|
26258
|
+
if (childController.props["navi-list"]) {
|
|
26259
|
+
// Controls with navi-list act as standalone list navigators and should
|
|
26260
|
+
// not be treated as the picker's synced child.
|
|
26261
|
+
return false;
|
|
26262
|
+
}
|
|
26263
|
+
return true;
|
|
26264
|
+
};
|
|
26265
|
+
|
|
26262
26266
|
return useMemo(
|
|
26263
26267
|
() => ({
|
|
26264
26268
|
controlType: "facade",
|
|
26269
|
+
props,
|
|
26265
26270
|
uiStateSignal: uiStateController.uiStateSignal,
|
|
26266
26271
|
registerChild: (child) => {
|
|
26272
|
+
if (!includeChildController(child)) {
|
|
26273
|
+
return;
|
|
26274
|
+
}
|
|
26267
26275
|
if (!firstChildControllerRef.current) {
|
|
26268
26276
|
firstChildControllerRef.current = child;
|
|
26269
26277
|
} else {
|
|
@@ -26480,18 +26488,19 @@ const useControlProps = (props, {
|
|
|
26480
26488
|
uiActionInternal,
|
|
26481
26489
|
readOnlySupported
|
|
26482
26490
|
}) => {
|
|
26491
|
+
const idDefault = useId();
|
|
26483
26492
|
const debugInteraction = useDebugInteraction();
|
|
26484
26493
|
const controlName = useContext(ControlNameContext);
|
|
26494
|
+
const controlToInterface = useContext(ControlToInterfaceContext);
|
|
26485
26495
|
const state = props[statePropName];
|
|
26496
|
+
props.name = props.name || controlName;
|
|
26497
|
+
props.id = props.id || controlToInterface?.id || idDefault;
|
|
26486
26498
|
if (isSignal(state)) {
|
|
26487
26499
|
props = {
|
|
26488
26500
|
...props,
|
|
26489
26501
|
[statePropName]: state.value
|
|
26490
26502
|
};
|
|
26491
26503
|
}
|
|
26492
|
-
if (!props.name && controlName) {
|
|
26493
|
-
props.name = controlName;
|
|
26494
|
-
}
|
|
26495
26504
|
const uiStateController = useUIStateController(props, controlType, {
|
|
26496
26505
|
statePropName,
|
|
26497
26506
|
defaultStatePropName,
|
|
@@ -26517,13 +26526,6 @@ const useControlProps = (props, {
|
|
|
26517
26526
|
actionAfterChange = actionInteraction === "change",
|
|
26518
26527
|
actionDebounce
|
|
26519
26528
|
} = props;
|
|
26520
|
-
let isCheckable = false;
|
|
26521
|
-
// Effect to run when the Enter key is pressed.
|
|
26522
|
-
// For most inputs Enter submits the surrounding form; for checkables Enter
|
|
26523
|
-
// synthesizes a click so the browser's native checkbox/radio activation runs
|
|
26524
|
-
// (which then fires input -> goes through the action pipeline).
|
|
26525
|
-
let enterEffect;
|
|
26526
|
-
let spaceEffect;
|
|
26527
26529
|
const updateUIState = e => {
|
|
26528
26530
|
const value = readControlValue(ref.current);
|
|
26529
26531
|
uiStateController.setUIState(value, e);
|
|
@@ -26559,9 +26561,6 @@ const useControlProps = (props, {
|
|
|
26559
26561
|
interaction.effect?.(e);
|
|
26560
26562
|
return true;
|
|
26561
26563
|
};
|
|
26562
|
-
const asBrowserAction = (interaction, e) => {
|
|
26563
|
-
return asInteraction(interaction, e);
|
|
26564
|
-
};
|
|
26565
26564
|
const lastEventRequestingActionRef = useRef();
|
|
26566
26565
|
const lastActionValueRef = useRef(NO_ACTION_YET);
|
|
26567
26566
|
const wasCheckedAtMousedownRef = useRef(false);
|
|
@@ -26579,12 +26578,7 @@ const useControlProps = (props, {
|
|
|
26579
26578
|
// because at that point asAction hasn't run yet and we must not pre-empt its dedup.
|
|
26580
26579
|
controlHostProps.onnavi_ui_state_change = e => {
|
|
26581
26580
|
const originatingEvent = e.detail.event;
|
|
26582
|
-
if (
|
|
26583
|
-
const sourceIsOwnInput = originatingEvent?.type === "input" && originatingEvent?.target === ref.current;
|
|
26584
|
-
if (!sourceIsOwnInput) {
|
|
26585
|
-
lastActionValueRef.current = e.detail.value;
|
|
26586
|
-
}
|
|
26587
|
-
} else if (originatingEvent?.type === "radio_sibling_uncheck") {
|
|
26581
|
+
if (originatingEvent?.type === "radio_sibling_uncheck") {
|
|
26588
26582
|
lastActionValueRef.current = e.detail.value;
|
|
26589
26583
|
}
|
|
26590
26584
|
};
|
|
@@ -26622,181 +26616,238 @@ const useControlProps = (props, {
|
|
|
26622
26616
|
interaction.effect?.(e);
|
|
26623
26617
|
return true;
|
|
26624
26618
|
};
|
|
26625
|
-
const
|
|
26626
|
-
|
|
26627
|
-
|
|
26628
|
-
|
|
26629
|
-
|
|
26630
|
-
}
|
|
26631
|
-
return interaction.callback(interaction, e, {
|
|
26632
|
-
ifValueModified
|
|
26633
|
-
});
|
|
26634
|
-
};
|
|
26635
|
-
let mousedownInteraction;
|
|
26636
|
-
let clickInteraction;
|
|
26637
|
-
let inputInteraction;
|
|
26638
|
-
let keydownInteraction = {
|
|
26639
|
-
name: "keydown",
|
|
26640
|
-
callback: asBrowserAction
|
|
26641
|
-
};
|
|
26642
|
-
// a custom concept being combination of "input", "change" and may other events
|
|
26643
|
-
// this even if trigerred when value changes and can be controlled by actionDebounce and actionAfterChange
|
|
26644
|
-
let naviChangeInteraction;
|
|
26645
|
-
if (controlType === "button") {
|
|
26646
|
-
mousedownInteraction = {
|
|
26647
|
-
name: "mousedown",
|
|
26648
|
-
callback: actionOnMouseDown ? asAction : asInteraction
|
|
26649
|
-
// effect: updateUIState,
|
|
26650
|
-
};
|
|
26651
|
-
clickInteraction = {
|
|
26652
|
-
name: "click",
|
|
26653
|
-
callback: actionOnMouseDown ? asInteraction : asAction
|
|
26654
|
-
// effect: updateUIState,
|
|
26655
|
-
};
|
|
26656
|
-
} else if (controlType === "input" || controlType === "picker") {
|
|
26657
|
-
isCheckable = props.type === "radio" || props.type === "checkbox";
|
|
26658
|
-
// On input, gate the interaction (readonly check) and update UI state to reflect the new value.
|
|
26659
|
-
inputInteraction = {
|
|
26660
|
-
name: "input",
|
|
26661
|
-
callback: asInteraction,
|
|
26662
|
-
effect: updateUIState
|
|
26663
|
-
};
|
|
26664
|
-
naviChangeInteraction = {
|
|
26665
|
-
name: "navi_change",
|
|
26666
|
-
callback: asAction
|
|
26667
|
-
};
|
|
26668
|
-
enterEffect = e => {
|
|
26669
|
-
const input = e.currentTarget;
|
|
26670
|
-
dispatchNaviCommand(input, "--navi-send", e);
|
|
26671
|
-
};
|
|
26672
|
-
if (controlType === "picker") {
|
|
26673
|
-
mousedownInteraction = {
|
|
26674
|
-
name: "mousedown to open picker",
|
|
26675
|
-
callback: asInteraction
|
|
26676
|
-
};
|
|
26677
|
-
clickInteraction = {
|
|
26678
|
-
name: "click to open picker",
|
|
26679
|
-
callback: asInteraction
|
|
26680
|
-
};
|
|
26681
|
-
}
|
|
26682
|
-
if (isCheckable) {
|
|
26683
|
-
inputInteraction = {
|
|
26684
|
-
name: "input",
|
|
26685
|
-
callback: asAction
|
|
26619
|
+
const getDefaultInteractionDefinitions = () => {
|
|
26620
|
+
const keyDownDefault = () => {
|
|
26621
|
+
return {
|
|
26622
|
+
name: "keydown",
|
|
26623
|
+
type: "interaction"
|
|
26686
26624
|
};
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26699
|
-
|
|
26700
|
-
|
|
26701
|
-
|
|
26702
|
-
|
|
26703
|
-
|
|
26704
|
-
|
|
26705
|
-
|
|
26625
|
+
};
|
|
26626
|
+
if (controlType === "button") {
|
|
26627
|
+
return {
|
|
26628
|
+
keyDown: keyDownDefault,
|
|
26629
|
+
mouseDown: () => {
|
|
26630
|
+
return {
|
|
26631
|
+
name: "mousedown",
|
|
26632
|
+
type: actionOnMouseDown ? "action" : "interaction"
|
|
26633
|
+
};
|
|
26634
|
+
},
|
|
26635
|
+
click: () => {
|
|
26636
|
+
return {
|
|
26637
|
+
name: "click",
|
|
26638
|
+
type: actionOnMouseDown ? "interaction" : "action",
|
|
26639
|
+
always: e => {
|
|
26640
|
+
const button = e.currentTarget;
|
|
26641
|
+
if (button.form) {
|
|
26642
|
+
e.preventDefault(); // prevent form submission
|
|
26643
|
+
}
|
|
26644
|
+
}
|
|
26645
|
+
};
|
|
26706
26646
|
}
|
|
26707
26647
|
};
|
|
26708
|
-
|
|
26709
|
-
|
|
26710
|
-
|
|
26711
|
-
const
|
|
26712
|
-
|
|
26713
|
-
|
|
26714
|
-
e.
|
|
26648
|
+
}
|
|
26649
|
+
if (controlType === "input") {
|
|
26650
|
+
if (props.type === "radio" || props.type === "checkbox") {
|
|
26651
|
+
const isRadio = props.type === "radio";
|
|
26652
|
+
return {
|
|
26653
|
+
keyDown: e => {
|
|
26654
|
+
if (e.key === "Enter") {
|
|
26655
|
+
const inputEl = ref.current;
|
|
26656
|
+
const isRadio = props.type === "radio";
|
|
26657
|
+
const always = () => {
|
|
26658
|
+
if (inputEl.form) {
|
|
26659
|
+
e.preventDefault();
|
|
26660
|
+
}
|
|
26661
|
+
};
|
|
26662
|
+
if (isRadio) {
|
|
26663
|
+
return {
|
|
26664
|
+
name: "enter to check radio",
|
|
26665
|
+
effect: e => {
|
|
26666
|
+
dispatchRequestSetUIState(inputEl, true, {
|
|
26667
|
+
event: e
|
|
26668
|
+
});
|
|
26669
|
+
},
|
|
26670
|
+
always
|
|
26671
|
+
};
|
|
26672
|
+
}
|
|
26673
|
+
const checked = inputEl.checked;
|
|
26674
|
+
if (checked) {
|
|
26675
|
+
return {
|
|
26676
|
+
name: "enter to uncheck checkbox",
|
|
26677
|
+
effect: e => {
|
|
26678
|
+
dispatchRequestSetUIState(inputEl, undefined, {
|
|
26679
|
+
event: e
|
|
26680
|
+
});
|
|
26681
|
+
},
|
|
26682
|
+
always
|
|
26683
|
+
};
|
|
26684
|
+
}
|
|
26685
|
+
return {
|
|
26686
|
+
name: "enter to check",
|
|
26687
|
+
effect: e => {
|
|
26688
|
+
dispatchRequestSetUIState(inputEl, true, {
|
|
26689
|
+
event: e
|
|
26690
|
+
});
|
|
26691
|
+
},
|
|
26692
|
+
always
|
|
26693
|
+
};
|
|
26694
|
+
}
|
|
26695
|
+
if (isRadio && e.key === " ") {
|
|
26696
|
+
const inputEl = e.currentTarget;
|
|
26697
|
+
if (inputEl.checked) {
|
|
26698
|
+
wasCheckedAtMousedownRef.current = true;
|
|
26699
|
+
onClick(e);
|
|
26700
|
+
}
|
|
26701
|
+
}
|
|
26702
|
+
return keyDownDefault();
|
|
26703
|
+
},
|
|
26704
|
+
mouseDown: e => {
|
|
26705
|
+
if (isRadio) {
|
|
26706
|
+
wasCheckedAtMousedownRef.current = e.currentTarget.checked;
|
|
26707
|
+
}
|
|
26708
|
+
},
|
|
26709
|
+
// For checkables, click does NOT update state — it only gates the
|
|
26710
|
+
// browser's native check/uncheck via interaction constraints (e.g.
|
|
26711
|
+
// readOnly). State actually changes via the "input" event that the
|
|
26712
|
+
// browser fires right after, which routes through asAction so the
|
|
26713
|
+
// full action pipeline (constraints, navi_action_allowed, sibling
|
|
26714
|
+
// uncheck for radios…) runs in one place.
|
|
26715
|
+
click: () => {
|
|
26716
|
+
return {
|
|
26717
|
+
name: `click on ${props.type}`,
|
|
26718
|
+
type: "interaction",
|
|
26719
|
+
// When a radio is already checked and gets clicked, the browser does NOT
|
|
26720
|
+
// fire an input event (state doesn't change), so asAction never runs.
|
|
26721
|
+
// We still want uiAction + command to fire. We can tell whether the click
|
|
26722
|
+
// is on an already-checked radio by looking at wasCheckedAtMousedownRef:
|
|
26723
|
+
// if it was checked at mousedown, the input event won't come, so we do it here.
|
|
26724
|
+
effect: isRadio ? e => {
|
|
26725
|
+
if (wasCheckedAtMousedownRef.current) {
|
|
26726
|
+
updateUIState(e);
|
|
26727
|
+
}
|
|
26728
|
+
} : undefined
|
|
26729
|
+
};
|
|
26730
|
+
},
|
|
26731
|
+
input: () => {
|
|
26732
|
+
return {
|
|
26733
|
+
name: "input",
|
|
26734
|
+
type: "action"
|
|
26735
|
+
};
|
|
26715
26736
|
}
|
|
26716
|
-
|
|
26717
|
-
|
|
26718
|
-
|
|
26719
|
-
if (
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
event: e
|
|
26728
|
-
});
|
|
26729
|
-
if (checkable.form) {
|
|
26730
|
-
e.preventDefault();
|
|
26737
|
+
};
|
|
26738
|
+
}
|
|
26739
|
+
const keyDownDefaultOnInput = e => {
|
|
26740
|
+
if (e.key === "Enter") {
|
|
26741
|
+
const input = e.currentTarget;
|
|
26742
|
+
return {
|
|
26743
|
+
name: "enter to --navi-send",
|
|
26744
|
+
effect: () => {
|
|
26745
|
+
triggerNaviCommand(input, "--navi-send", e);
|
|
26746
|
+
}
|
|
26747
|
+
};
|
|
26731
26748
|
}
|
|
26749
|
+
return keyDownDefault();
|
|
26732
26750
|
};
|
|
26733
|
-
if (props.type === "
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26751
|
+
if (props.type === "range") {
|
|
26752
|
+
return {
|
|
26753
|
+
keyDown: keyDownDefaultOnInput,
|
|
26754
|
+
mouseDown: () => {
|
|
26755
|
+
return {
|
|
26756
|
+
name: "mousedown",
|
|
26757
|
+
type: "interaction",
|
|
26758
|
+
effect: updateUIState
|
|
26759
|
+
};
|
|
26760
|
+
},
|
|
26761
|
+
input: {
|
|
26762
|
+
name: "input",
|
|
26763
|
+
type: "interaction",
|
|
26764
|
+
effect: updateUIState
|
|
26765
|
+
},
|
|
26766
|
+
naviChange: () => {
|
|
26767
|
+
return {
|
|
26768
|
+
name: "navi_change",
|
|
26769
|
+
type: "action"
|
|
26770
|
+
};
|
|
26739
26771
|
}
|
|
26740
26772
|
};
|
|
26741
26773
|
}
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
|
|
26745
|
-
|
|
26746
|
-
|
|
26774
|
+
return {
|
|
26775
|
+
keyDown: keyDownDefaultOnInput,
|
|
26776
|
+
input: () => {
|
|
26777
|
+
return {
|
|
26778
|
+
name: "input",
|
|
26779
|
+
type: "interaction",
|
|
26780
|
+
effect: updateUIState
|
|
26781
|
+
};
|
|
26782
|
+
},
|
|
26783
|
+
naviChange: () => {
|
|
26784
|
+
return {
|
|
26785
|
+
name: "navi_change",
|
|
26786
|
+
type: "action"
|
|
26787
|
+
};
|
|
26788
|
+
}
|
|
26747
26789
|
};
|
|
26748
|
-
}
|
|
26749
|
-
|
|
26790
|
+
}
|
|
26791
|
+
return null;
|
|
26792
|
+
};
|
|
26793
|
+
const defaultInteractionDefinitions = getDefaultInteractionDefinitions();
|
|
26794
|
+
const {
|
|
26795
|
+
interactionDefinitions
|
|
26796
|
+
} = props;
|
|
26797
|
+
const applyInteraction = (interactionName, e, {
|
|
26798
|
+
ifValueModified
|
|
26799
|
+
} = {}) => {
|
|
26800
|
+
const defaultInteractionDefinition = defaultInteractionDefinitions?.[interactionName];
|
|
26801
|
+
const customInteractionDefinition = interactionDefinitions?.[interactionName];
|
|
26802
|
+
const interaction = customInteractionDefinition?.(e) ?? defaultInteractionDefinition?.(e);
|
|
26803
|
+
if (!interaction) {
|
|
26804
|
+
return false;
|
|
26805
|
+
}
|
|
26806
|
+
const {
|
|
26807
|
+
name,
|
|
26808
|
+
effect,
|
|
26809
|
+
type = "interaction",
|
|
26810
|
+
always
|
|
26811
|
+
} = interaction;
|
|
26812
|
+
const dispatchFn = type === "action" ? asAction : asInteraction;
|
|
26813
|
+
const applied = dispatchFn({
|
|
26814
|
+
name,
|
|
26815
|
+
effect
|
|
26816
|
+
}, e, {
|
|
26817
|
+
ifValueModified
|
|
26818
|
+
});
|
|
26819
|
+
always?.(e);
|
|
26820
|
+
return applied;
|
|
26821
|
+
};
|
|
26750
26822
|
const onMouseDown = e => {
|
|
26751
26823
|
props.onMouseDown?.(e);
|
|
26752
|
-
|
|
26753
|
-
wasCheckedAtMousedownRef.current = e.currentTarget.checked;
|
|
26754
|
-
}
|
|
26755
|
-
applyInteraction(mousedownInteraction, e);
|
|
26824
|
+
applyInteraction("mouseDown", e);
|
|
26756
26825
|
transferFocusToTarget(e);
|
|
26757
26826
|
};
|
|
26758
26827
|
const onClick = e => {
|
|
26759
26828
|
props.onClick?.(e);
|
|
26760
|
-
applyInteraction(
|
|
26829
|
+
applyInteraction("click", e);
|
|
26761
26830
|
transferFocusToTarget(e);
|
|
26762
|
-
const controlHost = e.currentTarget;
|
|
26763
|
-
if (controlHost.form) {
|
|
26764
|
-
if (controlType === "button") {
|
|
26765
|
-
// prevent form submission
|
|
26766
|
-
e.preventDefault();
|
|
26767
|
-
} else if (controlHost.closest("button")) {
|
|
26768
|
-
// prevent button form submission by click
|
|
26769
|
-
// (When an input is inside a <button> like for a picker)
|
|
26770
|
-
// any click in the picker could trigger form submission as browser see this as click on button inside form
|
|
26771
|
-
e.preventDefault();
|
|
26772
|
-
}
|
|
26773
|
-
}
|
|
26774
26831
|
};
|
|
26775
26832
|
const onKeyDown = e => {
|
|
26776
26833
|
props.onKeyDown?.(e);
|
|
26777
|
-
|
|
26778
|
-
enterEffect(e);
|
|
26779
|
-
return;
|
|
26780
|
-
}
|
|
26781
|
-
if (e.key === " " && spaceEffect) {
|
|
26782
|
-
spaceEffect(e);
|
|
26783
|
-
return;
|
|
26784
|
-
}
|
|
26785
|
-
applyInteraction(keydownInteraction, e);
|
|
26834
|
+
applyInteraction("keyDown", e);
|
|
26786
26835
|
};
|
|
26787
26836
|
const onInput = e => {
|
|
26788
26837
|
props.onInput?.(e);
|
|
26789
|
-
applyInteraction(
|
|
26838
|
+
applyInteraction("input", e, {
|
|
26790
26839
|
ifValueModified: true
|
|
26791
26840
|
});
|
|
26792
26841
|
};
|
|
26793
|
-
|
|
26842
|
+
// a custom concept being combination of "input", "change" and may other events
|
|
26843
|
+
// this even if trigerred when value changes and can be controlled by actionDebounce and actionAfterChange
|
|
26844
|
+
const hasNaviChangeInteractionDefinition = Boolean(interactionDefinitions?.naviChange || defaultInteractionDefinitions?.naviChange);
|
|
26794
26845
|
const refCallback = useCallback(field => {
|
|
26795
|
-
if (!
|
|
26846
|
+
if (!hasNaviChangeInteractionDefinition || actionInteraction === "custom") {
|
|
26796
26847
|
return undefined;
|
|
26797
26848
|
}
|
|
26798
26849
|
return addInputEffect(field, e => {
|
|
26799
|
-
applyInteraction(
|
|
26850
|
+
applyInteraction("naviChange", e, {
|
|
26800
26851
|
ifValueModified: true
|
|
26801
26852
|
});
|
|
26802
26853
|
}, {
|
|
@@ -26804,7 +26855,7 @@ const useControlProps = (props, {
|
|
|
26804
26855
|
debounce: actionDebounce,
|
|
26805
26856
|
debugInteraction
|
|
26806
26857
|
});
|
|
26807
|
-
}, [actionInteraction, actionAfterChange, actionDebounce,
|
|
26858
|
+
}, [actionInteraction, actionAfterChange, actionDebounce, hasNaviChangeInteractionDefinition]);
|
|
26808
26859
|
const refComposed = useComposeElementRef(refCallback, ref);
|
|
26809
26860
|
const onPaste = e => {
|
|
26810
26861
|
props.onPaste?.(e);
|
|
@@ -26937,7 +26988,7 @@ const useControlgroupProps = (props, {
|
|
|
26937
26988
|
*/
|
|
26938
26989
|
const useControlFacadeProps = (props, options) => {
|
|
26939
26990
|
const [controlRootProps, controlHostProps, uiStateController] = useControlProps(props, options);
|
|
26940
|
-
const facadeController = useUIFacadeStateController(uiStateController);
|
|
26991
|
+
const facadeController = useUIFacadeStateController(props, uiStateController);
|
|
26941
26992
|
return [controlRootProps, controlHostProps, {
|
|
26942
26993
|
value: facadeController
|
|
26943
26994
|
}];
|
|
@@ -27018,14 +27069,11 @@ const useInteractiveProps = (props, {
|
|
|
27018
27069
|
autoFocusVisible,
|
|
27019
27070
|
autoSelect
|
|
27020
27071
|
} = props;
|
|
27021
|
-
useAutoFocus(ref, autoFocus, {
|
|
27072
|
+
const autoFocusProps = useAutoFocus(ref, autoFocus, {
|
|
27022
27073
|
focusVisible: autoFocusVisible,
|
|
27023
27074
|
autoSelect
|
|
27024
27075
|
});
|
|
27025
|
-
Object.assign(controlHostProps,
|
|
27026
|
-
"navi-autofocus": autoFocus ? autoFocus === true ? "" : autoFocus : undefined,
|
|
27027
|
-
"navi-autofocus-select": autoFocus && autoSelect ? "" : undefined
|
|
27028
|
-
});
|
|
27076
|
+
Object.assign(controlHostProps, autoFocusProps);
|
|
27029
27077
|
}
|
|
27030
27078
|
{
|
|
27031
27079
|
const {
|
|
@@ -27038,13 +27086,12 @@ const useInteractiveProps = (props, {
|
|
|
27038
27086
|
readOnly,
|
|
27039
27087
|
loading
|
|
27040
27088
|
} = props;
|
|
27041
|
-
const idResolved = id || controlToInterfaceContext?.id;
|
|
27042
27089
|
const disabledResolved = disabled || controlDisabled;
|
|
27043
27090
|
const requiredResolved = required || controlRequired;
|
|
27044
27091
|
const loadingResolved = loading || actionStatus.loading || controlLoading && parentActionRequester === ref.current;
|
|
27045
27092
|
const readOnlyResolved = readOnly || controlReadOnly || loadingResolved || uiStateController.readOnly;
|
|
27046
27093
|
Object.assign(controlHostProps, {
|
|
27047
|
-
|
|
27094
|
+
id,
|
|
27048
27095
|
"navi-control-proxy-for": naviProxyFor,
|
|
27049
27096
|
name,
|
|
27050
27097
|
type,
|
|
@@ -27079,6 +27126,7 @@ const useInteractiveProps = (props, {
|
|
|
27079
27126
|
}
|
|
27080
27127
|
{
|
|
27081
27128
|
const uiState = uiStateController.uiStateSignal.value;
|
|
27129
|
+
const isCheckable = uiStateController.controlType === "input" && (props.type === "radio" || props.type === "checkbox");
|
|
27082
27130
|
Object.assign(controlHostProps, {
|
|
27083
27131
|
onnavi_clear_ui_state: e => {
|
|
27084
27132
|
uiStateController.clearUIState(e);
|
|
@@ -27091,6 +27139,16 @@ const useInteractiveProps = (props, {
|
|
|
27091
27139
|
},
|
|
27092
27140
|
onnavi_set_ui_state: e => {
|
|
27093
27141
|
uiStateController.setUIState(e.detail.value, e);
|
|
27142
|
+
},
|
|
27143
|
+
onnavi_request_check: e => {
|
|
27144
|
+
if (isCheckable) {
|
|
27145
|
+
uiStateController.setUIState(true, e);
|
|
27146
|
+
}
|
|
27147
|
+
},
|
|
27148
|
+
onnavi_request_uncheck: e => {
|
|
27149
|
+
if (isCheckable) {
|
|
27150
|
+
uiStateController.setUIState(false, e);
|
|
27151
|
+
}
|
|
27094
27152
|
}
|
|
27095
27153
|
});
|
|
27096
27154
|
// Mirror ui state handlers on the root so events dispatched on the root element
|
|
@@ -27099,7 +27157,9 @@ const useInteractiveProps = (props, {
|
|
|
27099
27157
|
onnavi_clear_ui_state: controlHostProps.onnavi_clear_ui_state,
|
|
27100
27158
|
onnavi_reset_ui_state: controlHostProps.onnavi_reset_ui_state,
|
|
27101
27159
|
onnavi_get_ui_state: controlHostProps.onnavi_get_ui_state,
|
|
27102
|
-
onnavi_set_ui_state: controlHostProps.onnavi_set_ui_state
|
|
27160
|
+
onnavi_set_ui_state: controlHostProps.onnavi_set_ui_state,
|
|
27161
|
+
onnavi_request_check: controlHostProps.onnavi_request_check,
|
|
27162
|
+
onnavi_request_uncheck: controlHostProps.onnavi_request_uncheck
|
|
27103
27163
|
});
|
|
27104
27164
|
const {
|
|
27105
27165
|
statePropName
|
|
@@ -28553,7 +28613,7 @@ const LinkPlain = props => {
|
|
|
28553
28613
|
selection,
|
|
28554
28614
|
selectionController
|
|
28555
28615
|
});
|
|
28556
|
-
useAutoFocus(ref, autoFocus);
|
|
28616
|
+
const autoFocusProps = useAutoFocus(ref, autoFocus);
|
|
28557
28617
|
useConstraints(ref, constraints);
|
|
28558
28618
|
const shouldDimColor = readOnly || disabled;
|
|
28559
28619
|
useDimColorWhen(ref, shouldDimColor);
|
|
@@ -28605,10 +28665,7 @@ const LinkPlain = props => {
|
|
|
28605
28665
|
color: anchor && !innerChildren ? "inherit" : undefined,
|
|
28606
28666
|
id: anchor ? href.slice(1) : undefined,
|
|
28607
28667
|
...remainingProps,
|
|
28608
|
-
|
|
28609
|
-
,
|
|
28610
|
-
|
|
28611
|
-
"navi-autofocus": autoFocus ? "" : undefined,
|
|
28668
|
+
...autoFocusProps,
|
|
28612
28669
|
ref: ref,
|
|
28613
28670
|
href: href,
|
|
28614
28671
|
rel: innerRel,
|
|
@@ -29920,7 +29977,7 @@ const InputCheckboxFieldInterface = props => {
|
|
|
29920
29977
|
color: "var(--loader-color)"
|
|
29921
29978
|
}), visualVnode, jsx(RealInputCheckbox, {
|
|
29922
29979
|
...checkboxHostProps,
|
|
29923
|
-
switch: switchProp
|
|
29980
|
+
switch: switchProp ? "" : undefined
|
|
29924
29981
|
})]
|
|
29925
29982
|
});
|
|
29926
29983
|
};
|
|
@@ -30056,23 +30113,20 @@ const Field = props => {
|
|
|
30056
30113
|
const refDefault = useRef();
|
|
30057
30114
|
props.ref = props.ref || refDefault;
|
|
30058
30115
|
const {
|
|
30059
|
-
|
|
30060
|
-
vertical
|
|
30116
|
+
ownLabel
|
|
30061
30117
|
} = props;
|
|
30062
|
-
if (
|
|
30063
|
-
|
|
30064
|
-
}
|
|
30065
|
-
if (props.as === "label") {
|
|
30066
|
-
return jsx(FieldAsLabel, {
|
|
30118
|
+
if (ownLabel) {
|
|
30119
|
+
return jsx(FieldAsContainer, {
|
|
30067
30120
|
...props
|
|
30068
30121
|
});
|
|
30069
30122
|
}
|
|
30070
|
-
return jsx(
|
|
30123
|
+
return jsx(FieldAsLabel, {
|
|
30071
30124
|
...props
|
|
30072
30125
|
});
|
|
30073
30126
|
};
|
|
30074
30127
|
const FieldAsLabel = props => {
|
|
30075
30128
|
return jsx(FieldUI, {
|
|
30129
|
+
as: "label",
|
|
30076
30130
|
...props
|
|
30077
30131
|
});
|
|
30078
30132
|
};
|
|
@@ -32486,8 +32540,6 @@ const InputTextualFirstResolver = props => {
|
|
|
32486
32540
|
const Next = useNextResolver();
|
|
32487
32541
|
const defaultRef = useRef(null);
|
|
32488
32542
|
props.ref = props.ref || defaultRef;
|
|
32489
|
-
const idDefault = useId(); // needed by ui state controller and slot labels
|
|
32490
|
-
props.id = props.id || idDefault;
|
|
32491
32543
|
resolveInputProps(props);
|
|
32492
32544
|
return jsx(Next, {
|
|
32493
32545
|
...props
|
|
@@ -32853,11 +32905,10 @@ const FormControl = props => {
|
|
|
32853
32905
|
onSubmit: e => {
|
|
32854
32906
|
const form = e.currentTarget;
|
|
32855
32907
|
e.preventDefault();
|
|
32856
|
-
|
|
32908
|
+
dispatchRequestAction(form, {
|
|
32857
32909
|
event: e,
|
|
32858
|
-
|
|
32859
|
-
|
|
32860
|
-
requester: form,
|
|
32910
|
+
requester: e.submitter || form,
|
|
32911
|
+
actionOrigin: "form_submit",
|
|
32861
32912
|
meta: {
|
|
32862
32913
|
isSubmit: true
|
|
32863
32914
|
}
|
|
@@ -32892,7 +32943,7 @@ const getFormManagedControls = form => {
|
|
|
32892
32943
|
// Exclude inputs that are inside any control group (radio, checkbox, control_group…)
|
|
32893
32944
|
// — the group host is the managed control for the form, not its individual child inputs.
|
|
32894
32945
|
const controlGroupHost = element.closest("[navi-control-group]");
|
|
32895
|
-
if (controlGroupHost && form.contains(controlGroupHost)) {
|
|
32946
|
+
if (controlGroupHost && controlGroupHost !== form && form.contains(controlGroupHost)) {
|
|
32896
32947
|
continue;
|
|
32897
32948
|
}
|
|
32898
32949
|
managedControls.push(element);
|
|
@@ -33314,7 +33365,7 @@ const markAutofocusRestoreOnClose = (containerEl) => {
|
|
|
33314
33365
|
// as a result document.activeElement is not up-to-date (can be document.body for instance)
|
|
33315
33366
|
const getFocusedBeforeTransfer = (e) => {
|
|
33316
33367
|
const initiator = e.detail.eventChain[0];
|
|
33317
|
-
if (initiator.type === "mousedown"
|
|
33368
|
+
if (initiator.type === "mousedown") {
|
|
33318
33369
|
// if we we had let browser give focus, the element would be the one that would be focused
|
|
33319
33370
|
return initiator.currentTarget;
|
|
33320
33371
|
}
|
|
@@ -33325,7 +33376,7 @@ const getFocusedBeforeTransfer = (e) => {
|
|
|
33325
33376
|
return document.activeElement;
|
|
33326
33377
|
};
|
|
33327
33378
|
|
|
33328
|
-
const transferFocus = (containerEl, debugFocus, e) => {
|
|
33379
|
+
const transferFocus = (containerEl, debugFocus, e, fallback) => {
|
|
33329
33380
|
let target;
|
|
33330
33381
|
let reason;
|
|
33331
33382
|
if (containerEl.hasAttribute("navi-autofocus-restore")) {
|
|
@@ -33365,6 +33416,11 @@ const transferFocus = (containerEl, debugFocus, e) => {
|
|
|
33365
33416
|
target = naviAutoFocusFallback;
|
|
33366
33417
|
}
|
|
33367
33418
|
}
|
|
33419
|
+
if (!target) {
|
|
33420
|
+
if (fallback) {
|
|
33421
|
+
target = fallback;
|
|
33422
|
+
}
|
|
33423
|
+
}
|
|
33368
33424
|
if (!target) {
|
|
33369
33425
|
return;
|
|
33370
33426
|
}
|
|
@@ -33432,6 +33488,7 @@ const Dialog = props => {
|
|
|
33432
33488
|
const ref = rest.ref || defaultRef;
|
|
33433
33489
|
const debugPopup = useDebugPopup();
|
|
33434
33490
|
const debugFocus = useDebugFocus();
|
|
33491
|
+
const autoFocusProps = useAutoFocus(ref, props.autoFocus);
|
|
33435
33492
|
const openedRef = useRef(false);
|
|
33436
33493
|
const [addCleanup, cleanup] = useCleanup();
|
|
33437
33494
|
const open = (e, {
|
|
@@ -33448,7 +33505,7 @@ const Dialog = props => {
|
|
|
33448
33505
|
dialogEl.style.setProperty("--anchor-height", `${snapToPixel(height)}px`);
|
|
33449
33506
|
const focusedBeforeOpen = getFocusedBeforeTransfer(e);
|
|
33450
33507
|
dialogEl.showModal();
|
|
33451
|
-
transferFocus(dialogEl, debugFocus, e);
|
|
33508
|
+
transferFocus(dialogEl, debugFocus, e, focusedBeforeOpen);
|
|
33452
33509
|
if (scrollTrap) {
|
|
33453
33510
|
addCleanup(trapScrollInside(dialogEl));
|
|
33454
33511
|
}
|
|
@@ -33513,9 +33570,11 @@ const Dialog = props => {
|
|
|
33513
33570
|
};
|
|
33514
33571
|
return jsx(Box, {
|
|
33515
33572
|
...rest,
|
|
33573
|
+
...autoFocusProps,
|
|
33516
33574
|
as: "dialog",
|
|
33517
33575
|
ref: ref,
|
|
33518
33576
|
baseClassName: "navi_dialog",
|
|
33577
|
+
pseudoClasses: DIALOG_PSEUDO_CLASSES,
|
|
33519
33578
|
onMouseDown: e => {
|
|
33520
33579
|
rest.onMouseDown?.(e);
|
|
33521
33580
|
// The <dialog> element covers the full viewport; clicking the backdrop
|
|
@@ -33544,6 +33603,7 @@ const Dialog = props => {
|
|
|
33544
33603
|
children: children
|
|
33545
33604
|
});
|
|
33546
33605
|
};
|
|
33606
|
+
const DIALOG_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible", ":focus-within"];
|
|
33547
33607
|
|
|
33548
33608
|
installImportMetaCssBuild(import.meta);const css$q = /* css */`
|
|
33549
33609
|
.navi_popover {
|
|
@@ -33588,6 +33648,7 @@ const Popover = props => {
|
|
|
33588
33648
|
const id = rest.id || defaultId;
|
|
33589
33649
|
const debugPopup = useDebugPopup();
|
|
33590
33650
|
const debugFocus = useDebugFocus();
|
|
33651
|
+
const autoFocusProps = useAutoFocus(ref, props.autoFocus);
|
|
33591
33652
|
const [opened, setOpened] = useState(false);
|
|
33592
33653
|
const openedRef = useRef(opened);
|
|
33593
33654
|
openedRef.current = opened;
|
|
@@ -33599,7 +33660,7 @@ const Popover = props => {
|
|
|
33599
33660
|
const popoverEl = ref.current;
|
|
33600
33661
|
const focusedBeforeOpen = getFocusedBeforeTransfer(e);
|
|
33601
33662
|
popoverEl.showPopover();
|
|
33602
|
-
transferFocus(popoverEl, debugFocus, e);
|
|
33663
|
+
transferFocus(popoverEl, debugFocus, e, focusedBeforeOpen);
|
|
33603
33664
|
const effectiveAnchor = anchor || document.documentElement;
|
|
33604
33665
|
const positionPopover = positionEvent => {
|
|
33605
33666
|
const {
|
|
@@ -33718,9 +33779,10 @@ const Popover = props => {
|
|
|
33718
33779
|
id: id,
|
|
33719
33780
|
popover: "manual",
|
|
33720
33781
|
...rest,
|
|
33782
|
+
...autoFocusProps,
|
|
33721
33783
|
ref: ref,
|
|
33722
33784
|
baseClassName: "navi_popover",
|
|
33723
|
-
pseudoClasses:
|
|
33785
|
+
pseudoClasses: POPOVER_PSEUDO_CLASSES,
|
|
33724
33786
|
onnavi_request_open: e => {
|
|
33725
33787
|
const {
|
|
33726
33788
|
anchor
|
|
@@ -33748,7 +33810,7 @@ const Popover = props => {
|
|
|
33748
33810
|
}), children]
|
|
33749
33811
|
});
|
|
33750
33812
|
};
|
|
33751
|
-
const
|
|
33813
|
+
const POPOVER_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible", ":focus-within"];
|
|
33752
33814
|
|
|
33753
33815
|
installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
33754
33816
|
.navi_picker {
|
|
@@ -33770,7 +33832,7 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33770
33832
|
border-radius: var(--picker-border-radius);
|
|
33771
33833
|
outline-width: var(--picker-outline-width);
|
|
33772
33834
|
outline-color: var(--picker-outline-color);
|
|
33773
|
-
outline-offset:
|
|
33835
|
+
outline-offset: 0px;
|
|
33774
33836
|
box-shadow:
|
|
33775
33837
|
0 4px 8px rgba(0, 0, 0, 0.08),
|
|
33776
33838
|
0 12px 40px rgba(0, 0, 0, 0.22);
|
|
@@ -33828,6 +33890,10 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33828
33890
|
overflow: auto;
|
|
33829
33891
|
overscroll-behavior: none;
|
|
33830
33892
|
}
|
|
33893
|
+
|
|
33894
|
+
&[data-focus-visible] {
|
|
33895
|
+
outline-style: solid;
|
|
33896
|
+
}
|
|
33831
33897
|
}
|
|
33832
33898
|
|
|
33833
33899
|
&[aria-expanded="true"] {
|
|
@@ -33856,7 +33922,7 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33856
33922
|
border-radius: var(--picker-border-radius);
|
|
33857
33923
|
outline-width: var(--picker-outline-width);
|
|
33858
33924
|
outline-color: var(--picker-outline-color);
|
|
33859
|
-
outline-offset:
|
|
33925
|
+
outline-offset: 0;
|
|
33860
33926
|
box-shadow:
|
|
33861
33927
|
0 4px 8px rgba(0, 0, 0, 0.08),
|
|
33862
33928
|
0 12px 40px rgba(0, 0, 0, 0.22);
|
|
@@ -33868,6 +33934,10 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33868
33934
|
flex-direction: column;
|
|
33869
33935
|
}
|
|
33870
33936
|
|
|
33937
|
+
&[data-focus-visible] {
|
|
33938
|
+
outline-style: solid;
|
|
33939
|
+
}
|
|
33940
|
+
|
|
33871
33941
|
&::backdrop {
|
|
33872
33942
|
background: rgba(0, 0, 0, 0.4);
|
|
33873
33943
|
}
|
|
@@ -33975,10 +34045,14 @@ const PickerCustom = props => {
|
|
|
33975
34045
|
const valueAtOpenRef = useRef(null);
|
|
33976
34046
|
const activeElementAtOpenRef = useRef(null);
|
|
33977
34047
|
const onOpen = e => {
|
|
33978
|
-
activeElementAtOpenRef.current = e.detail.focusedBeforeOpen;
|
|
33979
34048
|
expandedRef.current = true;
|
|
33980
34049
|
setExpanded(true);
|
|
33981
|
-
|
|
34050
|
+
const focusedBeforeOpen = e.detail.focusedBeforeOpen;
|
|
34051
|
+
activeElementAtOpenRef.current = focusedBeforeOpen;
|
|
34052
|
+
debugFocus(e, "picked opened, store element focused", focusedBeforeOpen);
|
|
34053
|
+
const valueAtOpen = getPickerInputUIState(ref.current);
|
|
34054
|
+
valueAtOpenRef.current = valueAtOpen;
|
|
34055
|
+
debugPopup(e, `picker opened, store value at open (${valueAtOpen})`);
|
|
33982
34056
|
};
|
|
33983
34057
|
const restoreFocus = e => {
|
|
33984
34058
|
const activeElementAtOpen = activeElementAtOpenRef.current;
|
|
@@ -34002,6 +34076,11 @@ const PickerCustom = props => {
|
|
|
34002
34076
|
const onClose = e => {
|
|
34003
34077
|
const cancelEvent = findEvent(e, eInChain => eInChain.type === "navi_request_close" && eInChain.detail.isCancel);
|
|
34004
34078
|
const isCancel = Boolean(cancelEvent);
|
|
34079
|
+
const mousedownEvent = findEvent(e, "mousedown");
|
|
34080
|
+
if (mousedownEvent) {
|
|
34081
|
+
debugPopup(e, `closed by mousedown -> disable next click`);
|
|
34082
|
+
disableClickFor();
|
|
34083
|
+
}
|
|
34005
34084
|
expandedRef.current = false;
|
|
34006
34085
|
setExpanded(false);
|
|
34007
34086
|
// Reset so the next opening re-evaluates screen size
|
|
@@ -34047,17 +34126,12 @@ const PickerCustom = props => {
|
|
|
34047
34126
|
});
|
|
34048
34127
|
};
|
|
34049
34128
|
const requestClose = (e = new CustomEvent("programmatic"), {
|
|
34050
|
-
|
|
34129
|
+
isCancel = false
|
|
34051
34130
|
} = {}) => {
|
|
34052
|
-
const mousedownEvent = findEvent(e, "mousedown");
|
|
34053
|
-
if (mousedownEvent) {
|
|
34054
|
-
debugPopup(e, `disable click`);
|
|
34055
|
-
disableClickFor();
|
|
34056
|
-
}
|
|
34057
34131
|
const popupEl = popupRef.current;
|
|
34058
34132
|
return dispatchCustomEvent(popupEl, "navi_request_close", {
|
|
34059
34133
|
event: e,
|
|
34060
|
-
|
|
34134
|
+
isCancel
|
|
34061
34135
|
});
|
|
34062
34136
|
};
|
|
34063
34137
|
const onInteraction = (e, {
|
|
@@ -34109,91 +34183,97 @@ const PickerCustom = props => {
|
|
|
34109
34183
|
}
|
|
34110
34184
|
});
|
|
34111
34185
|
{
|
|
34112
|
-
const {
|
|
34113
|
-
onMouseDown,
|
|
34114
|
-
onClick,
|
|
34115
|
-
onKeyDown
|
|
34116
|
-
} = props;
|
|
34117
34186
|
const onKeyDownShortcuts = createOnKeyDownForShortcuts({
|
|
34118
|
-
|
|
34119
|
-
|
|
34187
|
+
"a-z": e => {
|
|
34188
|
+
return {
|
|
34189
|
+
name: "letter key to open",
|
|
34190
|
+
effect: () => {
|
|
34191
|
+
requestOpen(e);
|
|
34192
|
+
}
|
|
34193
|
+
};
|
|
34194
|
+
},
|
|
34195
|
+
"0-9": e => {
|
|
34196
|
+
return {
|
|
34197
|
+
name: "numeric key to open",
|
|
34198
|
+
effect: () => {
|
|
34199
|
+
requestOpen(e);
|
|
34200
|
+
}
|
|
34201
|
+
};
|
|
34202
|
+
},
|
|
34203
|
+
"arrowdown": e => {
|
|
34204
|
+
return {
|
|
34120
34205
|
name: "arrow_down_to_open",
|
|
34121
34206
|
effect: () => {
|
|
34122
|
-
e.preventDefault(); // prevent container scroll
|
|
34123
34207
|
requestOpen(e);
|
|
34208
|
+
e.preventDefault(); // prevent container scroll
|
|
34124
34209
|
}
|
|
34125
|
-
}
|
|
34210
|
+
};
|
|
34126
34211
|
},
|
|
34127
|
-
arrowup: e => {
|
|
34128
|
-
|
|
34212
|
+
"arrowup": e => {
|
|
34213
|
+
return {
|
|
34129
34214
|
name: "arrow_up_to_open",
|
|
34130
34215
|
effect: () => {
|
|
34131
|
-
e.preventDefault(); // prevent container scroll
|
|
34132
34216
|
requestOpen(e);
|
|
34217
|
+
e.preventDefault(); // prevent container scroll
|
|
34133
34218
|
}
|
|
34134
|
-
}
|
|
34219
|
+
};
|
|
34135
34220
|
},
|
|
34136
|
-
space: e => {
|
|
34137
|
-
|
|
34221
|
+
"space": e => {
|
|
34222
|
+
return {
|
|
34138
34223
|
name: "space_to_open",
|
|
34139
34224
|
effect: () => {
|
|
34140
|
-
e.preventDefault(); // prevent scroll
|
|
34141
34225
|
requestOpen(e);
|
|
34226
|
+
e.preventDefault(); // prevent scroll
|
|
34142
34227
|
}
|
|
34143
|
-
}
|
|
34228
|
+
};
|
|
34144
34229
|
},
|
|
34145
|
-
escape: e => {
|
|
34230
|
+
"escape": e => {
|
|
34146
34231
|
if (!expandedRef.current) {
|
|
34147
|
-
return;
|
|
34232
|
+
return null;
|
|
34148
34233
|
}
|
|
34149
|
-
|
|
34234
|
+
return {
|
|
34150
34235
|
name: "escape_to_cancel",
|
|
34151
34236
|
effect: () => {
|
|
34152
|
-
e.preventDefault(); // prevent browser from closing the dialog (if any)
|
|
34153
34237
|
requestClose(e, {
|
|
34154
|
-
|
|
34238
|
+
isCancel: true
|
|
34155
34239
|
});
|
|
34240
|
+
e.preventDefault(); // prevent browser from closing the dialog (if any)
|
|
34156
34241
|
}
|
|
34157
|
-
}
|
|
34242
|
+
};
|
|
34158
34243
|
}
|
|
34159
34244
|
});
|
|
34160
34245
|
Object.assign(pickerProps, {
|
|
34161
|
-
|
|
34162
|
-
|
|
34163
|
-
|
|
34164
|
-
|
|
34165
|
-
|
|
34166
|
-
|
|
34167
|
-
|
|
34168
|
-
|
|
34169
|
-
|
|
34246
|
+
interactionDefinitions: {
|
|
34247
|
+
mouseDown: e => {
|
|
34248
|
+
if (expandedRef.current) {
|
|
34249
|
+
return {
|
|
34250
|
+
name: "mousedown to close picker",
|
|
34251
|
+
effect: () => requestClose(e)
|
|
34252
|
+
};
|
|
34253
|
+
}
|
|
34254
|
+
return {
|
|
34255
|
+
name: "mousedown to open picker",
|
|
34256
|
+
effect: () => {
|
|
34170
34257
|
debugFocus(e, `prevent browser giving focus to button (mousedown.preventDefault())`);
|
|
34171
34258
|
requestOpen(e);
|
|
34259
|
+
e.preventDefault(); // prevent browser trying to give focus to the select (popover will take focus)
|
|
34172
34260
|
}
|
|
34173
|
-
}
|
|
34174
|
-
}
|
|
34175
|
-
|
|
34176
|
-
|
|
34177
|
-
|
|
34178
|
-
|
|
34179
|
-
|
|
34180
|
-
|
|
34181
|
-
|
|
34182
|
-
|
|
34183
|
-
|
|
34184
|
-
|
|
34185
|
-
|
|
34186
|
-
|
|
34187
|
-
|
|
34188
|
-
|
|
34189
|
-
e.preventDefault();
|
|
34190
|
-
requestOpen(e);
|
|
34191
|
-
}
|
|
34192
|
-
});
|
|
34193
|
-
},
|
|
34194
|
-
onKeyDown: e => {
|
|
34195
|
-
onKeyDown?.(e);
|
|
34196
|
-
onKeyDownShortcuts(e);
|
|
34261
|
+
};
|
|
34262
|
+
},
|
|
34263
|
+
click: e => {
|
|
34264
|
+
// When a label is clicked it transfers focus to the select
|
|
34265
|
+
// in that case we want to open it (otherwise we have already opened on mousedown interaction)
|
|
34266
|
+
return {
|
|
34267
|
+
name: e.detail === 0 ? "click (keyboard or progammatic) to open picker" : "click to open picker",
|
|
34268
|
+
effect: () => {
|
|
34269
|
+
requestOpen(e);
|
|
34270
|
+
e.preventDefault();
|
|
34271
|
+
}
|
|
34272
|
+
};
|
|
34273
|
+
},
|
|
34274
|
+
keyDown: e => {
|
|
34275
|
+
return onKeyDownShortcuts(e);
|
|
34276
|
+
}
|
|
34197
34277
|
}
|
|
34198
34278
|
});
|
|
34199
34279
|
Object.assign(popupProps, {
|
|
@@ -34202,7 +34282,7 @@ const PickerCustom = props => {
|
|
|
34202
34282
|
return;
|
|
34203
34283
|
}
|
|
34204
34284
|
// mousedown inside popover should not bubble to the select (would re-open it if that mousedown closes it)
|
|
34205
|
-
debugPopup(e, `
|
|
34285
|
+
debugPopup(e, `"mousedown" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
34206
34286
|
e.stopPropagation();
|
|
34207
34287
|
},
|
|
34208
34288
|
onClick: e => {
|
|
@@ -34210,18 +34290,21 @@ const PickerCustom = props => {
|
|
|
34210
34290
|
return;
|
|
34211
34291
|
}
|
|
34212
34292
|
// click inside popover should not bubble to the picker (would re-open it if that click closes it)
|
|
34213
|
-
|
|
34214
|
-
// the picker is inside a <form> and the click lands on a non-button element
|
|
34215
|
-
debugPopup(e, `popover click stopPropagation + preventDefault`);
|
|
34293
|
+
debugPopup(e, `"click" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
34216
34294
|
e.stopPropagation();
|
|
34217
|
-
|
|
34295
|
+
// Here we can't preventDefault because the click might be needed to check a radio for instance.
|
|
34296
|
+
// As a result we have to let it go through which means it could trigger form submission
|
|
34297
|
+
// but we've put type="button" on the picker to ensure it can't submit the form
|
|
34298
|
+
// so browser won't submit eventual form for clicks inside the popover/dialog
|
|
34299
|
+
// e.preventDefault();
|
|
34218
34300
|
},
|
|
34219
34301
|
onKeyDown: e => {
|
|
34220
34302
|
// some keys pressed inside popover should not reach the picker button
|
|
34221
34303
|
// (like enter that would try to request action of closest form otherwise for instance)
|
|
34222
34304
|
if (e.key === "Enter") {
|
|
34305
|
+
debugPopup(e, `"enter" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
34223
34306
|
e.stopPropagation();
|
|
34224
|
-
// preventDefault prevents the browser from
|
|
34307
|
+
// preventDefault prevents the browser from dispatching a "click" on the
|
|
34225
34308
|
// picker button when focus moves to it synchronously during enterEffect
|
|
34226
34309
|
e.preventDefault();
|
|
34227
34310
|
}
|
|
@@ -34292,7 +34375,10 @@ const PickerContentInsidePopover = props => {
|
|
|
34292
34375
|
viewportSpacing: viewportSpacing,
|
|
34293
34376
|
scrollTrap: scrollTrap,
|
|
34294
34377
|
pointerTrap: pointerTrap,
|
|
34295
|
-
focusTrap: focusTrap
|
|
34378
|
+
focusTrap: focusTrap
|
|
34379
|
+
/* make popover focusable so it can be the first focus target when opening */,
|
|
34380
|
+
tabIndex: -1,
|
|
34381
|
+
autoFocus: "fallback",
|
|
34296
34382
|
children: [popoverMode === "attached" ? jsx("div", {
|
|
34297
34383
|
className: "navi_picker_anchor_clone",
|
|
34298
34384
|
onMouseDown: e => {
|
|
@@ -34328,6 +34414,7 @@ const PickerContentInsideDialog = props => {
|
|
|
34328
34414
|
scrollTrap: scrollTrap,
|
|
34329
34415
|
pointerTrap: pointerTrap,
|
|
34330
34416
|
centerInVisualViewport: true,
|
|
34417
|
+
autoFocus: "fallback",
|
|
34331
34418
|
children: children
|
|
34332
34419
|
})
|
|
34333
34420
|
});
|
|
@@ -34474,6 +34561,7 @@ const TimeDate = ({
|
|
|
34474
34561
|
if (children === undefined) {
|
|
34475
34562
|
return jsx(TimeText, {
|
|
34476
34563
|
...props,
|
|
34564
|
+
capitalize: false,
|
|
34477
34565
|
children: formatDatePlaceholder(lang)
|
|
34478
34566
|
});
|
|
34479
34567
|
}
|
|
@@ -34580,6 +34668,7 @@ const TimeDatetime = ({
|
|
|
34580
34668
|
if (children === undefined) {
|
|
34581
34669
|
return jsx(TimeText, {
|
|
34582
34670
|
...props,
|
|
34671
|
+
capitalize: false,
|
|
34583
34672
|
children: formatDatetimePlaceholder(lang)
|
|
34584
34673
|
});
|
|
34585
34674
|
}
|
|
@@ -37765,7 +37854,6 @@ const PickerDateUI = props => {
|
|
|
37765
37854
|
type: "date",
|
|
37766
37855
|
color: "var(--picker-placeholder-color",
|
|
37767
37856
|
capitalize: true,
|
|
37768
|
-
maxLines: "1",
|
|
37769
37857
|
...props
|
|
37770
37858
|
});
|
|
37771
37859
|
}
|
|
@@ -37774,7 +37862,6 @@ const PickerDateUI = props => {
|
|
|
37774
37862
|
return jsx(Time, {
|
|
37775
37863
|
type: "date",
|
|
37776
37864
|
capitalize: true,
|
|
37777
|
-
maxLines: "1",
|
|
37778
37865
|
...props,
|
|
37779
37866
|
children: value
|
|
37780
37867
|
});
|
|
@@ -37798,7 +37885,6 @@ const PickerMonthUI = props => {
|
|
|
37798
37885
|
return jsx(Time, {
|
|
37799
37886
|
type: "month",
|
|
37800
37887
|
color: "var(--picker-placeholder-color",
|
|
37801
|
-
maxLines: "1",
|
|
37802
37888
|
...props
|
|
37803
37889
|
});
|
|
37804
37890
|
}
|
|
@@ -37806,7 +37892,6 @@ const PickerMonthUI = props => {
|
|
|
37806
37892
|
}
|
|
37807
37893
|
return jsx(Time, {
|
|
37808
37894
|
type: "month",
|
|
37809
|
-
maxLines: "1",
|
|
37810
37895
|
capitalize: true,
|
|
37811
37896
|
...props,
|
|
37812
37897
|
children: value
|
|
@@ -37831,7 +37916,6 @@ const PickerWeekUI = props => {
|
|
|
37831
37916
|
return jsx(Time, {
|
|
37832
37917
|
type: "week",
|
|
37833
37918
|
color: "var(--picker-placeholder-color",
|
|
37834
|
-
maxLines: "1",
|
|
37835
37919
|
...props
|
|
37836
37920
|
});
|
|
37837
37921
|
}
|
|
@@ -37840,7 +37924,6 @@ const PickerWeekUI = props => {
|
|
|
37840
37924
|
return jsx(Time, {
|
|
37841
37925
|
type: "week",
|
|
37842
37926
|
capitalize: true,
|
|
37843
|
-
maxLines: "1",
|
|
37844
37927
|
...props,
|
|
37845
37928
|
children: value
|
|
37846
37929
|
});
|
|
@@ -37864,7 +37947,6 @@ const PickerTimeUI = props => {
|
|
|
37864
37947
|
return jsx(Time, {
|
|
37865
37948
|
type: "time",
|
|
37866
37949
|
color: "var(--picker-placeholder-color",
|
|
37867
|
-
maxLines: "1",
|
|
37868
37950
|
...props
|
|
37869
37951
|
});
|
|
37870
37952
|
}
|
|
@@ -37872,7 +37954,6 @@ const PickerTimeUI = props => {
|
|
|
37872
37954
|
}
|
|
37873
37955
|
return jsx(Time, {
|
|
37874
37956
|
type: "time",
|
|
37875
|
-
maxLines: "1",
|
|
37876
37957
|
...props,
|
|
37877
37958
|
children: value
|
|
37878
37959
|
});
|
|
@@ -37896,7 +37977,6 @@ const PickerDatetimeUI = props => {
|
|
|
37896
37977
|
return jsx(Time, {
|
|
37897
37978
|
type: "datetime",
|
|
37898
37979
|
color: "var(--picker-placeholder-color",
|
|
37899
|
-
maxLines: "1",
|
|
37900
37980
|
...props
|
|
37901
37981
|
});
|
|
37902
37982
|
}
|
|
@@ -37904,7 +37984,6 @@ const PickerDatetimeUI = props => {
|
|
|
37904
37984
|
}
|
|
37905
37985
|
return jsx(Time, {
|
|
37906
37986
|
type: "datetime",
|
|
37907
|
-
maxLines: "1",
|
|
37908
37987
|
children: value
|
|
37909
37988
|
});
|
|
37910
37989
|
};
|
|
@@ -38095,7 +38174,6 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38095
38174
|
font-size: var(--picker-font-size);
|
|
38096
38175
|
font-family: var(--picker-font-family);
|
|
38097
38176
|
text-align: inherit;
|
|
38098
|
-
text-overflow: ellipsis;
|
|
38099
38177
|
background-color: var(--x-picker-background-color);
|
|
38100
38178
|
border-width: var(--picker-border-width);
|
|
38101
38179
|
border-style: solid;
|
|
@@ -38108,7 +38186,6 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38108
38186
|
cursor: var(--x-picker-cursor, pointer);
|
|
38109
38187
|
pointer-events: auto;
|
|
38110
38188
|
user-select: none;
|
|
38111
|
-
overflow: hidden;
|
|
38112
38189
|
-webkit-tap-highlight-color: var(--navi-control-tap-highlight-color);
|
|
38113
38190
|
|
|
38114
38191
|
.navi_picker_value {
|
|
@@ -38122,9 +38199,7 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38122
38199
|
padding-bottom: var(--x-picker-padding-bottom);
|
|
38123
38200
|
padding-left: var(--x-picker-padding-left);
|
|
38124
38201
|
flex-grow: 1;
|
|
38125
|
-
|
|
38126
|
-
white-space: nowrap;
|
|
38127
|
-
overflow: hidden;
|
|
38202
|
+
justify-content: inherit;
|
|
38128
38203
|
|
|
38129
38204
|
&[navi-placeholder] {
|
|
38130
38205
|
color: var(--picker-placeholder-color);
|
|
@@ -38149,6 +38224,11 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38149
38224
|
max-height: 100%;
|
|
38150
38225
|
}
|
|
38151
38226
|
}
|
|
38227
|
+
&[navi-single-line] {
|
|
38228
|
+
.navi_picker_right_slot {
|
|
38229
|
+
align-self: center;
|
|
38230
|
+
}
|
|
38231
|
+
}
|
|
38152
38232
|
.navi_picker_input {
|
|
38153
38233
|
position: absolute;
|
|
38154
38234
|
inset: 0;
|
|
@@ -38169,16 +38249,6 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38169
38249
|
text-align: initial; /* Don't inherit picker text align */
|
|
38170
38250
|
}
|
|
38171
38251
|
|
|
38172
|
-
&[data-line-clamp] {
|
|
38173
|
-
overflow-wrap: anywhere;
|
|
38174
|
-
.navi_picker_value {
|
|
38175
|
-
display: -webkit-box;
|
|
38176
|
-
white-space: normal;
|
|
38177
|
-
-webkit-box-orient: vertical;
|
|
38178
|
-
-webkit-line-clamp: var(--picker-max-lines);
|
|
38179
|
-
}
|
|
38180
|
-
}
|
|
38181
|
-
|
|
38182
38252
|
/* Hover */
|
|
38183
38253
|
&[data-hover] {
|
|
38184
38254
|
--x-picker-background-color: var(--picker-background-color-hover);
|
|
@@ -38265,9 +38335,10 @@ const PickerButton = props => {
|
|
|
38265
38335
|
icon,
|
|
38266
38336
|
placeholder,
|
|
38267
38337
|
ui,
|
|
38268
|
-
maxLines,
|
|
38338
|
+
maxLines = 1,
|
|
38269
38339
|
headless
|
|
38270
38340
|
} = props;
|
|
38341
|
+
const isSingleLine = maxLines === 1;
|
|
38271
38342
|
const inputRef = useRef(null);
|
|
38272
38343
|
const [pickerRemainingProps, inputProps, facadeChildrenProps] = useControlFacadeProps({
|
|
38273
38344
|
...props,
|
|
@@ -38287,20 +38358,17 @@ const PickerButton = props => {
|
|
|
38287
38358
|
children
|
|
38288
38359
|
} = inputProps;
|
|
38289
38360
|
const loading = basePseudoState[":-navi-loading"];
|
|
38290
|
-
const hasLineClamp = maxLines && maxLines > 1;
|
|
38291
38361
|
return jsxs(Box, {
|
|
38292
38362
|
as: headless ? "div" : "button",
|
|
38363
|
+
type: "button" /* ensure click inside the picker cannot submit ancestor form if any */,
|
|
38293
38364
|
ref: ref,
|
|
38294
38365
|
baseClassName: "navi_picker",
|
|
38295
38366
|
pseudoClasses: PICKER_BUTTON_PSEUDO_CLASSES,
|
|
38296
38367
|
disabled: disabled,
|
|
38297
|
-
"data-line-clamp": hasLineClamp ? "" : undefined,
|
|
38298
38368
|
"data-variant": variant,
|
|
38299
|
-
style: {
|
|
38300
|
-
"--picker-max-lines": maxLines
|
|
38301
|
-
},
|
|
38302
38369
|
"navi-visually-hidden": headless ? "" : undefined,
|
|
38303
38370
|
"navi-picker": "",
|
|
38371
|
+
"navi-single-line": isSingleLine ? "" : undefined,
|
|
38304
38372
|
...pickerRemainingProps,
|
|
38305
38373
|
basePseudoState: basePseudoState,
|
|
38306
38374
|
styleCSSVars: PickerStyleCSSVars
|
|
@@ -38338,7 +38406,7 @@ const PickerButton = props => {
|
|
|
38338
38406
|
children: [jsx(LoadingOutline, {
|
|
38339
38407
|
loading: loading,
|
|
38340
38408
|
color: "var(--picker-loader-color)",
|
|
38341
|
-
inset: -
|
|
38409
|
+
inset: -2
|
|
38342
38410
|
}), jsx(PickerInput, {
|
|
38343
38411
|
...inputProps,
|
|
38344
38412
|
// eslint-disable-next-line react/no-children-prop
|
|
@@ -38352,6 +38420,7 @@ const PickerButton = props => {
|
|
|
38352
38420
|
}), variant === "icon" || headless ? null : jsx(Text, {
|
|
38353
38421
|
className: "navi_picker_value",
|
|
38354
38422
|
"navi-placeholder": value === undefined || value === "" ? "" : undefined,
|
|
38423
|
+
maxLines: maxLines,
|
|
38355
38424
|
children: jsx(PickerContext.Provider, {
|
|
38356
38425
|
value: {
|
|
38357
38426
|
value,
|
|
@@ -38435,8 +38504,6 @@ const PickerFirstResolver = props => {
|
|
|
38435
38504
|
const Next = useNextResolver();
|
|
38436
38505
|
const defaultRef = useRef(null);
|
|
38437
38506
|
props.ref = props.ref || defaultRef;
|
|
38438
|
-
const idDefault = useId(); // needed by ui state controller
|
|
38439
|
-
props.id = props.id || idDefault;
|
|
38440
38507
|
resolveInputProps(props);
|
|
38441
38508
|
return jsx(Next, {
|
|
38442
38509
|
...props
|