pixelize-design-library 2.4.2-beta.45 → 2.4.2-beta.47
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/CHANGELOG.md +34 -4
- package/COMPONENT-GOTCHAS.md +47 -15
- package/dist/Components/Dropdown/DropDown.js +10 -8
- package/dist/Components/SearchSelect/SearchSelect.js +20 -15
- package/dist/Components/SearchSelect/searchSelectSize.d.ts +0 -5
- package/dist/Components/SearchSelect/searchSelectSize.js +0 -12
- package/dist/Components/SegmentedControl/SegmentedControl.js +4 -2
- package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +1 -1
- package/dist/Components/SegmentedControl/segmentedInk.d.ts +3 -3
- package/dist/Components/SegmentedControl/segmentedInk.js +8 -11
- package/dist/Components/Select/SelectPopup.js +16 -15
- package/dist/Components/Select/optionRowInk.d.ts +7 -0
- package/dist/Components/Select/optionRowInk.js +17 -0
- package/dist/Components/SelectSearch/SelectSearch.js +8 -4
- package/dist/Components/Switch/Switch.d.ts +1 -1
- package/dist/Components/Switch/Switch.js +4 -2
- package/dist/Components/Switch/SwitchProps.d.ts +1 -1
- package/dist/Components/Table/filters/CompactSelect.js +6 -3
- package/dist/Theme/chakra/Menu.styles.d.ts +6 -0
- package/dist/Theme/chakra/Menu.styles.js +8 -0
- package/dist/Theme/chakra/componentStyles.d.ts +6 -0
- package/dist/Theme/tokens/builders/outlineRung.d.ts +2 -0
- package/dist/Theme/tokens/builders/outlineRung.js +14 -1
- package/dist/Theme/tokens/builders/rowStates.js +3 -4
- package/dist/Theme/tokens/primitives/optionRowSizes.d.ts +20 -0
- package/dist/Theme/tokens/primitives/optionRowSizes.js +29 -0
- package/dist/esm/Components/Dropdown/DropDown.js +10 -8
- package/dist/esm/Components/SearchSelect/SearchSelect.js +20 -15
- package/dist/esm/Components/SearchSelect/searchSelectSize.js +0 -12
- package/dist/esm/Components/SegmentedControl/SegmentedControl.js +4 -2
- package/dist/esm/Components/SegmentedControl/segmentedInk.js +8 -11
- package/dist/esm/Components/Select/SelectPopup.js +17 -16
- package/dist/esm/Components/Select/optionRowInk.js +11 -0
- package/dist/esm/Components/SelectSearch/SelectSearch.js +9 -5
- package/dist/esm/Components/Switch/Switch.js +4 -2
- package/dist/esm/Components/Table/filters/CompactSelect.js +7 -4
- package/dist/esm/Theme/chakra/Menu.styles.js +8 -0
- package/dist/esm/Theme/tokens/builders/outlineRung.js +12 -0
- package/dist/esm/Theme/tokens/builders/rowStates.js +4 -5
- package/dist/esm/Theme/tokens/primitives/optionRowSizes.js +25 -0
- package/dist/esm/index.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +9 -4
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.optionRowWeight = exports.OPTION_ROW_SELECTED_WEIGHT = exports.OPTION_ROW_REST_WEIGHT = exports.optionRowDescriptionInk = exports.optionRowInk = void 0;
|
|
4
|
+
const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
5
|
+
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
6
|
+
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
7
|
+
/** Selection is carried by weight and a trailing brand check, so the label itself stays neutral. */
|
|
8
|
+
const optionRowInk = (colors, isSingleSelected) => isSingleSelected ? colors.text[900] : colors.text[700];
|
|
9
|
+
exports.optionRowInk = optionRowInk;
|
|
10
|
+
// A 12px sub-line reached only 4.00:1 on the hovered selected row in dark.
|
|
11
|
+
const optionRowDescriptionInk = (colors) => (0, outlineRung_1.liftToContrast)(colors.textMuted, (0, rowStates_1.listRowBackgrounds)(colors).selectedActive, accentText_1.AA_NORMAL_TEXT);
|
|
12
|
+
exports.optionRowDescriptionInk = optionRowDescriptionInk;
|
|
13
|
+
exports.OPTION_ROW_REST_WEIGHT = 400;
|
|
14
|
+
// NOTE: 600 beside a tinted fill and a check read as three signals for one state.
|
|
15
|
+
exports.OPTION_ROW_SELECTED_WEIGHT = 500;
|
|
16
|
+
const optionRowWeight = (isSingleSelected) => isSingleSelected ? exports.OPTION_ROW_SELECTED_WEIGHT : exports.OPTION_ROW_REST_WEIGHT;
|
|
17
|
+
exports.optionRowWeight = optionRowWeight;
|
|
@@ -14,21 +14,25 @@ const useFieldIds_1 = require("../Common/useFieldIds");
|
|
|
14
14
|
const FieldMessage_1 = __importDefault(require("../Common/FieldMessage"));
|
|
15
15
|
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
16
16
|
const controlSizes_1 = require("../../Theme/tokens/primitives/controlSizes");
|
|
17
|
+
const optionRowSizes_1 = require("../../Theme/tokens/primitives/optionRowSizes");
|
|
17
18
|
const motion_2 = require("../../Theme/chakra/motion");
|
|
18
19
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
19
20
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
21
|
+
const optionRowInk_1 = require("../Select/optionRowInk");
|
|
20
22
|
const useExclusiveOverlay_1 = require("../../Hooks/useExclusiveOverlay");
|
|
21
23
|
const lucide_react_1 = require("lucide-react");
|
|
22
24
|
const ROW_TRANSITION = (0, motion_1.transitionFor)(["background-color"]);
|
|
23
|
-
const RenderOptions = ({ isOptionLoading, filteredOptions, loadingText, handleOptionClick, isProfile, isColorOptions, isOptionSelected, activeIndex, optionRefs, }) => {
|
|
25
|
+
const RenderOptions = ({ isOptionLoading, filteredOptions, loadingText, handleOptionClick, isProfile, isColorOptions, isOptionSelected, activeIndex, optionRefs, size, isMultipleSelect, }) => {
|
|
24
26
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
25
27
|
const rowBackgrounds = (0, react_1.useMemo)(() => (0, rowStates_1.listRowBackgrounds)(theme.colors), [theme.colors]);
|
|
28
|
+
const spec = (0, optionRowSizes_1.optionRowSpec)(size);
|
|
26
29
|
if (isOptionLoading) {
|
|
27
30
|
return ((0, jsx_runtime_1.jsxs)(react_2.VStack, { py: 2, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: "sm", color: theme.colors.accentText }), (0, jsx_runtime_1.jsx)(react_2.Text, { color: theme.colors.accentText, children: loadingText })] }));
|
|
28
31
|
}
|
|
29
32
|
if (filteredOptions === null || filteredOptions === void 0 ? void 0 : filteredOptions.length) {
|
|
30
33
|
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: filteredOptions.map((option, index) => {
|
|
31
34
|
const selected = isOptionSelected(option);
|
|
35
|
+
const isSingleSelected = selected && !isMultipleSelect;
|
|
32
36
|
const isActive = index === activeIndex;
|
|
33
37
|
const rowBg = selected
|
|
34
38
|
? isActive
|
|
@@ -39,14 +43,14 @@ const RenderOptions = ({ isOptionLoading, filteredOptions, loadingText, handleOp
|
|
|
39
43
|
: "transparent";
|
|
40
44
|
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "button", type: "button", ref: (el) => {
|
|
41
45
|
optionRefs.current[index] = el;
|
|
42
|
-
}, role: "option", "aria-selected": selected, tabIndex: isActive ? 0 : -1, w:
|
|
46
|
+
}, role: "option", "aria-selected": selected, tabIndex: isActive ? 0 : -1, w: `calc(100% - 2 * ${spec.mx})`, minH: spec.minH, textAlign: "left", border: "none", fontFamily: "inherit", bg: rowBg, mx: spec.mx, px: spec.px, py: spec.py, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, fontSize: spec.fontSize, cursor: "pointer", color: (0, optionRowInk_1.optionRowInk)(theme.colors, isSingleSelected), fontWeight: (0, optionRowInk_1.optionRowWeight)(isSingleSelected), transition: ROW_TRANSITION, sx: (0, motion_2.reducedMotionSx)(), _hover: {
|
|
43
47
|
bg: selected ? rowBackgrounds.selectedActive : rowBackgrounds.active,
|
|
44
48
|
}, onClick: () => handleOptionClick(option), onKeyDown: (e) => {
|
|
45
49
|
if (e.key === "Enter" || e.key === " ") {
|
|
46
50
|
e.preventDefault();
|
|
47
51
|
handleOptionClick(option);
|
|
48
52
|
}
|
|
49
|
-
}, display: "flex", alignItems: "center", gap:
|
|
53
|
+
}, display: "flex", alignItems: "center", gap: spec.gap, children: [isProfile && ((0, jsx_runtime_1.jsx)(react_2.Box, { children: (0, jsx_runtime_1.jsx)(Avatar_1.default, { size: spec.avatarSize, name: option.label.toLocaleUpperCase(), src: option.profileUrl ? option.profileUrl : undefined }) })), isColorOptions && ((0, jsx_runtime_1.jsx)(react_2.Box, { width: "0.8rem", height: "0.8rem", content: "", backgroundColor: option === null || option === void 0 ? void 0 : option.color, borderRadius: "50%" })), (0, jsx_runtime_1.jsxs)(react_2.Box, { flex: 1, minW: 0, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { children: option.label }), option.email && ((0, jsx_runtime_1.jsx)(react_2.Box, { fontSize: spec.descriptionFontSize, color: (0, optionRowInk_1.optionRowDescriptionInk)(theme.colors), children: option.email }))] }), isSingleSelected && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 }))] }, option.id));
|
|
50
54
|
}) }));
|
|
51
55
|
}
|
|
52
56
|
return ((0, jsx_runtime_1.jsx)(react_2.Text, { px: 4, py: 2, color: theme.colors.textMuted, children: "No options found" }));
|
|
@@ -230,7 +234,7 @@ function SelectSearch({ options, initialSelectedOption, onOptionSelect, inputOnc
|
|
|
230
234
|
return;
|
|
231
235
|
}
|
|
232
236
|
openDropdown();
|
|
233
|
-
}, onChange: (e) => handleInputChange(e.target.value), placeholder: placeholder, onKeyDown: handleKeyDown, name: name, role: "combobox", "aria-autocomplete": "list", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-required": isRequired || undefined, cursor: "pointer", sx: { ...(0, fieldStyles_1.fieldControlStyles)(theme.colors), ...(0, fieldStyles_1.controlSizeStyles)(size), ...(0, motion_2.reducedMotionSx)() }, style: inputStyle }), rightIcon && ((0, jsx_runtime_1.jsx)(react_2.InputRightElement, { pointerEvents: "none", children: rightIcon, style: { ...rightElementStyle } }))] }), isOpen && ((0, jsx_runtime_1.jsxs)(react_2.Box, { ref: dropdownRef, marginTop: "0.313rem", position: "absolute", top: position === "below" ? "100%" : "auto", bottom: position === "above" ? "100%" : "auto", left: 0, right: 0, border: "0.063rem solid", borderColor: theme.colors.boxborder[200], borderRadius: "xl", boxShadow: theme.shadows.lg, bg: theme.colors.surfaceRaised, maxHeight: "9.375rem", overflowY: "auto", zIndex: 10, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { id: listboxId, role: "listbox", "aria-label": label !== null && label !== void 0 ? label : placeholder, onKeyDown: handleListKeyDown, children: (0, jsx_runtime_1.jsx)(RenderOptions, { isOptionLoading: isOptionLoading, filteredOptions: filteredOptions, loadingText: loadingText, handleOptionClick: handleOptionClick, isProfile: isProfile, isColorOptions: isColorOptions, isOptionSelected: isOptionSelected, activeIndex: activeIndex, optionRefs: optionRefs }) }), (0, jsx_runtime_1.jsx)("hr", {}), isBottomIcon && ((0, jsx_runtime_1.jsxs)(react_2.Box, { position: "sticky", bottom: 0, background: theme.colors.surfaceRaised, display: "flex", alignItems: "center", pl: "1rem", cursor: "pointer", onClick: handleBottomClick, borderColor: theme.colors.boxborder[200], zIndex: 1, borderTop: `1px solid ${theme.colors.boxborder[200]}`, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { children: BottomIcon ? BottomIcon : (0, jsx_runtime_1.jsx)(lucide_react_1.PlusIcon, { size: "1rem" }) }), (0, jsx_runtime_1.jsx)(react_2.Box, { fontSize: "0.875rem", color: BottomTextColor
|
|
237
|
+
}, onChange: (e) => handleInputChange(e.target.value), placeholder: placeholder, onKeyDown: handleKeyDown, name: name, role: "combobox", "aria-autocomplete": "list", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-required": isRequired || undefined, cursor: "pointer", sx: { ...(0, fieldStyles_1.fieldControlStyles)(theme.colors), ...(0, fieldStyles_1.controlSizeStyles)(size), ...(0, motion_2.reducedMotionSx)() }, style: inputStyle }), rightIcon && ((0, jsx_runtime_1.jsx)(react_2.InputRightElement, { pointerEvents: "none", children: rightIcon, style: { ...rightElementStyle } }))] }), isOpen && ((0, jsx_runtime_1.jsxs)(react_2.Box, { ref: dropdownRef, marginTop: "0.313rem", position: "absolute", top: position === "below" ? "100%" : "auto", bottom: position === "above" ? "100%" : "auto", left: 0, right: 0, border: "0.063rem solid", borderColor: theme.colors.boxborder[200], borderRadius: "xl", boxShadow: theme.shadows.lg, bg: theme.colors.surfaceRaised, maxHeight: "9.375rem", overflowY: "auto", zIndex: 10, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { id: listboxId, role: "listbox", "aria-label": label !== null && label !== void 0 ? label : placeholder, onKeyDown: handleListKeyDown, children: (0, jsx_runtime_1.jsx)(RenderOptions, { isOptionLoading: isOptionLoading, filteredOptions: filteredOptions, loadingText: loadingText, handleOptionClick: handleOptionClick, isProfile: isProfile, isColorOptions: isColorOptions, isOptionSelected: isOptionSelected, activeIndex: activeIndex, optionRefs: optionRefs, size: size, isMultipleSelect: isMultipleSelect }) }), (0, jsx_runtime_1.jsx)("hr", {}), isBottomIcon && ((0, jsx_runtime_1.jsxs)(react_2.Box, { position: "sticky", bottom: 0, background: theme.colors.surfaceRaised, display: "flex", alignItems: "center", pl: "1rem", cursor: "pointer", onClick: handleBottomClick, borderColor: theme.colors.boxborder[200], zIndex: 1, borderTop: `1px solid ${theme.colors.boxborder[200]}`, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { children: BottomIcon ? BottomIcon : (0, jsx_runtime_1.jsx)(lucide_react_1.PlusIcon, { size: "1rem" }) }), (0, jsx_runtime_1.jsx)(react_2.Box, { fontSize: "0.875rem", color: BottomTextColor
|
|
234
238
|
? BottomTextColor
|
|
235
239
|
: theme.colors.accentText, padding: "8px 8px", children: BottomText || "Add New" })] }))] })), (0, jsx_runtime_1.jsx)(FieldMessage_1.default, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }) }) }));
|
|
236
240
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SwitchProps } from "./SwitchProps";
|
|
2
|
-
/** @deprecated
|
|
2
|
+
/** @deprecated Two labelled choices; for a single on/off field use `ToggleSwitch`. Renders `SegmentedControl` at 12.5rem when labelled. */
|
|
3
3
|
export default function Switch({ leftText, rightText, leftIcon, rightIcon, onSwitchToggle, switchActive, surface, "aria-label": ariaLabel, }: SwitchProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -7,11 +7,13 @@ exports.default = Switch;
|
|
|
7
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
8
|
const react_1 = require("@chakra-ui/react");
|
|
9
9
|
const SegmentedControl_1 = __importDefault(require("../SegmentedControl/SegmentedControl"));
|
|
10
|
-
/** @deprecated
|
|
10
|
+
/** @deprecated Two labelled choices; for a single on/off field use `ToggleSwitch`. Renders `SegmentedControl` at 12.5rem when labelled. */
|
|
11
11
|
function Switch({ leftText, rightText, leftIcon, rightIcon, onSwitchToggle, switchActive, surface, "aria-label": ariaLabel, }) {
|
|
12
12
|
const options = [
|
|
13
13
|
{ value: "left", label: leftText, icon: leftIcon },
|
|
14
14
|
{ value: "right", label: rightText, icon: rightIcon },
|
|
15
15
|
];
|
|
16
|
-
|
|
16
|
+
// Unlabelled segments have nothing to size to, so the 12.5rem floor only overflowed its cell.
|
|
17
|
+
const hasLabels = Boolean(leftText || rightText);
|
|
18
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Box, { w: "fit-content", maxW: "100%", minW: hasLabels ? "12.5rem" : undefined, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.default, { options: options, value: switchActive, onChange: onSwitchToggle, variant: "brandSlide", surface: surface, isFullWidth: true, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : `${leftText} or ${rightText}` }) }));
|
|
17
19
|
}
|
|
@@ -8,7 +8,7 @@ export type SwitchProps = {
|
|
|
8
8
|
onSwitchToggle: (value: "left" | "right") => void;
|
|
9
9
|
/** Controlled selected side; the pill only moves when this changes. */
|
|
10
10
|
switchActive: "left" | "right";
|
|
11
|
-
/** Background this sits on.
|
|
11
|
+
/** Background this sits on. Optional: the track is opaque on any backdrop; pass it only to aim the plate's hairline edge at the real backdrop. */
|
|
12
12
|
surface?: string;
|
|
13
13
|
/** Names the radiogroup; pass it, as there is no visible group label. */
|
|
14
14
|
"aria-label"?: string;
|
|
@@ -6,10 +6,13 @@ const react_2 = require("@chakra-ui/react");
|
|
|
6
6
|
const lucide_react_1 = require("lucide-react");
|
|
7
7
|
const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
|
|
8
8
|
const rowStates_1 = require("../../../Theme/tokens/builders/rowStates");
|
|
9
|
+
const optionRowSizes_1 = require("../../../Theme/tokens/primitives/optionRowSizes");
|
|
10
|
+
const optionRowInk_1 = require("../../Select/optionRowInk");
|
|
9
11
|
const useExclusiveOverlay_1 = require("../../../Hooks/useExclusiveOverlay");
|
|
10
12
|
const CompactSelect = ({ options, value, placeholder = "Select", height = "22px", onSelect, size = "xs" }) => {
|
|
11
13
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
12
14
|
const rowBackgrounds = (0, react_1.useMemo)(() => (0, rowStates_1.listRowBackgrounds)(theme.colors), [theme.colors]);
|
|
15
|
+
const spec = (0, optionRowSizes_1.optionRowSpec)("xs");
|
|
13
16
|
const [isOpen, setIsOpen] = (0, react_1.useState)(false);
|
|
14
17
|
const requestExclusiveOpen = (0, useExclusiveOverlay_1.useExclusiveOverlay)(isOpen, () => setIsOpen(false));
|
|
15
18
|
const openDropdown = () => {
|
|
@@ -63,15 +66,15 @@ const CompactSelect = ({ options, value, placeholder = "Select", height = "22px"
|
|
|
63
66
|
: isActive
|
|
64
67
|
? rowBackgrounds.active
|
|
65
68
|
: theme.colors.transparent;
|
|
66
|
-
return ((0, jsx_runtime_1.
|
|
69
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "button", type: "button", role: "option", "aria-selected": selected, tabIndex: isActive ? 0 : -1, ref: (el) => {
|
|
67
70
|
optionRefs.current[i] = el;
|
|
68
|
-
}, w:
|
|
71
|
+
}, w: `calc(100% - 2 * ${spec.mx})`, minH: spec.minH, textAlign: "left", border: "none", fontFamily: "inherit", display: "flex", alignItems: "center", gap: spec.gap, mx: spec.mx, px: spec.px, py: spec.py, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, cursor: "pointer", color: (0, optionRowInk_1.optionRowInk)(theme.colors, selected), fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(selected), _hover: {
|
|
69
72
|
bg: selected ? rowBackgrounds.selectedActive : rowBackgrounds.active,
|
|
70
73
|
}, bg: rowBg, onClick: () => {
|
|
71
74
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(opt);
|
|
72
75
|
setIsOpen(false);
|
|
73
76
|
setQuery("");
|
|
74
|
-
}, children: (0, jsx_runtime_1.jsx)(react_2.Text, {
|
|
77
|
+
}, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { flex: 1, minW: 0, isTruncated: true, children: opt.label }), selected && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 }))] }, opt.id));
|
|
75
78
|
}), filtered.length === 0 && ((0, jsx_runtime_1.jsx)(react_2.Box, { px: 2, py: 2, children: (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "0.75rem", color: theme.colors.textMuted, children: "No results" }) }))] })] }) }) }) })] }));
|
|
76
79
|
};
|
|
77
80
|
exports.default = CompactSelect;
|
|
@@ -4,7 +4,9 @@ exports.Menu = void 0;
|
|
|
4
4
|
const react_1 = require("@chakra-ui/react");
|
|
5
5
|
const anatomy_1 = require("@chakra-ui/anatomy");
|
|
6
6
|
const rowStates_1 = require("../tokens/builders/rowStates");
|
|
7
|
+
const optionRowSizes_1 = require("../tokens/primitives/optionRowSizes");
|
|
7
8
|
const paletteFromTheme_1 = require("./paletteFromTheme");
|
|
9
|
+
const itemRowSpec = optionRowSizes_1.OPTION_ROW_SIZES.sm;
|
|
8
10
|
const { defineMultiStyleConfig, definePartsStyle } = (0, react_1.createMultiStyleConfigHelpers)(anatomy_1.menuAnatomy.keys);
|
|
9
11
|
const $bg = (0, react_1.cssVar)("menu-bg");
|
|
10
12
|
// NOTE: fixes Chakra's MenuList/MenuItem both hardcoding --menu-bg to white via dead _dark — item needs its own var too, or hover snaps back to white.
|
|
@@ -24,6 +26,12 @@ const baseStyle = definePartsStyle(({ theme }) => {
|
|
|
24
26
|
borderColor: colors.boxborder[200],
|
|
25
27
|
},
|
|
26
28
|
item: {
|
|
29
|
+
minH: itemRowSpec.minH,
|
|
30
|
+
px: itemRowSpec.px,
|
|
31
|
+
py: itemRowSpec.py,
|
|
32
|
+
mx: itemRowSpec.mx,
|
|
33
|
+
fontSize: itemRowSpec.fontSize,
|
|
34
|
+
borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS,
|
|
27
35
|
bg: "transparent",
|
|
28
36
|
_focus: { [$itemBg.variable]: hoverBg, bg: $itemBg.reference },
|
|
29
37
|
_hover: { [$itemBg.variable]: hoverBg, bg: $itemBg.reference },
|
|
@@ -176,6 +176,12 @@ export declare const componentStyles: {
|
|
|
176
176
|
borderColor: string;
|
|
177
177
|
};
|
|
178
178
|
item: {
|
|
179
|
+
minH: string;
|
|
180
|
+
px: string;
|
|
181
|
+
py: string;
|
|
182
|
+
mx: string;
|
|
183
|
+
fontSize: string;
|
|
184
|
+
borderRadius: string;
|
|
179
185
|
bg: string;
|
|
180
186
|
_focus: {
|
|
181
187
|
[x: string]: string;
|
|
@@ -4,6 +4,8 @@ export declare const AA_NON_TEXT = 3;
|
|
|
4
4
|
export declare const CHROME_BORDER = 1.5;
|
|
5
5
|
export declare const CHROME_RULE = 1.31;
|
|
6
6
|
export declare function ruleInk(hex: string, surfaces: readonly string[], target: number): string;
|
|
7
|
+
export declare const STACKED_STATE_STEP = 1.08;
|
|
8
|
+
export declare function tintStep(seed: string, backdrop: string, target: number): string;
|
|
7
9
|
export declare function liftToContrast(hex: string, backdrop: string, target?: number): string;
|
|
8
10
|
export declare function softenToContrast(hex: string, backdrop: string, target: number): string;
|
|
9
11
|
export declare function withOutlineRung(palette: PaletteProps): PaletteProps;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CHROME_RULE = exports.CHROME_BORDER = exports.AA_NON_TEXT = exports.MIN_PERCEPTIBLE_STEP = void 0;
|
|
3
|
+
exports.STACKED_STATE_STEP = exports.CHROME_RULE = exports.CHROME_BORDER = exports.AA_NON_TEXT = exports.MIN_PERCEPTIBLE_STEP = void 0;
|
|
4
4
|
exports.ruleInk = ruleInk;
|
|
5
|
+
exports.tintStep = tintStep;
|
|
5
6
|
exports.liftToContrast = liftToContrast;
|
|
6
7
|
exports.softenToContrast = softenToContrast;
|
|
7
8
|
exports.withOutlineRung = withOutlineRung;
|
|
@@ -20,6 +21,18 @@ function ruleInk(hex, surfaces, target) {
|
|
|
20
21
|
? softenToContrast(hex, loudest, target)
|
|
21
22
|
: hex;
|
|
22
23
|
}
|
|
24
|
+
exports.STACKED_STATE_STEP = 1.08;
|
|
25
|
+
// NOTE: walks out from the backdrop, so it settles a too-loud seed down as readily as it lifts a faint one.
|
|
26
|
+
function tintStep(seed, backdrop, target) {
|
|
27
|
+
const { s } = (0, color_1.hexToHsl)(seed);
|
|
28
|
+
const away = (0, color_1.relativeLuminance)(backdrop) < 0.5 ? 1 : -1;
|
|
29
|
+
for (let l = (0, color_1.hexToHsl)(backdrop).l; l >= 0 && l <= 100; l += away) {
|
|
30
|
+
const next = (0, color_1.withLightness)(seed, l, s);
|
|
31
|
+
if ((0, color_1.contrastRatio)(next, backdrop) >= target)
|
|
32
|
+
return next;
|
|
33
|
+
}
|
|
34
|
+
return (0, color_1.withLightness)(seed, away > 0 ? 100 : 0, s);
|
|
35
|
+
}
|
|
23
36
|
function liftToContrast(hex, backdrop, target = exports.AA_NON_TEXT) {
|
|
24
37
|
if ((0, color_1.contrastRatio)(hex, backdrop) >= target)
|
|
25
38
|
return hex;
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.listRowBackgrounds = void 0;
|
|
4
4
|
const outlineRung_1 = require("./outlineRung");
|
|
5
|
-
// NOTE: rungs stay neutral/pale — seeding from primary[500] instead once painted SelectV2's hover solid brand fill.
|
|
6
5
|
const listRowBackgrounds = (colors) => {
|
|
7
|
-
//
|
|
8
|
-
const selected = (0, outlineRung_1.
|
|
6
|
+
// Raw tertiary reaches 1.29:1 and reads as pressed; dark aliases it to the panel at 1.025:1.
|
|
7
|
+
const selected = (0, outlineRung_1.tintStep)(colors.backgroundColor.tertiary, colors.surfaceRaised, outlineRung_1.MIN_PERCEPTIBLE_STEP);
|
|
9
8
|
return {
|
|
10
9
|
active: (0, outlineRung_1.resolveHoverTint)(colors.gray[50], colors.surfaceRaised),
|
|
11
10
|
selected,
|
|
12
|
-
selectedActive: (0, outlineRung_1.
|
|
11
|
+
selectedActive: (0, outlineRung_1.tintStep)(selected, selected, outlineRung_1.STACKED_STATE_STEP),
|
|
13
12
|
};
|
|
14
13
|
};
|
|
15
14
|
exports.listRowBackgrounds = listRowBackgrounds;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ControlSize } from "./controlSizes";
|
|
2
|
+
export type OptionRowSpec = {
|
|
3
|
+
minH: string;
|
|
4
|
+
px: string;
|
|
5
|
+
py: string;
|
|
6
|
+
gap: string;
|
|
7
|
+
/** Inset so the row's rounded fill stops short of the popup edge. */
|
|
8
|
+
mx: string;
|
|
9
|
+
fontSize: string;
|
|
10
|
+
descriptionFontSize: string;
|
|
11
|
+
iconSize: number;
|
|
12
|
+
/** Avatar token for rows that lead with one — an `sm` avatar alone outgrows an `sm` row. */
|
|
13
|
+
avatarSize: "2xs" | "xs" | "sm";
|
|
14
|
+
/** Fixed row height the virtualiser scrolls by; must match a single-line row. */
|
|
15
|
+
virtualRowHeightPx: number;
|
|
16
|
+
};
|
|
17
|
+
/** One density for every option row — Select, SearchSelect, SelectSearch, table filters and Chakra's MenuItem. */
|
|
18
|
+
export declare const OPTION_ROW_SIZES: Readonly<Record<ControlSize, OptionRowSpec>>;
|
|
19
|
+
export declare const OPTION_ROW_RADIUS = "md";
|
|
20
|
+
export declare const optionRowSpec: (size: ControlSize) => OptionRowSpec;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.optionRowSpec = exports.OPTION_ROW_RADIUS = exports.OPTION_ROW_SIZES = void 0;
|
|
4
|
+
/** One density for every option row — Select, SearchSelect, SelectSearch, table filters and Chakra's MenuItem. */
|
|
5
|
+
exports.OPTION_ROW_SIZES = {
|
|
6
|
+
xs: {
|
|
7
|
+
minH: "1.5rem", px: "0.5rem", py: "0.125rem", gap: "0.375rem", mx: "0.25rem",
|
|
8
|
+
fontSize: "0.75rem", descriptionFontSize: "0.6875rem", iconSize: 12,
|
|
9
|
+
avatarSize: "2xs", virtualRowHeightPx: 24,
|
|
10
|
+
},
|
|
11
|
+
sm: {
|
|
12
|
+
minH: "1.875rem", px: "0.625rem", py: "0.25rem", gap: "0.5rem", mx: "0.375rem",
|
|
13
|
+
fontSize: "0.8125rem", descriptionFontSize: "0.75rem", iconSize: 14,
|
|
14
|
+
avatarSize: "xs", virtualRowHeightPx: 30,
|
|
15
|
+
},
|
|
16
|
+
md: {
|
|
17
|
+
minH: "2.125rem", px: "0.75rem", py: "0.3125rem", gap: "0.5rem", mx: "0.375rem",
|
|
18
|
+
fontSize: "0.875rem", descriptionFontSize: "0.75rem", iconSize: 16,
|
|
19
|
+
avatarSize: "xs", virtualRowHeightPx: 34,
|
|
20
|
+
},
|
|
21
|
+
lg: {
|
|
22
|
+
minH: "2.5rem", px: "0.875rem", py: "0.375rem", gap: "0.625rem", mx: "0.5rem",
|
|
23
|
+
fontSize: "1rem", descriptionFontSize: "0.8125rem", iconSize: 18,
|
|
24
|
+
avatarSize: "sm", virtualRowHeightPx: 40,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
exports.OPTION_ROW_RADIUS = "md";
|
|
28
|
+
const optionRowSpec = (size) => exports.OPTION_ROW_SIZES[size];
|
|
29
|
+
exports.optionRowSpec = optionRowSpec;
|
|
@@ -9,16 +9,17 @@ import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
|
|
|
9
9
|
import { ChevronDown } from "lucide-react";
|
|
10
10
|
import { rafThrottle } from "../../Utils/rafThrottle.js";
|
|
11
11
|
import { CONTROL_SIZES } from "../../Theme/tokens/primitives/controlSizes.js";
|
|
12
|
+
import { optionRowSpec, OPTION_ROW_RADIUS } from "../../Theme/tokens/primitives/optionRowSizes.js";
|
|
12
13
|
import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
13
14
|
import { reducedMotionSx } from "../../Theme/chakra/motion.js";
|
|
14
15
|
import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
|
|
15
16
|
const MotionBox = motion(forwardRef((props, ref) => (_jsx(Box, { ref: ref, ...props }))));
|
|
16
17
|
const SIZE_MAP = {
|
|
17
|
-
xs: { btn: "xs",
|
|
18
|
-
sm: { btn: "sm",
|
|
19
|
-
md: { btn: "md",
|
|
20
|
-
lg: { btn: "lg",
|
|
21
|
-
xl: { btn: "lg",
|
|
18
|
+
xs: { btn: "xs", img: "1rem", chevron: CONTROL_SIZES.xs.iconSize, minW: "8rem", gap: 1.5, rowSize: "xs" },
|
|
19
|
+
sm: { btn: "sm", img: "1rem", chevron: CONTROL_SIZES.sm.iconSize, minW: "9rem", gap: 2, rowSize: "sm" },
|
|
20
|
+
md: { btn: "md", img: "1.125rem", chevron: CONTROL_SIZES.md.iconSize, minW: "11rem", gap: 2, rowSize: "md" },
|
|
21
|
+
lg: { btn: "lg", img: "1.25rem", chevron: CONTROL_SIZES.lg.iconSize, minW: "12rem", gap: 2.5, rowSize: "lg" },
|
|
22
|
+
xl: { btn: "lg", img: "1.4rem", chevron: 20, minW: "13rem", gap: 3, rowSize: "lg" },
|
|
22
23
|
};
|
|
23
24
|
const TRIGGER_TRANSITION = transitionFor(["background-color", "border-color", "color", "box-shadow"]);
|
|
24
25
|
const ROW_TRANSITION = transitionFor(["background-color", "color"]);
|
|
@@ -35,6 +36,7 @@ const Dropdown = forwardRef(({ dropDownButtonStyle, ButtonText, options, handleO
|
|
|
35
36
|
const menuRef = useRef(null);
|
|
36
37
|
const [menuPos, setMenuPos] = useState({ top: 0, left: 0, width: 0 });
|
|
37
38
|
const s = SIZE_MAP[(_a = size !== null && size !== void 0 ? size : optionsSize) !== null && _a !== void 0 ? _a : "sm"];
|
|
39
|
+
const rowSpec = optionRowSpec(s.rowSize);
|
|
38
40
|
const prefersReducedMotion = useReducedMotion();
|
|
39
41
|
const menuId = useId();
|
|
40
42
|
// Roving-tabindex keyboard nav for the menu (ARIA APG menu-button pattern):
|
|
@@ -230,7 +232,7 @@ const Dropdown = forwardRef(({ dropDownButtonStyle, ButtonText, options, handleO
|
|
|
230
232
|
const Chevron = isVisibleIconShow ? (_jsx(Box, { as: "span", display: "inline-flex", transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", sx: reducedMotionSx(), children: _jsx(ChevronDown, { size: s.chevron }) })) : undefined;
|
|
231
233
|
const dropDownToggleOption = () => {
|
|
232
234
|
const ButtonToggle = (_jsx(Button, { ref: buttonTriggerRef, onClick: handleToggle, onKeyDown: handleTriggerKeyDown, size: s.btn, colorScheme: "primary", borderRadius: "lg", fontWeight: 600, "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": menuId, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : (ButtonText ? undefined : "Open menu"), rightIcon: Chevron, sx: { transition: TRIGGER_TRANSITION, ...reducedMotionSx(), ...dropDownButtonStyle }, ...buttonProps, children: _jsxs(Box, { as: "span", display: "inline-flex", alignItems: "center", gap: DropdownIcon && ButtonText ? s.gap : 0, children: [DropdownIcon, ButtonText] }) }));
|
|
233
|
-
const TextToggle = (_jsxs(Flex, { ref: textTriggerRef, onClick: handleToggle, onKeyDown: handleTriggerKeyDown, align: "center", gap: 1.5, cursor: "pointer", fontSize:
|
|
235
|
+
const TextToggle = (_jsxs(Flex, { ref: textTriggerRef, onClick: handleToggle, onKeyDown: handleTriggerKeyDown, align: "center", gap: 1.5, cursor: "pointer", fontSize: rowSpec.fontSize, fontWeight: 500, color: theme.colors.text[700], borderRadius: "md", transition: ROW_TRANSITION, _hover: { color: theme.colors.accentText }, sx: { ...focusVisibleStyles(theme.colors), ...reducedMotionSx() }, style: dropDownButtonStyle, role: "button", tabIndex: 0, "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": menuId, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : (text ? undefined : "Open menu"), children: [DropdownIcon, text, Chevron] }));
|
|
234
236
|
return dropdownType === "button" ? ButtonToggle : TextToggle;
|
|
235
237
|
};
|
|
236
238
|
const DropdownContentRender = () => {
|
|
@@ -240,7 +242,7 @@ const Dropdown = forwardRef(({ dropDownButtonStyle, ButtonText, options, handleO
|
|
|
240
242
|
}, role: "menuitem", tabIndex: index === activeIndex ? 0 : -1, onClick: () => {
|
|
241
243
|
handleListItemClick(option.id, option.label);
|
|
242
244
|
focusTrigger();
|
|
243
|
-
}, onKeyDown: (e) => handleItemKeyDown(e, option.id, option.label), mx:
|
|
245
|
+
}, onKeyDown: (e) => handleItemKeyDown(e, option.id, option.label), mx: rowSpec.mx, px: rowSpec.px, py: rowSpec.py, minH: rowSpec.minH, borderRadius: OPTION_ROW_RADIUS, display: "flex", alignItems: "center", gap: rowSpec.gap, fontSize: rowSpec.fontSize, cursor: "pointer", color: theme.colors.text[700], transition: ROW_TRANSITION, _hover: {
|
|
244
246
|
bg: hoverTint,
|
|
245
247
|
color: theme.colors.accentText,
|
|
246
248
|
}, _focus: {
|
|
@@ -253,7 +255,7 @@ const Dropdown = forwardRef(({ dropDownButtonStyle, ButtonText, options, handleO
|
|
|
253
255
|
boxShadow: `inset 0 0 0 2px ${theme.colors.focusRing.ring}`,
|
|
254
256
|
}, borderBottom: divider
|
|
255
257
|
? `0.063rem solid ${theme.colors.boxborder[200]}`
|
|
256
|
-
: undefined, sx: { "&:last-child": { borderBottom: "none" }, ...reducedMotionSx() }, style: ItemStyle, fontWeight: 500, children: [option.image && (_jsx(Box, { as: option.image, boxSize: s.img, flexShrink: 0, color: theme.colors.textMuted, style: ImageStyle })), _jsx(Box, { as: "span", fontSize:
|
|
258
|
+
: undefined, sx: { "&:last-child": { borderBottom: "none" }, ...reducedMotionSx() }, style: ItemStyle, fontWeight: 500, children: [option.image && (_jsx(Box, { as: option.image, boxSize: s.img, flexShrink: 0, color: theme.colors.textMuted, style: ImageStyle })), _jsx(Box, { as: "span", fontSize: rowSpec.fontSize, isTruncated: true, style: LabelStyle, children: option.label })] }, option.id))) }));
|
|
257
259
|
};
|
|
258
260
|
return (_jsxs(Box, { ref: dropdownRef, position: "relative", display: "inline-block", children: [dropDownToggleOption(), isOpen && (_jsx(Portal, { children: _jsx(MotionBox, { ref: setMenuRef, position: "fixed", top: `${menuPos.top}px`, left: `${menuPos.left}px`, minW: `${menuPos.width}px`, zIndex: 1500, initial: prefersReducedMotion ? false : { opacity: 0, y: -6 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -6 }, style: headStyle, children: DropdownContentRender() }) }))] }));
|
|
259
261
|
});
|
|
@@ -6,6 +6,8 @@ import { TextLabel } from "../Common/FormLabel.js";
|
|
|
6
6
|
import Tag from "../Tag/Tag.js";
|
|
7
7
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
8
8
|
import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
9
|
+
import { optionRowSpec } from "../../Theme/tokens/primitives/optionRowSizes.js";
|
|
10
|
+
import { optionRowInk, optionRowWeight } from "../Select/optionRowInk.js";
|
|
9
11
|
import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
|
|
10
12
|
import FieldMessage from "../Common/FieldMessage.js";
|
|
11
13
|
import { useFieldIds } from "../Common/useFieldIds.js";
|
|
@@ -37,6 +39,7 @@ const normalizeMultiValue = (val) => {
|
|
|
37
39
|
const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOptionLoading = false, isLoading = false, loadingText = "Loading...", placeholder = "Select", value, searchQuery = "", isMultiple = false, isRequired = false, error = false, errorMessage, helperText, width = "100%", height, size = "sm", isDisabled = false, chip, information, isInformation, informationMessage, rightIcon, addNew, isSelectAll = false, pagination, insideSelect, isRemoveAllIcon, "aria-label": ariaLabel, }) => {
|
|
38
40
|
var _a, _b, _c, _d, _e;
|
|
39
41
|
const s = getSearchSelectSizeStyles(size);
|
|
42
|
+
const spec = optionRowSpec(size);
|
|
40
43
|
const emitChange = onChange !== null && onChange !== void 0 ? onChange : onSelect;
|
|
41
44
|
const insideSelectBoxHeight = height !== null && height !== void 0 ? height : s.insideSelectDefaultHeight;
|
|
42
45
|
const [inputValue, setInputValue] = useState(searchQuery);
|
|
@@ -456,16 +459,14 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
456
459
|
}, children: [_jsxs(Box, { minW: "4.5rem", cursor: "pointer", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, width: insideSelect.width, height: insideSelectBoxHeight, pl: "0.5rem", pr: "0.375rem", children: [insideLabel ? (_jsx(SelectTruncatedLabel, { label: insideLabel, maxWidth: insideSelect.width || "6rem", fontSize: s.fieldFontSize })) : (_jsx(Text, { fontSize: s.fieldFontSize, fontWeight: 500, color: theme.colors.textMuted, isTruncated: true, maxW: insideSelect.width || "6rem", children: insidePlaceholder })), _jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, color: customSelectOpen ? theme.colors.accentText : theme.colors.textMuted, transition: CHEVRON_TRANSITION, transform: customSelectOpen ? "rotate(180deg)" : "rotate(0deg)", children: _jsx(ChevronDown, { size: s.chevronSize }) })] }), customSelectOpen && (_jsx(Portal, { children: _jsx(Box, { ref: setInsideSelectMenuRef, id: insideSelectPortalId, position: "fixed", top: `${customSelectPos.top}px`, left: `${customSelectPos.left}px`, zIndex: 1510, bg: theme.colors.surfaceRaised, border: "0.063rem solid", borderColor: theme.colors.boxborder[200], borderRadius: "xl", boxShadow: theme.shadows.lg, minW: `${customSelectPos.width}px`, maxW: "20rem", py: 1, overflow: "hidden", children: (_c = insideSelect.option) === null || _c === void 0 ? void 0 : _c.map((item) => {
|
|
457
460
|
var _a;
|
|
458
461
|
const selected = ((_a = insideSelect.value) === null || _a === void 0 ? void 0 : _a.id) === item.id;
|
|
459
|
-
return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap:
|
|
460
|
-
? theme.colors.accentText
|
|
461
|
-
: theme.colors.text[700], bg: selected ? rowBackgrounds.selected : "transparent", _hover: {
|
|
462
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: optionRowWeight(selected), color: optionRowInk(theme.colors, selected), bg: selected ? rowBackgrounds.selected : "transparent", _hover: {
|
|
462
463
|
bg: selected
|
|
463
464
|
? rowBackgrounds.selectedActive
|
|
464
465
|
: rowBackgrounds.active,
|
|
465
466
|
}, cursor: "pointer", onClick: (e) => {
|
|
466
467
|
e.stopPropagation();
|
|
467
468
|
handleCustomSelect(item);
|
|
468
|
-
}, children: [_jsx(Text, { isTruncated: true, children: item.label }), selected && (_jsx(Check, { size:
|
|
469
|
+
}, children: [_jsx(Text, { isTruncated: true, children: item.label }), selected && (_jsx(Check, { size: spec.iconSize, color: theme.colors.accentText }))] }, item.id));
|
|
469
470
|
}) }) }))] }));
|
|
470
471
|
})(), isMultiple
|
|
471
472
|
? selectedValues
|
|
@@ -497,16 +498,20 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
497
498
|
else {
|
|
498
499
|
openMenu();
|
|
499
500
|
}
|
|
500
|
-
}, "aria-label": "toggle-dropdown", display: "flex", alignItems: "center", borderRadius: "sm", color: theme.colors.textMuted, cursor: isDisabled ? "not-allowed" : "pointer", sx: focusVisibleStyles(theme.colors), children: isOpen ? (_jsx(ChevronUp, { size: s.chevronSize })) : (_jsx(ChevronDown, { size: s.chevronSize })) }))] })] }) }), isOpen && (_jsx(Portal, { children: _jsxs(Box, { ref: setDropdownMenuRef, id: listboxId, role: "listbox", "aria-multiselectable": isMultiple || undefined, position: "fixed", top: `${dropdownPos.top}px`, left: `${dropdownPos.left}px`, width: `${dropdownPos.width}px`, zIndex: 1500, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [isMultiple && isSelectAll && (_jsxs(Box, { py:
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
501
|
+
}, "aria-label": "toggle-dropdown", display: "flex", alignItems: "center", borderRadius: "sm", color: theme.colors.textMuted, cursor: isDisabled ? "not-allowed" : "pointer", sx: focusVisibleStyles(theme.colors), children: isOpen ? (_jsx(ChevronUp, { size: s.chevronSize })) : (_jsx(ChevronDown, { size: s.chevronSize })) }))] })] }) }), isOpen && (_jsx(Portal, { children: _jsxs(Box, { ref: setDropdownMenuRef, id: listboxId, role: "listbox", "aria-multiselectable": isMultiple || undefined, position: "fixed", top: `${dropdownPos.top}px`, left: `${dropdownPos.left}px`, width: `${dropdownPos.width}px`, zIndex: 1500, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [isMultiple && isSelectAll && (_jsxs(Box, { py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", onClick: handleSelectAll, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.text[700], borderBottom: `0.063rem solid ${theme.colors.boxborder[200]}`, children: [_jsx(Checkbox, { isChecked: allFilteredSelected, sx: { pointerEvents: "none" }, size: s.checkboxSize }), _jsx(Text, { fontSize: spec.fontSize, children: allFilteredSelected ? "Unselect All" : "Select All" })] })), _jsx(Box, { flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", borderBottom: "0.063rem solid", borderColor: theme.colors.boxborder[200], children: isOptionLoading && filteredOptions.length === 0 ? (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] })) : !isOptionLoading && filteredOptions.length === 0 ? (_jsx(Text, { p: 3, fontSize: spec.fontSize, color: theme.colors.textMuted, children: "No results found" })) : (_jsxs(_Fragment, { children: [filteredOptions.map((option, index) => {
|
|
502
|
+
const selected = isSelected(option.id);
|
|
503
|
+
const singleSelected = selected && !isMultiple;
|
|
504
|
+
return (_jsxs(Box, { id: getOptionId(index), role: "option", "aria-selected": selected, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", bg: selected
|
|
505
|
+
? index === activeIndex
|
|
506
|
+
? rowBackgrounds.selectedActive
|
|
507
|
+
: rowBackgrounds.selected
|
|
508
|
+
: index === activeIndex
|
|
509
|
+
? rowBackgrounds.active
|
|
510
|
+
: "transparent", _hover: {
|
|
511
|
+
bg: selected ? rowBackgrounds.selectedActive : rowBackgrounds.active,
|
|
512
|
+
}, onClick: () => handleSelect(option), onMouseEnter: () => setActiveIndex(index), display: "flex", alignItems: "center", gap: spec.gap, color: optionRowInk(theme.colors, singleSelected),
|
|
513
|
+
// `view` is consumer JSX with no size of its own; it inherited the 16px body font.
|
|
514
|
+
fontSize: spec.fontSize, fontWeight: optionRowWeight(singleSelected), borderBottom: `0.063rem solid ${theme.colors.boxborder[200]}`, children: [isMultiple && (_jsx(Checkbox, { isChecked: selected, sx: { pointerEvents: "none" }, size: s.checkboxSize })), option.view ? (_jsx(_Fragment, { children: option.view })) : (_jsx(Text, { isTruncated: true, children: option.label })), singleSelected && (_jsx(Box, { as: Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 }))] }, option.id));
|
|
515
|
+
}), ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", justifyContent: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] }))] })) }), (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && (_jsxs(Box, { py: spec.py, px: spec.px, borderTop: "0.063rem solid", borderColor: theme.colors.boxborder[200], bg: theme.colors.surfaceRaised, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.accentText, fontWeight: 500, minH: spec.minH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: rowBackgrounds.active }, children: [addNew.icon || _jsx(Plus, { size: s.addNewIconSize }), _jsx(Text, { fontSize: spec.fontSize, children: addNew.text || "Add New" })] }))] }) })), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
511
516
|
};
|
|
512
517
|
export default SearchSelect;
|
|
@@ -18,9 +18,6 @@ const SIZE_STYLES = {
|
|
|
18
18
|
clearIconSize: 10,
|
|
19
19
|
addNewIconSize: 14,
|
|
20
20
|
insideSelectMarginLeft: "-0.5rem",
|
|
21
|
-
dropdownTextFontSize: "xs",
|
|
22
|
-
optionRowMinH: "1.5rem",
|
|
23
|
-
optionRowPy: 1.5,
|
|
24
21
|
checkboxSize: "sm",
|
|
25
22
|
},
|
|
26
23
|
sm: {
|
|
@@ -39,9 +36,6 @@ const SIZE_STYLES = {
|
|
|
39
36
|
clearIconSize: 12,
|
|
40
37
|
addNewIconSize: 16,
|
|
41
38
|
insideSelectMarginLeft: "-0.625rem",
|
|
42
|
-
dropdownTextFontSize: "sm",
|
|
43
|
-
optionRowMinH: "2rem",
|
|
44
|
-
optionRowPy: 2,
|
|
45
39
|
checkboxSize: "sm",
|
|
46
40
|
},
|
|
47
41
|
md: {
|
|
@@ -60,9 +54,6 @@ const SIZE_STYLES = {
|
|
|
60
54
|
clearIconSize: 12,
|
|
61
55
|
addNewIconSize: 16,
|
|
62
56
|
insideSelectMarginLeft: "-0.75rem",
|
|
63
|
-
dropdownTextFontSize: "sm",
|
|
64
|
-
optionRowMinH: "2.5rem",
|
|
65
|
-
optionRowPy: 2,
|
|
66
57
|
checkboxSize: "md",
|
|
67
58
|
},
|
|
68
59
|
lg: {
|
|
@@ -81,9 +72,6 @@ const SIZE_STYLES = {
|
|
|
81
72
|
clearIconSize: 14,
|
|
82
73
|
addNewIconSize: 18,
|
|
83
74
|
insideSelectMarginLeft: "-0.875rem",
|
|
84
|
-
dropdownTextFontSize: "md",
|
|
85
|
-
optionRowMinH: "2.75rem",
|
|
86
|
-
optionRowPy: 2.5,
|
|
87
75
|
checkboxSize: "lg",
|
|
88
76
|
},
|
|
89
77
|
};
|
|
@@ -35,7 +35,9 @@ export default function SegmentedControl({ options, value, onChange, size = "sm"
|
|
|
35
35
|
const innerRadius = isUnderlineSlide ? "none" : `calc(${radii.lg} - ${TRACK_PADDING})`;
|
|
36
36
|
const focusShadow = segmentedFocusShadow(colors, trackSurface);
|
|
37
37
|
// Inset, never a border: a border consumes the content box and the absolute pill stops centring.
|
|
38
|
-
const plateEdge = isBrandSlide
|
|
38
|
+
const plateEdge = isBrandSlide
|
|
39
|
+
? `inset 0 0 0 1px ${segmentedPlateEdge(colors, surface)}`
|
|
40
|
+
: undefined;
|
|
39
41
|
// The transparent outline replaces the UA ring and is what Windows high-contrast repaints.
|
|
40
42
|
const segmentFocus = {
|
|
41
43
|
outline: "2px solid transparent",
|
|
@@ -82,7 +84,7 @@ export default function SegmentedControl({ options, value, onChange, size = "sm"
|
|
|
82
84
|
selectAt(enabled[enabled.length - 1]);
|
|
83
85
|
}
|
|
84
86
|
};
|
|
85
|
-
return (_jsxs(Flex, { ref: trackRef, role: "radiogroup", "aria-label": ariaLabel, onKeyDown: handleKeyDown, onFocus: (event) => setShowFocus(event.target.matches(":focus-visible")), onBlur: () => setShowFocus(false), boxShadow: showFocus && !isUnderlineSlide ? focusShadow : plateEdge, position: "relative", display: "inline-flex", align: "center", gap: isUnderlineSlide ? "0" : TRACK_PADDING, p: isUnderlineSlide ? "0" : TRACK_PADDING, h: control.height, boxSizing: "border-box", borderRadius: isUnderlineSlide ? "none" : "lg", borderBottom: isUnderlineSlide ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, bg:
|
|
87
|
+
return (_jsxs(Flex, { ref: trackRef, role: "radiogroup", "aria-label": ariaLabel, onKeyDown: handleKeyDown, onFocus: (event) => setShowFocus(event.target.matches(":focus-visible")), onBlur: () => setShowFocus(false), boxShadow: showFocus && !isUnderlineSlide ? focusShadow : plateEdge, position: "relative", display: "inline-flex", align: "center", gap: isUnderlineSlide ? "0" : TRACK_PADDING, p: isUnderlineSlide ? "0" : TRACK_PADDING, h: control.height, boxSizing: "border-box", borderRadius: isUnderlineSlide ? "none" : "lg", borderBottom: isUnderlineSlide ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, bg: isUnderlineSlide ? "transparent" : trackSurface, w: isFullWidth ? "100%" : "fit-content", minW: "max-content", children: [isBrandSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", top: TRACK_PADDING, left: 0, h: segmentH, w: `${pill.width}px`, borderRadius: innerRadius, bg: brandFill.fill, boxShadow: `0 0.125rem 0.5rem ${colors.boxShadow.primary}`, transform: `translateX(${pill.left}px)`, transition: SLIDE, sx: reducedMotionSx() })), isUnderlineSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", bottom: "-0.0625rem", left: 0, h: "0.125rem", w: `${pill.width}px`, borderRadius: "full", bg: colors.primary[500], transform: `translateX(${pill.left}px)`, transition: SLIDE, sx: reducedMotionSx() })), options.map((option, index) => {
|
|
86
88
|
const isSelected = option.value === value;
|
|
87
89
|
return (_jsxs(Box, { as: "button", type: "button", ref: (node) => {
|
|
88
90
|
refs.current[index] = node;
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { focusGlowShadow } from "../../Theme/chakra/focusRing.styles.js";
|
|
2
|
-
import { compositeOver } from "../../Theme/tokens/builders/color.js";
|
|
3
2
|
import { AA_NON_TEXT, liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
|
|
4
3
|
import { AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
|
|
5
|
-
/** The surface a segment's label really sits on
|
|
4
|
+
/** The surface a segment's label really sits on. */
|
|
6
5
|
export const segmentedTrackSurface = (variant, colors, surface) => {
|
|
7
|
-
const canvas = surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main;
|
|
8
6
|
if (variant === "underlineSlide")
|
|
9
|
-
return
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
if (variant === "brandSlide")
|
|
13
|
-
return
|
|
14
|
-
}
|
|
7
|
+
return surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main;
|
|
8
|
+
// brandSlide paints an opaque plate on every backdrop: a tint composites into an unknown
|
|
9
|
+
// parent, so its ink was derived from a canvas the control was not actually on.
|
|
10
|
+
if (variant === "brandSlide")
|
|
11
|
+
return colors.surfaceRaised;
|
|
15
12
|
return colors.background[600];
|
|
16
13
|
};
|
|
17
14
|
/** Unselected label ink, lifted to AA against the track it is drawn on. */
|
|
18
15
|
export const segmentedRestInk = (colors, trackSurface) => liftToContrast(colors.text[600], trackSurface, AA_NORMAL_TEXT);
|
|
19
16
|
/** A radiogroup is one tab stop, so the group wears the ring: a brand hairline plus the field bloom. */
|
|
20
17
|
export const segmentedFocusShadow = (colors, trackSurface) => `inset 0 0 0 1px ${liftToContrast(colors.primary[500], trackSurface, AA_NON_TEXT)}, ${focusGlowShadow(colors)}`;
|
|
21
|
-
/**
|
|
18
|
+
/** The plate can match its backdrop's lightness, so its edge carries the boundary.
|
|
22
19
|
* Drawn inset by the caller: a real border consumes the content box and decentres the sliding pill. */
|
|
23
|
-
export const segmentedPlateEdge = (colors, surface) => liftToContrast(colors.boxborder[500], surface, AA_NON_TEXT);
|
|
20
|
+
export const segmentedPlateEdge = (colors, surface) => liftToContrast(colors.boxborder[500], surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main, AA_NON_TEXT);
|