@jsenv/navi 0.25.0 → 0.25.2
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 +1291 -1045
- package/dist/jsenv_navi.js.map +75 -52
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isValidElement, h, createContext, options, toChildArray, render, create
|
|
|
3
3
|
import { useErrorBoundary, useLayoutEffect, useEffect, useMemo, useRef, useState, useCallback, useContext, useImperativeHandle, useId } from "preact/hooks";
|
|
4
4
|
import { jsxs, jsx, Fragment } from "preact/jsx-runtime";
|
|
5
5
|
import { signal, effect, computed, batch, useSignal } from "@preact/signals";
|
|
6
|
-
import { createIterableWeakSet, mergeOneStyle, stringifyStyle, createPubSub, mergeTwoStyles, normalizeStyles, createGroupTransitionController, getElementSignature, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, findBefore, findAfter, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, allowWheelThrough, resolveCSSColor, createStyleController, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, resolveCSSSize, activeElementSignal, canInterceptKeys,
|
|
6
|
+
import { createIterableWeakSet, mergeOneStyle, stringifyStyle, createPubSub, mergeTwoStyles, normalizeStyles, createGroupTransitionController, getElementSignature, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, findBefore, findAfter, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, allowWheelThrough, resolveCSSColor, createStyleController, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, resolveCSSSize, activeElementSignal, canInterceptKeys, contrastColor, hasCSSSizeUnit, initFocusGroup, elementIsFocusable, resolveColorLuminance, dragAfterThreshold, getScrollContainer, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
|
|
7
7
|
export { contrastColor } from "@jsenv/dom";
|
|
8
8
|
import { prefixFirstAndIndentRemainingLines } from "@jsenv/humanize";
|
|
9
9
|
import { createValidity } from "@jsenv/validity";
|
|
@@ -6298,15 +6298,21 @@ const DIMENSION_PROPS = {
|
|
|
6298
6298
|
}
|
|
6299
6299
|
const inHorizontalFlexFlow =
|
|
6300
6300
|
parentBoxFlow === "flex-x" || parentBoxFlow === "inline-flex-x";
|
|
6301
|
-
if (
|
|
6302
|
-
//
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
return {
|
|
6301
|
+
if (inHorizontalFlexFlow) {
|
|
6302
|
+
// Parent is flex-x: grow as flex item
|
|
6303
|
+
return { flexGrow: 1, flexBasis: "0%" };
|
|
6304
|
+
}
|
|
6305
|
+
if (parentBoxFlow === "flex-y" || parentBoxFlow === "inline-flex-y") {
|
|
6306
|
+
return {
|
|
6307
|
+
alignSelf: "stretch",
|
|
6308
|
+
// Here flex grow is "useless" for the item itself
|
|
6309
|
+
// buuut it would allow children (hello ".navi_text_sizer")
|
|
6310
|
+
// to inherit expand behavior
|
|
6311
|
+
flexGrow: 1,
|
|
6312
|
+
};
|
|
6307
6313
|
}
|
|
6308
|
-
//
|
|
6309
|
-
return {
|
|
6314
|
+
// Can't use flexGrow — parent is not flex-x
|
|
6315
|
+
return { width: "100%" };
|
|
6310
6316
|
},
|
|
6311
6317
|
expandY: (value, { parentBoxFlow }) => {
|
|
6312
6318
|
if (!value) {
|
|
@@ -6314,15 +6320,21 @@ const DIMENSION_PROPS = {
|
|
|
6314
6320
|
}
|
|
6315
6321
|
const inVerticalFlexFlow =
|
|
6316
6322
|
parentBoxFlow === "flex-y" || parentBoxFlow === "inline-flex-y";
|
|
6317
|
-
if (
|
|
6318
|
-
//
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
return {
|
|
6323
|
+
if (inVerticalFlexFlow) {
|
|
6324
|
+
// Parent is flex-y: grow as flex item
|
|
6325
|
+
return { flexGrow: 1, flexBasis: "0%" };
|
|
6326
|
+
}
|
|
6327
|
+
if (parentBoxFlow === "flex-x" || parentBoxFlow === "inline-flex-x") {
|
|
6328
|
+
return {
|
|
6329
|
+
alignSelf: "stretch",
|
|
6330
|
+
// Here flex grow is "useless" for the item itself
|
|
6331
|
+
// buuut it would allow children (hello ".navi_text_sizer")
|
|
6332
|
+
// to inherit expand behavior
|
|
6333
|
+
flexGrow: 1,
|
|
6334
|
+
};
|
|
6323
6335
|
}
|
|
6324
|
-
//
|
|
6325
|
-
return {
|
|
6336
|
+
// Can't use flexGrow — parent is not flex-y
|
|
6337
|
+
return { height: "100%" };
|
|
6326
6338
|
},
|
|
6327
6339
|
shrinkX: (value) => {
|
|
6328
6340
|
if (!value || value === "0") {
|
|
@@ -7233,6 +7245,9 @@ const PSEUDO_CLASSES = {
|
|
|
7233
7245
|
":-navi-status-error": {
|
|
7234
7246
|
attribute: "data-status-error",
|
|
7235
7247
|
},
|
|
7248
|
+
":navi-expanded": {
|
|
7249
|
+
attribute: "data-expanded",
|
|
7250
|
+
},
|
|
7236
7251
|
};
|
|
7237
7252
|
|
|
7238
7253
|
const NAVI_PSEUDO_STATE_CUSTOM_EVENT = "navi_pseudo_state";
|
|
@@ -16474,7 +16489,7 @@ const stickCalloutToAnchor = (calloutElement, anchorElement) => {
|
|
|
16474
16489
|
}
|
|
16475
16490
|
calloutElement.setAttribute("data-position", position);
|
|
16476
16491
|
calloutStyleController.set(calloutElement, {
|
|
16477
|
-
opacity: visibilityRatio ? 1 : 0,
|
|
16492
|
+
opacity: visibilityRatio > 0 ? 1 : 0,
|
|
16478
16493
|
transform: {
|
|
16479
16494
|
translateX: calloutLeft,
|
|
16480
16495
|
translateY: calloutTop
|
|
@@ -20264,6 +20279,109 @@ const isSameKey = (browserEventKey, key) => {
|
|
|
20264
20279
|
return false;
|
|
20265
20280
|
};
|
|
20266
20281
|
|
|
20282
|
+
/**
|
|
20283
|
+
* Toggles a `data-dark-background` attribute on the referenced element based on its
|
|
20284
|
+
* computed background color. Pair it with a CSS variable to get automatic
|
|
20285
|
+
* light/dark text without hard-coding colors:
|
|
20286
|
+
*
|
|
20287
|
+
* ```css
|
|
20288
|
+
* .my-element {
|
|
20289
|
+
* --color-contrasting: black;
|
|
20290
|
+
* &[data-dark-background] {
|
|
20291
|
+
* --color-contrasting: white;
|
|
20292
|
+
* }
|
|
20293
|
+
* color: var(--color-contrasting);
|
|
20294
|
+
* }
|
|
20295
|
+
* ```
|
|
20296
|
+
*
|
|
20297
|
+
* - `data-dark-background` is **set** when the background is dark enough that white text
|
|
20298
|
+
* provides better (or equal) contrast.
|
|
20299
|
+
* - `data-dark-background` is **absent** when black text is the better choice.
|
|
20300
|
+
*
|
|
20301
|
+
* @param {import("preact").RefObject} ref - Ref to the element that receives
|
|
20302
|
+
* the `data-dark-background` attribute and is also passed to `contrastColor` for
|
|
20303
|
+
* resolving CSS variables.
|
|
20304
|
+
* @param {object} [options]
|
|
20305
|
+
* @param {string} [options.backgroundElementSelector] - CSS selector relative
|
|
20306
|
+
* to `ref.current` pointing to a child element whose `background-color`
|
|
20307
|
+
* should be tested instead of the element itself. Useful when the element
|
|
20308
|
+
* has a transparent background but contains a coloured child (e.g. a fill
|
|
20309
|
+
* bar inside a track).
|
|
20310
|
+
*/
|
|
20311
|
+
|
|
20312
|
+
const useDarkBackgroundAttribute = (
|
|
20313
|
+
ref,
|
|
20314
|
+
deps = [],
|
|
20315
|
+
{
|
|
20316
|
+
backgroundElementSelector,
|
|
20317
|
+
attributeName = "data-dark-background",
|
|
20318
|
+
hardcoded = {},
|
|
20319
|
+
} = {},
|
|
20320
|
+
) => {
|
|
20321
|
+
const innerDeps = [
|
|
20322
|
+
...deps,
|
|
20323
|
+
// ref can change is the component pass a different ref on different render based on some logic
|
|
20324
|
+
// (can be used to control which element backgroundColor is being checked by switching the ref to another element)
|
|
20325
|
+
ref,
|
|
20326
|
+
// backgroundElementSelector can change if the component pass a different selector on different render based on some logic
|
|
20327
|
+
// (can be used to control which element backgroundColor is being checked by switching the selector to point to another element)
|
|
20328
|
+
backgroundElementSelector,
|
|
20329
|
+
];
|
|
20330
|
+
|
|
20331
|
+
const hardcodedMap = new Map();
|
|
20332
|
+
for (const key of Object.keys(hardcoded)) {
|
|
20333
|
+
const value = hardcoded[key];
|
|
20334
|
+
innerDeps.push(key, value);
|
|
20335
|
+
const colorString = normalizeColorString(key);
|
|
20336
|
+
hardcodedMap.set(colorString, value);
|
|
20337
|
+
}
|
|
20338
|
+
|
|
20339
|
+
useLayoutEffect(() => {
|
|
20340
|
+
const el = ref.current;
|
|
20341
|
+
if (!el) {
|
|
20342
|
+
return undefined;
|
|
20343
|
+
}
|
|
20344
|
+
let elementToCheck = el;
|
|
20345
|
+
if (backgroundElementSelector) {
|
|
20346
|
+
elementToCheck = el.querySelector(backgroundElementSelector);
|
|
20347
|
+
if (!elementToCheck) {
|
|
20348
|
+
return undefined;
|
|
20349
|
+
}
|
|
20350
|
+
}
|
|
20351
|
+
const updateAttribute = () => {
|
|
20352
|
+
const computedStyle = getComputedStyle(elementToCheck);
|
|
20353
|
+
const backgroundColor = computedStyle.backgroundColor;
|
|
20354
|
+
if (!backgroundColor) {
|
|
20355
|
+
el.removeAttribute(attributeName);
|
|
20356
|
+
return;
|
|
20357
|
+
}
|
|
20358
|
+
const backgroundColorString = normalizeColorString(backgroundColor, el);
|
|
20359
|
+
const hardcodedContrast = hardcodedMap.get(backgroundColorString);
|
|
20360
|
+
const contrastingColor =
|
|
20361
|
+
hardcodedContrast || contrastColor(backgroundColor, el);
|
|
20362
|
+
if (contrastingColor === "white") {
|
|
20363
|
+
el.setAttribute(attributeName, "");
|
|
20364
|
+
} else {
|
|
20365
|
+
el.removeAttribute(attributeName);
|
|
20366
|
+
}
|
|
20367
|
+
};
|
|
20368
|
+
updateAttribute();
|
|
20369
|
+
el.addEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttribute);
|
|
20370
|
+
return () => {
|
|
20371
|
+
el.removeEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttribute);
|
|
20372
|
+
el.removeAttribute(attributeName);
|
|
20373
|
+
};
|
|
20374
|
+
}, innerDeps);
|
|
20375
|
+
};
|
|
20376
|
+
|
|
20377
|
+
const normalizeColorString = (color, el) => {
|
|
20378
|
+
const colorRgba = resolveCSSColor(color, el);
|
|
20379
|
+
if (!colorRgba) {
|
|
20380
|
+
return "";
|
|
20381
|
+
}
|
|
20382
|
+
return String(colorRgba);
|
|
20383
|
+
};
|
|
20384
|
+
|
|
20267
20385
|
const useInitialTextSelection = (ref, textSelection) => {
|
|
20268
20386
|
const deps = [];
|
|
20269
20387
|
if (Array.isArray(textSelection)) {
|
|
@@ -20363,8 +20481,7 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
20363
20481
|
}
|
|
20364
20482
|
};
|
|
20365
20483
|
|
|
20366
|
-
installImportMetaCssBuild(import.meta)
|
|
20367
|
-
const css$v = /* css */`
|
|
20484
|
+
installImportMetaCssBuild(import.meta);const css$v = /* css */`
|
|
20368
20485
|
@layer navi {
|
|
20369
20486
|
.navi_text {
|
|
20370
20487
|
&[data-skeleton] {
|
|
@@ -20379,23 +20496,27 @@ const css$v = /* css */`
|
|
|
20379
20496
|
.navi_text {
|
|
20380
20497
|
position: relative;
|
|
20381
20498
|
|
|
20499
|
+
&[data-dark-background] {
|
|
20500
|
+
color: white;
|
|
20501
|
+
}
|
|
20502
|
+
|
|
20382
20503
|
/* There is a chrome specific bug that prevents text-transform: capitalize to be applied in nested DOM structure */
|
|
20383
20504
|
/* The CSS below ensure capitalize is propagated to the bold clones */
|
|
20384
20505
|
&[data-capitalize] {
|
|
20385
20506
|
&::first-letter {
|
|
20386
20507
|
text-transform: uppercase;
|
|
20387
20508
|
}
|
|
20388
|
-
.
|
|
20509
|
+
.navi_text_sizer_placeholder::first-letter {
|
|
20389
20510
|
text-transform: uppercase;
|
|
20390
20511
|
}
|
|
20391
|
-
.
|
|
20512
|
+
.navi_text_sizer_overlay::first-letter {
|
|
20392
20513
|
text-transform: uppercase;
|
|
20393
20514
|
}
|
|
20394
20515
|
}
|
|
20395
20516
|
|
|
20396
|
-
.
|
|
20397
|
-
.
|
|
20398
|
-
.
|
|
20517
|
+
.navi_text_sizer,
|
|
20518
|
+
.navi_text_sizer_placeholder,
|
|
20519
|
+
.navi_text_sizer_overlay {
|
|
20399
20520
|
display: inherit;
|
|
20400
20521
|
width: inherit;
|
|
20401
20522
|
min-width: inherit;
|
|
@@ -20403,6 +20524,7 @@ const css$v = /* css */`
|
|
|
20403
20524
|
min-height: inherit;
|
|
20404
20525
|
flex-grow: inherit;
|
|
20405
20526
|
align-items: inherit;
|
|
20527
|
+
align-self: inherit;
|
|
20406
20528
|
justify-content: inherit;
|
|
20407
20529
|
gap: inherit;
|
|
20408
20530
|
text-align: inherit;
|
|
@@ -20507,15 +20629,14 @@ const css$v = /* css */`
|
|
|
20507
20629
|
}
|
|
20508
20630
|
}
|
|
20509
20631
|
|
|
20510
|
-
.
|
|
20632
|
+
.navi_text_sizer {
|
|
20511
20633
|
position: relative;
|
|
20512
20634
|
display: inline-block;
|
|
20513
20635
|
|
|
20514
|
-
.
|
|
20515
|
-
font-weight: bold;
|
|
20636
|
+
.navi_text_sizer_placeholder {
|
|
20516
20637
|
opacity: 0;
|
|
20517
20638
|
}
|
|
20518
|
-
.
|
|
20639
|
+
.navi_text_sizer_overlay {
|
|
20519
20640
|
position: absolute;
|
|
20520
20641
|
inset: 0;
|
|
20521
20642
|
}
|
|
@@ -20534,26 +20655,14 @@ const css$v = /* css */`
|
|
|
20534
20655
|
-webkit-text-fill-color: transparent;
|
|
20535
20656
|
opacity: 0;
|
|
20536
20657
|
}
|
|
20537
|
-
|
|
20658
|
+
.navi_text[data-contains-absolute-child] {
|
|
20659
|
+
display: inline-block;
|
|
20660
|
+
}
|
|
20538
20661
|
.navi_text[data-bold] {
|
|
20539
20662
|
.navi_text_bold_background {
|
|
20540
20663
|
opacity: 1;
|
|
20541
20664
|
}
|
|
20542
20665
|
}
|
|
20543
|
-
|
|
20544
|
-
.navi_text[data-bold-transition] {
|
|
20545
|
-
.navi_text_bold_foreground {
|
|
20546
|
-
transition-property: font-weight;
|
|
20547
|
-
transition-duration: 0.3s;
|
|
20548
|
-
transition-timing-function: ease;
|
|
20549
|
-
}
|
|
20550
|
-
|
|
20551
|
-
.navi_text_bold_background {
|
|
20552
|
-
transition-property: opacity;
|
|
20553
|
-
transition-duration: 0.3s;
|
|
20554
|
-
transition-timing-function: ease;
|
|
20555
|
-
}
|
|
20556
|
-
}
|
|
20557
20666
|
`;
|
|
20558
20667
|
const REGULAR_SPACE = jsx("span", {
|
|
20559
20668
|
"data-navi-space": "",
|
|
@@ -20672,6 +20781,61 @@ const shouldInjectSpacingBetween = (left, right) => {
|
|
|
20672
20781
|
return true;
|
|
20673
20782
|
};
|
|
20674
20783
|
const OverflowPinnedElementContext = createContext(null);
|
|
20784
|
+
/**
|
|
20785
|
+
* Text component for rendering inline or block text with layout-stable style changes.
|
|
20786
|
+
*
|
|
20787
|
+
* Most props are forwarded to the underlying `Box` component (as, style, bold, noWrap, …).
|
|
20788
|
+
* The props listed below are specific to Text.
|
|
20789
|
+
*
|
|
20790
|
+
* @param {object} props
|
|
20791
|
+
*
|
|
20792
|
+
* @param {boolean} [props.overflowEllipsis]
|
|
20793
|
+
* Truncates overflowing text with an ellipsis.
|
|
20794
|
+
*
|
|
20795
|
+
* @param {boolean} [props.overflowPinned]
|
|
20796
|
+
* Must be used inside a `<Text overflowEllipsis>` parent.
|
|
20797
|
+
* Pins this element outside the truncated text flow (e.g. a badge or icon).
|
|
20798
|
+
*
|
|
20799
|
+
* @param {string} [props.spacing]
|
|
20800
|
+
* Controls the separator injected between child nodes.
|
|
20801
|
+
* Accepts a size/spacing scale key, a CSS length, or `"pre"` / `0` to disable.
|
|
20802
|
+
* Defaults to a regular space character (or a padding-based fake space when
|
|
20803
|
+
* `preventSpaceUnderlines` is active).
|
|
20804
|
+
*
|
|
20805
|
+
* @param {boolean} [props.loading]
|
|
20806
|
+
* Renders a shimmer skeleton in place of the text.
|
|
20807
|
+
*
|
|
20808
|
+
* @param {boolean} [props.skeleton]
|
|
20809
|
+
* Same as `loading` but without the shimmer animation.
|
|
20810
|
+
*
|
|
20811
|
+
* @param {boolean} [props.preventSpaceUnderlines]
|
|
20812
|
+
* Replaces real space characters between children with padding-based spaces
|
|
20813
|
+
* to avoid the underline browsers draw under spaces inside links.
|
|
20814
|
+
*
|
|
20815
|
+
* @param {object} [props.holdSpaceForStyle]
|
|
20816
|
+
* Prevents layout shifts when text styles change (font-weight, font-size, …).
|
|
20817
|
+
* Pass an object of CSS-in-JS style properties representing the "maximum" state of the text.
|
|
20818
|
+
* An invisible placeholder is rendered with those styles to reserve the space,
|
|
20819
|
+
* and the real visible text is layered on top via `position: absolute`.
|
|
20820
|
+
* Only works reliably with single-line (`noWrap`) text.
|
|
20821
|
+
* Example: `holdSpaceForStyle={{ fontWeight: "bold", fontSize: "1.5rem" }}`
|
|
20822
|
+
*
|
|
20823
|
+
* @param {boolean} [props.boldStable]
|
|
20824
|
+
* Alternative to `holdSpaceForStyle` for multi-line text.
|
|
20825
|
+
* Keeps a consistent visual width regardless of font-weight by painting normal-weight
|
|
20826
|
+
* text on top of a bold background using `background-clip: text`.
|
|
20827
|
+
* Does not support font-size changes.
|
|
20828
|
+
*
|
|
20829
|
+
* @param {boolean} [props.capitalize]
|
|
20830
|
+
* Applies `text-transform: uppercase` to the first letter via CSS.
|
|
20831
|
+
*
|
|
20832
|
+
* @param {string|Array} [props.selectRange]
|
|
20833
|
+
* Selects a portion of the text on mount. Forwarded to `useInitialTextSelection`.
|
|
20834
|
+
*
|
|
20835
|
+
* @param {*} [props.childrenOutsideFlow]
|
|
20836
|
+
* Rendered after children but outside the text flow (useful for overlays
|
|
20837
|
+
* like the skeleton container).
|
|
20838
|
+
*/
|
|
20675
20839
|
const Text = props => {
|
|
20676
20840
|
import.meta.css = [css$v, "@jsenv/navi/src/text/text.jsx"];
|
|
20677
20841
|
if (props.loading || props.skeleton) {
|
|
@@ -20801,21 +20965,25 @@ const TextWithSelectRange = ({
|
|
|
20801
20965
|
const TextBasic = ({
|
|
20802
20966
|
spacing,
|
|
20803
20967
|
preventSpaceUnderlines = false,
|
|
20804
|
-
boldTransition,
|
|
20805
20968
|
boldStable,
|
|
20806
|
-
|
|
20969
|
+
holdSpaceForStyle,
|
|
20807
20970
|
capitalize,
|
|
20808
20971
|
children,
|
|
20809
20972
|
childrenOutsideFlow,
|
|
20973
|
+
basePseudoState,
|
|
20810
20974
|
...rest
|
|
20811
20975
|
}) => {
|
|
20976
|
+
const defaultRef = useRef();
|
|
20977
|
+
const ref = rest.ref || defaultRef;
|
|
20978
|
+
const bgDeps = basePseudoState ? Object.values(basePseudoState) : [];
|
|
20979
|
+
useDarkBackgroundAttribute(ref, bgDeps);
|
|
20812
20980
|
const defaultSpace = preventSpaceUnderlines ? FAKE_SPACE : REGULAR_SPACE;
|
|
20813
20981
|
const resolvedSpacing = spacing ?? defaultSpace;
|
|
20814
20982
|
const boxProps = {
|
|
20815
20983
|
"as": "span",
|
|
20816
|
-
"data-bold-transition": boldTransition ? "" : undefined,
|
|
20817
20984
|
"data-capitalize": capitalize ? "" : undefined,
|
|
20818
20985
|
...rest,
|
|
20986
|
+
ref,
|
|
20819
20987
|
"baseClassName": withPropsClassName("navi_text", rest.baseClassName)
|
|
20820
20988
|
};
|
|
20821
20989
|
const shouldPreserveSpacing = rest.as === "pre" || rest.flex || rest.grid;
|
|
@@ -20832,6 +21000,7 @@ const TextBasic = ({
|
|
|
20832
21000
|
...boxProps,
|
|
20833
21001
|
bold: undefined,
|
|
20834
21002
|
"data-bold": bold ? "" : undefined,
|
|
21003
|
+
"data-contains-absolute-child": "",
|
|
20835
21004
|
children: [jsx("span", {
|
|
20836
21005
|
className: "navi_text_bold_background",
|
|
20837
21006
|
"aria-hidden": "true",
|
|
@@ -20839,25 +21008,23 @@ const TextBasic = ({
|
|
|
20839
21008
|
}), children, childrenOutsideFlow]
|
|
20840
21009
|
});
|
|
20841
21010
|
}
|
|
20842
|
-
if (
|
|
20843
|
-
|
|
20844
|
-
|
|
20845
|
-
//
|
|
20846
|
-
//
|
|
20847
|
-
//
|
|
20848
|
-
// ne fonctionne que sur une seule ligne de texte (donc lorsque noWrap est actif)
|
|
20849
|
-
// on pourrait auto-active cela sur une prop genre boldCanChange
|
|
21011
|
+
if (holdSpaceForStyle) {
|
|
21012
|
+
// The sizer technique prevents layout shifts when styles that affect text dimensions change.
|
|
21013
|
+
// - navi_text_sizer_placeholder: invisible, rendered with holdSpaceForStyle applied so it
|
|
21014
|
+
// always occupies the "maximum" dimensions (e.g. bold + larger font-size).
|
|
21015
|
+
// - navi_text_sizer_overlay: absolutely positioned on top, renders the actual visible text
|
|
21016
|
+
// with its current style. Transitions can be applied on this element from the outside.
|
|
20850
21017
|
return jsxs(Box, {
|
|
20851
21018
|
...boxProps,
|
|
20852
21019
|
children: [jsxs("span", {
|
|
20853
|
-
className: "
|
|
21020
|
+
className: "navi_text_sizer",
|
|
20854
21021
|
children: [jsx("span", {
|
|
20855
|
-
className: "
|
|
21022
|
+
className: "navi_text_sizer_placeholder",
|
|
20856
21023
|
"aria-hidden": "true",
|
|
21024
|
+
style: holdSpaceForStyle,
|
|
20857
21025
|
children: children
|
|
20858
21026
|
}), jsx("span", {
|
|
20859
|
-
className: "
|
|
20860
|
-
"data-align": alignX,
|
|
21027
|
+
className: "navi_text_sizer_overlay",
|
|
20861
21028
|
children: children
|
|
20862
21029
|
})]
|
|
20863
21030
|
}), childrenOutsideFlow]
|
|
@@ -21153,109 +21320,6 @@ const Icon = ({
|
|
|
21153
21320
|
});
|
|
21154
21321
|
};
|
|
21155
21322
|
|
|
21156
|
-
/**
|
|
21157
|
-
* Toggles a `data-dark-background` attribute on the referenced element based on its
|
|
21158
|
-
* computed background color. Pair it with a CSS variable to get automatic
|
|
21159
|
-
* light/dark text without hard-coding colors:
|
|
21160
|
-
*
|
|
21161
|
-
* ```css
|
|
21162
|
-
* .my-element {
|
|
21163
|
-
* --color-contrasting: black;
|
|
21164
|
-
* &[data-dark-background] {
|
|
21165
|
-
* --color-contrasting: white;
|
|
21166
|
-
* }
|
|
21167
|
-
* color: var(--color-contrasting);
|
|
21168
|
-
* }
|
|
21169
|
-
* ```
|
|
21170
|
-
*
|
|
21171
|
-
* - `data-dark-background` is **set** when the background is dark enough that white text
|
|
21172
|
-
* provides better (or equal) contrast.
|
|
21173
|
-
* - `data-dark-background` is **absent** when black text is the better choice.
|
|
21174
|
-
*
|
|
21175
|
-
* @param {import("preact").RefObject} ref - Ref to the element that receives
|
|
21176
|
-
* the `data-dark-background` attribute and is also passed to `contrastColor` for
|
|
21177
|
-
* resolving CSS variables.
|
|
21178
|
-
* @param {object} [options]
|
|
21179
|
-
* @param {string} [options.backgroundElementSelector] - CSS selector relative
|
|
21180
|
-
* to `ref.current` pointing to a child element whose `background-color`
|
|
21181
|
-
* should be tested instead of the element itself. Useful when the element
|
|
21182
|
-
* has a transparent background but contains a coloured child (e.g. a fill
|
|
21183
|
-
* bar inside a track).
|
|
21184
|
-
*/
|
|
21185
|
-
|
|
21186
|
-
const useDarkBackgroundAttribute = (
|
|
21187
|
-
ref,
|
|
21188
|
-
deps = [],
|
|
21189
|
-
{
|
|
21190
|
-
backgroundElementSelector,
|
|
21191
|
-
attributeName = "data-dark-background",
|
|
21192
|
-
hardcoded = {},
|
|
21193
|
-
} = {},
|
|
21194
|
-
) => {
|
|
21195
|
-
const innerDeps = [
|
|
21196
|
-
...deps,
|
|
21197
|
-
// ref can change is the component pass a different ref on different render based on some logic
|
|
21198
|
-
// (can be used to control which element backgroundColor is being checked by switching the ref to another element)
|
|
21199
|
-
ref,
|
|
21200
|
-
// backgroundElementSelector can change if the component pass a different selector on different render based on some logic
|
|
21201
|
-
// (can be used to control which element backgroundColor is being checked by switching the selector to point to another element)
|
|
21202
|
-
backgroundElementSelector,
|
|
21203
|
-
];
|
|
21204
|
-
|
|
21205
|
-
const hardcodedMap = new Map();
|
|
21206
|
-
for (const key of Object.keys(hardcoded)) {
|
|
21207
|
-
const value = hardcoded[key];
|
|
21208
|
-
innerDeps.push(key, value);
|
|
21209
|
-
const colorString = normalizeColorString(key);
|
|
21210
|
-
hardcodedMap.set(colorString, value);
|
|
21211
|
-
}
|
|
21212
|
-
|
|
21213
|
-
useLayoutEffect(() => {
|
|
21214
|
-
const el = ref.current;
|
|
21215
|
-
if (!el) {
|
|
21216
|
-
return undefined;
|
|
21217
|
-
}
|
|
21218
|
-
let elementToCheck = el;
|
|
21219
|
-
if (backgroundElementSelector) {
|
|
21220
|
-
elementToCheck = el.querySelector(backgroundElementSelector);
|
|
21221
|
-
if (!elementToCheck) {
|
|
21222
|
-
return undefined;
|
|
21223
|
-
}
|
|
21224
|
-
}
|
|
21225
|
-
const updateAttribute = () => {
|
|
21226
|
-
const computedStyle = getComputedStyle(elementToCheck);
|
|
21227
|
-
const backgroundColor = computedStyle.backgroundColor;
|
|
21228
|
-
if (!backgroundColor) {
|
|
21229
|
-
el.removeAttribute(attributeName);
|
|
21230
|
-
return;
|
|
21231
|
-
}
|
|
21232
|
-
const backgroundColorString = normalizeColorString(backgroundColor, el);
|
|
21233
|
-
const hardcodedContrast = hardcodedMap.get(backgroundColorString);
|
|
21234
|
-
const contrastingColor =
|
|
21235
|
-
hardcodedContrast || contrastColor(backgroundColor, el);
|
|
21236
|
-
if (contrastingColor === "white") {
|
|
21237
|
-
el.setAttribute(attributeName, "");
|
|
21238
|
-
} else {
|
|
21239
|
-
el.removeAttribute(attributeName);
|
|
21240
|
-
}
|
|
21241
|
-
};
|
|
21242
|
-
updateAttribute();
|
|
21243
|
-
el.addEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttribute);
|
|
21244
|
-
return () => {
|
|
21245
|
-
el.removeEventListener(NAVI_PSEUDO_STATE_CUSTOM_EVENT, updateAttribute);
|
|
21246
|
-
el.removeAttribute(attributeName);
|
|
21247
|
-
};
|
|
21248
|
-
}, innerDeps);
|
|
21249
|
-
};
|
|
21250
|
-
|
|
21251
|
-
const normalizeColorString = (color, el) => {
|
|
21252
|
-
const colorRgba = resolveCSSColor(color, el);
|
|
21253
|
-
if (!colorRgba) {
|
|
21254
|
-
return "";
|
|
21255
|
-
}
|
|
21256
|
-
return String(colorRgba);
|
|
21257
|
-
};
|
|
21258
|
-
|
|
21259
21323
|
const useFormEvents = (
|
|
21260
21324
|
elementRef,
|
|
21261
21325
|
{
|
|
@@ -21777,8 +21841,7 @@ const useUIState = (uiStateController) => {
|
|
|
21777
21841
|
return trackedUIState;
|
|
21778
21842
|
};
|
|
21779
21843
|
|
|
21780
|
-
installImportMetaCssBuild(import.meta)
|
|
21781
|
-
const css$s = /* css */`
|
|
21844
|
+
installImportMetaCssBuild(import.meta);const css$s = /* css */`
|
|
21782
21845
|
@layer navi {
|
|
21783
21846
|
.navi_button {
|
|
21784
21847
|
--button-outline-width: 1px;
|
|
@@ -22575,8 +22638,7 @@ const useDimColorWhen = (elementRef, shouldDim) => {
|
|
|
22575
22638
|
});
|
|
22576
22639
|
};
|
|
22577
22640
|
|
|
22578
|
-
installImportMetaCssBuild(import.meta)
|
|
22579
|
-
const css$r = /* css */`
|
|
22641
|
+
installImportMetaCssBuild(import.meta);const css$r = /* css */`
|
|
22580
22642
|
@layer navi {
|
|
22581
22643
|
.navi_link {
|
|
22582
22644
|
--link-border-radius: unset;
|
|
@@ -22722,7 +22784,7 @@ const css$r = /* css */`
|
|
|
22722
22784
|
}
|
|
22723
22785
|
|
|
22724
22786
|
/* Dark background */
|
|
22725
|
-
&[data-dark-background] {
|
|
22787
|
+
&[data-dark-background].navi_text {
|
|
22726
22788
|
--x-link-contrasting-color: white;
|
|
22727
22789
|
--x-link-color: var(--link-color, white);
|
|
22728
22790
|
}
|
|
@@ -23028,7 +23090,6 @@ const LinkPlain = props => {
|
|
|
23028
23090
|
isCurrent
|
|
23029
23091
|
} = getHrefTargetInfo(href);
|
|
23030
23092
|
const innerCurrent = current || isCurrent;
|
|
23031
|
-
useDarkBackgroundAttribute(ref, [selected, innerCurrent], {});
|
|
23032
23093
|
const innerTarget = target === undefined ? isSameSite ? "_self" : "_blank" : target;
|
|
23033
23094
|
const innerRel = rel === undefined ? isSameSite ? undefined : "noopener noreferrer" : rel;
|
|
23034
23095
|
let innerEndIcon;
|
|
@@ -23081,7 +23142,9 @@ const LinkPlain = props => {
|
|
|
23081
23142
|
onnavi_value: e => {
|
|
23082
23143
|
e.detail.setValue(value);
|
|
23083
23144
|
},
|
|
23084
|
-
|
|
23145
|
+
holdSpaceForStyle: currentEffectBold ? {
|
|
23146
|
+
fontWeight: "bold"
|
|
23147
|
+
} : undefined,
|
|
23085
23148
|
preventSpaceUnderlines: true,
|
|
23086
23149
|
overflowEllipsis: overflowEllipsis
|
|
23087
23150
|
// Visual
|
|
@@ -25686,6 +25749,16 @@ const InputRangeWithAction = props => {
|
|
|
25686
25749
|
});
|
|
25687
25750
|
};
|
|
25688
25751
|
|
|
25752
|
+
const ChevronDownSvg = () => {
|
|
25753
|
+
return jsx("svg", {
|
|
25754
|
+
viewBox: "0 0 16 16",
|
|
25755
|
+
fill: "currentColor",
|
|
25756
|
+
children: jsx("path", {
|
|
25757
|
+
d: "M4.427 7.427l3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427z"
|
|
25758
|
+
})
|
|
25759
|
+
});
|
|
25760
|
+
};
|
|
25761
|
+
|
|
25689
25762
|
const SearchSvg = () => jsx("svg", {
|
|
25690
25763
|
viewBox: "0 0 24 24",
|
|
25691
25764
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -25695,7 +25768,23 @@ const SearchSvg = () => jsx("svg", {
|
|
|
25695
25768
|
})
|
|
25696
25769
|
});
|
|
25697
25770
|
|
|
25698
|
-
installImportMetaCssBuild(import.meta)
|
|
25771
|
+
installImportMetaCssBuild(import.meta);/**
|
|
25772
|
+
* Input component for all textual input types.
|
|
25773
|
+
*
|
|
25774
|
+
* Supports:
|
|
25775
|
+
* - text (default)
|
|
25776
|
+
* - password
|
|
25777
|
+
* - hidden
|
|
25778
|
+
* - email
|
|
25779
|
+
* - url
|
|
25780
|
+
* - search
|
|
25781
|
+
* - tel
|
|
25782
|
+
* - etc.
|
|
25783
|
+
*
|
|
25784
|
+
* For non-textual inputs, specialized components will be used:
|
|
25785
|
+
* - <InputCheckbox /> for type="checkbox"
|
|
25786
|
+
* - <InputRadio /> for type="radio"
|
|
25787
|
+
*/
|
|
25699
25788
|
const css$j = /* css */`
|
|
25700
25789
|
@layer navi {
|
|
25701
25790
|
.navi_input {
|
|
@@ -25754,8 +25843,8 @@ const css$j = /* css */`
|
|
|
25754
25843
|
border-radius: inherit;
|
|
25755
25844
|
cursor: inherit;
|
|
25756
25845
|
|
|
25757
|
-
--
|
|
25758
|
-
--
|
|
25846
|
+
--left-slot-size: 0px;
|
|
25847
|
+
--right-slot-size: 0px;
|
|
25759
25848
|
--x-outline-width: var(--outline-width);
|
|
25760
25849
|
--x-border-radius: var(--border-radius);
|
|
25761
25850
|
--x-border-width: var(--border-width);
|
|
@@ -25786,9 +25875,9 @@ const css$j = /* css */`
|
|
|
25786
25875
|
.navi_native_input {
|
|
25787
25876
|
box-sizing: border-box;
|
|
25788
25877
|
padding-top: var(--x-padding-top-base);
|
|
25789
|
-
padding-right: calc(var(--x-padding-right-base) + var(--
|
|
25878
|
+
padding-right: calc(var(--x-padding-right-base) + var(--right-slot-size));
|
|
25790
25879
|
padding-bottom: var(--x-padding-bottom-base);
|
|
25791
|
-
padding-left: calc(var(--x-padding-left-base) + var(--
|
|
25880
|
+
padding-left: calc(var(--x-padding-left-base) + var(--left-slot-size));
|
|
25792
25881
|
color: var(--x-color);
|
|
25793
25882
|
font-size: var(--font-size);
|
|
25794
25883
|
background-color: var(--x-background-color);
|
|
@@ -25811,17 +25900,9 @@ const css$j = /* css */`
|
|
|
25811
25900
|
}
|
|
25812
25901
|
}
|
|
25813
25902
|
|
|
25814
|
-
.
|
|
25815
|
-
position: absolute;
|
|
25816
|
-
top: 0;
|
|
25817
|
-
bottom: 0;
|
|
25818
|
-
left: var(--x-padding-left-base);
|
|
25819
|
-
font-size: var(--font-size);
|
|
25820
|
-
}
|
|
25821
|
-
.navi_input_end_button {
|
|
25903
|
+
.navi_input_slot {
|
|
25822
25904
|
position: absolute;
|
|
25823
25905
|
top: 0;
|
|
25824
|
-
right: var(--x-padding-right-base);
|
|
25825
25906
|
bottom: 0;
|
|
25826
25907
|
display: inline-flex;
|
|
25827
25908
|
margin: 0;
|
|
@@ -25831,34 +25912,43 @@ const css$j = /* css */`
|
|
|
25831
25912
|
font-size: var(--font-size);
|
|
25832
25913
|
background: none;
|
|
25833
25914
|
border: none;
|
|
25834
|
-
|
|
25835
|
-
|
|
25915
|
+
|
|
25916
|
+
&[data-left] {
|
|
25917
|
+
left: var(--x-padding-left-base);
|
|
25918
|
+
}
|
|
25919
|
+
&[data-right] {
|
|
25920
|
+
right: var(--x-padding-right-base);
|
|
25921
|
+
}
|
|
25922
|
+
&[data-hide-while-empty] {
|
|
25923
|
+
opacity: 0;
|
|
25924
|
+
pointer-events: none;
|
|
25925
|
+
}
|
|
25836
25926
|
}
|
|
25837
25927
|
&[data-has-value] {
|
|
25838
|
-
.
|
|
25928
|
+
.navi_input_slot[data-hide-while-empty] {
|
|
25839
25929
|
opacity: 1;
|
|
25840
25930
|
cursor: pointer;
|
|
25841
25931
|
pointer-events: auto;
|
|
25842
25932
|
}
|
|
25843
25933
|
|
|
25844
25934
|
&[data-readonly] {
|
|
25845
|
-
.
|
|
25935
|
+
.navi_input_slot[data-hide-while-empty] {
|
|
25846
25936
|
opacity: 0;
|
|
25847
25937
|
pointer-events: none;
|
|
25848
25938
|
}
|
|
25849
25939
|
}
|
|
25850
25940
|
&[data-disabled] {
|
|
25851
|
-
.
|
|
25941
|
+
.navi_input_slot[data-hide-while-empty] {
|
|
25852
25942
|
opacity: 0;
|
|
25853
25943
|
pointer-events: none;
|
|
25854
25944
|
}
|
|
25855
25945
|
}
|
|
25856
25946
|
}
|
|
25857
|
-
|
|
25858
|
-
--
|
|
25947
|
+
&:has(.navi_input_slot[data-left]) {
|
|
25948
|
+
--left-slot-size: 1em;
|
|
25859
25949
|
}
|
|
25860
|
-
|
|
25861
|
-
--
|
|
25950
|
+
&:has(.navi_input_slot[data-right]) {
|
|
25951
|
+
--right-slot-size: 1em;
|
|
25862
25952
|
}
|
|
25863
25953
|
|
|
25864
25954
|
/* Hover */
|
|
@@ -25964,7 +26054,7 @@ const InputStyleCSSVars = {
|
|
|
25964
26054
|
color: "--color-disabled"
|
|
25965
26055
|
}
|
|
25966
26056
|
};
|
|
25967
|
-
const InputPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":navi-has-value"];
|
|
26057
|
+
const InputPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":navi-has-value", ":navi-expanded"];
|
|
25968
26058
|
Object.assign(PSEUDO_CLASSES, {
|
|
25969
26059
|
":navi-has-value": {
|
|
25970
26060
|
attribute: "data-has-value",
|
|
@@ -25981,6 +26071,55 @@ Object.assign(PSEUDO_CLASSES, {
|
|
|
25981
26071
|
});
|
|
25982
26072
|
const InputPseudoElements = ["::-navi-loader"];
|
|
25983
26073
|
const InputChildPropSet = new Set([...fieldPropSet]);
|
|
26074
|
+
const InputNativeContext = createContext(null);
|
|
26075
|
+
const InputSlot = ({
|
|
26076
|
+
side,
|
|
26077
|
+
onClick,
|
|
26078
|
+
hideWhileEmpty,
|
|
26079
|
+
...props
|
|
26080
|
+
}) => {
|
|
26081
|
+
const ctx = useContext(InputNativeContext);
|
|
26082
|
+
const {
|
|
26083
|
+
id,
|
|
26084
|
+
readOnly,
|
|
26085
|
+
disabled
|
|
26086
|
+
} = ctx;
|
|
26087
|
+
return jsx(Label, {
|
|
26088
|
+
htmlFor: id,
|
|
26089
|
+
className: "navi_input_slot",
|
|
26090
|
+
disabled: disabled,
|
|
26091
|
+
readOnly: readOnly,
|
|
26092
|
+
"data-readonly": readOnly,
|
|
26093
|
+
"data-disabled": disabled,
|
|
26094
|
+
"data-left": side === "left" ? "" : undefined,
|
|
26095
|
+
"data-right": side === "right" ? "" : undefined,
|
|
26096
|
+
"data-hide-while-empty": hideWhileEmpty ? "" : undefined,
|
|
26097
|
+
flex: true,
|
|
26098
|
+
alignY: "center",
|
|
26099
|
+
onMouseDown: e => {
|
|
26100
|
+
e.preventDefault(); // keep focus in the input
|
|
26101
|
+
},
|
|
26102
|
+
onClick: e => {
|
|
26103
|
+
if (readOnly || disabled) {
|
|
26104
|
+
return;
|
|
26105
|
+
}
|
|
26106
|
+
onClick?.(e);
|
|
26107
|
+
},
|
|
26108
|
+
...props
|
|
26109
|
+
});
|
|
26110
|
+
};
|
|
26111
|
+
const InputLeftSlot = props => {
|
|
26112
|
+
return jsx(InputSlot, {
|
|
26113
|
+
...props,
|
|
26114
|
+
side: "left"
|
|
26115
|
+
});
|
|
26116
|
+
};
|
|
26117
|
+
const InputRightSlot = props => {
|
|
26118
|
+
return jsx(InputSlot, {
|
|
26119
|
+
...props,
|
|
26120
|
+
side: "right"
|
|
26121
|
+
});
|
|
26122
|
+
};
|
|
25984
26123
|
const InputTextualBasic = props => {
|
|
25985
26124
|
if (props.suggestions) {
|
|
25986
26125
|
return jsx(InputTextualWithSuggestions, {
|
|
@@ -25996,54 +26135,48 @@ const InputTextualWithSuggestions = ({
|
|
|
25996
26135
|
onInput,
|
|
25997
26136
|
onFocus,
|
|
25998
26137
|
onBlur,
|
|
26138
|
+
children,
|
|
25999
26139
|
...rest
|
|
26000
26140
|
}) => {
|
|
26001
26141
|
const defaultRef = useRef();
|
|
26002
26142
|
const ref = rest.ref || defaultRef;
|
|
26003
|
-
const [
|
|
26004
|
-
const
|
|
26005
|
-
|
|
26006
|
-
const
|
|
26007
|
-
|
|
26143
|
+
const [expanded, setExpanded] = useState(false);
|
|
26144
|
+
const expandedRef = useRef(expanded);
|
|
26145
|
+
expandedRef.current = expanded;
|
|
26146
|
+
const expand = () => {
|
|
26147
|
+
expandedRef.current = true;
|
|
26148
|
+
setExpanded(true);
|
|
26149
|
+
};
|
|
26150
|
+
const collapse = () => {
|
|
26151
|
+
expandedRef.current = false;
|
|
26152
|
+
setExpanded(false);
|
|
26153
|
+
};
|
|
26154
|
+
const showSuggestions = e => {
|
|
26155
|
+
if (expandedRef.current) {
|
|
26008
26156
|
return;
|
|
26009
26157
|
}
|
|
26010
26158
|
console.debug(`showPopover (e.type:${e.type})`);
|
|
26011
26159
|
const popoverEl = document.getElementById(suggestions);
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
26016
|
-
|
|
26017
|
-
|
|
26018
|
-
|
|
26019
|
-
});
|
|
26160
|
+
if (!popoverEl) {
|
|
26161
|
+
return;
|
|
26162
|
+
}
|
|
26163
|
+
popoverEl.dispatchEvent(new CustomEvent("navi_suggestion_list_open", {
|
|
26164
|
+
detail: {
|
|
26165
|
+
anchor: ref.current
|
|
26166
|
+
}
|
|
26167
|
+
}));
|
|
26168
|
+
expand();
|
|
26020
26169
|
};
|
|
26021
|
-
const
|
|
26022
|
-
if (!
|
|
26170
|
+
const hideSuggestions = e => {
|
|
26171
|
+
if (!expandedRef.current) {
|
|
26023
26172
|
return;
|
|
26024
26173
|
}
|
|
26025
26174
|
console.debug(`hidePopover (e.type:${e.type})`);
|
|
26026
|
-
suggestionsOpenRef.current = false;
|
|
26027
|
-
setSuggestionsOpen(false);
|
|
26028
|
-
window.removeEventListener("scroll", positionPopover, {
|
|
26029
|
-
capture: true
|
|
26030
|
-
});
|
|
26031
|
-
const popoverEl = document.getElementById(suggestions);
|
|
26032
|
-
if (popoverEl) {
|
|
26033
|
-
popoverEl.dispatchEvent(new CustomEvent("navi_suggestion_list_clear"));
|
|
26034
|
-
popoverEl.hidePopover();
|
|
26035
|
-
}
|
|
26036
|
-
setSuggestionsOpen(false);
|
|
26037
|
-
};
|
|
26038
|
-
const positionPopover = () => {
|
|
26039
|
-
const input = ref.current;
|
|
26040
|
-
const rect = input.getBoundingClientRect();
|
|
26041
26175
|
const popoverEl = document.getElementById(suggestions);
|
|
26042
26176
|
if (popoverEl) {
|
|
26043
|
-
popoverEl.
|
|
26044
|
-
popoverEl.style.left = `${rect.left}px`;
|
|
26045
|
-
popoverEl.style.width = `${rect.width}px`;
|
|
26177
|
+
popoverEl.dispatchEvent(new CustomEvent("navi_suggestion_list_close"));
|
|
26046
26178
|
}
|
|
26179
|
+
collapse();
|
|
26047
26180
|
};
|
|
26048
26181
|
const dispatchToSuggestionList = customEvent => {
|
|
26049
26182
|
const popoverEl = document.getElementById(suggestions);
|
|
@@ -26057,7 +26190,7 @@ const InputTextualWithSuggestions = ({
|
|
|
26057
26190
|
key: "arrowdown",
|
|
26058
26191
|
description: "Open popover and point to next suggestion",
|
|
26059
26192
|
handler: e => {
|
|
26060
|
-
|
|
26193
|
+
showSuggestions(e);
|
|
26061
26194
|
const popoverEl = document.getElementById(suggestions);
|
|
26062
26195
|
if (!popoverEl) {
|
|
26063
26196
|
return false;
|
|
@@ -26073,7 +26206,7 @@ const InputTextualWithSuggestions = ({
|
|
|
26073
26206
|
key: "arrowup",
|
|
26074
26207
|
description: "Open popover and point to previous suggestion",
|
|
26075
26208
|
handler: e => {
|
|
26076
|
-
|
|
26209
|
+
showSuggestions(e);
|
|
26077
26210
|
return dispatchToSuggestionList(new CustomEvent("navi_suggestion_list_navigate", {
|
|
26078
26211
|
detail: {
|
|
26079
26212
|
direction: "up"
|
|
@@ -26084,7 +26217,7 @@ const InputTextualWithSuggestions = ({
|
|
|
26084
26217
|
key: "home",
|
|
26085
26218
|
description: "Point to first suggestion",
|
|
26086
26219
|
handler: () => {
|
|
26087
|
-
if (!
|
|
26220
|
+
if (!expandedRef.current) {
|
|
26088
26221
|
return false;
|
|
26089
26222
|
}
|
|
26090
26223
|
return dispatchToSuggestionList(new CustomEvent("navi_suggestion_list_navigate", {
|
|
@@ -26097,7 +26230,7 @@ const InputTextualWithSuggestions = ({
|
|
|
26097
26230
|
key: "end",
|
|
26098
26231
|
description: "Point to last suggestion",
|
|
26099
26232
|
handler: () => {
|
|
26100
|
-
if (!
|
|
26233
|
+
if (!expandedRef.current) {
|
|
26101
26234
|
return false;
|
|
26102
26235
|
}
|
|
26103
26236
|
return dispatchToSuggestionList(new CustomEvent("navi_suggestion_list_navigate", {
|
|
@@ -26110,7 +26243,7 @@ const InputTextualWithSuggestions = ({
|
|
|
26110
26243
|
key: "enter",
|
|
26111
26244
|
description: "Confirm pointed suggestion",
|
|
26112
26245
|
handler: () => {
|
|
26113
|
-
if (!
|
|
26246
|
+
if (!expandedRef.current) {
|
|
26114
26247
|
return false;
|
|
26115
26248
|
}
|
|
26116
26249
|
return dispatchToSuggestionList(new CustomEvent("navi_suggestion_list_confirm", {
|
|
@@ -26121,10 +26254,10 @@ const InputTextualWithSuggestions = ({
|
|
|
26121
26254
|
key: "escape",
|
|
26122
26255
|
description: "Close popover",
|
|
26123
26256
|
handler: e => {
|
|
26124
|
-
if (!
|
|
26257
|
+
if (!expandedRef.current) {
|
|
26125
26258
|
return false;
|
|
26126
26259
|
}
|
|
26127
|
-
|
|
26260
|
+
hideSuggestions(e);
|
|
26128
26261
|
return true;
|
|
26129
26262
|
}
|
|
26130
26263
|
}]);
|
|
@@ -26139,7 +26272,7 @@ const InputTextualWithSuggestions = ({
|
|
|
26139
26272
|
inputEl.dispatchEvent(new Event("input", {
|
|
26140
26273
|
bubbles: true
|
|
26141
26274
|
}));
|
|
26142
|
-
|
|
26275
|
+
hideSuggestions(e);
|
|
26143
26276
|
};
|
|
26144
26277
|
popoverEl.addEventListener("navi_suggestion_list_selected", onSelected);
|
|
26145
26278
|
return () => {
|
|
@@ -26152,24 +26285,40 @@ const InputTextualWithSuggestions = ({
|
|
|
26152
26285
|
autoComplete: "off",
|
|
26153
26286
|
"aria-controls": suggestions,
|
|
26154
26287
|
"aria-haspopup": "listbox",
|
|
26155
|
-
"aria-expanded":
|
|
26288
|
+
"aria-expanded": expanded,
|
|
26156
26289
|
"aria-autocomplete": "list",
|
|
26290
|
+
basePseudoState: {
|
|
26291
|
+
":navi-expanded": expanded
|
|
26292
|
+
},
|
|
26157
26293
|
onnavi_callout_open: e => {
|
|
26158
|
-
|
|
26294
|
+
hideSuggestions(e);
|
|
26159
26295
|
},
|
|
26160
26296
|
onFocus: e => {
|
|
26161
26297
|
onFocus?.(e);
|
|
26162
|
-
|
|
26298
|
+
showSuggestions(e);
|
|
26163
26299
|
},
|
|
26164
26300
|
onBlur: e => {
|
|
26165
26301
|
onBlur?.(e);
|
|
26166
|
-
|
|
26302
|
+
hideSuggestions(e);
|
|
26167
26303
|
},
|
|
26168
26304
|
onInput: e => {
|
|
26169
26305
|
onInput?.(e);
|
|
26170
|
-
|
|
26306
|
+
showSuggestions(e);
|
|
26171
26307
|
},
|
|
26172
|
-
...rest
|
|
26308
|
+
...rest,
|
|
26309
|
+
children: children || jsx(InputRightSlot, {
|
|
26310
|
+
onClick: e => {
|
|
26311
|
+
if (expanded) {
|
|
26312
|
+
hideSuggestions(e);
|
|
26313
|
+
} else {
|
|
26314
|
+
showSuggestions(e);
|
|
26315
|
+
}
|
|
26316
|
+
},
|
|
26317
|
+
children: jsx(Icon, {
|
|
26318
|
+
color: "rgba(28, 43, 52, 0.5)",
|
|
26319
|
+
children: jsx(ChevronDownSvg, {})
|
|
26320
|
+
})
|
|
26321
|
+
})
|
|
26173
26322
|
});
|
|
26174
26323
|
};
|
|
26175
26324
|
const InputTextualPlain = props => {
|
|
@@ -26188,8 +26337,8 @@ const InputTextualPlain = props => {
|
|
|
26188
26337
|
autoFocus,
|
|
26189
26338
|
autoFocusVisible,
|
|
26190
26339
|
autoSelect,
|
|
26191
|
-
|
|
26192
|
-
|
|
26340
|
+
basePseudoState,
|
|
26341
|
+
children,
|
|
26193
26342
|
...rest
|
|
26194
26343
|
} = props;
|
|
26195
26344
|
const defaultRef = useRef();
|
|
@@ -26248,17 +26397,45 @@ const InputTextualPlain = props => {
|
|
|
26248
26397
|
});
|
|
26249
26398
|
};
|
|
26250
26399
|
const renderInputMemoized = useCallback(renderInput, [type, uiState, innerValue, innerOnInput, innerId]);
|
|
26251
|
-
let
|
|
26252
|
-
if (
|
|
26253
|
-
|
|
26254
|
-
|
|
26255
|
-
|
|
26256
|
-
|
|
26257
|
-
|
|
26258
|
-
|
|
26259
|
-
|
|
26260
|
-
|
|
26261
|
-
|
|
26400
|
+
let innerChildren;
|
|
26401
|
+
if (children) {
|
|
26402
|
+
innerChildren = children;
|
|
26403
|
+
} else if (type === "search") {
|
|
26404
|
+
innerChildren = jsxs(Fragment, {
|
|
26405
|
+
children: [jsx(InputLeftSlot, {
|
|
26406
|
+
children: jsx(Icon, {
|
|
26407
|
+
color: "rgba(28, 43, 52, 0.5)",
|
|
26408
|
+
children: jsx(SearchSvg, {})
|
|
26409
|
+
})
|
|
26410
|
+
}), jsx(InputRightSlot, {
|
|
26411
|
+
hideWhileEmpty: true,
|
|
26412
|
+
onClick: () => {
|
|
26413
|
+
uiStateController.setUIState("", {
|
|
26414
|
+
trigger: "cancel_button"
|
|
26415
|
+
});
|
|
26416
|
+
ref.current.value = "";
|
|
26417
|
+
ref.current.dispatchEvent(new Event("navi_delete_content"));
|
|
26418
|
+
},
|
|
26419
|
+
children: jsx(Icon, {
|
|
26420
|
+
color: "rgba(28, 43, 52, 0.5)",
|
|
26421
|
+
children: jsx(CloseSvg, {})
|
|
26422
|
+
})
|
|
26423
|
+
})]
|
|
26424
|
+
});
|
|
26425
|
+
} else if (type === "email") {
|
|
26426
|
+
innerChildren = jsx(InputLeftSlot, {
|
|
26427
|
+
children: jsx(Icon, {
|
|
26428
|
+
color: "rgba(28, 43, 52, 0.5)",
|
|
26429
|
+
children: jsx(EmailSvg, {})
|
|
26430
|
+
})
|
|
26431
|
+
});
|
|
26432
|
+
} else if (type === "tel") {
|
|
26433
|
+
innerChildren = jsx(InputLeftSlot, {
|
|
26434
|
+
children: jsx(Icon, {
|
|
26435
|
+
color: "rgba(28, 43, 52, 0.5)",
|
|
26436
|
+
children: jsx(PhoneSvg, {})
|
|
26437
|
+
})
|
|
26438
|
+
});
|
|
26262
26439
|
}
|
|
26263
26440
|
return jsxs(Box, {
|
|
26264
26441
|
as: "span",
|
|
@@ -26268,6 +26445,7 @@ const InputTextualPlain = props => {
|
|
|
26268
26445
|
pseudoStateSelector: ".navi_native_input",
|
|
26269
26446
|
visualSelector: ".navi_native_input",
|
|
26270
26447
|
basePseudoState: {
|
|
26448
|
+
...basePseudoState,
|
|
26271
26449
|
":read-only": innerReadOnly,
|
|
26272
26450
|
":disabled": innerDisabled,
|
|
26273
26451
|
":-navi-loading": innerLoading
|
|
@@ -26276,48 +26454,20 @@ const InputTextualPlain = props => {
|
|
|
26276
26454
|
pseudoElements: InputPseudoElements,
|
|
26277
26455
|
hasChildFunction: true,
|
|
26278
26456
|
baseChildPropSet: InputChildPropSet,
|
|
26279
|
-
"data-start-icon": innerIcon ? "" : undefined,
|
|
26280
|
-
"data-end-icon": cancelButton ? "" : undefined,
|
|
26281
26457
|
...remainingProps,
|
|
26282
26458
|
ref: undefined,
|
|
26283
26459
|
children: [jsx(LoaderBackground, {
|
|
26284
26460
|
loading: innerLoading,
|
|
26285
26461
|
color: "var(--loader-color)",
|
|
26286
26462
|
inset: -1
|
|
26287
|
-
}),
|
|
26288
|
-
|
|
26289
|
-
|
|
26290
|
-
|
|
26291
|
-
|
|
26292
|
-
flex: true,
|
|
26293
|
-
alignY: "center",
|
|
26294
|
-
children: jsx(Icon, {
|
|
26295
|
-
color: "rgba(28, 43, 52, 0.5)",
|
|
26296
|
-
children: innerIcon
|
|
26297
|
-
})
|
|
26298
|
-
}), renderInputMemoized, cancelButton && jsx("label", {
|
|
26299
|
-
htmlFor: innerId,
|
|
26300
|
-
"data-readonly": innerReadOnly ? "" : undefined,
|
|
26301
|
-
"data-disabled": innerDisabled ? "" : undefined,
|
|
26302
|
-
className: "navi_input_end_button",
|
|
26303
|
-
onMouseDown: e => {
|
|
26304
|
-
e.preventDefault(); // keep focus in the input
|
|
26305
|
-
},
|
|
26306
|
-
onClick: () => {
|
|
26307
|
-
if (innerReadOnly || innerDisabled) {
|
|
26308
|
-
return;
|
|
26309
|
-
}
|
|
26310
|
-
uiStateController.setUIState("", {
|
|
26311
|
-
trigger: "cancel_button"
|
|
26312
|
-
});
|
|
26313
|
-
ref.current.value = "";
|
|
26314
|
-
ref.current.dispatchEvent(new Event("navi_delete_content"));
|
|
26463
|
+
}), renderInputMemoized, innerChildren ? jsx(InputNativeContext.Provider, {
|
|
26464
|
+
value: {
|
|
26465
|
+
id: innerId,
|
|
26466
|
+
readOnly: innerReadOnly,
|
|
26467
|
+
disabled: innerDisabled
|
|
26315
26468
|
},
|
|
26316
|
-
children:
|
|
26317
|
-
|
|
26318
|
-
children: jsx(CloseSvg, {})
|
|
26319
|
-
})
|
|
26320
|
-
})]
|
|
26469
|
+
children: innerChildren
|
|
26470
|
+
}) : null]
|
|
26321
26471
|
});
|
|
26322
26472
|
};
|
|
26323
26473
|
const InputTextualWithAction = props => {
|
|
@@ -27045,553 +27195,32 @@ const Group = ({
|
|
|
27045
27195
|
});
|
|
27046
27196
|
};
|
|
27047
27197
|
|
|
27048
|
-
const
|
|
27049
|
-
const
|
|
27050
|
-
|
|
27051
|
-
|
|
27052
|
-
|
|
27053
|
-
|
|
27054
|
-
|
|
27055
|
-
|
|
27056
|
-
|
|
27057
|
-
}) => {
|
|
27058
|
-
// Reset on each render to start fresh
|
|
27059
|
-
tracker.reset();
|
|
27060
|
-
return jsx(ItemTrackerContext.Provider, {
|
|
27061
|
-
value: tracker,
|
|
27062
|
-
children: children
|
|
27063
|
-
});
|
|
27064
|
-
};
|
|
27065
|
-
ItemTrackerProvider.items = items;
|
|
27066
|
-
return {
|
|
27067
|
-
ItemTrackerProvider,
|
|
27068
|
-
items,
|
|
27069
|
-
registerItem: data => {
|
|
27070
|
-
const index = itemCountRef.current++;
|
|
27071
|
-
items[index] = data;
|
|
27072
|
-
return index;
|
|
27073
|
-
},
|
|
27074
|
-
getItem: index => {
|
|
27075
|
-
return items[index];
|
|
27076
|
-
},
|
|
27077
|
-
getAllItems: () => {
|
|
27078
|
-
return items;
|
|
27079
|
-
},
|
|
27080
|
-
reset: () => {
|
|
27081
|
-
items.length = 0;
|
|
27082
|
-
itemCountRef.current = 0;
|
|
27198
|
+
const RadioList = props => {
|
|
27199
|
+
const uiStateController = useUIGroupStateController(props, "radio_list", {
|
|
27200
|
+
childComponentType: "radio",
|
|
27201
|
+
aggregateChildStates: childUIStateControllers => {
|
|
27202
|
+
let activeValue;
|
|
27203
|
+
for (const childUIStateController of childUIStateControllers) {
|
|
27204
|
+
if (childUIStateController.uiState) {
|
|
27205
|
+
activeValue = childUIStateController.uiState;
|
|
27206
|
+
break;
|
|
27083
27207
|
}
|
|
27084
|
-
}
|
|
27085
|
-
|
|
27086
|
-
return tracker.ItemTrackerProvider;
|
|
27087
|
-
};
|
|
27088
|
-
const useTrackItem = data => {
|
|
27089
|
-
const tracker = useContext(ItemTrackerContext);
|
|
27090
|
-
if (!tracker) {
|
|
27091
|
-
throw new Error("useTrackItem must be used within SimpleItemTrackerProvider");
|
|
27092
|
-
}
|
|
27093
|
-
return tracker.registerItem(data);
|
|
27094
|
-
};
|
|
27095
|
-
const useTrackedItem = index => {
|
|
27096
|
-
const trackedItems = useTrackedItems();
|
|
27097
|
-
const item = trackedItems[index];
|
|
27098
|
-
return item;
|
|
27099
|
-
};
|
|
27100
|
-
const useTrackedItems = () => {
|
|
27101
|
-
const tracker = useContext(ItemTrackerContext);
|
|
27102
|
-
if (!tracker) {
|
|
27103
|
-
throw new Error("useTrackedItems must be used within SimpleItemTrackerProvider");
|
|
27208
|
+
}
|
|
27209
|
+
return activeValue;
|
|
27104
27210
|
}
|
|
27105
|
-
|
|
27106
|
-
|
|
27107
|
-
|
|
27108
|
-
|
|
27109
|
-
|
|
27110
|
-
|
|
27111
|
-
|
|
27112
|
-
|
|
27113
|
-
|
|
27114
|
-
|
|
27115
|
-
|
|
27116
|
-
|
|
27117
|
-
|
|
27118
|
-
* <Suggestion value="b">Option B</Suggestion>
|
|
27119
|
-
* </SuggestionList>
|
|
27120
|
-
*
|
|
27121
|
-
* CSS vars on .navi_suggestion_list:
|
|
27122
|
-
* --suggestion-list-border-radius, --suggestion-list-border-width, --suggestion-list-border-color, --suggestion-list-background-color, --suggestion-list-max-height
|
|
27123
|
-
*
|
|
27124
|
-
* CSS vars on .navi_suggestion:
|
|
27125
|
-
* --suggestion-padding, --suggestion-color, --suggestion-background-color, --suggestion-font-weight
|
|
27126
|
-
* --suggestion-color-hover, --suggestion-background-color-hover
|
|
27127
|
-
* --suggestion-color-pointed, --suggestion-background-color-pointed
|
|
27128
|
-
* --suggestion-color-selected, --suggestion-background-color-selected, --suggestion-font-weight-selected
|
|
27129
|
-
* --suggestion-color-pointed-selected, --suggestion-background-color-pointed-selected
|
|
27130
|
-
* --suggestion-color-highlight, --suggestion-background-color-highlight
|
|
27131
|
-
*
|
|
27132
|
-
* CSS vars on .navi_suggestion_group_label:
|
|
27133
|
-
* --suggestion-group-label-padding, --suggestion-group-label-color, --suggestion-group-label-font-size, --suggestion-group-label-font-weight
|
|
27134
|
-
*/
|
|
27135
|
-
|
|
27136
|
-
const css$g = /* css */`
|
|
27137
|
-
@layer navi {
|
|
27138
|
-
.navi_suggestion_list {
|
|
27139
|
-
--suggestion-list-border-radius: 4px;
|
|
27140
|
-
--suggestion-list-border-width: 1px;
|
|
27141
|
-
--suggestion-list-border-color: light-dark(#ccc, #555);
|
|
27142
|
-
--suggestion-list-background-color: light-dark(#fff, #1e1e1e);
|
|
27143
|
-
--suggestion-list-max-height: 220px;
|
|
27144
|
-
}
|
|
27145
|
-
.navi_suggestion_group_label {
|
|
27146
|
-
--suggestion-group-label-padding: 4px 12px 2px;
|
|
27147
|
-
--suggestion-group-label-color: light-dark(#888, #aaa);
|
|
27148
|
-
--suggestion-group-label-font-size: 0.75em;
|
|
27149
|
-
--suggestion-group-label-font-weight: 600;
|
|
27150
|
-
}
|
|
27151
|
-
.navi_suggestion {
|
|
27152
|
-
--suggestion-padding: 8px 12px;
|
|
27153
|
-
--suggestion-color: inherit;
|
|
27154
|
-
--suggestion-background-color: transparent;
|
|
27155
|
-
--suggestion-font-weight: inherit;
|
|
27156
|
-
|
|
27157
|
-
/* Hover (mouse) */
|
|
27158
|
-
--suggestion-color-hover: var(--suggestion-color);
|
|
27159
|
-
--suggestion-background-color-hover: light-dark(#f5f5f5, #2a2a2a);
|
|
27160
|
-
|
|
27161
|
-
/* Pointed (keyboard navigation position) */
|
|
27162
|
-
--suggestion-color-pointed: var(--suggestion-color);
|
|
27163
|
-
--suggestion-background-color-pointed: light-dark(#e8f0fe, #1c3a6e);
|
|
27164
|
-
|
|
27165
|
-
/* Selected */
|
|
27166
|
-
--suggestion-color-selected: light-dark(#1a73e8, #7baaf7);
|
|
27167
|
-
--suggestion-background-color-selected: light-dark(#e8f0fe, #1c3a6e);
|
|
27168
|
-
--suggestion-font-weight-selected: 500;
|
|
27169
|
-
|
|
27170
|
-
/* Highlight (CSS Highlight API match) */
|
|
27171
|
-
--suggestion-color-highlight: inherit;
|
|
27172
|
-
--suggestion-background-color-highlight: #ffe066;
|
|
27173
|
-
--suggestion-color-pointed-selected: var(--suggestion-color-selected);
|
|
27174
|
-
--suggestion-background-color-pointed-selected: light-dark(
|
|
27175
|
-
#d2e3fc,
|
|
27176
|
-
#174ea6
|
|
27177
|
-
);
|
|
27178
|
-
}
|
|
27179
|
-
}
|
|
27180
|
-
|
|
27181
|
-
.navi_suggestion_list {
|
|
27182
|
-
--x-border-radius: var(--suggestion-list-border-radius);
|
|
27183
|
-
--x-border-width: var(--suggestion-list-border-width);
|
|
27184
|
-
--x-border-color: var(--suggestion-list-border-color);
|
|
27185
|
-
--x-background-color: var(--suggestion-list-background-color);
|
|
27186
|
-
box-sizing: border-box;
|
|
27187
|
-
max-height: var(--suggestion-list-max-height);
|
|
27188
|
-
|
|
27189
|
-
margin: 0;
|
|
27190
|
-
padding: 0;
|
|
27191
|
-
list-style: none;
|
|
27192
|
-
background-color: var(--x-background-color);
|
|
27193
|
-
border: var(--x-border-width) solid var(--x-border-color);
|
|
27194
|
-
border-radius: var(--x-border-radius);
|
|
27195
|
-
outline: none;
|
|
27196
|
-
overflow-y: auto;
|
|
27197
|
-
|
|
27198
|
-
/* Popover reset — browser adds border, background, padding, margin by default */
|
|
27199
|
-
&[popover] {
|
|
27200
|
-
position: fixed;
|
|
27201
|
-
inset: unset;
|
|
27202
|
-
margin: 0;
|
|
27203
|
-
padding: 0;
|
|
27204
|
-
border: none;
|
|
27205
|
-
}
|
|
27206
|
-
}
|
|
27207
|
-
::highlight(navi-suggestion-match) {
|
|
27208
|
-
color: var(--suggestion-color-highlight);
|
|
27209
|
-
background-color: var(--suggestion-background-color-highlight);
|
|
27210
|
-
}
|
|
27211
|
-
.navi_suggestion {
|
|
27212
|
-
--x-color: var(--suggestion-color);
|
|
27213
|
-
--x-background-color: var(--suggestion-background-color);
|
|
27214
|
-
--x-font-weight: var(--suggestion-font-weight);
|
|
27215
|
-
|
|
27216
|
-
padding: var(--suggestion-padding);
|
|
27217
|
-
color: var(--x-color);
|
|
27218
|
-
font-weight: var(--x-font-weight);
|
|
27219
|
-
background-color: var(--x-background-color);
|
|
27220
|
-
cursor: pointer;
|
|
27221
|
-
user-select: none;
|
|
27222
|
-
|
|
27223
|
-
&:hover {
|
|
27224
|
-
--x-color: var(--suggestion-color-hover);
|
|
27225
|
-
--x-background-color: var(--suggestion-background-color-hover);
|
|
27226
|
-
}
|
|
27227
|
-
|
|
27228
|
-
&[data-pointed] {
|
|
27229
|
-
--x-color: var(--suggestion-color-pointed);
|
|
27230
|
-
--x-background-color: var(--suggestion-background-color-pointed);
|
|
27231
|
-
}
|
|
27232
|
-
|
|
27233
|
-
&[data-selected] {
|
|
27234
|
-
--x-color: var(--suggestion-color-selected);
|
|
27235
|
-
--x-background-color: var(--suggestion-background-color-selected);
|
|
27236
|
-
--x-font-weight: var(--suggestion-font-weight-selected);
|
|
27237
|
-
}
|
|
27238
|
-
|
|
27239
|
-
&[data-pointed][data-selected] {
|
|
27240
|
-
--x-color: var(--suggestion-color-pointed-selected);
|
|
27241
|
-
--x-background-color: var(--suggestion-background-color-pointed-selected);
|
|
27242
|
-
}
|
|
27243
|
-
}
|
|
27244
|
-
.navi_suggestion_group_label {
|
|
27245
|
-
position: sticky;
|
|
27246
|
-
top: 0;
|
|
27247
|
-
z-index: 1;
|
|
27248
|
-
display: block;
|
|
27249
|
-
padding: var(--suggestion-group-label-padding);
|
|
27250
|
-
color: var(--suggestion-group-label-color);
|
|
27251
|
-
font-weight: var(--suggestion-group-label-font-weight);
|
|
27252
|
-
font-size: var(--suggestion-group-label-font-size);
|
|
27253
|
-
text-transform: uppercase;
|
|
27254
|
-
letter-spacing: 0.05em;
|
|
27255
|
-
background-color: var(--suggestion-group-label-background-color);
|
|
27256
|
-
user-select: none;
|
|
27257
|
-
}
|
|
27258
|
-
`;
|
|
27259
|
-
const SuggestionListStyleCSSVars = {
|
|
27260
|
-
borderRadius: "--suggestion-list-border-radius",
|
|
27261
|
-
borderWidth: "--suggestion-list-border-width",
|
|
27262
|
-
borderColor: "--suggestion-list-border-color",
|
|
27263
|
-
backgroundColor: "--suggestion-list-background-color",
|
|
27264
|
-
maxHeight: "--suggestion-list-max-height"
|
|
27265
|
-
};
|
|
27266
|
-
const SuggestionStyleCSSVars = {
|
|
27267
|
-
"padding": "--suggestion-padding",
|
|
27268
|
-
"color": "--suggestion-color",
|
|
27269
|
-
"backgroundColor": "--suggestion-background-color",
|
|
27270
|
-
"fontWeight": "--suggestion-font-weight",
|
|
27271
|
-
":-navi-pointed": {
|
|
27272
|
-
color: "--suggestion-color-pointed",
|
|
27273
|
-
backgroundColor: "--suggestion-background-color-pointed"
|
|
27274
|
-
},
|
|
27275
|
-
":hover": {
|
|
27276
|
-
color: "--suggestion-color-hover",
|
|
27277
|
-
backgroundColor: "--suggestion-background-color-hover"
|
|
27278
|
-
},
|
|
27279
|
-
":-navi-selected": {
|
|
27280
|
-
color: "--suggestion-color-selected",
|
|
27281
|
-
backgroundColor: "--suggestion-background-color-selected",
|
|
27282
|
-
fontWeight: "--suggestion-font-weight-selected"
|
|
27283
|
-
},
|
|
27284
|
-
"::highlight": {
|
|
27285
|
-
color: "--suggestion-color-highlight",
|
|
27286
|
-
backgroundColor: "--suggestion-background-color-highlight"
|
|
27287
|
-
}
|
|
27288
|
-
};
|
|
27289
|
-
|
|
27290
|
-
/**
|
|
27291
|
-
* Context OptionList provides downward to its Option children.
|
|
27292
|
-
*/
|
|
27293
|
-
const SuggestionListContext = createContext(null);
|
|
27294
|
-
const SuggestionList = ({
|
|
27295
|
-
popover,
|
|
27296
|
-
onChange: onChangeProp,
|
|
27297
|
-
highlight,
|
|
27298
|
-
children,
|
|
27299
|
-
...rest
|
|
27300
|
-
}) => {
|
|
27301
|
-
import.meta.css = [css$g, "@jsenv/navi/src/field/suggestion_list.jsx"];
|
|
27302
|
-
const ItemTrackerProvider = useSuggestionItemTrackerProvider();
|
|
27303
|
-
const [pointedValue, setPointedValue] = useState(null);
|
|
27304
|
-
const pointedValueRef = useRef(null);
|
|
27305
|
-
pointedValueRef.current = pointedValue;
|
|
27306
|
-
const ownId = useId();
|
|
27307
|
-
const id = rest.id ?? ownId;
|
|
27308
|
-
const defaultRef = useRef(null);
|
|
27309
|
-
const ref = rest.ref || defaultRef;
|
|
27310
|
-
useLayoutEffect(() => {
|
|
27311
|
-
if (!CSS.highlights) {
|
|
27312
|
-
return undefined;
|
|
27313
|
-
}
|
|
27314
|
-
if (!highlight) {
|
|
27315
|
-
CSS.highlights.delete("navi-suggestion-match");
|
|
27316
|
-
return undefined;
|
|
27317
|
-
}
|
|
27318
|
-
const listEl = ref.current;
|
|
27319
|
-
if (!listEl) {
|
|
27320
|
-
return undefined;
|
|
27321
|
-
}
|
|
27322
|
-
const ranges = [];
|
|
27323
|
-
const lowerHighlight = highlight.toLowerCase();
|
|
27324
|
-
for (const suggestionEl of listEl.querySelectorAll("[role='option']")) {
|
|
27325
|
-
const walker = document.createTreeWalker(suggestionEl, NodeFilter.SHOW_TEXT);
|
|
27326
|
-
let node;
|
|
27327
|
-
while (node = walker.nextNode()) {
|
|
27328
|
-
const text = node.textContent;
|
|
27329
|
-
const lowerText = text.toLowerCase();
|
|
27330
|
-
let index = lowerText.indexOf(lowerHighlight);
|
|
27331
|
-
while (index !== -1) {
|
|
27332
|
-
const range = new Range();
|
|
27333
|
-
range.setStart(node, index);
|
|
27334
|
-
range.setEnd(node, index + highlight.length);
|
|
27335
|
-
ranges.push(range);
|
|
27336
|
-
index = lowerText.indexOf(lowerHighlight, index + 1);
|
|
27337
|
-
}
|
|
27338
|
-
}
|
|
27339
|
-
}
|
|
27340
|
-
if (ranges.length === 0) {
|
|
27341
|
-
CSS.highlights.delete("navi-suggestion-match");
|
|
27342
|
-
} else {
|
|
27343
|
-
CSS.highlights.set("navi-suggestion-match", new Highlight(...ranges));
|
|
27344
|
-
}
|
|
27345
|
-
return () => {
|
|
27346
|
-
CSS.highlights.delete("navi-suggestion-match");
|
|
27347
|
-
};
|
|
27348
|
-
}, [highlight, children]);
|
|
27349
|
-
const effectiveOnChange = popover ? value => {
|
|
27350
|
-
onChangeProp?.(value);
|
|
27351
|
-
ref.current?.dispatchEvent(new CustomEvent("navi_suggestion_list_selected", {
|
|
27352
|
-
detail: {
|
|
27353
|
-
value
|
|
27354
|
-
},
|
|
27355
|
-
bubbles: true
|
|
27356
|
-
}));
|
|
27357
|
-
} : onChangeProp;
|
|
27358
|
-
const onChangeRef = useRef(effectiveOnChange);
|
|
27359
|
-
onChangeRef.current = effectiveOnChange;
|
|
27360
|
-
const navigate = direction => {
|
|
27361
|
-
const values = ItemTrackerProvider.items.filter(item => !item.hidden).map(item => item.value);
|
|
27362
|
-
if (values.length === 0) {
|
|
27363
|
-
return false;
|
|
27364
|
-
}
|
|
27365
|
-
const current = pointedValueRef.current;
|
|
27366
|
-
if (direction === "down") {
|
|
27367
|
-
const idx = current === null ? -1 : values.indexOf(current);
|
|
27368
|
-
setPointedValue(values[idx < values.length - 1 ? idx + 1 : idx]);
|
|
27369
|
-
} else if (direction === "up") {
|
|
27370
|
-
const idx = current === null ? -1 : values.indexOf(current);
|
|
27371
|
-
setPointedValue(values[idx > 0 ? idx - 1 : 0]);
|
|
27372
|
-
} else if (direction === "first") {
|
|
27373
|
-
setPointedValue(values[0]);
|
|
27374
|
-
} else if (direction === "last") {
|
|
27375
|
-
setPointedValue(values[values.length - 1]);
|
|
27376
|
-
}
|
|
27377
|
-
return true;
|
|
27378
|
-
};
|
|
27379
|
-
|
|
27380
|
-
// Listen for commands dispatched by a linked Input (combobox mode)
|
|
27381
|
-
const noopRef = useRef(null);
|
|
27382
|
-
useEffect(() => {
|
|
27383
|
-
if (!popover || !ref.current) {
|
|
27384
|
-
return undefined;
|
|
27385
|
-
}
|
|
27386
|
-
const el = ref.current;
|
|
27387
|
-
const onNavigate = e => {
|
|
27388
|
-
navigate(e.detail.direction);
|
|
27389
|
-
};
|
|
27390
|
-
const onConfirm = e => {
|
|
27391
|
-
const current = pointedValueRef.current;
|
|
27392
|
-
if (current !== null) {
|
|
27393
|
-
onChangeRef.current?.(current);
|
|
27394
|
-
e.preventDefault();
|
|
27395
|
-
}
|
|
27396
|
-
};
|
|
27397
|
-
const onClear = () => {
|
|
27398
|
-
setPointedValue(null);
|
|
27399
|
-
};
|
|
27400
|
-
el.addEventListener("navi_suggestion_list_navigate", onNavigate);
|
|
27401
|
-
el.addEventListener("navi_suggestion_list_confirm", onConfirm);
|
|
27402
|
-
el.addEventListener("navi_suggestion_list_clear", onClear);
|
|
27403
|
-
return () => {
|
|
27404
|
-
el.removeEventListener("navi_suggestion_list_navigate", onNavigate);
|
|
27405
|
-
el.removeEventListener("navi_suggestion_list_confirm", onConfirm);
|
|
27406
|
-
el.removeEventListener("navi_suggestion_list_clear", onClear);
|
|
27407
|
-
};
|
|
27408
|
-
}, [popover]);
|
|
27409
|
-
useKeyboardShortcuts(popover ? noopRef : ref, [{
|
|
27410
|
-
key: "arrowdown",
|
|
27411
|
-
description: "Point to next suggestion",
|
|
27412
|
-
handler: () => navigate("down")
|
|
27413
|
-
}, {
|
|
27414
|
-
key: "arrowup",
|
|
27415
|
-
description: "Point to previous suggestion",
|
|
27416
|
-
handler: () => navigate("up")
|
|
27417
|
-
}, {
|
|
27418
|
-
key: "home",
|
|
27419
|
-
description: "Point to first suggestion",
|
|
27420
|
-
handler: () => navigate("first")
|
|
27421
|
-
}, {
|
|
27422
|
-
key: "end",
|
|
27423
|
-
description: "Point to last suggestion",
|
|
27424
|
-
handler: () => navigate("last")
|
|
27425
|
-
}, {
|
|
27426
|
-
key: "enter",
|
|
27427
|
-
description: "Confirm pointed suggestion",
|
|
27428
|
-
handler: () => {
|
|
27429
|
-
const current = pointedValueRef.current;
|
|
27430
|
-
if (current === null) {
|
|
27431
|
-
return false;
|
|
27432
|
-
}
|
|
27433
|
-
onChangeRef.current?.(current);
|
|
27434
|
-
return true;
|
|
27435
|
-
}
|
|
27436
|
-
}, {
|
|
27437
|
-
key: "escape",
|
|
27438
|
-
description: "Clear pointed suggestion",
|
|
27439
|
-
handler: () => {
|
|
27440
|
-
setPointedValue(null);
|
|
27441
|
-
return true;
|
|
27442
|
-
}
|
|
27443
|
-
}]);
|
|
27444
|
-
const suggestionListContext = {
|
|
27445
|
-
pointedValue,
|
|
27446
|
-
setPointedValue,
|
|
27447
|
-
onSelect: effectiveOnChange
|
|
27448
|
-
};
|
|
27449
|
-
return jsx(Box, {
|
|
27450
|
-
as: "ul",
|
|
27451
|
-
ref: ref,
|
|
27452
|
-
id: id,
|
|
27453
|
-
role: "listbox",
|
|
27454
|
-
tabIndex: popover ? -1 : 0,
|
|
27455
|
-
popover: popover ? "manual" : undefined,
|
|
27456
|
-
...rest,
|
|
27457
|
-
baseClassName: "navi_suggestion_list",
|
|
27458
|
-
styleCSSVars: SuggestionListStyleCSSVars,
|
|
27459
|
-
children: jsx(SuggestionListContext.Provider, {
|
|
27460
|
-
value: suggestionListContext,
|
|
27461
|
-
children: jsx(ItemTrackerProvider, {
|
|
27462
|
-
children: children
|
|
27463
|
-
})
|
|
27464
|
-
})
|
|
27465
|
-
});
|
|
27466
|
-
};
|
|
27467
|
-
const SUGGESTION_PSEUDO_CLASSES = [":-navi-pointed", ":-navi-selected"];
|
|
27468
|
-
const SUGGESTION_PSEUDO_ELEMENTS = ["::highlight"];
|
|
27469
|
-
const Suggestion = ({
|
|
27470
|
-
value,
|
|
27471
|
-
selected,
|
|
27472
|
-
hidden,
|
|
27473
|
-
children,
|
|
27474
|
-
...rest
|
|
27475
|
-
}) => {
|
|
27476
|
-
import.meta.css = [css$g, "@jsenv/navi/src/field/suggestion_list.jsx"];
|
|
27477
|
-
const suggestionId = useId();
|
|
27478
|
-
const id = rest.id || suggestionId;
|
|
27479
|
-
useTrackSuggestion({
|
|
27480
|
-
value,
|
|
27481
|
-
suggestionId: id,
|
|
27482
|
-
hidden
|
|
27483
|
-
});
|
|
27484
|
-
const {
|
|
27485
|
-
pointedValue,
|
|
27486
|
-
setPointedValue,
|
|
27487
|
-
onSelect
|
|
27488
|
-
} = useContext(SuggestionListContext);
|
|
27489
|
-
const isPointed = pointedValue === value;
|
|
27490
|
-
const suggestionRef = useRef(null);
|
|
27491
|
-
useEffect(() => {
|
|
27492
|
-
const suggestionEl = suggestionRef.current;
|
|
27493
|
-
if (isPointed && suggestionEl) {
|
|
27494
|
-
suggestionEl.scrollIntoView({
|
|
27495
|
-
block: "nearest"
|
|
27496
|
-
});
|
|
27497
|
-
}
|
|
27498
|
-
}, [isPointed]);
|
|
27499
|
-
return jsx(Box, {
|
|
27500
|
-
as: "li",
|
|
27501
|
-
ref: suggestionRef,
|
|
27502
|
-
baseClassName: "navi_suggestion",
|
|
27503
|
-
id: suggestionId,
|
|
27504
|
-
role: "option",
|
|
27505
|
-
"aria-selected": selected,
|
|
27506
|
-
"aria-hidden": hidden ? true : undefined,
|
|
27507
|
-
hidden: hidden,
|
|
27508
|
-
basePseudoState: {
|
|
27509
|
-
":-navi-pointed": isPointed,
|
|
27510
|
-
":-navi-selected": selected
|
|
27511
|
-
},
|
|
27512
|
-
pseudoClasses: SUGGESTION_PSEUDO_CLASSES,
|
|
27513
|
-
pseudoElements: SUGGESTION_PSEUDO_ELEMENTS,
|
|
27514
|
-
styleCSSVars: SuggestionStyleCSSVars,
|
|
27515
|
-
onMouseEnter: () => {
|
|
27516
|
-
if (!hidden) {
|
|
27517
|
-
setPointedValue(value);
|
|
27518
|
-
}
|
|
27519
|
-
},
|
|
27520
|
-
onMouseLeave: () => {
|
|
27521
|
-
if (!hidden) {
|
|
27522
|
-
setPointedValue(null);
|
|
27523
|
-
}
|
|
27524
|
-
},
|
|
27525
|
-
onMouseDown: e => {
|
|
27526
|
-
if (hidden || e.button !== 0) {
|
|
27527
|
-
return;
|
|
27528
|
-
}
|
|
27529
|
-
onSelect?.(value);
|
|
27530
|
-
},
|
|
27531
|
-
...rest,
|
|
27532
|
-
children: children
|
|
27533
|
-
});
|
|
27534
|
-
};
|
|
27535
|
-
const SuggestionGroup = ({
|
|
27536
|
-
label,
|
|
27537
|
-
children,
|
|
27538
|
-
...rest
|
|
27539
|
-
}) => {
|
|
27540
|
-
import.meta.css = [css$g, "@jsenv/navi/src/field/suggestion_list.jsx"];
|
|
27541
|
-
const groupId = useId();
|
|
27542
|
-
return jsxs("li", {
|
|
27543
|
-
role: "presentation",
|
|
27544
|
-
...rest,
|
|
27545
|
-
children: [jsx("span", {
|
|
27546
|
-
id: groupId,
|
|
27547
|
-
role: "presentation",
|
|
27548
|
-
"aria-hidden": "true",
|
|
27549
|
-
style: {
|
|
27550
|
-
display: "contents"
|
|
27551
|
-
},
|
|
27552
|
-
children: typeof label === "string" ? jsx("span", {
|
|
27553
|
-
className: "navi_suggestion_group_label",
|
|
27554
|
-
children: label
|
|
27555
|
-
}) : label
|
|
27556
|
-
}), jsx("ul", {
|
|
27557
|
-
role: "group",
|
|
27558
|
-
"aria-labelledby": groupId,
|
|
27559
|
-
style: {
|
|
27560
|
-
margin: 0,
|
|
27561
|
-
padding: 0,
|
|
27562
|
-
listStyle: "none"
|
|
27563
|
-
},
|
|
27564
|
-
children: children
|
|
27565
|
-
})]
|
|
27566
|
-
});
|
|
27567
|
-
};
|
|
27568
|
-
|
|
27569
|
-
const RadioList = props => {
|
|
27570
|
-
const uiStateController = useUIGroupStateController(props, "radio_list", {
|
|
27571
|
-
childComponentType: "radio",
|
|
27572
|
-
aggregateChildStates: childUIStateControllers => {
|
|
27573
|
-
let activeValue;
|
|
27574
|
-
for (const childUIStateController of childUIStateControllers) {
|
|
27575
|
-
if (childUIStateController.uiState) {
|
|
27576
|
-
activeValue = childUIStateController.uiState;
|
|
27577
|
-
break;
|
|
27578
|
-
}
|
|
27579
|
-
}
|
|
27580
|
-
return activeValue;
|
|
27581
|
-
}
|
|
27582
|
-
});
|
|
27583
|
-
const uiState = useUIState(uiStateController);
|
|
27584
|
-
const radioList = renderActionableComponent(props, {
|
|
27585
|
-
Basic: RadioListBasic,
|
|
27586
|
-
WithAction: RadioListWithAction
|
|
27587
|
-
});
|
|
27588
|
-
return jsx(UIStateControllerContext.Provider, {
|
|
27589
|
-
value: uiStateController,
|
|
27590
|
-
children: jsx(UIStateContext.Provider, {
|
|
27591
|
-
value: uiState,
|
|
27592
|
-
children: radioList
|
|
27593
|
-
})
|
|
27594
|
-
});
|
|
27211
|
+
});
|
|
27212
|
+
const uiState = useUIState(uiStateController);
|
|
27213
|
+
const radioList = renderActionableComponent(props, {
|
|
27214
|
+
Basic: RadioListBasic,
|
|
27215
|
+
WithAction: RadioListWithAction
|
|
27216
|
+
});
|
|
27217
|
+
return jsx(UIStateControllerContext.Provider, {
|
|
27218
|
+
value: uiStateController,
|
|
27219
|
+
children: jsx(UIStateContext.Provider, {
|
|
27220
|
+
value: uiState,
|
|
27221
|
+
children: radioList
|
|
27222
|
+
})
|
|
27223
|
+
});
|
|
27595
27224
|
};
|
|
27596
27225
|
const Radio = InputRadio;
|
|
27597
27226
|
const RadioListBasic = props => {
|
|
@@ -27640,12 +27269,208 @@ const RadioListBasic = props => {
|
|
|
27640
27269
|
})
|
|
27641
27270
|
})
|
|
27642
27271
|
});
|
|
27643
|
-
};
|
|
27644
|
-
const RadioListWithAction = props => {
|
|
27645
|
-
const uiStateController = useContext(UIStateControllerContext);
|
|
27646
|
-
const uiState = useContext(UIStateContext);
|
|
27272
|
+
};
|
|
27273
|
+
const RadioListWithAction = props => {
|
|
27274
|
+
const uiStateController = useContext(UIStateControllerContext);
|
|
27275
|
+
const uiState = useContext(UIStateContext);
|
|
27276
|
+
const {
|
|
27277
|
+
action,
|
|
27278
|
+
onCancel,
|
|
27279
|
+
onActionPrevented,
|
|
27280
|
+
onActionStart,
|
|
27281
|
+
onActionAbort,
|
|
27282
|
+
onActionError,
|
|
27283
|
+
onActionEnd,
|
|
27284
|
+
actionErrorEffect,
|
|
27285
|
+
loading,
|
|
27286
|
+
children,
|
|
27287
|
+
...rest
|
|
27288
|
+
} = props;
|
|
27289
|
+
const innerRef = useRef();
|
|
27290
|
+
const [boundAction] = useActionBoundToOneParam(action, uiState);
|
|
27291
|
+
const {
|
|
27292
|
+
loading: actionLoading
|
|
27293
|
+
} = useActionStatus(boundAction);
|
|
27294
|
+
const executeAction = useExecuteAction(innerRef, {
|
|
27295
|
+
errorEffect: actionErrorEffect
|
|
27296
|
+
});
|
|
27297
|
+
const [actionRequester, setActionRequester] = useState(null);
|
|
27298
|
+
useActionEvents(innerRef, {
|
|
27299
|
+
onCancel: (e, reason) => {
|
|
27300
|
+
uiStateController.resetUIState(e);
|
|
27301
|
+
onCancel?.(e, reason);
|
|
27302
|
+
},
|
|
27303
|
+
onPrevented: onActionPrevented,
|
|
27304
|
+
onAction: actionEvent => {
|
|
27305
|
+
setActionRequester(actionEvent.detail.requester);
|
|
27306
|
+
executeAction(actionEvent);
|
|
27307
|
+
},
|
|
27308
|
+
onStart: onActionStart,
|
|
27309
|
+
onAbort: e => {
|
|
27310
|
+
uiStateController.resetUIState(e);
|
|
27311
|
+
onActionAbort?.(e);
|
|
27312
|
+
},
|
|
27313
|
+
onError: e => {
|
|
27314
|
+
uiStateController.resetUIState(e);
|
|
27315
|
+
onActionError?.(e);
|
|
27316
|
+
},
|
|
27317
|
+
onEnd: e => {
|
|
27318
|
+
onActionEnd?.(e);
|
|
27319
|
+
}
|
|
27320
|
+
});
|
|
27321
|
+
return jsx(RadioListBasic, {
|
|
27322
|
+
"data-action": boundAction,
|
|
27323
|
+
...rest,
|
|
27324
|
+
ref: innerRef,
|
|
27325
|
+
onChange: e => {
|
|
27326
|
+
const radio = e.target;
|
|
27327
|
+
const radioListContainer = innerRef.current;
|
|
27328
|
+
requestAction(radioListContainer, boundAction, {
|
|
27329
|
+
event: e,
|
|
27330
|
+
requester: radio,
|
|
27331
|
+
actionOrigin: "action_prop"
|
|
27332
|
+
});
|
|
27333
|
+
},
|
|
27334
|
+
loading: loading || actionLoading,
|
|
27335
|
+
children: jsx(LoadingElementContext.Provider, {
|
|
27336
|
+
value: actionRequester,
|
|
27337
|
+
children: children
|
|
27338
|
+
})
|
|
27339
|
+
});
|
|
27340
|
+
};
|
|
27341
|
+
|
|
27342
|
+
const useRefArray = (items, keyFromItem) => {
|
|
27343
|
+
const refMapRef = useRef(new Map());
|
|
27344
|
+
const previousKeySetRef = useRef(new Set());
|
|
27345
|
+
|
|
27346
|
+
return useMemo(() => {
|
|
27347
|
+
const refMap = refMapRef.current;
|
|
27348
|
+
const previousKeySet = previousKeySetRef.current;
|
|
27349
|
+
const currentKeySet = new Set();
|
|
27350
|
+
const refArray = [];
|
|
27351
|
+
|
|
27352
|
+
for (let i = 0; i < items.length; i++) {
|
|
27353
|
+
const item = items[i];
|
|
27354
|
+
const key = keyFromItem(item);
|
|
27355
|
+
currentKeySet.add(key);
|
|
27356
|
+
|
|
27357
|
+
const refForKey = refMap.get(key);
|
|
27358
|
+
if (refForKey) {
|
|
27359
|
+
refArray[i] = refForKey;
|
|
27360
|
+
} else {
|
|
27361
|
+
const newRef = createRef();
|
|
27362
|
+
refMap.set(key, newRef);
|
|
27363
|
+
refArray[i] = newRef;
|
|
27364
|
+
}
|
|
27365
|
+
}
|
|
27366
|
+
|
|
27367
|
+
for (const key of previousKeySet) {
|
|
27368
|
+
if (!currentKeySet.has(key)) {
|
|
27369
|
+
refMap.delete(key);
|
|
27370
|
+
}
|
|
27371
|
+
}
|
|
27372
|
+
previousKeySetRef.current = currentKeySet;
|
|
27373
|
+
|
|
27374
|
+
return refArray;
|
|
27375
|
+
}, [items]);
|
|
27376
|
+
};
|
|
27377
|
+
|
|
27378
|
+
installImportMetaCssBuild(import.meta);const useNavState = () => {};
|
|
27379
|
+
const css$g = /* css */`
|
|
27380
|
+
.navi_select[data-readonly] {
|
|
27381
|
+
pointer-events: none;
|
|
27382
|
+
}
|
|
27383
|
+
`;
|
|
27384
|
+
const Select = forwardRef((props, ref) => {
|
|
27385
|
+
import.meta.css = [css$g, "@jsenv/navi/src/field/select.jsx"];
|
|
27386
|
+
const select = renderActionableComponent(props, ref);
|
|
27387
|
+
return select;
|
|
27388
|
+
});
|
|
27389
|
+
const SelectControlled = forwardRef((props, ref) => {
|
|
27390
|
+
const {
|
|
27391
|
+
name,
|
|
27392
|
+
value,
|
|
27393
|
+
loading,
|
|
27394
|
+
disabled,
|
|
27395
|
+
readOnly,
|
|
27396
|
+
children,
|
|
27397
|
+
...rest
|
|
27398
|
+
} = props;
|
|
27399
|
+
const innerRef = useRef();
|
|
27400
|
+
useImperativeHandle(ref, () => innerRef.current);
|
|
27401
|
+
const selectElement = jsx("select", {
|
|
27402
|
+
className: "navi_select",
|
|
27403
|
+
ref: innerRef,
|
|
27404
|
+
"data-readonly": readOnly && !disabled ? "" : undefined,
|
|
27405
|
+
onKeyDown: e => {
|
|
27406
|
+
if (readOnly) {
|
|
27407
|
+
e.preventDefault();
|
|
27408
|
+
}
|
|
27409
|
+
},
|
|
27410
|
+
...rest,
|
|
27411
|
+
children: children.map(child => {
|
|
27412
|
+
const {
|
|
27413
|
+
label,
|
|
27414
|
+
readOnly: childReadOnly,
|
|
27415
|
+
disabled: childDisabled,
|
|
27416
|
+
loading: childLoading,
|
|
27417
|
+
value: childValue,
|
|
27418
|
+
...childRest
|
|
27419
|
+
} = child;
|
|
27420
|
+
return jsx("option", {
|
|
27421
|
+
name: name,
|
|
27422
|
+
value: childValue,
|
|
27423
|
+
selected: childValue === value,
|
|
27424
|
+
readOnly: readOnly || childReadOnly,
|
|
27425
|
+
disabled: disabled || childDisabled,
|
|
27426
|
+
loading: loading || childLoading,
|
|
27427
|
+
...childRest,
|
|
27428
|
+
children: label
|
|
27429
|
+
}, childValue);
|
|
27430
|
+
})
|
|
27431
|
+
});
|
|
27432
|
+
return jsx(LoaderBackground, {
|
|
27433
|
+
loading: loading,
|
|
27434
|
+
color: "light-dark(#355fcc, #3b82f6)",
|
|
27435
|
+
inset: -1,
|
|
27436
|
+
children: selectElement
|
|
27437
|
+
});
|
|
27438
|
+
});
|
|
27439
|
+
forwardRef((props, ref) => {
|
|
27440
|
+
const {
|
|
27441
|
+
value: initialValue,
|
|
27442
|
+
id,
|
|
27443
|
+
children,
|
|
27444
|
+
...rest
|
|
27445
|
+
} = props;
|
|
27446
|
+
const innerRef = useRef();
|
|
27447
|
+
useImperativeHandle(ref, () => innerRef.current);
|
|
27448
|
+
const [navState, setNavState] = useNavState();
|
|
27449
|
+
const valueAtStart = navState === undefined ? initialValue : navState;
|
|
27450
|
+
const [value, setValue] = useState(valueAtStart);
|
|
27451
|
+
useEffect(() => {
|
|
27452
|
+
setNavState(value);
|
|
27453
|
+
}, [value]);
|
|
27454
|
+
return jsx(SelectControlled, {
|
|
27455
|
+
ref: innerRef,
|
|
27456
|
+
value: value,
|
|
27457
|
+
onChange: event => {
|
|
27458
|
+
const select = event.target;
|
|
27459
|
+
const selectedValue = select.value;
|
|
27460
|
+
setValue(selectedValue);
|
|
27461
|
+
},
|
|
27462
|
+
...rest,
|
|
27463
|
+
children: children
|
|
27464
|
+
});
|
|
27465
|
+
});
|
|
27466
|
+
forwardRef((props, ref) => {
|
|
27647
27467
|
const {
|
|
27468
|
+
id,
|
|
27469
|
+
name,
|
|
27470
|
+
value: externalValue,
|
|
27471
|
+
valueSignal,
|
|
27648
27472
|
action,
|
|
27473
|
+
children,
|
|
27649
27474
|
onCancel,
|
|
27650
27475
|
onActionPrevented,
|
|
27651
27476
|
onActionStart,
|
|
@@ -27653,272 +27478,693 @@ const RadioListWithAction = props => {
|
|
|
27653
27478
|
onActionError,
|
|
27654
27479
|
onActionEnd,
|
|
27655
27480
|
actionErrorEffect,
|
|
27656
|
-
loading,
|
|
27657
|
-
children,
|
|
27658
27481
|
...rest
|
|
27659
27482
|
} = props;
|
|
27660
27483
|
const innerRef = useRef();
|
|
27661
|
-
|
|
27484
|
+
useImperativeHandle(ref, () => innerRef.current);
|
|
27485
|
+
const [navState, setNavState, resetNavState] = useNavState();
|
|
27486
|
+
const [boundAction, value, setValue, initialValue] = useActionBoundToOneParam(action, name);
|
|
27662
27487
|
const {
|
|
27663
27488
|
loading: actionLoading
|
|
27664
27489
|
} = useActionStatus(boundAction);
|
|
27665
27490
|
const executeAction = useExecuteAction(innerRef, {
|
|
27666
27491
|
errorEffect: actionErrorEffect
|
|
27667
27492
|
});
|
|
27668
|
-
|
|
27493
|
+
useEffect(() => {
|
|
27494
|
+
setNavState(value);
|
|
27495
|
+
}, [value]);
|
|
27496
|
+
const actionRequesterRef = useRef(null);
|
|
27669
27497
|
useActionEvents(innerRef, {
|
|
27670
27498
|
onCancel: (e, reason) => {
|
|
27671
|
-
|
|
27499
|
+
resetNavState();
|
|
27500
|
+
setValue(initialValue);
|
|
27672
27501
|
onCancel?.(e, reason);
|
|
27673
27502
|
},
|
|
27674
27503
|
onPrevented: onActionPrevented,
|
|
27675
27504
|
onAction: actionEvent => {
|
|
27676
|
-
|
|
27505
|
+
actionRequesterRef.current = actionEvent.detail.requester;
|
|
27677
27506
|
executeAction(actionEvent);
|
|
27678
27507
|
},
|
|
27679
27508
|
onStart: onActionStart,
|
|
27680
27509
|
onAbort: e => {
|
|
27681
|
-
|
|
27510
|
+
setValue(initialValue);
|
|
27682
27511
|
onActionAbort?.(e);
|
|
27683
27512
|
},
|
|
27684
|
-
onError:
|
|
27685
|
-
|
|
27686
|
-
onActionError?.(
|
|
27513
|
+
onError: error => {
|
|
27514
|
+
setValue(initialValue);
|
|
27515
|
+
onActionError?.(error);
|
|
27687
27516
|
},
|
|
27688
|
-
onEnd:
|
|
27689
|
-
|
|
27517
|
+
onEnd: () => {
|
|
27518
|
+
resetNavState();
|
|
27519
|
+
onActionEnd?.();
|
|
27690
27520
|
}
|
|
27691
27521
|
});
|
|
27692
|
-
|
|
27693
|
-
|
|
27694
|
-
...rest,
|
|
27522
|
+
const childRefArray = useRefArray(children, child => child.value);
|
|
27523
|
+
return jsx(SelectControlled, {
|
|
27695
27524
|
ref: innerRef,
|
|
27696
|
-
|
|
27697
|
-
|
|
27525
|
+
name: name,
|
|
27526
|
+
value: value,
|
|
27527
|
+
"data-action": boundAction,
|
|
27528
|
+
onChange: event => {
|
|
27529
|
+
const select = event.target;
|
|
27530
|
+
const selectedValue = select.value;
|
|
27531
|
+
setValue(selectedValue);
|
|
27698
27532
|
const radioListContainer = innerRef.current;
|
|
27533
|
+
const optionSelected = select.querySelector(`option[value="${selectedValue}"]`);
|
|
27699
27534
|
requestAction(radioListContainer, boundAction, {
|
|
27700
|
-
event
|
|
27701
|
-
requester:
|
|
27702
|
-
actionOrigin: "action_prop"
|
|
27535
|
+
event,
|
|
27536
|
+
requester: optionSelected
|
|
27703
27537
|
});
|
|
27704
27538
|
},
|
|
27705
|
-
|
|
27706
|
-
children:
|
|
27707
|
-
|
|
27708
|
-
|
|
27539
|
+
...rest,
|
|
27540
|
+
children: children.map((child, i) => {
|
|
27541
|
+
const childRef = childRefArray[i];
|
|
27542
|
+
return {
|
|
27543
|
+
...child,
|
|
27544
|
+
ref: childRef,
|
|
27545
|
+
loading: child.loading || actionLoading && actionRequesterRef.current === childRef.current,
|
|
27546
|
+
readOnly: child.readOnly || actionLoading
|
|
27547
|
+
};
|
|
27709
27548
|
})
|
|
27710
27549
|
});
|
|
27550
|
+
});
|
|
27551
|
+
|
|
27552
|
+
const createItemTracker = () => {
|
|
27553
|
+
const ItemTrackerContext = createContext();
|
|
27554
|
+
const useItemTrackerProvider = () => {
|
|
27555
|
+
const itemsRef = useRef([]);
|
|
27556
|
+
const items = itemsRef.current;
|
|
27557
|
+
const itemCountRef = useRef(0);
|
|
27558
|
+
const tracker = useMemo(() => {
|
|
27559
|
+
const ItemTrackerProvider = ({
|
|
27560
|
+
children
|
|
27561
|
+
}) => {
|
|
27562
|
+
// Reset on each render to start fresh
|
|
27563
|
+
tracker.reset();
|
|
27564
|
+
return jsx(ItemTrackerContext.Provider, {
|
|
27565
|
+
value: tracker,
|
|
27566
|
+
children: children
|
|
27567
|
+
});
|
|
27568
|
+
};
|
|
27569
|
+
ItemTrackerProvider.items = items;
|
|
27570
|
+
return {
|
|
27571
|
+
ItemTrackerProvider,
|
|
27572
|
+
items,
|
|
27573
|
+
registerItem: data => {
|
|
27574
|
+
const index = itemCountRef.current++;
|
|
27575
|
+
items[index] = data;
|
|
27576
|
+
return index;
|
|
27577
|
+
},
|
|
27578
|
+
getItem: index => {
|
|
27579
|
+
return items[index];
|
|
27580
|
+
},
|
|
27581
|
+
getAllItems: () => {
|
|
27582
|
+
return items;
|
|
27583
|
+
},
|
|
27584
|
+
reset: () => {
|
|
27585
|
+
items.length = 0;
|
|
27586
|
+
itemCountRef.current = 0;
|
|
27587
|
+
}
|
|
27588
|
+
};
|
|
27589
|
+
}, []);
|
|
27590
|
+
return tracker.ItemTrackerProvider;
|
|
27591
|
+
};
|
|
27592
|
+
const useTrackItem = data => {
|
|
27593
|
+
const tracker = useContext(ItemTrackerContext);
|
|
27594
|
+
if (!tracker) {
|
|
27595
|
+
throw new Error("useTrackItem must be used within SimpleItemTrackerProvider");
|
|
27596
|
+
}
|
|
27597
|
+
return tracker.registerItem(data);
|
|
27598
|
+
};
|
|
27599
|
+
const useTrackedItem = index => {
|
|
27600
|
+
const trackedItems = useTrackedItems();
|
|
27601
|
+
const item = trackedItems[index];
|
|
27602
|
+
return item;
|
|
27603
|
+
};
|
|
27604
|
+
const useTrackedItems = () => {
|
|
27605
|
+
const tracker = useContext(ItemTrackerContext);
|
|
27606
|
+
if (!tracker) {
|
|
27607
|
+
throw new Error("useTrackedItems must be used within SimpleItemTrackerProvider");
|
|
27608
|
+
}
|
|
27609
|
+
return tracker.items;
|
|
27610
|
+
};
|
|
27611
|
+
return [useItemTrackerProvider, useTrackItem, useTrackedItem, useTrackedItems];
|
|
27711
27612
|
};
|
|
27712
27613
|
|
|
27713
|
-
const
|
|
27714
|
-
const refMapRef = useRef(new Map());
|
|
27715
|
-
const previousKeySetRef = useRef(new Set());
|
|
27614
|
+
installImportMetaCssBuild(import.meta);const [useSuggestionItemTrackerProvider, useTrackSuggestion] = createItemTracker();
|
|
27716
27615
|
|
|
27717
|
-
|
|
27718
|
-
|
|
27719
|
-
|
|
27720
|
-
|
|
27721
|
-
|
|
27616
|
+
/**
|
|
27617
|
+
* SuggestionList + Suggestion: a composable accessible listbox.
|
|
27618
|
+
*
|
|
27619
|
+
* Usage:
|
|
27620
|
+
* <SuggestionList id="my-list" value={selected} onChange={setSelected}>
|
|
27621
|
+
* <Suggestion value="a">Option A</Suggestion>
|
|
27622
|
+
* <Suggestion value="b">Option B</Suggestion>
|
|
27623
|
+
* </SuggestionList>
|
|
27624
|
+
*
|
|
27625
|
+
* CSS vars on .navi_suggestion_list:
|
|
27626
|
+
* --suggestion-list-border-radius, --suggestion-list-border-width, --suggestion-list-border-color, --suggestion-list-background-color, --suggestion-list-max-height
|
|
27627
|
+
*
|
|
27628
|
+
* CSS vars on .navi_suggestion:
|
|
27629
|
+
* --suggestion-padding, --suggestion-color, --suggestion-background-color, --suggestion-font-weight
|
|
27630
|
+
* --suggestion-color-hover, --suggestion-background-color-hover
|
|
27631
|
+
* --suggestion-color-pointed, --suggestion-background-color-pointed
|
|
27632
|
+
* --suggestion-color-selected, --suggestion-background-color-selected, --suggestion-font-weight-selected
|
|
27633
|
+
* --suggestion-color-pointed-selected, --suggestion-background-color-pointed-selected
|
|
27634
|
+
* --suggestion-color-highlight, --suggestion-background-color-highlight
|
|
27635
|
+
*
|
|
27636
|
+
* CSS vars on .navi_suggestion_group_label:
|
|
27637
|
+
* --suggestion-group-label-padding, --suggestion-group-label-color, --suggestion-group-label-font-size, --suggestion-group-label-font-weight
|
|
27638
|
+
*/
|
|
27639
|
+
|
|
27640
|
+
const css$f = /* css */`
|
|
27641
|
+
@layer navi {
|
|
27642
|
+
.navi_suggestion_list {
|
|
27643
|
+
--suggestion-list-border-radius: 4px;
|
|
27644
|
+
--suggestion-list-border-width: 1px;
|
|
27645
|
+
--suggestion-list-border-color: light-dark(#ccc, #555);
|
|
27646
|
+
--suggestion-list-border-style: solid;
|
|
27647
|
+
--suggestion-list-background-color: light-dark(#fff, #1e1e1e);
|
|
27648
|
+
--suggestion-list-max-height: 220px;
|
|
27649
|
+
}
|
|
27650
|
+
.navi_suggestion {
|
|
27651
|
+
--suggestion-padding: 8px 12px;
|
|
27652
|
+
--suggestion-color: inherit;
|
|
27653
|
+
--suggestion-font-weight: inherit;
|
|
27654
|
+
|
|
27655
|
+
/* Hover (mouse) */
|
|
27656
|
+
--suggestion-color-hover: var(--suggestion-color);
|
|
27657
|
+
--suggestion-background-color-hover: light-dark(#f5f5f5, #2a2a2a);
|
|
27658
|
+
|
|
27659
|
+
/* Pointed (keyboard navigation position) */
|
|
27660
|
+
--suggestion-color-pointed: var(--suggestion-color);
|
|
27661
|
+
--suggestion-background-color-pointed: light-dark(#e8f0fe, #1c3a6e);
|
|
27662
|
+
|
|
27663
|
+
/* Selected */
|
|
27664
|
+
--suggestion-color-selected: light-dark(#1a73e8, #7baaf7);
|
|
27665
|
+
--suggestion-background-color-selected: light-dark(#e8f0fe, #1c3a6e);
|
|
27666
|
+
--suggestion-font-weight-selected: 500;
|
|
27667
|
+
|
|
27668
|
+
/* Highlight (CSS Highlight API match) */
|
|
27669
|
+
--suggestion-color-highlight: inherit;
|
|
27670
|
+
--suggestion-background-color-highlight: #ffe066;
|
|
27671
|
+
--suggestion-color-pointed-selected: var(--suggestion-color-selected);
|
|
27672
|
+
--suggestion-background-color-pointed-selected: light-dark(
|
|
27673
|
+
#d2e3fc,
|
|
27674
|
+
#174ea6
|
|
27675
|
+
);
|
|
27676
|
+
}
|
|
27677
|
+
}
|
|
27678
|
+
|
|
27679
|
+
.navi_suggestion_list {
|
|
27680
|
+
--x-border-radius: var(--suggestion-list-border-radius);
|
|
27681
|
+
--x-border-width: var(--suggestion-list-border-width);
|
|
27682
|
+
--x-border-color: var(--suggestion-list-border-color);
|
|
27683
|
+
--x-border-style: var(--suggestion-list-border-style);
|
|
27684
|
+
--x-background-color: var(--suggestion-list-background-color);
|
|
27685
|
+
width: fit-content;
|
|
27686
|
+
max-width: 100%;
|
|
27687
|
+
|
|
27688
|
+
max-height: var(--suggestion-list-max-height);
|
|
27689
|
+
background-color: var(--x-background-color);
|
|
27690
|
+
border: var(--x-border-width) var(--x-border-style) var(--x-border-color);
|
|
27691
|
+
border-radius: var(--x-border-radius);
|
|
27692
|
+
transition: opacity 0.2s ease;
|
|
27693
|
+
overflow: auto;
|
|
27694
|
+
|
|
27695
|
+
/* Popover reset — browser adds border, background, padding, margin by default */
|
|
27696
|
+
&[popover] {
|
|
27697
|
+
position: absolute;
|
|
27698
|
+
inset: unset;
|
|
27699
|
+
min-width: var(--suggestion-list-anchor-width, 0px);
|
|
27700
|
+
max-width: 95vw;
|
|
27701
|
+
margin: 0;
|
|
27702
|
+
padding: 0;
|
|
27703
|
+
/* border: none; */
|
|
27704
|
+
}
|
|
27705
|
+
&[data-anchor-hidden] {
|
|
27706
|
+
opacity: 0;
|
|
27707
|
+
pointer-events: none;
|
|
27708
|
+
}
|
|
27709
|
+
}
|
|
27710
|
+
|
|
27711
|
+
.navi_suggestion_listbox {
|
|
27712
|
+
box-sizing: border-box;
|
|
27713
|
+
width: max-content;
|
|
27714
|
+
min-width: 100%;
|
|
27715
|
+
margin: 0;
|
|
27716
|
+
padding: 0;
|
|
27717
|
+
list-style: none;
|
|
27718
|
+
}
|
|
27719
|
+
::highlight(navi-suggestion-match) {
|
|
27720
|
+
color: var(--suggestion-color-highlight);
|
|
27721
|
+
background-color: var(--suggestion-background-color-highlight);
|
|
27722
|
+
}
|
|
27723
|
+
.navi_suggestion {
|
|
27724
|
+
--x-color: var(--suggestion-color);
|
|
27725
|
+
--x-background-color: var(--suggestion-background-color);
|
|
27726
|
+
--x-font-weight: var(--suggestion-font-weight);
|
|
27727
|
+
display: flex;
|
|
27728
|
+
box-sizing: border-box;
|
|
27729
|
+
width: max-content;
|
|
27730
|
+
min-width: 100%;
|
|
27731
|
+
|
|
27732
|
+
padding: var(--suggestion-padding);
|
|
27733
|
+
flex-direction: column;
|
|
27734
|
+
color: var(--x-color);
|
|
27735
|
+
font-weight: var(--x-font-weight);
|
|
27736
|
+
background-color: var(--x-background-color);
|
|
27737
|
+
cursor: pointer;
|
|
27738
|
+
user-select: none;
|
|
27722
27739
|
|
|
27723
|
-
|
|
27724
|
-
|
|
27725
|
-
|
|
27726
|
-
|
|
27740
|
+
&:hover {
|
|
27741
|
+
--x-color: var(--suggestion-color-hover);
|
|
27742
|
+
--x-background-color: var(--suggestion-background-color-hover);
|
|
27743
|
+
}
|
|
27727
27744
|
|
|
27728
|
-
|
|
27729
|
-
|
|
27730
|
-
|
|
27731
|
-
} else {
|
|
27732
|
-
const newRef = createRef();
|
|
27733
|
-
refMap.set(key, newRef);
|
|
27734
|
-
refArray[i] = newRef;
|
|
27735
|
-
}
|
|
27745
|
+
&[data-pointed] {
|
|
27746
|
+
--x-color: var(--suggestion-color-pointed);
|
|
27747
|
+
--x-background-color: var(--suggestion-background-color-pointed);
|
|
27736
27748
|
}
|
|
27737
27749
|
|
|
27738
|
-
|
|
27739
|
-
|
|
27740
|
-
|
|
27741
|
-
|
|
27750
|
+
&[data-selected] {
|
|
27751
|
+
--x-color: var(--suggestion-color-selected);
|
|
27752
|
+
--x-background-color: var(--suggestion-background-color-selected);
|
|
27753
|
+
--x-font-weight: var(--suggestion-font-weight-selected);
|
|
27742
27754
|
}
|
|
27743
|
-
previousKeySetRef.current = currentKeySet;
|
|
27744
27755
|
|
|
27745
|
-
|
|
27746
|
-
|
|
27747
|
-
|
|
27756
|
+
&[data-pointed][data-selected] {
|
|
27757
|
+
--x-color: var(--suggestion-color-pointed-selected);
|
|
27758
|
+
--x-background-color: var(--suggestion-background-color-pointed-selected);
|
|
27759
|
+
}
|
|
27760
|
+
}
|
|
27761
|
+
.navi_suggestion_group_label {
|
|
27762
|
+
position: sticky;
|
|
27763
|
+
top: 0;
|
|
27764
|
+
z-index: 1;
|
|
27765
|
+
display: block;
|
|
27766
|
+
background-color: var(
|
|
27767
|
+
--suggestion-group-label-background-color,
|
|
27768
|
+
var(--suggestion-list-background-color)
|
|
27769
|
+
);
|
|
27770
|
+
user-select: none;
|
|
27748
27771
|
|
|
27749
|
-
|
|
27750
|
-
|
|
27751
|
-
|
|
27752
|
-
|
|
27772
|
+
&[data-default-label] {
|
|
27773
|
+
padding: 4px 12px 2px;
|
|
27774
|
+
color: light-dark(#888, #aaa);
|
|
27775
|
+
font-weight: 600;
|
|
27776
|
+
font-size: 0.75em;
|
|
27777
|
+
text-transform: uppercase;
|
|
27778
|
+
letter-spacing: 0.05em;
|
|
27779
|
+
}
|
|
27780
|
+
}
|
|
27781
|
+
.navi_suggestion_list_empty {
|
|
27782
|
+
display: none;
|
|
27783
|
+
padding: var(--suggestion-padding);
|
|
27784
|
+
color: var(--suggestion-group-label-color);
|
|
27785
|
+
font-size: 0.9em;
|
|
27786
|
+
text-align: center;
|
|
27787
|
+
user-select: none;
|
|
27788
|
+
}
|
|
27789
|
+
/* Show the empty state only when there are no visible suggestions */
|
|
27790
|
+
.navi_suggestion_list:not(:has([role="option"]:not([hidden]))) {
|
|
27791
|
+
.navi_suggestion_list_empty {
|
|
27792
|
+
display: block;
|
|
27793
|
+
}
|
|
27753
27794
|
}
|
|
27754
27795
|
`;
|
|
27755
|
-
const
|
|
27756
|
-
|
|
27757
|
-
|
|
27758
|
-
|
|
27759
|
-
|
|
27760
|
-
|
|
27761
|
-
|
|
27762
|
-
|
|
27763
|
-
|
|
27764
|
-
|
|
27765
|
-
|
|
27766
|
-
|
|
27767
|
-
|
|
27768
|
-
|
|
27769
|
-
|
|
27770
|
-
|
|
27771
|
-
|
|
27772
|
-
|
|
27773
|
-
|
|
27774
|
-
|
|
27775
|
-
|
|
27776
|
-
|
|
27777
|
-
|
|
27796
|
+
const SuggestionListStyleCSSVars = {
|
|
27797
|
+
borderRadius: "--suggestion-list-border-radius",
|
|
27798
|
+
borderWidth: "--suggestion-list-border-width",
|
|
27799
|
+
borderColor: "--suggestion-list-border-color",
|
|
27800
|
+
backgroundColor: "--suggestion-list-background-color",
|
|
27801
|
+
maxHeight: "--suggestion-list-max-height"
|
|
27802
|
+
};
|
|
27803
|
+
const SuggestionStyleCSSVars = {
|
|
27804
|
+
"padding": "--suggestion-padding",
|
|
27805
|
+
"color": "--suggestion-color",
|
|
27806
|
+
"backgroundColor": "--suggestion-background-color",
|
|
27807
|
+
"fontWeight": "--suggestion-font-weight",
|
|
27808
|
+
":-navi-pointed": {
|
|
27809
|
+
color: "--suggestion-color-pointed",
|
|
27810
|
+
backgroundColor: "--suggestion-background-color-pointed"
|
|
27811
|
+
},
|
|
27812
|
+
":hover": {
|
|
27813
|
+
color: "--suggestion-color-hover",
|
|
27814
|
+
backgroundColor: "--suggestion-background-color-hover"
|
|
27815
|
+
},
|
|
27816
|
+
":-navi-selected": {
|
|
27817
|
+
color: "--suggestion-color-selected",
|
|
27818
|
+
backgroundColor: "--suggestion-background-color-selected",
|
|
27819
|
+
fontWeight: "--suggestion-font-weight-selected"
|
|
27820
|
+
},
|
|
27821
|
+
"::highlight": {
|
|
27822
|
+
color: "--suggestion-color-highlight",
|
|
27823
|
+
backgroundColor: "--suggestion-background-color-highlight"
|
|
27824
|
+
}
|
|
27825
|
+
};
|
|
27826
|
+
|
|
27827
|
+
/**
|
|
27828
|
+
* Context OptionList provides downward to its Option children.
|
|
27829
|
+
*/
|
|
27830
|
+
const SuggestionListContext = createContext(null);
|
|
27831
|
+
const SuggestionList = ({
|
|
27832
|
+
popover,
|
|
27833
|
+
onChange: onChangeProp,
|
|
27834
|
+
highlight,
|
|
27835
|
+
emptyState = "No results",
|
|
27836
|
+
children,
|
|
27837
|
+
...rest
|
|
27838
|
+
}) => {
|
|
27839
|
+
import.meta.css = [css$f, "@jsenv/navi/src/field/suggestion_list.jsx"];
|
|
27840
|
+
const ItemTrackerProvider = useSuggestionItemTrackerProvider();
|
|
27841
|
+
const [pointedValue, setPointedValue] = useState(null);
|
|
27842
|
+
const pointedValueRef = useRef(null);
|
|
27843
|
+
pointedValueRef.current = pointedValue;
|
|
27844
|
+
const ownId = useId();
|
|
27845
|
+
const id = rest.id ?? ownId;
|
|
27846
|
+
const defaultRef = useRef(null);
|
|
27847
|
+
const ref = rest.ref || defaultRef;
|
|
27848
|
+
useLayoutEffect(() => {
|
|
27849
|
+
if (!CSS.highlights) {
|
|
27850
|
+
return undefined;
|
|
27851
|
+
}
|
|
27852
|
+
if (!highlight) {
|
|
27853
|
+
CSS.highlights.delete("navi-suggestion-match");
|
|
27854
|
+
return undefined;
|
|
27855
|
+
}
|
|
27856
|
+
const listEl = ref.current;
|
|
27857
|
+
if (!listEl) {
|
|
27858
|
+
return undefined;
|
|
27859
|
+
}
|
|
27860
|
+
const ranges = [];
|
|
27861
|
+
const lowerHighlight = highlight.toLowerCase();
|
|
27862
|
+
for (const suggestionEl of listEl.querySelectorAll("[role='option']")) {
|
|
27863
|
+
const walker = document.createTreeWalker(suggestionEl, NodeFilter.SHOW_TEXT);
|
|
27864
|
+
let node;
|
|
27865
|
+
while (node = walker.nextNode()) {
|
|
27866
|
+
const text = node.textContent;
|
|
27867
|
+
const lowerText = text.toLowerCase();
|
|
27868
|
+
let index = lowerText.indexOf(lowerHighlight);
|
|
27869
|
+
while (index !== -1) {
|
|
27870
|
+
const range = new Range();
|
|
27871
|
+
range.setStart(node, index);
|
|
27872
|
+
range.setEnd(node, index + highlight.length);
|
|
27873
|
+
ranges.push(range);
|
|
27874
|
+
index = lowerText.indexOf(lowerHighlight, index + 1);
|
|
27875
|
+
}
|
|
27876
|
+
}
|
|
27877
|
+
}
|
|
27878
|
+
if (ranges.length === 0) {
|
|
27879
|
+
CSS.highlights.delete("navi-suggestion-match");
|
|
27880
|
+
} else {
|
|
27881
|
+
CSS.highlights.set("navi-suggestion-match", new Highlight(...ranges));
|
|
27882
|
+
}
|
|
27883
|
+
return () => {
|
|
27884
|
+
CSS.highlights.delete("navi-suggestion-match");
|
|
27885
|
+
};
|
|
27886
|
+
}, [highlight, children]);
|
|
27887
|
+
const effectiveOnChange = popover ? value => {
|
|
27888
|
+
onChangeProp?.(value);
|
|
27889
|
+
ref.current?.dispatchEvent(new CustomEvent("navi_suggestion_list_selected", {
|
|
27890
|
+
detail: {
|
|
27891
|
+
value
|
|
27892
|
+
},
|
|
27893
|
+
bubbles: true
|
|
27894
|
+
}));
|
|
27895
|
+
} : onChangeProp;
|
|
27896
|
+
const onChangeRef = useRef(effectiveOnChange);
|
|
27897
|
+
onChangeRef.current = effectiveOnChange;
|
|
27898
|
+
const navigate = direction => {
|
|
27899
|
+
const values = ItemTrackerProvider.items.filter(item => !item.hidden).map(item => item.value);
|
|
27900
|
+
if (values.length === 0) {
|
|
27901
|
+
return false;
|
|
27902
|
+
}
|
|
27903
|
+
const current = pointedValueRef.current;
|
|
27904
|
+
if (direction === "down") {
|
|
27905
|
+
const idx = current === null ? -1 : values.indexOf(current);
|
|
27906
|
+
setPointedValue(values[idx < values.length - 1 ? idx + 1 : idx]);
|
|
27907
|
+
} else if (direction === "up") {
|
|
27908
|
+
const idx = current === null ? -1 : values.indexOf(current);
|
|
27909
|
+
setPointedValue(values[idx > 0 ? idx - 1 : 0]);
|
|
27910
|
+
} else if (direction === "first") {
|
|
27911
|
+
setPointedValue(values[0]);
|
|
27912
|
+
} else if (direction === "last") {
|
|
27913
|
+
setPointedValue(values[values.length - 1]);
|
|
27914
|
+
}
|
|
27915
|
+
return true;
|
|
27916
|
+
};
|
|
27917
|
+
|
|
27918
|
+
// Listen for commands dispatched by a linked Input (combobox mode)
|
|
27919
|
+
const noopRef = useRef(null);
|
|
27920
|
+
useEffect(() => {
|
|
27921
|
+
if (!popover || !ref.current) {
|
|
27922
|
+
return undefined;
|
|
27923
|
+
}
|
|
27924
|
+
const el = ref.current;
|
|
27925
|
+
let positionEffectCleanup = null;
|
|
27926
|
+
const positionPopover = anchor => {
|
|
27927
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
27928
|
+
el.style.setProperty("--suggestion-list-anchor-width", `${anchorRect.width}px`);
|
|
27929
|
+
const minLeft = 1;
|
|
27930
|
+
const {
|
|
27931
|
+
left,
|
|
27932
|
+
top
|
|
27933
|
+
} = pickPositionRelativeTo(el, anchor, {
|
|
27934
|
+
positionPreference: "below",
|
|
27935
|
+
minLeft
|
|
27936
|
+
});
|
|
27937
|
+
el.style.top = `${top}px`;
|
|
27938
|
+
const popoverRect = el.getBoundingClientRect();
|
|
27939
|
+
const maxWidth = parseFloat(getComputedStyle(el).maxWidth);
|
|
27940
|
+
if (!isNaN(maxWidth) && popoverRect.width >= maxWidth - 1) {
|
|
27941
|
+
const viewportWidth = document.documentElement.clientWidth;
|
|
27942
|
+
const centeredLeft = (viewportWidth - popoverRect.width) / 2;
|
|
27943
|
+
el.style.left = `${Math.max(centeredLeft, minLeft)}px`;
|
|
27944
|
+
} else {
|
|
27945
|
+
el.style.left = `${Math.max(left, minLeft)}px`;
|
|
27946
|
+
}
|
|
27947
|
+
};
|
|
27948
|
+
const onOpen = e => {
|
|
27949
|
+
const anchor = e.detail?.anchor;
|
|
27950
|
+
el.showPopover();
|
|
27951
|
+
if (anchor) {
|
|
27952
|
+
positionEffectCleanup = visibleRectEffect(anchor, ({
|
|
27953
|
+
visibilityRatio
|
|
27954
|
+
}) => {
|
|
27955
|
+
if (visibilityRatio <= 0.2) {
|
|
27956
|
+
el.setAttribute("data-anchor-hidden", "");
|
|
27957
|
+
return;
|
|
27958
|
+
}
|
|
27959
|
+
el.removeAttribute("data-anchor-hidden");
|
|
27960
|
+
positionPopover(anchor);
|
|
27961
|
+
});
|
|
27962
|
+
}
|
|
27963
|
+
};
|
|
27964
|
+
const onClose = () => {
|
|
27965
|
+
if (positionEffectCleanup) {
|
|
27966
|
+
positionEffectCleanup.disconnect();
|
|
27967
|
+
positionEffectCleanup = null;
|
|
27968
|
+
}
|
|
27969
|
+
el.removeAttribute("data-anchor-hidden");
|
|
27970
|
+
el.dispatchEvent(new CustomEvent("navi_suggestion_list_clear"));
|
|
27971
|
+
el.hidePopover();
|
|
27972
|
+
};
|
|
27973
|
+
const onNavigate = e => {
|
|
27974
|
+
navigate(e.detail.direction);
|
|
27975
|
+
};
|
|
27976
|
+
const onConfirm = e => {
|
|
27977
|
+
const current = pointedValueRef.current;
|
|
27978
|
+
if (current !== null) {
|
|
27979
|
+
onChangeRef.current?.(current);
|
|
27778
27980
|
e.preventDefault();
|
|
27779
27981
|
}
|
|
27780
|
-
}
|
|
27982
|
+
};
|
|
27983
|
+
const onClear = () => {
|
|
27984
|
+
setPointedValue(null);
|
|
27985
|
+
};
|
|
27986
|
+
el.addEventListener("navi_suggestion_list_open", onOpen);
|
|
27987
|
+
el.addEventListener("navi_suggestion_list_close", onClose);
|
|
27988
|
+
el.addEventListener("navi_suggestion_list_navigate", onNavigate);
|
|
27989
|
+
el.addEventListener("navi_suggestion_list_confirm", onConfirm);
|
|
27990
|
+
el.addEventListener("navi_suggestion_list_clear", onClear);
|
|
27991
|
+
return () => {
|
|
27992
|
+
el.removeEventListener("navi_suggestion_list_open", onOpen);
|
|
27993
|
+
el.removeEventListener("navi_suggestion_list_close", onClose);
|
|
27994
|
+
el.removeEventListener("navi_suggestion_list_navigate", onNavigate);
|
|
27995
|
+
el.removeEventListener("navi_suggestion_list_confirm", onConfirm);
|
|
27996
|
+
el.removeEventListener("navi_suggestion_list_clear", onClear);
|
|
27997
|
+
if (positionEffectCleanup) {
|
|
27998
|
+
positionEffectCleanup.disconnect();
|
|
27999
|
+
}
|
|
28000
|
+
};
|
|
28001
|
+
}, [popover]);
|
|
28002
|
+
useKeyboardShortcuts(popover ? noopRef : ref, [{
|
|
28003
|
+
key: "arrowdown",
|
|
28004
|
+
description: "Point to next suggestion",
|
|
28005
|
+
handler: () => navigate("down")
|
|
28006
|
+
}, {
|
|
28007
|
+
key: "arrowup",
|
|
28008
|
+
description: "Point to previous suggestion",
|
|
28009
|
+
handler: () => navigate("up")
|
|
28010
|
+
}, {
|
|
28011
|
+
key: "home",
|
|
28012
|
+
description: "Point to first suggestion",
|
|
28013
|
+
handler: () => navigate("first")
|
|
28014
|
+
}, {
|
|
28015
|
+
key: "end",
|
|
28016
|
+
description: "Point to last suggestion",
|
|
28017
|
+
handler: () => navigate("last")
|
|
28018
|
+
}, {
|
|
28019
|
+
key: "enter",
|
|
28020
|
+
description: "Confirm pointed suggestion",
|
|
28021
|
+
handler: () => {
|
|
28022
|
+
const current = pointedValueRef.current;
|
|
28023
|
+
if (current === null) {
|
|
28024
|
+
return false;
|
|
28025
|
+
}
|
|
28026
|
+
onChangeRef.current?.(current);
|
|
28027
|
+
return true;
|
|
28028
|
+
}
|
|
28029
|
+
}, {
|
|
28030
|
+
key: "escape",
|
|
28031
|
+
description: "Clear pointed suggestion",
|
|
28032
|
+
handler: () => {
|
|
28033
|
+
setPointedValue(null);
|
|
28034
|
+
return true;
|
|
28035
|
+
}
|
|
28036
|
+
}]);
|
|
28037
|
+
const suggestionListContext = {
|
|
28038
|
+
pointedValue,
|
|
28039
|
+
setPointedValue,
|
|
28040
|
+
onSelect: effectiveOnChange
|
|
28041
|
+
};
|
|
28042
|
+
return jsx(Box, {
|
|
28043
|
+
ref: ref,
|
|
28044
|
+
id: id,
|
|
28045
|
+
popover: popover ? "manual" : undefined,
|
|
28046
|
+
tabIndex: popover ? -1 : 0,
|
|
27781
28047
|
...rest,
|
|
27782
|
-
|
|
27783
|
-
|
|
27784
|
-
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
27794
|
-
|
|
27795
|
-
|
|
27796
|
-
|
|
27797
|
-
loading: loading || childLoading,
|
|
27798
|
-
...childRest,
|
|
27799
|
-
children: label
|
|
27800
|
-
}, childValue);
|
|
28048
|
+
baseClassName: "navi_suggestion_list",
|
|
28049
|
+
children: jsx(Box, {
|
|
28050
|
+
as: "ul",
|
|
28051
|
+
role: "listbox",
|
|
28052
|
+
baseClassName: "navi_suggestion_listbox",
|
|
28053
|
+
styleCSSVars: SuggestionListStyleCSSVars,
|
|
28054
|
+
children: jsxs(SuggestionListContext.Provider, {
|
|
28055
|
+
value: suggestionListContext,
|
|
28056
|
+
children: [jsx(ItemTrackerProvider, {
|
|
28057
|
+
children: children
|
|
28058
|
+
}), emptyState && jsx("li", {
|
|
28059
|
+
className: "navi_suggestion_list_empty",
|
|
28060
|
+
children: emptyState
|
|
28061
|
+
})]
|
|
28062
|
+
})
|
|
27801
28063
|
})
|
|
27802
28064
|
});
|
|
27803
|
-
|
|
27804
|
-
|
|
27805
|
-
|
|
27806
|
-
|
|
27807
|
-
|
|
27808
|
-
|
|
27809
|
-
|
|
27810
|
-
|
|
27811
|
-
|
|
27812
|
-
|
|
27813
|
-
|
|
27814
|
-
|
|
27815
|
-
|
|
27816
|
-
|
|
27817
|
-
|
|
27818
|
-
|
|
27819
|
-
|
|
27820
|
-
const valueAtStart = navState === undefined ? initialValue : navState;
|
|
27821
|
-
const [value, setValue] = useState(valueAtStart);
|
|
27822
|
-
useEffect(() => {
|
|
27823
|
-
setNavState(value);
|
|
27824
|
-
}, [value]);
|
|
27825
|
-
return jsx(SelectControlled, {
|
|
27826
|
-
ref: innerRef,
|
|
27827
|
-
value: value,
|
|
27828
|
-
onChange: event => {
|
|
27829
|
-
const select = event.target;
|
|
27830
|
-
const selectedValue = select.value;
|
|
27831
|
-
setValue(selectedValue);
|
|
27832
|
-
},
|
|
27833
|
-
...rest,
|
|
27834
|
-
children: children
|
|
28065
|
+
};
|
|
28066
|
+
const SUGGESTION_PSEUDO_CLASSES = [":-navi-pointed", ":-navi-selected"];
|
|
28067
|
+
const SUGGESTION_PSEUDO_ELEMENTS = ["::highlight"];
|
|
28068
|
+
const Suggestion = ({
|
|
28069
|
+
value,
|
|
28070
|
+
selected,
|
|
28071
|
+
hidden,
|
|
28072
|
+
children,
|
|
28073
|
+
...rest
|
|
28074
|
+
}) => {
|
|
28075
|
+
import.meta.css = [css$f, "@jsenv/navi/src/field/suggestion_list.jsx"];
|
|
28076
|
+
const suggestionId = useId();
|
|
28077
|
+
const id = rest.id || suggestionId;
|
|
28078
|
+
useTrackSuggestion({
|
|
28079
|
+
value,
|
|
28080
|
+
suggestionId: id,
|
|
28081
|
+
hidden
|
|
27835
28082
|
});
|
|
27836
|
-
});
|
|
27837
|
-
forwardRef((props, ref) => {
|
|
27838
|
-
const {
|
|
27839
|
-
id,
|
|
27840
|
-
name,
|
|
27841
|
-
value: externalValue,
|
|
27842
|
-
valueSignal,
|
|
27843
|
-
action,
|
|
27844
|
-
children,
|
|
27845
|
-
onCancel,
|
|
27846
|
-
onActionPrevented,
|
|
27847
|
-
onActionStart,
|
|
27848
|
-
onActionAbort,
|
|
27849
|
-
onActionError,
|
|
27850
|
-
onActionEnd,
|
|
27851
|
-
actionErrorEffect,
|
|
27852
|
-
...rest
|
|
27853
|
-
} = props;
|
|
27854
|
-
const innerRef = useRef();
|
|
27855
|
-
useImperativeHandle(ref, () => innerRef.current);
|
|
27856
|
-
const [navState, setNavState, resetNavState] = useNavState();
|
|
27857
|
-
const [boundAction, value, setValue, initialValue] = useActionBoundToOneParam(action, name);
|
|
27858
28083
|
const {
|
|
27859
|
-
|
|
27860
|
-
|
|
27861
|
-
|
|
27862
|
-
|
|
27863
|
-
|
|
28084
|
+
pointedValue,
|
|
28085
|
+
setPointedValue,
|
|
28086
|
+
onSelect
|
|
28087
|
+
} = useContext(SuggestionListContext);
|
|
28088
|
+
const isPointed = pointedValue === value;
|
|
28089
|
+
const suggestionRef = useRef(null);
|
|
27864
28090
|
useEffect(() => {
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27869
|
-
|
|
27870
|
-
|
|
27871
|
-
|
|
27872
|
-
|
|
28091
|
+
const suggestionEl = suggestionRef.current;
|
|
28092
|
+
if (isPointed && suggestionEl) {
|
|
28093
|
+
suggestionEl.scrollIntoView({
|
|
28094
|
+
block: "nearest"
|
|
28095
|
+
});
|
|
28096
|
+
}
|
|
28097
|
+
}, [isPointed]);
|
|
28098
|
+
return jsx(Box, {
|
|
28099
|
+
as: "li",
|
|
28100
|
+
ref: suggestionRef,
|
|
28101
|
+
baseClassName: "navi_suggestion",
|
|
28102
|
+
id: suggestionId,
|
|
28103
|
+
role: "option",
|
|
28104
|
+
"aria-selected": selected,
|
|
28105
|
+
"aria-hidden": hidden ? true : undefined,
|
|
28106
|
+
hidden: hidden,
|
|
28107
|
+
basePseudoState: {
|
|
28108
|
+
":-navi-pointed": isPointed,
|
|
28109
|
+
":-navi-selected": selected
|
|
27873
28110
|
},
|
|
27874
|
-
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
28111
|
+
pseudoClasses: SUGGESTION_PSEUDO_CLASSES,
|
|
28112
|
+
pseudoElements: SUGGESTION_PSEUDO_ELEMENTS,
|
|
28113
|
+
styleCSSVars: SuggestionStyleCSSVars,
|
|
28114
|
+
onMouseEnter: () => {
|
|
28115
|
+
if (!hidden) {
|
|
28116
|
+
setPointedValue(value);
|
|
28117
|
+
}
|
|
27878
28118
|
},
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
28119
|
+
onMouseLeave: () => {
|
|
28120
|
+
if (!hidden) {
|
|
28121
|
+
setPointedValue(null);
|
|
28122
|
+
}
|
|
27883
28123
|
},
|
|
27884
|
-
|
|
27885
|
-
|
|
27886
|
-
|
|
28124
|
+
onMouseDown: e => {
|
|
28125
|
+
if (hidden || e.button !== 0) {
|
|
28126
|
+
return;
|
|
28127
|
+
}
|
|
28128
|
+
onSelect?.(value);
|
|
27887
28129
|
},
|
|
27888
|
-
|
|
27889
|
-
|
|
27890
|
-
onActionEnd?.();
|
|
27891
|
-
}
|
|
28130
|
+
...rest,
|
|
28131
|
+
children: children
|
|
27892
28132
|
});
|
|
27893
|
-
|
|
27894
|
-
|
|
27895
|
-
|
|
27896
|
-
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
const radioListContainer = innerRef.current;
|
|
27904
|
-
const optionSelected = select.querySelector(`option[value="${selectedValue}"]`);
|
|
27905
|
-
requestAction(radioListContainer, boundAction, {
|
|
27906
|
-
event,
|
|
27907
|
-
requester: optionSelected
|
|
27908
|
-
});
|
|
27909
|
-
},
|
|
28133
|
+
};
|
|
28134
|
+
const SuggestionGroup = ({
|
|
28135
|
+
label,
|
|
28136
|
+
children,
|
|
28137
|
+
...rest
|
|
28138
|
+
}) => {
|
|
28139
|
+
import.meta.css = [css$f, "@jsenv/navi/src/field/suggestion_list.jsx"];
|
|
28140
|
+
const groupId = useId();
|
|
28141
|
+
return jsxs("li", {
|
|
28142
|
+
role: "presentation",
|
|
27910
28143
|
...rest,
|
|
27911
|
-
children:
|
|
27912
|
-
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
28144
|
+
children: [jsx("span", {
|
|
28145
|
+
id: groupId,
|
|
28146
|
+
role: "presentation",
|
|
28147
|
+
"aria-hidden": "true",
|
|
28148
|
+
style: {
|
|
28149
|
+
display: "contents"
|
|
28150
|
+
},
|
|
28151
|
+
children: jsx("span", {
|
|
28152
|
+
className: "navi_suggestion_group_label",
|
|
28153
|
+
"data-default-label": typeof label === "string" ? "" : undefined,
|
|
28154
|
+
children: label
|
|
28155
|
+
})
|
|
28156
|
+
}), jsx("ul", {
|
|
28157
|
+
role: "group",
|
|
28158
|
+
"aria-labelledby": groupId,
|
|
28159
|
+
style: {
|
|
28160
|
+
margin: 0,
|
|
28161
|
+
padding: 0,
|
|
28162
|
+
listStyle: "none"
|
|
28163
|
+
},
|
|
28164
|
+
children: children
|
|
28165
|
+
})]
|
|
27920
28166
|
});
|
|
27921
|
-
}
|
|
28167
|
+
};
|
|
27922
28168
|
|
|
27923
28169
|
const TableSelectionContext = createContext();
|
|
27924
28170
|
const useTableSelectionContextValue = (
|