@jsenv/navi 0.27.35 → 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 -611
- package/dist/jsenv_navi.js.map +68 -66
- 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,9 +23719,7 @@ const TextOverflow = ({
|
|
|
23740
23719
|
children,
|
|
23741
23720
|
...rest
|
|
23742
23721
|
}) => {
|
|
23743
|
-
const [overflowPinned, setOverflowPinned] = useState(null);
|
|
23744
23722
|
return jsx(TextDispatcher, {
|
|
23745
|
-
flex: true,
|
|
23746
23723
|
block: true,
|
|
23747
23724
|
as: "div",
|
|
23748
23725
|
pre: noWrap === undefined ? true : undefined
|
|
@@ -23754,44 +23731,10 @@ const TextOverflow = ({
|
|
|
23754
23731
|
...rest,
|
|
23755
23732
|
maxLines: undefined,
|
|
23756
23733
|
"data-text-overflow": "",
|
|
23757
|
-
spacing:
|
|
23758
|
-
|
|
23759
|
-
|
|
23760
|
-
children: [overflowPinned && overflowPinned.position === "start" ? overflowPinned.vnode : null, jsx(OverflowPinnedContext.Provider, {
|
|
23761
|
-
value: setOverflowPinned,
|
|
23762
|
-
children: jsx(Text, {
|
|
23763
|
-
className: "navi_text_overflow_text",
|
|
23764
|
-
spacing: spacing,
|
|
23765
|
-
capitalize: capitalize,
|
|
23766
|
-
children: children
|
|
23767
|
-
})
|
|
23768
|
-
}), overflowPinned && overflowPinned.position === "end" ? overflowPinned.vnode : null]
|
|
23769
|
-
})
|
|
23770
|
-
});
|
|
23771
|
-
};
|
|
23772
|
-
const TextOverflowPinned = props => {
|
|
23773
|
-
const {
|
|
23774
|
-
overflowPinned
|
|
23775
|
-
} = props;
|
|
23776
|
-
const setOverflowPinned = useContext(OverflowPinnedContext);
|
|
23777
|
-
const text = jsx(Text, {
|
|
23778
|
-
...props,
|
|
23779
|
-
"data-overflow-pinned": "",
|
|
23780
|
-
overflowPinned: undefined
|
|
23734
|
+
spacing: spacing,
|
|
23735
|
+
capitalize: capitalize,
|
|
23736
|
+
children: children
|
|
23781
23737
|
});
|
|
23782
|
-
if (!setOverflowPinned) {
|
|
23783
|
-
console.warn(`<Text overflowPinned> declared outside a <Text maxLines="1">`);
|
|
23784
|
-
return text;
|
|
23785
|
-
}
|
|
23786
|
-
if (overflowPinned) {
|
|
23787
|
-
setOverflowPinned({
|
|
23788
|
-
vnode: text,
|
|
23789
|
-
position: overflowPinned === true ? "end" : overflowPinned
|
|
23790
|
-
});
|
|
23791
|
-
return null;
|
|
23792
|
-
}
|
|
23793
|
-
setOverflowPinned(null);
|
|
23794
|
-
return text;
|
|
23795
23738
|
};
|
|
23796
23739
|
const TextWithSelectRange = ({
|
|
23797
23740
|
ref,
|
|
@@ -24552,7 +24495,15 @@ const useAutoFocus = (
|
|
|
24552
24495
|
// }
|
|
24553
24496
|
// }, []);
|
|
24554
24497
|
|
|
24555
|
-
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
|
+
};
|
|
24556
24507
|
};
|
|
24557
24508
|
|
|
24558
24509
|
let blurEvent = null;
|
|
@@ -24576,7 +24527,189 @@ document.body.addEventListener(
|
|
|
24576
24527
|
{ capture: true },
|
|
24577
24528
|
);
|
|
24578
24529
|
|
|
24579
|
-
|
|
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 = (
|
|
24580
24713
|
element,
|
|
24581
24714
|
command,
|
|
24582
24715
|
event,
|
|
@@ -24615,6 +24748,7 @@ const dispatchNaviCommand = (
|
|
|
24615
24748
|
value,
|
|
24616
24749
|
});
|
|
24617
24750
|
};
|
|
24751
|
+
|
|
24618
24752
|
// Returns the target explicitly declared via HTML attributes (commandfor / navi-command-target),
|
|
24619
24753
|
// or undefined when no such attribute is present.
|
|
24620
24754
|
// Returns null when the attribute is present but the target element was not found (already warned).
|
|
@@ -24680,6 +24814,13 @@ const resolveCommandValue = (source, event) => {
|
|
|
24680
24814
|
if (source.hasAttribute("command-value")) {
|
|
24681
24815
|
return source.getAttribute("command-value");
|
|
24682
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
|
+
}
|
|
24683
24824
|
return getUIStateFromElement(source);
|
|
24684
24825
|
};
|
|
24685
24826
|
|
|
@@ -24772,7 +24913,7 @@ registerNaviCommand("--navi-clear", (source, event) => {
|
|
|
24772
24913
|
target,
|
|
24773
24914
|
implementation: () => {
|
|
24774
24915
|
if (fromInput) ; else {
|
|
24775
|
-
|
|
24916
|
+
triggerNaviCommand(source, "--navi-close", event, {
|
|
24776
24917
|
optional: true,
|
|
24777
24918
|
});
|
|
24778
24919
|
}
|
|
@@ -24927,11 +25068,11 @@ const executeNaviDefine = (source, event, target) => {
|
|
|
24927
25068
|
// send button's undefined value would corrupt the picker state.
|
|
24928
25069
|
const skipUpdate = resolvePickerInnerControl(target) !== null;
|
|
24929
25070
|
if (!skipUpdate) {
|
|
24930
|
-
|
|
25071
|
+
triggerNaviCommand(source, "--navi-update", event);
|
|
24931
25072
|
}
|
|
24932
25073
|
// The picker's onClose already dispatches the action with the final value.
|
|
24933
25074
|
// Dispatching again here would fire the action twice.
|
|
24934
|
-
return
|
|
25075
|
+
return triggerNaviCommand(target, "--navi-close", event);
|
|
24935
25076
|
};
|
|
24936
25077
|
|
|
24937
25078
|
registerNaviCommand("--navi-scroll", (source, event) => {
|
|
@@ -24950,6 +25091,36 @@ registerNaviCommand("--navi-scroll", (source, event) => {
|
|
|
24950
25091
|
},
|
|
24951
25092
|
};
|
|
24952
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
|
+
});
|
|
24953
25124
|
registerNaviCommand("--navi-select", (source, event) => {
|
|
24954
25125
|
const target =
|
|
24955
25126
|
resolveExplicitTarget(source) || resolveFirstParentControl(source);
|
|
@@ -25040,223 +25211,42 @@ const CONTROL_PROP_SET = new Set([
|
|
|
25040
25211
|
"autoFocusVisible",
|
|
25041
25212
|
"autoSelect",
|
|
25042
25213
|
|
|
25043
|
-
"onMouseDown",
|
|
25044
|
-
"onClick",
|
|
25045
|
-
"onKeyDown",
|
|
25046
|
-
"onPaste",
|
|
25047
|
-
"onInput",
|
|
25048
|
-
|
|
25049
|
-
|
|
25050
|
-
"
|
|
25051
|
-
"
|
|
25052
|
-
"
|
|
25053
|
-
"
|
|
25054
|
-
"
|
|
25055
|
-
"
|
|
25056
|
-
"
|
|
25057
|
-
"
|
|
25058
|
-
"
|
|
25059
|
-
|
|
25060
|
-
|
|
25061
|
-
"
|
|
25062
|
-
"
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25066
|
-
const
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
const
|
|
25070
|
-
const
|
|
25071
|
-
const
|
|
25072
|
-
const
|
|
25073
|
-
createContext();
|
|
25074
|
-
|
|
25075
|
-
|
|
25076
|
-
const
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
* Converts a JS value into the form expected by the browser DOM property for a
|
|
25080
|
-
* given control type/input type combination.
|
|
25081
|
-
*
|
|
25082
|
-
* For example:
|
|
25083
|
-
* - `datetime-local` inputs expect a local datetime string without timezone
|
|
25084
|
-
* - `number`/`range` inputs expect a numeric string or number
|
|
25085
|
-
* - `color` inputs require a non-empty hex string (falls back to `#000000`)
|
|
25086
|
-
* - All other inputs receive the value as-is (undefined → "")
|
|
25087
|
-
*
|
|
25088
|
-
* Returns either the converted value directly, or a converter function when the
|
|
25089
|
-
* conversion depends on the runtime value (e.g. plain inputs return `asInputValue`).
|
|
25090
|
-
*
|
|
25091
|
-
* @param {any} value - The JS value to convert.
|
|
25092
|
-
* @param {{ controlType: string, type: string }} options
|
|
25093
|
-
* @returns {any} The DOM-compatible value or a converter function.
|
|
25094
|
-
*/
|
|
25095
|
-
const asControlHostValue = (
|
|
25096
|
-
jsValue,
|
|
25097
|
-
{ controlType, type, inputMode },
|
|
25098
|
-
) => {
|
|
25099
|
-
if (controlType === "input" || controlType === "picker") {
|
|
25100
|
-
if (type === "datetime-local") {
|
|
25101
|
-
return asDatetimeLocalString(jsValue);
|
|
25102
|
-
}
|
|
25103
|
-
if (type === "number" || type === "range" || inputMode === "numeric") {
|
|
25104
|
-
return asNumberString(jsValue);
|
|
25105
|
-
}
|
|
25106
|
-
if (type === "color") {
|
|
25107
|
-
return asColorString(jsValue);
|
|
25108
|
-
}
|
|
25109
|
-
return asInputValue(jsValue);
|
|
25110
|
-
}
|
|
25111
|
-
return jsValue;
|
|
25112
|
-
};
|
|
25113
|
-
// As explained in https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/datetime-local#setting_timezones
|
|
25114
|
-
// datetime-local does not support timezones
|
|
25115
|
-
const asDatetimeLocalString = (dateTimeString) => {
|
|
25116
|
-
const date = new Date(dateTimeString);
|
|
25117
|
-
if (isNaN(date.getTime())) {
|
|
25118
|
-
return dateTimeString;
|
|
25119
|
-
}
|
|
25120
|
-
const year = date.getFullYear();
|
|
25121
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
25122
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
25123
|
-
const hours = String(date.getHours()).padStart(2, "0");
|
|
25124
|
-
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
25125
|
-
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
25126
|
-
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
|
|
25127
|
-
};
|
|
25128
|
-
const asNumberString = (jsValue) => {
|
|
25129
|
-
if (jsValue === undefined) {
|
|
25130
|
-
return "";
|
|
25131
|
-
}
|
|
25132
|
-
return jsValue;
|
|
25133
|
-
};
|
|
25134
|
-
// Browser requires a non-empty value for <input type="color">.
|
|
25135
|
-
// When our logical value is empty we give it #000000 so it doesn't choke.
|
|
25136
|
-
// The UI uses the original (possibly empty) value to show the checkerboard.
|
|
25137
|
-
const asColorString = (jsValue) => {
|
|
25138
|
-
return jsValue || "#000000";
|
|
25139
|
-
};
|
|
25140
|
-
const asInputValue = (jsValue) => {
|
|
25141
|
-
if (jsValue === undefined) {
|
|
25142
|
-
return "";
|
|
25143
|
-
}
|
|
25144
|
-
return jsValue;
|
|
25145
|
-
};
|
|
25146
|
-
|
|
25147
|
-
/**
|
|
25148
|
-
* Reads the current logical JS value from a control host DOM element.
|
|
25149
|
-
*
|
|
25150
|
-
* Handles all navi control host element types:
|
|
25151
|
-
* - `<button>` — reads via `navi_get_value` custom event, falls back to `button.value`
|
|
25152
|
-
* - `<input type="number|range">` — parses as a number, returns `undefined` when empty
|
|
25153
|
-
* - `<input type="checkbox|radio">` — returns `undefined` when unchecked, otherwise reads
|
|
25154
|
-
* via `navi_get_value` custom event (to preserve the original JS type of the value prop)
|
|
25155
|
-
* - `<input type="datetime-local">` — converts the local datetime string to an ISO 8601 string
|
|
25156
|
-
* - `<input type="navi_picker">` — delegates to the controller via `navi_get_ui_state`
|
|
25157
|
-
* - All other inputs — returns `input.value` as a string
|
|
25158
|
-
*
|
|
25159
|
-
* @param {HTMLElement} controlHost - The control host DOM element to read from.
|
|
25160
|
-
* @returns {any} The current logical value of the control.
|
|
25161
|
-
*/
|
|
25162
|
-
const readControlValue = (controlHost) => {
|
|
25163
|
-
if (
|
|
25164
|
-
controlHost.tagName === "BUTTON" ||
|
|
25165
|
-
controlHost.getAttribute("role") === "button"
|
|
25166
|
-
) {
|
|
25167
|
-
return readValueFromButton(controlHost);
|
|
25168
|
-
}
|
|
25169
|
-
if (controlHost.tagName === "INPUT") {
|
|
25170
|
-
// important: input.type = "navi_js"; followed by input.type; returns "text"
|
|
25171
|
-
// so use getAttribute
|
|
25172
|
-
const type = controlHost.getAttribute("type");
|
|
25173
|
-
|
|
25174
|
-
if (
|
|
25175
|
-
type === "number" ||
|
|
25176
|
-
type === "range" ||
|
|
25177
|
-
controlHost.inputMode === "numeric"
|
|
25178
|
-
) {
|
|
25179
|
-
return readNumberFromInput(controlHost);
|
|
25180
|
-
}
|
|
25181
|
-
if (type === "color") {
|
|
25182
|
-
return readValueFromControlHost(controlHost);
|
|
25183
|
-
}
|
|
25184
|
-
if (type === "checkbox" || type === "radio") {
|
|
25185
|
-
return readValueFromCheckableInput(controlHost);
|
|
25186
|
-
}
|
|
25187
|
-
if (type === "datetime-local") {
|
|
25188
|
-
return readDatetimeLocalFromInput(controlHost);
|
|
25189
|
-
}
|
|
25190
|
-
if (type === "navi_js") {
|
|
25191
|
-
return getUIStateFromElement(controlHost);
|
|
25192
|
-
}
|
|
25193
|
-
return readValueFromInput(controlHost);
|
|
25194
|
-
}
|
|
25195
|
-
if (controlHost.hasAttribute("navi-control-host")) {
|
|
25196
|
-
// Non-button, non-input navi controls (e.g. Badge.Button rendered as span)
|
|
25197
|
-
return readValueFromControlHost(controlHost);
|
|
25198
|
-
}
|
|
25199
|
-
return readValueFromElement(controlHost);
|
|
25200
|
-
};
|
|
25201
|
-
const readValueFromControlHost = (controlHost) => {
|
|
25202
|
-
return readValueFromNaviCustomEvent(controlHost, controlHost.value);
|
|
25203
|
-
};
|
|
25204
|
-
const readValueFromButton = (button) => {
|
|
25205
|
-
return readValueFromControlHost(button);
|
|
25206
|
-
};
|
|
25207
|
-
const readDatetimeLocalFromInput = (input) => {
|
|
25208
|
-
const localDateTimeString = input.value;
|
|
25209
|
-
if (localDateTimeString === "") {
|
|
25210
|
-
return "";
|
|
25211
|
-
}
|
|
25212
|
-
const localDate = new Date(localDateTimeString);
|
|
25213
|
-
if (isNaN(localDate.getTime())) {
|
|
25214
|
-
return localDateTimeString;
|
|
25215
|
-
}
|
|
25216
|
-
return localDate.toISOString();
|
|
25217
|
-
};
|
|
25218
|
-
const readNumberFromInput = (input) => {
|
|
25219
|
-
const numberString = input.value;
|
|
25220
|
-
if (numberString === "") {
|
|
25221
|
-
return "";
|
|
25222
|
-
}
|
|
25223
|
-
const asNumber = Number(numberString);
|
|
25224
|
-
if (isNaN(asNumber)) {
|
|
25225
|
-
return numberString;
|
|
25226
|
-
}
|
|
25227
|
-
return asNumber;
|
|
25228
|
-
};
|
|
25229
|
-
const readValueFromCheckableInput = (input) => {
|
|
25230
|
-
const checked = input.checked;
|
|
25231
|
-
if (!checked) {
|
|
25232
|
-
return undefined;
|
|
25233
|
-
}
|
|
25234
|
-
return readValueFromControlHost(input);
|
|
25235
|
-
};
|
|
25236
|
-
const readValueFromInput = (input) => {
|
|
25237
|
-
const value = input.value;
|
|
25238
|
-
return value;
|
|
25239
|
-
};
|
|
25240
|
-
const readValueFromElement = (element) => {
|
|
25241
|
-
const value = element.value;
|
|
25242
|
-
return value;
|
|
25243
|
-
};
|
|
25244
|
-
const readValueFromNaviCustomEvent = (field, fallback) => {
|
|
25245
|
-
// prefer the value given as prop (respect original type, browser would convert to string)
|
|
25246
|
-
let responded;
|
|
25247
|
-
let value;
|
|
25248
|
-
dispatchCustomEvent(field, "navi_get_value", {
|
|
25249
|
-
respondWith: (jsValue) => {
|
|
25250
|
-
responded = true;
|
|
25251
|
-
value = jsValue;
|
|
25252
|
-
},
|
|
25253
|
-
});
|
|
25254
|
-
if (responded) {
|
|
25255
|
-
return value;
|
|
25256
|
-
}
|
|
25257
|
-
return fallback;
|
|
25258
|
-
};
|
|
25259
|
-
|
|
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
|
+
|
|
25260
25250
|
// In-memory registry of all mounted ui state controllers keyed by their id.
|
|
25261
25251
|
// Allows direct controller access without dispatching DOM events — used by external
|
|
25262
25252
|
// callers (e.g. selectable_list) to call setUIState by id instead of via the DOM.
|
|
@@ -25352,6 +25342,7 @@ const onUIStateControllerDestroyed = (uiStateController) => {
|
|
|
25352
25342
|
* registerChild(child): void; // Called on child mount
|
|
25353
25343
|
* onChildInteraction(child, e, { stateChanged: boolean }): void; // Called on user interaction
|
|
25354
25344
|
* unregisterChild(child): void; // Called on child unmount
|
|
25345
|
+
* props: Object;
|
|
25355
25346
|
* }
|
|
25356
25347
|
* ```
|
|
25357
25348
|
*/
|
|
@@ -25575,7 +25566,7 @@ const useUIStateController = (
|
|
|
25575
25566
|
if (command) {
|
|
25576
25567
|
const element = uiStateController.elementRef.current;
|
|
25577
25568
|
if (element) {
|
|
25578
|
-
|
|
25569
|
+
triggerNaviCommand(element, command, e);
|
|
25579
25570
|
}
|
|
25580
25571
|
}
|
|
25581
25572
|
};
|
|
@@ -25615,8 +25606,8 @@ const useUIStateController = (
|
|
|
25615
25606
|
uiStateController.props.type === "radio" &&
|
|
25616
25607
|
shouldNotifyParent(e)
|
|
25617
25608
|
) {
|
|
25618
|
-
onUIAction();
|
|
25619
25609
|
notifyParentAboutChildInteraction(e, { stateChanged: false });
|
|
25610
|
+
onUIAction();
|
|
25620
25611
|
}
|
|
25621
25612
|
return false;
|
|
25622
25613
|
}
|
|
@@ -25941,6 +25932,10 @@ const useUIGroupStateController = (
|
|
|
25941
25932
|
`${controlType}.applyState(${JSON.stringify(newUIState)}, "${e.type}") -> updates from ${JSON.stringify(currentUIState)} to ${JSON.stringify(newUIState)}`,
|
|
25942
25933
|
);
|
|
25943
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 });
|
|
25944
25939
|
if (internalBehavior) {
|
|
25945
25940
|
// Fire uiAction only — skip command to avoid re-triggering the same command
|
|
25946
25941
|
// that caused this setUIState call in the first place.
|
|
@@ -25957,7 +25952,6 @@ const useUIGroupStateController = (
|
|
|
25957
25952
|
value: newUIState,
|
|
25958
25953
|
});
|
|
25959
25954
|
}
|
|
25960
|
-
notifyParentAboutChildInteraction(e, { stateChanged: true });
|
|
25961
25955
|
};
|
|
25962
25956
|
|
|
25963
25957
|
useLayoutEffect(() => {
|
|
@@ -25975,6 +25969,7 @@ const useUIGroupStateController = (
|
|
|
25975
25969
|
existingUIStateController.id = id;
|
|
25976
25970
|
existingUIStateController.name = name;
|
|
25977
25971
|
existingUIStateController.value = value;
|
|
25972
|
+
existingUIStateController.props = props;
|
|
25978
25973
|
uiActionRef.current = uiAction;
|
|
25979
25974
|
return existingUIStateController;
|
|
25980
25975
|
}
|
|
@@ -25998,6 +25993,7 @@ const useUIGroupStateController = (
|
|
|
25998
25993
|
id,
|
|
25999
25994
|
name,
|
|
26000
25995
|
value,
|
|
25996
|
+
props,
|
|
26001
25997
|
uiState: fallbackState,
|
|
26002
25998
|
uiStateSignal,
|
|
26003
25999
|
wantRequesterButtonState,
|
|
@@ -26093,7 +26089,7 @@ const useUIGroupStateController = (
|
|
|
26093
26089
|
if (command) {
|
|
26094
26090
|
const el = ref.current;
|
|
26095
26091
|
if (el) {
|
|
26096
|
-
|
|
26092
|
+
triggerNaviCommand(el, command, e);
|
|
26097
26093
|
}
|
|
26098
26094
|
}
|
|
26099
26095
|
},
|
|
@@ -26234,7 +26230,7 @@ const EMPTY_OBJECT = {};
|
|
|
26234
26230
|
* inside the picker popup. It also means `commands.js` no longer has to
|
|
26235
26231
|
* manually re-dispatch to inner controls.
|
|
26236
26232
|
*/
|
|
26237
|
-
const useUIFacadeStateController = (uiStateController) => {
|
|
26233
|
+
const useUIFacadeStateController = (props, uiStateController) => {
|
|
26238
26234
|
const firstChildControllerRef = useRef(null);
|
|
26239
26235
|
const updatingRef = useRef(false);
|
|
26240
26236
|
|
|
@@ -26258,11 +26254,24 @@ const useUIFacadeStateController = (uiStateController) => {
|
|
|
26258
26254
|
});
|
|
26259
26255
|
}, []);
|
|
26260
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
|
+
|
|
26261
26266
|
return useMemo(
|
|
26262
26267
|
() => ({
|
|
26263
26268
|
controlType: "facade",
|
|
26269
|
+
props,
|
|
26264
26270
|
uiStateSignal: uiStateController.uiStateSignal,
|
|
26265
26271
|
registerChild: (child) => {
|
|
26272
|
+
if (!includeChildController(child)) {
|
|
26273
|
+
return;
|
|
26274
|
+
}
|
|
26266
26275
|
if (!firstChildControllerRef.current) {
|
|
26267
26276
|
firstChildControllerRef.current = child;
|
|
26268
26277
|
} else {
|
|
@@ -26479,18 +26488,19 @@ const useControlProps = (props, {
|
|
|
26479
26488
|
uiActionInternal,
|
|
26480
26489
|
readOnlySupported
|
|
26481
26490
|
}) => {
|
|
26491
|
+
const idDefault = useId();
|
|
26482
26492
|
const debugInteraction = useDebugInteraction();
|
|
26483
26493
|
const controlName = useContext(ControlNameContext);
|
|
26494
|
+
const controlToInterface = useContext(ControlToInterfaceContext);
|
|
26484
26495
|
const state = props[statePropName];
|
|
26496
|
+
props.name = props.name || controlName;
|
|
26497
|
+
props.id = props.id || controlToInterface?.id || idDefault;
|
|
26485
26498
|
if (isSignal(state)) {
|
|
26486
26499
|
props = {
|
|
26487
26500
|
...props,
|
|
26488
26501
|
[statePropName]: state.value
|
|
26489
26502
|
};
|
|
26490
26503
|
}
|
|
26491
|
-
if (!props.name && controlName) {
|
|
26492
|
-
props.name = controlName;
|
|
26493
|
-
}
|
|
26494
26504
|
const uiStateController = useUIStateController(props, controlType, {
|
|
26495
26505
|
statePropName,
|
|
26496
26506
|
defaultStatePropName,
|
|
@@ -26516,13 +26526,6 @@ const useControlProps = (props, {
|
|
|
26516
26526
|
actionAfterChange = actionInteraction === "change",
|
|
26517
26527
|
actionDebounce
|
|
26518
26528
|
} = props;
|
|
26519
|
-
let isCheckable = false;
|
|
26520
|
-
// Effect to run when the Enter key is pressed.
|
|
26521
|
-
// For most inputs Enter submits the surrounding form; for checkables Enter
|
|
26522
|
-
// synthesizes a click so the browser's native checkbox/radio activation runs
|
|
26523
|
-
// (which then fires input -> goes through the action pipeline).
|
|
26524
|
-
let enterEffect;
|
|
26525
|
-
let spaceEffect;
|
|
26526
26529
|
const updateUIState = e => {
|
|
26527
26530
|
const value = readControlValue(ref.current);
|
|
26528
26531
|
uiStateController.setUIState(value, e);
|
|
@@ -26558,9 +26561,6 @@ const useControlProps = (props, {
|
|
|
26558
26561
|
interaction.effect?.(e);
|
|
26559
26562
|
return true;
|
|
26560
26563
|
};
|
|
26561
|
-
const asBrowserAction = (interaction, e) => {
|
|
26562
|
-
return asInteraction(interaction, e);
|
|
26563
|
-
};
|
|
26564
26564
|
const lastEventRequestingActionRef = useRef();
|
|
26565
26565
|
const lastActionValueRef = useRef(NO_ACTION_YET);
|
|
26566
26566
|
const wasCheckedAtMousedownRef = useRef(false);
|
|
@@ -26578,12 +26578,7 @@ const useControlProps = (props, {
|
|
|
26578
26578
|
// because at that point asAction hasn't run yet and we must not pre-empt its dedup.
|
|
26579
26579
|
controlHostProps.onnavi_ui_state_change = e => {
|
|
26580
26580
|
const originatingEvent = e.detail.event;
|
|
26581
|
-
if (
|
|
26582
|
-
const sourceIsOwnInput = originatingEvent?.type === "input" && originatingEvent?.target === ref.current;
|
|
26583
|
-
if (!sourceIsOwnInput) {
|
|
26584
|
-
lastActionValueRef.current = e.detail.value;
|
|
26585
|
-
}
|
|
26586
|
-
} else if (originatingEvent?.type === "radio_sibling_uncheck") {
|
|
26581
|
+
if (originatingEvent?.type === "radio_sibling_uncheck") {
|
|
26587
26582
|
lastActionValueRef.current = e.detail.value;
|
|
26588
26583
|
}
|
|
26589
26584
|
};
|
|
@@ -26621,181 +26616,238 @@ const useControlProps = (props, {
|
|
|
26621
26616
|
interaction.effect?.(e);
|
|
26622
26617
|
return true;
|
|
26623
26618
|
};
|
|
26624
|
-
const
|
|
26625
|
-
|
|
26626
|
-
|
|
26627
|
-
|
|
26628
|
-
|
|
26629
|
-
}
|
|
26630
|
-
return interaction.callback(interaction, e, {
|
|
26631
|
-
ifValueModified
|
|
26632
|
-
});
|
|
26633
|
-
};
|
|
26634
|
-
let mousedownInteraction;
|
|
26635
|
-
let clickInteraction;
|
|
26636
|
-
let inputInteraction;
|
|
26637
|
-
let keydownInteraction = {
|
|
26638
|
-
name: "keydown",
|
|
26639
|
-
callback: asBrowserAction
|
|
26640
|
-
};
|
|
26641
|
-
// a custom concept being combination of "input", "change" and may other events
|
|
26642
|
-
// this even if trigerred when value changes and can be controlled by actionDebounce and actionAfterChange
|
|
26643
|
-
let naviChangeInteraction;
|
|
26644
|
-
if (controlType === "button") {
|
|
26645
|
-
mousedownInteraction = {
|
|
26646
|
-
name: "mousedown",
|
|
26647
|
-
callback: actionOnMouseDown ? asAction : asInteraction
|
|
26648
|
-
// effect: updateUIState,
|
|
26649
|
-
};
|
|
26650
|
-
clickInteraction = {
|
|
26651
|
-
name: "click",
|
|
26652
|
-
callback: actionOnMouseDown ? asInteraction : asAction
|
|
26653
|
-
// effect: updateUIState,
|
|
26654
|
-
};
|
|
26655
|
-
} else if (controlType === "input" || controlType === "picker") {
|
|
26656
|
-
isCheckable = props.type === "radio" || props.type === "checkbox";
|
|
26657
|
-
// On input, gate the interaction (readonly check) and update UI state to reflect the new value.
|
|
26658
|
-
inputInteraction = {
|
|
26659
|
-
name: "input",
|
|
26660
|
-
callback: asInteraction,
|
|
26661
|
-
effect: updateUIState
|
|
26662
|
-
};
|
|
26663
|
-
naviChangeInteraction = {
|
|
26664
|
-
name: "navi_change",
|
|
26665
|
-
callback: asAction
|
|
26666
|
-
};
|
|
26667
|
-
enterEffect = e => {
|
|
26668
|
-
const input = e.currentTarget;
|
|
26669
|
-
dispatchNaviCommand(input, "--navi-send", e);
|
|
26670
|
-
};
|
|
26671
|
-
if (controlType === "picker") {
|
|
26672
|
-
mousedownInteraction = {
|
|
26673
|
-
name: "mousedown to open picker",
|
|
26674
|
-
callback: asInteraction
|
|
26675
|
-
};
|
|
26676
|
-
clickInteraction = {
|
|
26677
|
-
name: "click to open picker",
|
|
26678
|
-
callback: asInteraction
|
|
26679
|
-
};
|
|
26680
|
-
}
|
|
26681
|
-
if (isCheckable) {
|
|
26682
|
-
inputInteraction = {
|
|
26683
|
-
name: "input",
|
|
26684
|
-
callback: asAction
|
|
26619
|
+
const getDefaultInteractionDefinitions = () => {
|
|
26620
|
+
const keyDownDefault = () => {
|
|
26621
|
+
return {
|
|
26622
|
+
name: "keydown",
|
|
26623
|
+
type: "interaction"
|
|
26685
26624
|
};
|
|
26686
|
-
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26699
|
-
|
|
26700
|
-
|
|
26701
|
-
|
|
26702
|
-
|
|
26703
|
-
|
|
26704
|
-
|
|
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
|
+
};
|
|
26705
26646
|
}
|
|
26706
26647
|
};
|
|
26707
|
-
|
|
26708
|
-
|
|
26709
|
-
|
|
26710
|
-
const
|
|
26711
|
-
|
|
26712
|
-
|
|
26713
|
-
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
|
+
};
|
|
26714
26736
|
}
|
|
26715
|
-
|
|
26716
|
-
|
|
26717
|
-
|
|
26718
|
-
if (
|
|
26719
|
-
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
event: e
|
|
26727
|
-
});
|
|
26728
|
-
if (checkable.form) {
|
|
26729
|
-
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
|
+
};
|
|
26730
26748
|
}
|
|
26749
|
+
return keyDownDefault();
|
|
26731
26750
|
};
|
|
26732
|
-
if (props.type === "
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
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
|
+
};
|
|
26738
26771
|
}
|
|
26739
26772
|
};
|
|
26740
26773
|
}
|
|
26741
|
-
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
|
|
26745
|
-
|
|
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
|
+
}
|
|
26746
26789
|
};
|
|
26747
|
-
}
|
|
26748
|
-
|
|
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
|
+
};
|
|
26749
26822
|
const onMouseDown = e => {
|
|
26750
26823
|
props.onMouseDown?.(e);
|
|
26751
|
-
|
|
26752
|
-
wasCheckedAtMousedownRef.current = e.currentTarget.checked;
|
|
26753
|
-
}
|
|
26754
|
-
applyInteraction(mousedownInteraction, e);
|
|
26824
|
+
applyInteraction("mouseDown", e);
|
|
26755
26825
|
transferFocusToTarget(e);
|
|
26756
26826
|
};
|
|
26757
26827
|
const onClick = e => {
|
|
26758
26828
|
props.onClick?.(e);
|
|
26759
|
-
applyInteraction(
|
|
26829
|
+
applyInteraction("click", e);
|
|
26760
26830
|
transferFocusToTarget(e);
|
|
26761
|
-
const controlHost = e.currentTarget;
|
|
26762
|
-
if (controlHost.form) {
|
|
26763
|
-
if (controlType === "button") {
|
|
26764
|
-
// prevent form submission
|
|
26765
|
-
e.preventDefault();
|
|
26766
|
-
} else if (controlHost.closest("button")) {
|
|
26767
|
-
// prevent button form submission by click
|
|
26768
|
-
// (When an input is inside a <button> like for a picker)
|
|
26769
|
-
// any click in the picker could trigger form submission as browser see this as click on button inside form
|
|
26770
|
-
e.preventDefault();
|
|
26771
|
-
}
|
|
26772
|
-
}
|
|
26773
26831
|
};
|
|
26774
26832
|
const onKeyDown = e => {
|
|
26775
26833
|
props.onKeyDown?.(e);
|
|
26776
|
-
|
|
26777
|
-
enterEffect(e);
|
|
26778
|
-
return;
|
|
26779
|
-
}
|
|
26780
|
-
if (e.key === " " && spaceEffect) {
|
|
26781
|
-
spaceEffect(e);
|
|
26782
|
-
return;
|
|
26783
|
-
}
|
|
26784
|
-
applyInteraction(keydownInteraction, e);
|
|
26834
|
+
applyInteraction("keyDown", e);
|
|
26785
26835
|
};
|
|
26786
26836
|
const onInput = e => {
|
|
26787
26837
|
props.onInput?.(e);
|
|
26788
|
-
applyInteraction(
|
|
26838
|
+
applyInteraction("input", e, {
|
|
26789
26839
|
ifValueModified: true
|
|
26790
26840
|
});
|
|
26791
26841
|
};
|
|
26792
|
-
|
|
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);
|
|
26793
26845
|
const refCallback = useCallback(field => {
|
|
26794
|
-
if (!
|
|
26846
|
+
if (!hasNaviChangeInteractionDefinition || actionInteraction === "custom") {
|
|
26795
26847
|
return undefined;
|
|
26796
26848
|
}
|
|
26797
26849
|
return addInputEffect(field, e => {
|
|
26798
|
-
applyInteraction(
|
|
26850
|
+
applyInteraction("naviChange", e, {
|
|
26799
26851
|
ifValueModified: true
|
|
26800
26852
|
});
|
|
26801
26853
|
}, {
|
|
@@ -26803,7 +26855,7 @@ const useControlProps = (props, {
|
|
|
26803
26855
|
debounce: actionDebounce,
|
|
26804
26856
|
debugInteraction
|
|
26805
26857
|
});
|
|
26806
|
-
}, [actionInteraction, actionAfterChange, actionDebounce,
|
|
26858
|
+
}, [actionInteraction, actionAfterChange, actionDebounce, hasNaviChangeInteractionDefinition]);
|
|
26807
26859
|
const refComposed = useComposeElementRef(refCallback, ref);
|
|
26808
26860
|
const onPaste = e => {
|
|
26809
26861
|
props.onPaste?.(e);
|
|
@@ -26936,7 +26988,7 @@ const useControlgroupProps = (props, {
|
|
|
26936
26988
|
*/
|
|
26937
26989
|
const useControlFacadeProps = (props, options) => {
|
|
26938
26990
|
const [controlRootProps, controlHostProps, uiStateController] = useControlProps(props, options);
|
|
26939
|
-
const facadeController = useUIFacadeStateController(uiStateController);
|
|
26991
|
+
const facadeController = useUIFacadeStateController(props, uiStateController);
|
|
26940
26992
|
return [controlRootProps, controlHostProps, {
|
|
26941
26993
|
value: facadeController
|
|
26942
26994
|
}];
|
|
@@ -27017,14 +27069,11 @@ const useInteractiveProps = (props, {
|
|
|
27017
27069
|
autoFocusVisible,
|
|
27018
27070
|
autoSelect
|
|
27019
27071
|
} = props;
|
|
27020
|
-
useAutoFocus(ref, autoFocus, {
|
|
27072
|
+
const autoFocusProps = useAutoFocus(ref, autoFocus, {
|
|
27021
27073
|
focusVisible: autoFocusVisible,
|
|
27022
27074
|
autoSelect
|
|
27023
27075
|
});
|
|
27024
|
-
Object.assign(controlHostProps,
|
|
27025
|
-
"navi-autofocus": autoFocus ? autoFocus === true ? "" : autoFocus : undefined,
|
|
27026
|
-
"navi-autofocus-select": autoFocus && autoSelect ? "" : undefined
|
|
27027
|
-
});
|
|
27076
|
+
Object.assign(controlHostProps, autoFocusProps);
|
|
27028
27077
|
}
|
|
27029
27078
|
{
|
|
27030
27079
|
const {
|
|
@@ -27037,13 +27086,12 @@ const useInteractiveProps = (props, {
|
|
|
27037
27086
|
readOnly,
|
|
27038
27087
|
loading
|
|
27039
27088
|
} = props;
|
|
27040
|
-
const idResolved = id || controlToInterfaceContext?.id;
|
|
27041
27089
|
const disabledResolved = disabled || controlDisabled;
|
|
27042
27090
|
const requiredResolved = required || controlRequired;
|
|
27043
27091
|
const loadingResolved = loading || actionStatus.loading || controlLoading && parentActionRequester === ref.current;
|
|
27044
27092
|
const readOnlyResolved = readOnly || controlReadOnly || loadingResolved || uiStateController.readOnly;
|
|
27045
27093
|
Object.assign(controlHostProps, {
|
|
27046
|
-
|
|
27094
|
+
id,
|
|
27047
27095
|
"navi-control-proxy-for": naviProxyFor,
|
|
27048
27096
|
name,
|
|
27049
27097
|
type,
|
|
@@ -27078,6 +27126,7 @@ const useInteractiveProps = (props, {
|
|
|
27078
27126
|
}
|
|
27079
27127
|
{
|
|
27080
27128
|
const uiState = uiStateController.uiStateSignal.value;
|
|
27129
|
+
const isCheckable = uiStateController.controlType === "input" && (props.type === "radio" || props.type === "checkbox");
|
|
27081
27130
|
Object.assign(controlHostProps, {
|
|
27082
27131
|
onnavi_clear_ui_state: e => {
|
|
27083
27132
|
uiStateController.clearUIState(e);
|
|
@@ -27090,6 +27139,16 @@ const useInteractiveProps = (props, {
|
|
|
27090
27139
|
},
|
|
27091
27140
|
onnavi_set_ui_state: e => {
|
|
27092
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
|
+
}
|
|
27093
27152
|
}
|
|
27094
27153
|
});
|
|
27095
27154
|
// Mirror ui state handlers on the root so events dispatched on the root element
|
|
@@ -27098,7 +27157,9 @@ const useInteractiveProps = (props, {
|
|
|
27098
27157
|
onnavi_clear_ui_state: controlHostProps.onnavi_clear_ui_state,
|
|
27099
27158
|
onnavi_reset_ui_state: controlHostProps.onnavi_reset_ui_state,
|
|
27100
27159
|
onnavi_get_ui_state: controlHostProps.onnavi_get_ui_state,
|
|
27101
|
-
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
|
|
27102
27163
|
});
|
|
27103
27164
|
const {
|
|
27104
27165
|
statePropName
|
|
@@ -28552,7 +28613,7 @@ const LinkPlain = props => {
|
|
|
28552
28613
|
selection,
|
|
28553
28614
|
selectionController
|
|
28554
28615
|
});
|
|
28555
|
-
useAutoFocus(ref, autoFocus);
|
|
28616
|
+
const autoFocusProps = useAutoFocus(ref, autoFocus);
|
|
28556
28617
|
useConstraints(ref, constraints);
|
|
28557
28618
|
const shouldDimColor = readOnly || disabled;
|
|
28558
28619
|
useDimColorWhen(ref, shouldDimColor);
|
|
@@ -28604,10 +28665,7 @@ const LinkPlain = props => {
|
|
|
28604
28665
|
color: anchor && !innerChildren ? "inherit" : undefined,
|
|
28605
28666
|
id: anchor ? href.slice(1) : undefined,
|
|
28606
28667
|
...remainingProps,
|
|
28607
|
-
|
|
28608
|
-
,
|
|
28609
|
-
|
|
28610
|
-
"navi-autofocus": autoFocus ? "" : undefined,
|
|
28668
|
+
...autoFocusProps,
|
|
28611
28669
|
ref: ref,
|
|
28612
28670
|
href: href,
|
|
28613
28671
|
rel: innerRel,
|
|
@@ -29919,7 +29977,7 @@ const InputCheckboxFieldInterface = props => {
|
|
|
29919
29977
|
color: "var(--loader-color)"
|
|
29920
29978
|
}), visualVnode, jsx(RealInputCheckbox, {
|
|
29921
29979
|
...checkboxHostProps,
|
|
29922
|
-
switch: switchProp
|
|
29980
|
+
switch: switchProp ? "" : undefined
|
|
29923
29981
|
})]
|
|
29924
29982
|
});
|
|
29925
29983
|
};
|
|
@@ -30055,23 +30113,20 @@ const Field = props => {
|
|
|
30055
30113
|
const refDefault = useRef();
|
|
30056
30114
|
props.ref = props.ref || refDefault;
|
|
30057
30115
|
const {
|
|
30058
|
-
|
|
30059
|
-
vertical
|
|
30116
|
+
ownLabel
|
|
30060
30117
|
} = props;
|
|
30061
|
-
if (
|
|
30062
|
-
|
|
30063
|
-
}
|
|
30064
|
-
if (props.as === "label") {
|
|
30065
|
-
return jsx(FieldAsLabel, {
|
|
30118
|
+
if (ownLabel) {
|
|
30119
|
+
return jsx(FieldAsContainer, {
|
|
30066
30120
|
...props
|
|
30067
30121
|
});
|
|
30068
30122
|
}
|
|
30069
|
-
return jsx(
|
|
30123
|
+
return jsx(FieldAsLabel, {
|
|
30070
30124
|
...props
|
|
30071
30125
|
});
|
|
30072
30126
|
};
|
|
30073
30127
|
const FieldAsLabel = props => {
|
|
30074
30128
|
return jsx(FieldUI, {
|
|
30129
|
+
as: "label",
|
|
30075
30130
|
...props
|
|
30076
30131
|
});
|
|
30077
30132
|
};
|
|
@@ -32485,8 +32540,6 @@ const InputTextualFirstResolver = props => {
|
|
|
32485
32540
|
const Next = useNextResolver();
|
|
32486
32541
|
const defaultRef = useRef(null);
|
|
32487
32542
|
props.ref = props.ref || defaultRef;
|
|
32488
|
-
const idDefault = useId(); // needed by ui state controller and slot labels
|
|
32489
|
-
props.id = props.id || idDefault;
|
|
32490
32543
|
resolveInputProps(props);
|
|
32491
32544
|
return jsx(Next, {
|
|
32492
32545
|
...props
|
|
@@ -32852,11 +32905,10 @@ const FormControl = props => {
|
|
|
32852
32905
|
onSubmit: e => {
|
|
32853
32906
|
const form = e.currentTarget;
|
|
32854
32907
|
e.preventDefault();
|
|
32855
|
-
|
|
32908
|
+
dispatchRequestAction(form, {
|
|
32856
32909
|
event: e,
|
|
32857
|
-
|
|
32858
|
-
|
|
32859
|
-
requester: form,
|
|
32910
|
+
requester: e.submitter || form,
|
|
32911
|
+
actionOrigin: "form_submit",
|
|
32860
32912
|
meta: {
|
|
32861
32913
|
isSubmit: true
|
|
32862
32914
|
}
|
|
@@ -32891,7 +32943,7 @@ const getFormManagedControls = form => {
|
|
|
32891
32943
|
// Exclude inputs that are inside any control group (radio, checkbox, control_group…)
|
|
32892
32944
|
// — the group host is the managed control for the form, not its individual child inputs.
|
|
32893
32945
|
const controlGroupHost = element.closest("[navi-control-group]");
|
|
32894
|
-
if (controlGroupHost && form.contains(controlGroupHost)) {
|
|
32946
|
+
if (controlGroupHost && controlGroupHost !== form && form.contains(controlGroupHost)) {
|
|
32895
32947
|
continue;
|
|
32896
32948
|
}
|
|
32897
32949
|
managedControls.push(element);
|
|
@@ -33313,7 +33365,7 @@ const markAutofocusRestoreOnClose = (containerEl) => {
|
|
|
33313
33365
|
// as a result document.activeElement is not up-to-date (can be document.body for instance)
|
|
33314
33366
|
const getFocusedBeforeTransfer = (e) => {
|
|
33315
33367
|
const initiator = e.detail.eventChain[0];
|
|
33316
|
-
if (initiator.type === "mousedown"
|
|
33368
|
+
if (initiator.type === "mousedown") {
|
|
33317
33369
|
// if we we had let browser give focus, the element would be the one that would be focused
|
|
33318
33370
|
return initiator.currentTarget;
|
|
33319
33371
|
}
|
|
@@ -33324,7 +33376,7 @@ const getFocusedBeforeTransfer = (e) => {
|
|
|
33324
33376
|
return document.activeElement;
|
|
33325
33377
|
};
|
|
33326
33378
|
|
|
33327
|
-
const transferFocus = (containerEl, debugFocus, e) => {
|
|
33379
|
+
const transferFocus = (containerEl, debugFocus, e, fallback) => {
|
|
33328
33380
|
let target;
|
|
33329
33381
|
let reason;
|
|
33330
33382
|
if (containerEl.hasAttribute("navi-autofocus-restore")) {
|
|
@@ -33364,6 +33416,11 @@ const transferFocus = (containerEl, debugFocus, e) => {
|
|
|
33364
33416
|
target = naviAutoFocusFallback;
|
|
33365
33417
|
}
|
|
33366
33418
|
}
|
|
33419
|
+
if (!target) {
|
|
33420
|
+
if (fallback) {
|
|
33421
|
+
target = fallback;
|
|
33422
|
+
}
|
|
33423
|
+
}
|
|
33367
33424
|
if (!target) {
|
|
33368
33425
|
return;
|
|
33369
33426
|
}
|
|
@@ -33431,6 +33488,7 @@ const Dialog = props => {
|
|
|
33431
33488
|
const ref = rest.ref || defaultRef;
|
|
33432
33489
|
const debugPopup = useDebugPopup();
|
|
33433
33490
|
const debugFocus = useDebugFocus();
|
|
33491
|
+
const autoFocusProps = useAutoFocus(ref, props.autoFocus);
|
|
33434
33492
|
const openedRef = useRef(false);
|
|
33435
33493
|
const [addCleanup, cleanup] = useCleanup();
|
|
33436
33494
|
const open = (e, {
|
|
@@ -33447,7 +33505,7 @@ const Dialog = props => {
|
|
|
33447
33505
|
dialogEl.style.setProperty("--anchor-height", `${snapToPixel(height)}px`);
|
|
33448
33506
|
const focusedBeforeOpen = getFocusedBeforeTransfer(e);
|
|
33449
33507
|
dialogEl.showModal();
|
|
33450
|
-
transferFocus(dialogEl, debugFocus, e);
|
|
33508
|
+
transferFocus(dialogEl, debugFocus, e, focusedBeforeOpen);
|
|
33451
33509
|
if (scrollTrap) {
|
|
33452
33510
|
addCleanup(trapScrollInside(dialogEl));
|
|
33453
33511
|
}
|
|
@@ -33512,9 +33570,11 @@ const Dialog = props => {
|
|
|
33512
33570
|
};
|
|
33513
33571
|
return jsx(Box, {
|
|
33514
33572
|
...rest,
|
|
33573
|
+
...autoFocusProps,
|
|
33515
33574
|
as: "dialog",
|
|
33516
33575
|
ref: ref,
|
|
33517
33576
|
baseClassName: "navi_dialog",
|
|
33577
|
+
pseudoClasses: DIALOG_PSEUDO_CLASSES,
|
|
33518
33578
|
onMouseDown: e => {
|
|
33519
33579
|
rest.onMouseDown?.(e);
|
|
33520
33580
|
// The <dialog> element covers the full viewport; clicking the backdrop
|
|
@@ -33543,6 +33603,7 @@ const Dialog = props => {
|
|
|
33543
33603
|
children: children
|
|
33544
33604
|
});
|
|
33545
33605
|
};
|
|
33606
|
+
const DIALOG_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible", ":focus-within"];
|
|
33546
33607
|
|
|
33547
33608
|
installImportMetaCssBuild(import.meta);const css$q = /* css */`
|
|
33548
33609
|
.navi_popover {
|
|
@@ -33587,6 +33648,7 @@ const Popover = props => {
|
|
|
33587
33648
|
const id = rest.id || defaultId;
|
|
33588
33649
|
const debugPopup = useDebugPopup();
|
|
33589
33650
|
const debugFocus = useDebugFocus();
|
|
33651
|
+
const autoFocusProps = useAutoFocus(ref, props.autoFocus);
|
|
33590
33652
|
const [opened, setOpened] = useState(false);
|
|
33591
33653
|
const openedRef = useRef(opened);
|
|
33592
33654
|
openedRef.current = opened;
|
|
@@ -33598,7 +33660,7 @@ const Popover = props => {
|
|
|
33598
33660
|
const popoverEl = ref.current;
|
|
33599
33661
|
const focusedBeforeOpen = getFocusedBeforeTransfer(e);
|
|
33600
33662
|
popoverEl.showPopover();
|
|
33601
|
-
transferFocus(popoverEl, debugFocus, e);
|
|
33663
|
+
transferFocus(popoverEl, debugFocus, e, focusedBeforeOpen);
|
|
33602
33664
|
const effectiveAnchor = anchor || document.documentElement;
|
|
33603
33665
|
const positionPopover = positionEvent => {
|
|
33604
33666
|
const {
|
|
@@ -33717,9 +33779,10 @@ const Popover = props => {
|
|
|
33717
33779
|
id: id,
|
|
33718
33780
|
popover: "manual",
|
|
33719
33781
|
...rest,
|
|
33782
|
+
...autoFocusProps,
|
|
33720
33783
|
ref: ref,
|
|
33721
33784
|
baseClassName: "navi_popover",
|
|
33722
|
-
pseudoClasses:
|
|
33785
|
+
pseudoClasses: POPOVER_PSEUDO_CLASSES,
|
|
33723
33786
|
onnavi_request_open: e => {
|
|
33724
33787
|
const {
|
|
33725
33788
|
anchor
|
|
@@ -33747,7 +33810,7 @@ const Popover = props => {
|
|
|
33747
33810
|
}), children]
|
|
33748
33811
|
});
|
|
33749
33812
|
};
|
|
33750
|
-
const
|
|
33813
|
+
const POPOVER_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible", ":focus-within"];
|
|
33751
33814
|
|
|
33752
33815
|
installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
33753
33816
|
.navi_picker {
|
|
@@ -33769,7 +33832,7 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33769
33832
|
border-radius: var(--picker-border-radius);
|
|
33770
33833
|
outline-width: var(--picker-outline-width);
|
|
33771
33834
|
outline-color: var(--picker-outline-color);
|
|
33772
|
-
outline-offset:
|
|
33835
|
+
outline-offset: 0px;
|
|
33773
33836
|
box-shadow:
|
|
33774
33837
|
0 4px 8px rgba(0, 0, 0, 0.08),
|
|
33775
33838
|
0 12px 40px rgba(0, 0, 0, 0.22);
|
|
@@ -33827,6 +33890,10 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33827
33890
|
overflow: auto;
|
|
33828
33891
|
overscroll-behavior: none;
|
|
33829
33892
|
}
|
|
33893
|
+
|
|
33894
|
+
&[data-focus-visible] {
|
|
33895
|
+
outline-style: solid;
|
|
33896
|
+
}
|
|
33830
33897
|
}
|
|
33831
33898
|
|
|
33832
33899
|
&[aria-expanded="true"] {
|
|
@@ -33855,7 +33922,7 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33855
33922
|
border-radius: var(--picker-border-radius);
|
|
33856
33923
|
outline-width: var(--picker-outline-width);
|
|
33857
33924
|
outline-color: var(--picker-outline-color);
|
|
33858
|
-
outline-offset:
|
|
33925
|
+
outline-offset: 0;
|
|
33859
33926
|
box-shadow:
|
|
33860
33927
|
0 4px 8px rgba(0, 0, 0, 0.08),
|
|
33861
33928
|
0 12px 40px rgba(0, 0, 0, 0.22);
|
|
@@ -33867,6 +33934,10 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
|
|
|
33867
33934
|
flex-direction: column;
|
|
33868
33935
|
}
|
|
33869
33936
|
|
|
33937
|
+
&[data-focus-visible] {
|
|
33938
|
+
outline-style: solid;
|
|
33939
|
+
}
|
|
33940
|
+
|
|
33870
33941
|
&::backdrop {
|
|
33871
33942
|
background: rgba(0, 0, 0, 0.4);
|
|
33872
33943
|
}
|
|
@@ -33974,10 +34045,14 @@ const PickerCustom = props => {
|
|
|
33974
34045
|
const valueAtOpenRef = useRef(null);
|
|
33975
34046
|
const activeElementAtOpenRef = useRef(null);
|
|
33976
34047
|
const onOpen = e => {
|
|
33977
|
-
activeElementAtOpenRef.current = e.detail.focusedBeforeOpen;
|
|
33978
34048
|
expandedRef.current = true;
|
|
33979
34049
|
setExpanded(true);
|
|
33980
|
-
|
|
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})`);
|
|
33981
34056
|
};
|
|
33982
34057
|
const restoreFocus = e => {
|
|
33983
34058
|
const activeElementAtOpen = activeElementAtOpenRef.current;
|
|
@@ -34001,6 +34076,11 @@ const PickerCustom = props => {
|
|
|
34001
34076
|
const onClose = e => {
|
|
34002
34077
|
const cancelEvent = findEvent(e, eInChain => eInChain.type === "navi_request_close" && eInChain.detail.isCancel);
|
|
34003
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
|
+
}
|
|
34004
34084
|
expandedRef.current = false;
|
|
34005
34085
|
setExpanded(false);
|
|
34006
34086
|
// Reset so the next opening re-evaluates screen size
|
|
@@ -34046,17 +34126,12 @@ const PickerCustom = props => {
|
|
|
34046
34126
|
});
|
|
34047
34127
|
};
|
|
34048
34128
|
const requestClose = (e = new CustomEvent("programmatic"), {
|
|
34049
|
-
|
|
34129
|
+
isCancel = false
|
|
34050
34130
|
} = {}) => {
|
|
34051
|
-
const mousedownEvent = findEvent(e, "mousedown");
|
|
34052
|
-
if (mousedownEvent) {
|
|
34053
|
-
debugPopup(e, `disable click`);
|
|
34054
|
-
disableClickFor();
|
|
34055
|
-
}
|
|
34056
34131
|
const popupEl = popupRef.current;
|
|
34057
34132
|
return dispatchCustomEvent(popupEl, "navi_request_close", {
|
|
34058
34133
|
event: e,
|
|
34059
|
-
|
|
34134
|
+
isCancel
|
|
34060
34135
|
});
|
|
34061
34136
|
};
|
|
34062
34137
|
const onInteraction = (e, {
|
|
@@ -34108,91 +34183,97 @@ const PickerCustom = props => {
|
|
|
34108
34183
|
}
|
|
34109
34184
|
});
|
|
34110
34185
|
{
|
|
34111
|
-
const {
|
|
34112
|
-
onMouseDown,
|
|
34113
|
-
onClick,
|
|
34114
|
-
onKeyDown
|
|
34115
|
-
} = props;
|
|
34116
34186
|
const onKeyDownShortcuts = createOnKeyDownForShortcuts({
|
|
34117
|
-
|
|
34118
|
-
|
|
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 {
|
|
34119
34205
|
name: "arrow_down_to_open",
|
|
34120
34206
|
effect: () => {
|
|
34121
|
-
e.preventDefault(); // prevent container scroll
|
|
34122
34207
|
requestOpen(e);
|
|
34208
|
+
e.preventDefault(); // prevent container scroll
|
|
34123
34209
|
}
|
|
34124
|
-
}
|
|
34210
|
+
};
|
|
34125
34211
|
},
|
|
34126
|
-
arrowup: e => {
|
|
34127
|
-
|
|
34212
|
+
"arrowup": e => {
|
|
34213
|
+
return {
|
|
34128
34214
|
name: "arrow_up_to_open",
|
|
34129
34215
|
effect: () => {
|
|
34130
|
-
e.preventDefault(); // prevent container scroll
|
|
34131
34216
|
requestOpen(e);
|
|
34217
|
+
e.preventDefault(); // prevent container scroll
|
|
34132
34218
|
}
|
|
34133
|
-
}
|
|
34219
|
+
};
|
|
34134
34220
|
},
|
|
34135
|
-
space: e => {
|
|
34136
|
-
|
|
34221
|
+
"space": e => {
|
|
34222
|
+
return {
|
|
34137
34223
|
name: "space_to_open",
|
|
34138
34224
|
effect: () => {
|
|
34139
|
-
e.preventDefault(); // prevent scroll
|
|
34140
34225
|
requestOpen(e);
|
|
34226
|
+
e.preventDefault(); // prevent scroll
|
|
34141
34227
|
}
|
|
34142
|
-
}
|
|
34228
|
+
};
|
|
34143
34229
|
},
|
|
34144
|
-
escape: e => {
|
|
34230
|
+
"escape": e => {
|
|
34145
34231
|
if (!expandedRef.current) {
|
|
34146
|
-
return;
|
|
34232
|
+
return null;
|
|
34147
34233
|
}
|
|
34148
|
-
|
|
34234
|
+
return {
|
|
34149
34235
|
name: "escape_to_cancel",
|
|
34150
34236
|
effect: () => {
|
|
34151
|
-
e.preventDefault(); // prevent browser from closing the dialog (if any)
|
|
34152
34237
|
requestClose(e, {
|
|
34153
|
-
|
|
34238
|
+
isCancel: true
|
|
34154
34239
|
});
|
|
34240
|
+
e.preventDefault(); // prevent browser from closing the dialog (if any)
|
|
34155
34241
|
}
|
|
34156
|
-
}
|
|
34242
|
+
};
|
|
34157
34243
|
}
|
|
34158
34244
|
});
|
|
34159
34245
|
Object.assign(pickerProps, {
|
|
34160
|
-
|
|
34161
|
-
|
|
34162
|
-
|
|
34163
|
-
|
|
34164
|
-
|
|
34165
|
-
|
|
34166
|
-
|
|
34167
|
-
|
|
34168
|
-
|
|
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: () => {
|
|
34169
34257
|
debugFocus(e, `prevent browser giving focus to button (mousedown.preventDefault())`);
|
|
34170
34258
|
requestOpen(e);
|
|
34259
|
+
e.preventDefault(); // prevent browser trying to give focus to the select (popover will take focus)
|
|
34171
34260
|
}
|
|
34172
|
-
}
|
|
34173
|
-
}
|
|
34174
|
-
|
|
34175
|
-
|
|
34176
|
-
|
|
34177
|
-
|
|
34178
|
-
|
|
34179
|
-
|
|
34180
|
-
|
|
34181
|
-
|
|
34182
|
-
|
|
34183
|
-
|
|
34184
|
-
|
|
34185
|
-
|
|
34186
|
-
|
|
34187
|
-
|
|
34188
|
-
e.preventDefault();
|
|
34189
|
-
requestOpen(e);
|
|
34190
|
-
}
|
|
34191
|
-
});
|
|
34192
|
-
},
|
|
34193
|
-
onKeyDown: e => {
|
|
34194
|
-
onKeyDown?.(e);
|
|
34195
|
-
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
|
+
}
|
|
34196
34277
|
}
|
|
34197
34278
|
});
|
|
34198
34279
|
Object.assign(popupProps, {
|
|
@@ -34201,7 +34282,7 @@ const PickerCustom = props => {
|
|
|
34201
34282
|
return;
|
|
34202
34283
|
}
|
|
34203
34284
|
// mousedown inside popover should not bubble to the select (would re-open it if that mousedown closes it)
|
|
34204
|
-
debugPopup(e, `
|
|
34285
|
+
debugPopup(e, `"mousedown" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
34205
34286
|
e.stopPropagation();
|
|
34206
34287
|
},
|
|
34207
34288
|
onClick: e => {
|
|
@@ -34209,18 +34290,21 @@ const PickerCustom = props => {
|
|
|
34209
34290
|
return;
|
|
34210
34291
|
}
|
|
34211
34292
|
// click inside popover should not bubble to the picker (would re-open it if that click closes it)
|
|
34212
|
-
|
|
34213
|
-
// the picker is inside a <form> and the click lands on a non-button element
|
|
34214
|
-
debugPopup(e, `popover click stopPropagation + preventDefault`);
|
|
34293
|
+
debugPopup(e, `"click" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
34215
34294
|
e.stopPropagation();
|
|
34216
|
-
|
|
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();
|
|
34217
34300
|
},
|
|
34218
34301
|
onKeyDown: e => {
|
|
34219
34302
|
// some keys pressed inside popover should not reach the picker button
|
|
34220
34303
|
// (like enter that would try to request action of closest form otherwise for instance)
|
|
34221
34304
|
if (e.key === "Enter") {
|
|
34305
|
+
debugPopup(e, `"enter" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
34222
34306
|
e.stopPropagation();
|
|
34223
|
-
// preventDefault prevents the browser from
|
|
34307
|
+
// preventDefault prevents the browser from dispatching a "click" on the
|
|
34224
34308
|
// picker button when focus moves to it synchronously during enterEffect
|
|
34225
34309
|
e.preventDefault();
|
|
34226
34310
|
}
|
|
@@ -34291,7 +34375,10 @@ const PickerContentInsidePopover = props => {
|
|
|
34291
34375
|
viewportSpacing: viewportSpacing,
|
|
34292
34376
|
scrollTrap: scrollTrap,
|
|
34293
34377
|
pointerTrap: pointerTrap,
|
|
34294
|
-
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",
|
|
34295
34382
|
children: [popoverMode === "attached" ? jsx("div", {
|
|
34296
34383
|
className: "navi_picker_anchor_clone",
|
|
34297
34384
|
onMouseDown: e => {
|
|
@@ -34327,6 +34414,7 @@ const PickerContentInsideDialog = props => {
|
|
|
34327
34414
|
scrollTrap: scrollTrap,
|
|
34328
34415
|
pointerTrap: pointerTrap,
|
|
34329
34416
|
centerInVisualViewport: true,
|
|
34417
|
+
autoFocus: "fallback",
|
|
34330
34418
|
children: children
|
|
34331
34419
|
})
|
|
34332
34420
|
});
|
|
@@ -34473,6 +34561,7 @@ const TimeDate = ({
|
|
|
34473
34561
|
if (children === undefined) {
|
|
34474
34562
|
return jsx(TimeText, {
|
|
34475
34563
|
...props,
|
|
34564
|
+
capitalize: false,
|
|
34476
34565
|
children: formatDatePlaceholder(lang)
|
|
34477
34566
|
});
|
|
34478
34567
|
}
|
|
@@ -34579,6 +34668,7 @@ const TimeDatetime = ({
|
|
|
34579
34668
|
if (children === undefined) {
|
|
34580
34669
|
return jsx(TimeText, {
|
|
34581
34670
|
...props,
|
|
34671
|
+
capitalize: false,
|
|
34582
34672
|
children: formatDatetimePlaceholder(lang)
|
|
34583
34673
|
});
|
|
34584
34674
|
}
|
|
@@ -37764,7 +37854,6 @@ const PickerDateUI = props => {
|
|
|
37764
37854
|
type: "date",
|
|
37765
37855
|
color: "var(--picker-placeholder-color",
|
|
37766
37856
|
capitalize: true,
|
|
37767
|
-
maxLines: "1",
|
|
37768
37857
|
...props
|
|
37769
37858
|
});
|
|
37770
37859
|
}
|
|
@@ -37773,7 +37862,6 @@ const PickerDateUI = props => {
|
|
|
37773
37862
|
return jsx(Time, {
|
|
37774
37863
|
type: "date",
|
|
37775
37864
|
capitalize: true,
|
|
37776
|
-
maxLines: "1",
|
|
37777
37865
|
...props,
|
|
37778
37866
|
children: value
|
|
37779
37867
|
});
|
|
@@ -37797,7 +37885,6 @@ const PickerMonthUI = props => {
|
|
|
37797
37885
|
return jsx(Time, {
|
|
37798
37886
|
type: "month",
|
|
37799
37887
|
color: "var(--picker-placeholder-color",
|
|
37800
|
-
maxLines: "1",
|
|
37801
37888
|
...props
|
|
37802
37889
|
});
|
|
37803
37890
|
}
|
|
@@ -37805,7 +37892,6 @@ const PickerMonthUI = props => {
|
|
|
37805
37892
|
}
|
|
37806
37893
|
return jsx(Time, {
|
|
37807
37894
|
type: "month",
|
|
37808
|
-
maxLines: "1",
|
|
37809
37895
|
capitalize: true,
|
|
37810
37896
|
...props,
|
|
37811
37897
|
children: value
|
|
@@ -37830,7 +37916,6 @@ const PickerWeekUI = props => {
|
|
|
37830
37916
|
return jsx(Time, {
|
|
37831
37917
|
type: "week",
|
|
37832
37918
|
color: "var(--picker-placeholder-color",
|
|
37833
|
-
maxLines: "1",
|
|
37834
37919
|
...props
|
|
37835
37920
|
});
|
|
37836
37921
|
}
|
|
@@ -37839,7 +37924,6 @@ const PickerWeekUI = props => {
|
|
|
37839
37924
|
return jsx(Time, {
|
|
37840
37925
|
type: "week",
|
|
37841
37926
|
capitalize: true,
|
|
37842
|
-
maxLines: "1",
|
|
37843
37927
|
...props,
|
|
37844
37928
|
children: value
|
|
37845
37929
|
});
|
|
@@ -37863,7 +37947,6 @@ const PickerTimeUI = props => {
|
|
|
37863
37947
|
return jsx(Time, {
|
|
37864
37948
|
type: "time",
|
|
37865
37949
|
color: "var(--picker-placeholder-color",
|
|
37866
|
-
maxLines: "1",
|
|
37867
37950
|
...props
|
|
37868
37951
|
});
|
|
37869
37952
|
}
|
|
@@ -37871,7 +37954,6 @@ const PickerTimeUI = props => {
|
|
|
37871
37954
|
}
|
|
37872
37955
|
return jsx(Time, {
|
|
37873
37956
|
type: "time",
|
|
37874
|
-
maxLines: "1",
|
|
37875
37957
|
...props,
|
|
37876
37958
|
children: value
|
|
37877
37959
|
});
|
|
@@ -37895,7 +37977,6 @@ const PickerDatetimeUI = props => {
|
|
|
37895
37977
|
return jsx(Time, {
|
|
37896
37978
|
type: "datetime",
|
|
37897
37979
|
color: "var(--picker-placeholder-color",
|
|
37898
|
-
maxLines: "1",
|
|
37899
37980
|
...props
|
|
37900
37981
|
});
|
|
37901
37982
|
}
|
|
@@ -37903,7 +37984,6 @@ const PickerDatetimeUI = props => {
|
|
|
37903
37984
|
}
|
|
37904
37985
|
return jsx(Time, {
|
|
37905
37986
|
type: "datetime",
|
|
37906
|
-
maxLines: "1",
|
|
37907
37987
|
children: value
|
|
37908
37988
|
});
|
|
37909
37989
|
};
|
|
@@ -38094,7 +38174,6 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38094
38174
|
font-size: var(--picker-font-size);
|
|
38095
38175
|
font-family: var(--picker-font-family);
|
|
38096
38176
|
text-align: inherit;
|
|
38097
|
-
text-overflow: ellipsis;
|
|
38098
38177
|
background-color: var(--x-picker-background-color);
|
|
38099
38178
|
border-width: var(--picker-border-width);
|
|
38100
38179
|
border-style: solid;
|
|
@@ -38107,7 +38186,6 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38107
38186
|
cursor: var(--x-picker-cursor, pointer);
|
|
38108
38187
|
pointer-events: auto;
|
|
38109
38188
|
user-select: none;
|
|
38110
|
-
overflow: hidden;
|
|
38111
38189
|
-webkit-tap-highlight-color: var(--navi-control-tap-highlight-color);
|
|
38112
38190
|
|
|
38113
38191
|
.navi_picker_value {
|
|
@@ -38121,9 +38199,7 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38121
38199
|
padding-bottom: var(--x-picker-padding-bottom);
|
|
38122
38200
|
padding-left: var(--x-picker-padding-left);
|
|
38123
38201
|
flex-grow: 1;
|
|
38124
|
-
|
|
38125
|
-
white-space: nowrap;
|
|
38126
|
-
overflow: hidden;
|
|
38202
|
+
justify-content: inherit;
|
|
38127
38203
|
|
|
38128
38204
|
&[navi-placeholder] {
|
|
38129
38205
|
color: var(--picker-placeholder-color);
|
|
@@ -38148,6 +38224,11 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38148
38224
|
max-height: 100%;
|
|
38149
38225
|
}
|
|
38150
38226
|
}
|
|
38227
|
+
&[navi-single-line] {
|
|
38228
|
+
.navi_picker_right_slot {
|
|
38229
|
+
align-self: center;
|
|
38230
|
+
}
|
|
38231
|
+
}
|
|
38151
38232
|
.navi_picker_input {
|
|
38152
38233
|
position: absolute;
|
|
38153
38234
|
inset: 0;
|
|
@@ -38168,16 +38249,6 @@ installImportMetaCssBuild(import.meta);const css$i = /* css */`
|
|
|
38168
38249
|
text-align: initial; /* Don't inherit picker text align */
|
|
38169
38250
|
}
|
|
38170
38251
|
|
|
38171
|
-
&[data-line-clamp] {
|
|
38172
|
-
overflow-wrap: anywhere;
|
|
38173
|
-
.navi_picker_value {
|
|
38174
|
-
display: -webkit-box;
|
|
38175
|
-
white-space: normal;
|
|
38176
|
-
-webkit-box-orient: vertical;
|
|
38177
|
-
-webkit-line-clamp: var(--picker-max-lines);
|
|
38178
|
-
}
|
|
38179
|
-
}
|
|
38180
|
-
|
|
38181
38252
|
/* Hover */
|
|
38182
38253
|
&[data-hover] {
|
|
38183
38254
|
--x-picker-background-color: var(--picker-background-color-hover);
|
|
@@ -38264,9 +38335,10 @@ const PickerButton = props => {
|
|
|
38264
38335
|
icon,
|
|
38265
38336
|
placeholder,
|
|
38266
38337
|
ui,
|
|
38267
|
-
maxLines,
|
|
38338
|
+
maxLines = 1,
|
|
38268
38339
|
headless
|
|
38269
38340
|
} = props;
|
|
38341
|
+
const isSingleLine = maxLines === 1;
|
|
38270
38342
|
const inputRef = useRef(null);
|
|
38271
38343
|
const [pickerRemainingProps, inputProps, facadeChildrenProps] = useControlFacadeProps({
|
|
38272
38344
|
...props,
|
|
@@ -38286,20 +38358,17 @@ const PickerButton = props => {
|
|
|
38286
38358
|
children
|
|
38287
38359
|
} = inputProps;
|
|
38288
38360
|
const loading = basePseudoState[":-navi-loading"];
|
|
38289
|
-
const hasLineClamp = maxLines && maxLines > 1;
|
|
38290
38361
|
return jsxs(Box, {
|
|
38291
38362
|
as: headless ? "div" : "button",
|
|
38363
|
+
type: "button" /* ensure click inside the picker cannot submit ancestor form if any */,
|
|
38292
38364
|
ref: ref,
|
|
38293
38365
|
baseClassName: "navi_picker",
|
|
38294
38366
|
pseudoClasses: PICKER_BUTTON_PSEUDO_CLASSES,
|
|
38295
38367
|
disabled: disabled,
|
|
38296
|
-
"data-line-clamp": hasLineClamp ? "" : undefined,
|
|
38297
38368
|
"data-variant": variant,
|
|
38298
|
-
style: {
|
|
38299
|
-
"--picker-max-lines": maxLines
|
|
38300
|
-
},
|
|
38301
38369
|
"navi-visually-hidden": headless ? "" : undefined,
|
|
38302
38370
|
"navi-picker": "",
|
|
38371
|
+
"navi-single-line": isSingleLine ? "" : undefined,
|
|
38303
38372
|
...pickerRemainingProps,
|
|
38304
38373
|
basePseudoState: basePseudoState,
|
|
38305
38374
|
styleCSSVars: PickerStyleCSSVars
|
|
@@ -38337,7 +38406,7 @@ const PickerButton = props => {
|
|
|
38337
38406
|
children: [jsx(LoadingOutline, {
|
|
38338
38407
|
loading: loading,
|
|
38339
38408
|
color: "var(--picker-loader-color)",
|
|
38340
|
-
inset: -
|
|
38409
|
+
inset: -2
|
|
38341
38410
|
}), jsx(PickerInput, {
|
|
38342
38411
|
...inputProps,
|
|
38343
38412
|
// eslint-disable-next-line react/no-children-prop
|
|
@@ -38351,6 +38420,7 @@ const PickerButton = props => {
|
|
|
38351
38420
|
}), variant === "icon" || headless ? null : jsx(Text, {
|
|
38352
38421
|
className: "navi_picker_value",
|
|
38353
38422
|
"navi-placeholder": value === undefined || value === "" ? "" : undefined,
|
|
38423
|
+
maxLines: maxLines,
|
|
38354
38424
|
children: jsx(PickerContext.Provider, {
|
|
38355
38425
|
value: {
|
|
38356
38426
|
value,
|
|
@@ -38434,8 +38504,6 @@ const PickerFirstResolver = props => {
|
|
|
38434
38504
|
const Next = useNextResolver();
|
|
38435
38505
|
const defaultRef = useRef(null);
|
|
38436
38506
|
props.ref = props.ref || defaultRef;
|
|
38437
|
-
const idDefault = useId(); // needed by ui state controller
|
|
38438
|
-
props.id = props.id || idDefault;
|
|
38439
38507
|
resolveInputProps(props);
|
|
38440
38508
|
return jsx(Next, {
|
|
38441
38509
|
...props
|