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
package/CHANGELOG.md
CHANGED
|
@@ -14,10 +14,9 @@ next major.
|
|
|
14
14
|
|
|
15
15
|
### Added
|
|
16
16
|
|
|
17
|
-
- **`SegmentedControl` / `Switch` — `surface`.** The background the control is drawn on.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
canvas, so existing call sites are unchanged.
|
|
17
|
+
- **`SegmentedControl` / `Switch` — `surface`.** The background the control is drawn on. Optional:
|
|
18
|
+
the track is an opaque plate on any backdrop, so the labels read without it. Pass it to aim the
|
|
19
|
+
plate's hairline edge at the real backdrop rather than at the page canvas.
|
|
21
20
|
- **`MultiSelect` — `addOnBlur`.** Commits the typed address on focus-out as well as on Enter.
|
|
22
21
|
Defaults to `false`, so existing call sites keep Enter-only behaviour.
|
|
23
22
|
|
|
@@ -64,6 +63,37 @@ next major.
|
|
|
64
63
|
|
|
65
64
|
### Fixed
|
|
66
65
|
|
|
66
|
+
- **`SegmentedControl` / `Switch` guessed the backdrop it was drawn on.** `brandSlide` painted an
|
|
67
|
+
8% brand tint composited over `backgroundColor.main` and lifted its unselected-label ink against
|
|
68
|
+
that same assumed canvas, so on a brand-filled bar the tint sank into the bar and the label
|
|
69
|
+
measured **1.03–2.03:1 across all 9 brands x 2 modes** — crm's `Overview / Timeline` view switcher
|
|
70
|
+
on a `primary[700]` bar was unreadable. The track is now an opaque plate on every backdrop: rest
|
|
71
|
+
ink 7.58–18.39:1, plate-vs-bar 6.31–19.0:1, with no change required at the call site. `surface`
|
|
72
|
+
is now only a refinement for the plate's hairline edge, and a test gates the invariant.
|
|
73
|
+
- **A selected `Select`/`SearchSelect`/`SelectSearch`/`CompactSelect` option stacked brand ink on a
|
|
74
|
+
brand-tinted row** — `CompactSelect` had no selected styling at all. The label is now `text[900]`
|
|
75
|
+
at weight 500 with the brand carried by a trailing check, matching `OrgSwitcher`. The row tint
|
|
76
|
+
itself is also settled to one perceptible step off the panel instead of taking the raw token
|
|
77
|
+
(`selected` 1.16–1.29:1 → 1.151–1.191:1; the pressed-and-selected state above it 1.34–1.52:1 →
|
|
78
|
+
1.259–1.340:1), so a too-saturated tint is pulled down as reliably as a too-faint one is lifted.
|
|
79
|
+
- **Every dropdown row is now one size.** `Select`, `SearchSelect`, `SelectSearch`,
|
|
80
|
+
`CompactSelect`'s filter, `DropDown`'s menu items and every themed `Menu` item shared six
|
|
81
|
+
different paddings for the same row; `sm` is now 30px tall everywhere (`padding: 4px 10px`,
|
|
82
|
+
`font-size: 13px`) instead of `SearchSelect` alone rendering ~34px with a 16px font. Row
|
|
83
|
+
content — including a `SearchSelect` `option.view` or `Select` `renderOption` node — now inherits
|
|
84
|
+
its font size from the row, so consumer-rendered row content is no longer stuck at the page's
|
|
85
|
+
16px body font.
|
|
86
|
+
- **A dropdown row's description sub-line fell under AA in dark mode.** Settling the selected rung
|
|
87
|
+
left `textMuted` at **4.00–4.22:1** on the hovered-selected row across 6 of 9 brands (dark
|
|
88
|
+
`surfaceRaised` carries only ~0.86 of headroom, so any lighter rung eats it). The sub-line ink is
|
|
89
|
+
now derived once per palette against that busiest rung, clearing 4.5:1 everywhere; light mode is
|
|
90
|
+
unchanged because it already cleared.
|
|
91
|
+
- **`Switch` no longer forces a 200px-wide track when it has no labels.** The width floor now only
|
|
92
|
+
applies once a label is set, so an unlabelled instance — the wrong component for a boolean field,
|
|
93
|
+
but one that shipped live in a 120px table cell — shrinks to fit instead of overflowing by 80px.
|
|
94
|
+
Use `ToggleSwitch` for an on/off field; swapping it in trades `role="radiogroup"` for
|
|
95
|
+
`role="switch"`, the same `getByRole` break the `InputSwitch` → `ToggleSwitch` migration caused.
|
|
96
|
+
|
|
67
97
|
- **`SegmentedControl`'s pill bulged past the plate's corners.** The pill and the segments carried
|
|
68
98
|
the track's own `lg` radius while sitting 2px inside it, so the plate's band stayed even along the
|
|
69
99
|
straight edges but pinched to nothing at the corners. They now round concentrically at
|
package/COMPONENT-GOTCHAS.md
CHANGED
|
@@ -49,27 +49,23 @@ opt-in, never assume a non-default look).
|
|
|
49
49
|
Pick by density and emphasis, not by taste. When unsure, or migrating an existing call site, leave
|
|
50
50
|
it on `"default"` — it's the only variant guaranteed to match previously-shipped screenshots.
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
**`brandSlide` no longer guesses its backdrop — and `surface` is now optional.** Until
|
|
53
|
+
2.4.2-beta.46 the track was an 8% brand tint composited over the **page canvas**, and the inactive
|
|
54
|
+
label's ink was lifted against that same assumed canvas. Dropped into a brand-filled header both
|
|
55
|
+
went wrong at once: the tint sank into the bar, and the label landed at **1.03–2.03:1** — rendered,
|
|
56
|
+
effectively invisible. The track is now an opaque plate (`colors.surfaceRaised`) whatever it sits
|
|
57
|
+
on, so a control on a coloured bar is readable with no prop passed (rest ink 7.58–18.39:1).
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
lightness (as low as 1.56:1 on radiant), so hue alone would not separate them.
|
|
59
|
+
Pass `surface` only to aim the plate's 1px edge at the real backdrop instead of the page canvas.
|
|
60
|
+
The edge is what carries the boundary: in dark mode the plate and a brand bar can sit at almost the
|
|
61
|
+
same lightness (as low as 1.56:1 on radiant), so hue alone would not separate them.
|
|
62
62
|
|
|
63
63
|
```tsx
|
|
64
64
|
<Box bg={theme.colors.primary[700]}>
|
|
65
|
-
<Switch ... surface={theme.colors.primary[700]} /> //
|
|
65
|
+
<Switch ... surface={theme.colors.primary[700]} /> // optional: sharpens the plate's edge
|
|
66
66
|
</Box>
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
Pass the same value you gave the wrapper's `bg`. It re-aims the unselected, selected and hover inks
|
|
70
|
-
together, and flips direction correctly in dark mode. `HeaderActions` takes `surface` for the same
|
|
71
|
-
reason.
|
|
72
|
-
|
|
73
69
|
### Which "select" component — `Select` is the only one to reach for now
|
|
74
70
|
|
|
75
71
|
There is now **one** picker. `Select` does both modes off the `native` prop:
|
|
@@ -131,7 +127,7 @@ is now the library's own themed component, with a real dual-thumb mode and a `ma
|
|
|
131
127
|
| One of 2-5 named options | `SegmentedControl` | `role="radiogroup"`, keyboard-navigable, supports per-option disabling |
|
|
132
128
|
| Switching between views or panels | `Tabs` | `role="tablist"` + `aria-controls`, count badges, lazy panels, vertical orientation |
|
|
133
129
|
| *(avoid)* | `InputSwitch` | `@deprecated` — now a thin wrapper that renders `ToggleSwitch` |
|
|
134
|
-
| *(avoid)* | `Switch` | `@deprecated` — a
|
|
130
|
+
| *(avoid)* | `Switch` | `@deprecated` — a two-label segmented control (`leftText`/`rightText`), not a boolean; for a single on/off field use `ToggleSwitch` instead |
|
|
135
131
|
|
|
136
132
|
**Apply:** "on vs off" is always `ToggleSwitch`; 2+ named options is always `SegmentedControl`.
|
|
137
133
|
|
|
@@ -1347,3 +1343,39 @@ Not just the columns — the select/edit/create header bar disappears too, since
|
|
|
1347
1343
|
returns `null` before rendering anything.
|
|
1348
1344
|
|
|
1349
1345
|
**Apply:** there's no prop for hiding only the board body while keeping header actions visible.
|
|
1346
|
+
|
|
1347
|
+
### `Switch` was forced to 200px even with no labels — it overflowed a narrow cell
|
|
1348
|
+
|
|
1349
|
+
`Switch` (`leftText`/`rightText`) kept a 12.5rem minimum width regardless of whether any label was
|
|
1350
|
+
set. Used as a boolean toggle — the wrong component for that, but the mistake shipped live in
|
|
1351
|
+
tickets' project custom-fields page — an unlabelled instance rendered as two unlabelled brand-filled
|
|
1352
|
+
pills at 200px inside a 120px table cell, overflowing by 80px. The width floor now only applies once
|
|
1353
|
+
`leftText`/`rightText` is actually set; an unlabelled instance shrinks to fit (~58px measured).
|
|
1354
|
+
|
|
1355
|
+
**Apply:** for a single on/off field use `ToggleSwitch`, not `Switch`, even now that it no longer
|
|
1356
|
+
overflows — a two-option segmented control still announces as `role="radiogroup"`, not
|
|
1357
|
+
`role="switch"`. Swapping `Switch` for `ToggleSwitch` changes `getByRole("radio", …)` queries to
|
|
1358
|
+
`getByRole("switch", …)` — the same test break the `InputSwitch` → `ToggleSwitch` migration caused.
|
|
1359
|
+
|
|
1360
|
+
### A `SearchSelect`/`Select` custom row node inherits the row's font size and padding — don't restate them
|
|
1361
|
+
|
|
1362
|
+
`SearchSelect`'s `option.view` and `Select`'s `renderOption` render straight inside the option row,
|
|
1363
|
+
which now carries its own `fontSize`/`px`/`py` so a plain-text row is legible with no extra markup.
|
|
1364
|
+
A custom node that also sets its own `px`/`py`/`fontSize` stacks on top of the row's rather than
|
|
1365
|
+
replacing it — this is what made rows in crm-frontend render 57–75px tall instead of the ~30px every
|
|
1366
|
+
other row uses.
|
|
1367
|
+
|
|
1368
|
+
**Apply:** size a custom `option.view`/`renderOption` node yourself only when it needs something
|
|
1369
|
+
*different* from the row default — inherit by default, don't restate it.
|
|
1370
|
+
|
|
1371
|
+
### A custom option row's sub-line must not use `colors.textMuted` directly
|
|
1372
|
+
|
|
1373
|
+
A selected row sits on a tinted fill, and the hovered-selected rung is the busiest one in the
|
|
1374
|
+
ladder. In dark mode `surfaceRaised` carries only ~0.86 of contrast headroom above AA, so raw
|
|
1375
|
+
`textMuted` on that rung measures **4.00–4.22:1** in 6 of 9 brands — under the 4.5:1 a 12px
|
|
1376
|
+
sub-line needs. The library's own rows no longer use it.
|
|
1377
|
+
|
|
1378
|
+
**Apply:** in a `SearchSelect` `option.view` or `Select` `renderOption` node, read the sub-line ink
|
|
1379
|
+
from `optionRowDescriptionInk(theme.colors)` (exported from the package root) rather than
|
|
1380
|
+
`theme.colors.textMuted`. It is a no-op in light mode. The row's main label is
|
|
1381
|
+
`optionRowInk(theme.colors, isSelected)` with `optionRowWeight(isSelected)` — never brand ink.
|
|
@@ -11,16 +11,17 @@ const useExclusiveOverlay_1 = require("../../Hooks/useExclusiveOverlay");
|
|
|
11
11
|
const lucide_react_1 = require("lucide-react");
|
|
12
12
|
const rafThrottle_1 = require("../../Utils/rafThrottle");
|
|
13
13
|
const controlSizes_1 = require("../../Theme/tokens/primitives/controlSizes");
|
|
14
|
+
const optionRowSizes_1 = require("../../Theme/tokens/primitives/optionRowSizes");
|
|
14
15
|
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
15
16
|
const motion_2 = require("../../Theme/chakra/motion");
|
|
16
17
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
17
18
|
const MotionBox = (0, framer_motion_1.motion)((0, react_1.forwardRef)((props, ref) => ((0, jsx_runtime_1.jsx)(react_2.Box, { ref: ref, ...props }))));
|
|
18
19
|
const SIZE_MAP = {
|
|
19
|
-
xs: { btn: "xs",
|
|
20
|
-
sm: { btn: "sm",
|
|
21
|
-
md: { btn: "md",
|
|
22
|
-
lg: { btn: "lg",
|
|
23
|
-
xl: { btn: "lg",
|
|
20
|
+
xs: { btn: "xs", img: "1rem", chevron: controlSizes_1.CONTROL_SIZES.xs.iconSize, minW: "8rem", gap: 1.5, rowSize: "xs" },
|
|
21
|
+
sm: { btn: "sm", img: "1rem", chevron: controlSizes_1.CONTROL_SIZES.sm.iconSize, minW: "9rem", gap: 2, rowSize: "sm" },
|
|
22
|
+
md: { btn: "md", img: "1.125rem", chevron: controlSizes_1.CONTROL_SIZES.md.iconSize, minW: "11rem", gap: 2, rowSize: "md" },
|
|
23
|
+
lg: { btn: "lg", img: "1.25rem", chevron: controlSizes_1.CONTROL_SIZES.lg.iconSize, minW: "12rem", gap: 2.5, rowSize: "lg" },
|
|
24
|
+
xl: { btn: "lg", img: "1.4rem", chevron: 20, minW: "13rem", gap: 3, rowSize: "lg" },
|
|
24
25
|
};
|
|
25
26
|
const TRIGGER_TRANSITION = (0, motion_1.transitionFor)(["background-color", "border-color", "color", "box-shadow"]);
|
|
26
27
|
const ROW_TRANSITION = (0, motion_1.transitionFor)(["background-color", "color"]);
|
|
@@ -37,6 +38,7 @@ const Dropdown = (0, react_1.forwardRef)(({ dropDownButtonStyle, ButtonText, opt
|
|
|
37
38
|
const menuRef = (0, react_1.useRef)(null);
|
|
38
39
|
const [menuPos, setMenuPos] = (0, react_1.useState)({ top: 0, left: 0, width: 0 });
|
|
39
40
|
const s = SIZE_MAP[(_a = size !== null && size !== void 0 ? size : optionsSize) !== null && _a !== void 0 ? _a : "sm"];
|
|
41
|
+
const rowSpec = (0, optionRowSizes_1.optionRowSpec)(s.rowSize);
|
|
40
42
|
const prefersReducedMotion = (0, framer_motion_1.useReducedMotion)();
|
|
41
43
|
const menuId = (0, react_1.useId)();
|
|
42
44
|
// Roving-tabindex keyboard nav for the menu (ARIA APG menu-button pattern):
|
|
@@ -232,7 +234,7 @@ const Dropdown = (0, react_1.forwardRef)(({ dropDownButtonStyle, ButtonText, opt
|
|
|
232
234
|
const Chevron = isVisibleIconShow ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", sx: (0, motion_2.reducedMotionSx)(), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: s.chevron }) })) : undefined;
|
|
233
235
|
const dropDownToggleOption = () => {
|
|
234
236
|
const ButtonToggle = ((0, jsx_runtime_1.jsx)(react_2.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, ...(0, motion_2.reducedMotionSx)(), ...dropDownButtonStyle }, ...buttonProps, children: (0, jsx_runtime_1.jsxs)(react_2.Box, { as: "span", display: "inline-flex", alignItems: "center", gap: DropdownIcon && ButtonText ? s.gap : 0, children: [DropdownIcon, ButtonText] }) }));
|
|
235
|
-
const TextToggle = ((0, jsx_runtime_1.jsxs)(react_2.Flex, { ref: textTriggerRef, onClick: handleToggle, onKeyDown: handleTriggerKeyDown, align: "center", gap: 1.5, cursor: "pointer", fontSize:
|
|
237
|
+
const TextToggle = ((0, jsx_runtime_1.jsxs)(react_2.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: { ...(0, focusRing_styles_1.focusVisibleStyles)(theme.colors), ...(0, motion_2.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] }));
|
|
236
238
|
return dropdownType === "button" ? ButtonToggle : TextToggle;
|
|
237
239
|
};
|
|
238
240
|
const DropdownContentRender = () => {
|
|
@@ -242,7 +244,7 @@ const Dropdown = (0, react_1.forwardRef)(({ dropDownButtonStyle, ButtonText, opt
|
|
|
242
244
|
}, role: "menuitem", tabIndex: index === activeIndex ? 0 : -1, onClick: () => {
|
|
243
245
|
handleListItemClick(option.id, option.label);
|
|
244
246
|
focusTrigger();
|
|
245
|
-
}, onKeyDown: (e) => handleItemKeyDown(e, option.id, option.label), mx:
|
|
247
|
+
}, onKeyDown: (e) => handleItemKeyDown(e, option.id, option.label), mx: rowSpec.mx, px: rowSpec.px, py: rowSpec.py, minH: rowSpec.minH, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, display: "flex", alignItems: "center", gap: rowSpec.gap, fontSize: rowSpec.fontSize, cursor: "pointer", color: theme.colors.text[700], transition: ROW_TRANSITION, _hover: {
|
|
246
248
|
bg: hoverTint,
|
|
247
249
|
color: theme.colors.accentText,
|
|
248
250
|
}, _focus: {
|
|
@@ -255,7 +257,7 @@ const Dropdown = (0, react_1.forwardRef)(({ dropDownButtonStyle, ButtonText, opt
|
|
|
255
257
|
boxShadow: `inset 0 0 0 2px ${theme.colors.focusRing.ring}`,
|
|
256
258
|
}, borderBottom: divider
|
|
257
259
|
? `0.063rem solid ${theme.colors.boxborder[200]}`
|
|
258
|
-
: undefined, sx: { "&:last-child": { borderBottom: "none" }, ...(0, motion_2.reducedMotionSx)() }, style: ItemStyle, fontWeight: 500, children: [option.image && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: option.image, boxSize: s.img, flexShrink: 0, color: theme.colors.textMuted, style: ImageStyle })), (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", fontSize:
|
|
260
|
+
: undefined, sx: { "&:last-child": { borderBottom: "none" }, ...(0, motion_2.reducedMotionSx)() }, style: ItemStyle, fontWeight: 500, children: [option.image && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: option.image, boxSize: s.img, flexShrink: 0, color: theme.colors.textMuted, style: ImageStyle })), (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", fontSize: rowSpec.fontSize, isTruncated: true, style: LabelStyle, children: option.label })] }, option.id))) }));
|
|
259
261
|
};
|
|
260
262
|
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { ref: dropdownRef, position: "relative", display: "inline-block", children: [dropDownToggleOption(), isOpen && ((0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.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() }) }))] }));
|
|
261
263
|
});
|
|
@@ -44,6 +44,8 @@ const FormLabel_1 = require("../Common/FormLabel");
|
|
|
44
44
|
const Tag_1 = __importDefault(require("../Tag/Tag"));
|
|
45
45
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
46
46
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
47
|
+
const optionRowSizes_1 = require("../../Theme/tokens/primitives/optionRowSizes");
|
|
48
|
+
const optionRowInk_1 = require("../Select/optionRowInk");
|
|
47
49
|
const useExclusiveOverlay_1 = require("../../Hooks/useExclusiveOverlay");
|
|
48
50
|
const FieldMessage_1 = __importDefault(require("../Common/FieldMessage"));
|
|
49
51
|
const useFieldIds_1 = require("../Common/useFieldIds");
|
|
@@ -75,6 +77,7 @@ const normalizeMultiValue = (val) => {
|
|
|
75
77
|
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, }) => {
|
|
76
78
|
var _a, _b, _c, _d, _e;
|
|
77
79
|
const s = (0, searchSelectSize_1.getSearchSelectSizeStyles)(size);
|
|
80
|
+
const spec = (0, optionRowSizes_1.optionRowSpec)(size);
|
|
78
81
|
const emitChange = onChange !== null && onChange !== void 0 ? onChange : onSelect;
|
|
79
82
|
const insideSelectBoxHeight = height !== null && height !== void 0 ? height : s.insideSelectDefaultHeight;
|
|
80
83
|
const [inputValue, setInputValue] = (0, react_1.useState)(searchQuery);
|
|
@@ -494,16 +497,14 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
494
497
|
}, children: [(0, jsx_runtime_1.jsxs)(react_2.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 ? ((0, jsx_runtime_1.jsx)(SelectTruncatedLabel_1.default, { label: insideLabel, maxWidth: insideSelect.width || "6rem", fontSize: s.fieldFontSize })) : ((0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: s.fieldFontSize, fontWeight: 500, color: theme.colors.textMuted, isTruncated: true, maxW: insideSelect.width || "6rem", children: insidePlaceholder })), (0, jsx_runtime_1.jsx)(react_2.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: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: s.chevronSize }) })] }), customSelectOpen && ((0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.jsx)(react_2.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) => {
|
|
495
498
|
var _a;
|
|
496
499
|
const selected = ((_a = insideSelect.value) === null || _a === void 0 ? void 0 : _a.id) === item.id;
|
|
497
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap:
|
|
498
|
-
? theme.colors.accentText
|
|
499
|
-
: theme.colors.text[700], bg: selected ? rowBackgrounds.selected : "transparent", _hover: {
|
|
500
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(selected), color: (0, optionRowInk_1.optionRowInk)(theme.colors, selected), bg: selected ? rowBackgrounds.selected : "transparent", _hover: {
|
|
500
501
|
bg: selected
|
|
501
502
|
? rowBackgrounds.selectedActive
|
|
502
503
|
: rowBackgrounds.active,
|
|
503
504
|
}, cursor: "pointer", onClick: (e) => {
|
|
504
505
|
e.stopPropagation();
|
|
505
506
|
handleCustomSelect(item);
|
|
506
|
-
}, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { isTruncated: true, children: item.label }), selected && ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size:
|
|
507
|
+
}, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { isTruncated: true, children: item.label }), selected && ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size: spec.iconSize, color: theme.colors.accentText }))] }, item.id));
|
|
507
508
|
}) }) }))] }));
|
|
508
509
|
})(), isMultiple
|
|
509
510
|
? selectedValues
|
|
@@ -535,16 +536,20 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
535
536
|
else {
|
|
536
537
|
openMenu();
|
|
537
538
|
}
|
|
538
|
-
}, "aria-label": "toggle-dropdown", display: "flex", alignItems: "center", borderRadius: "sm", color: theme.colors.textMuted, cursor: isDisabled ? "not-allowed" : "pointer", sx: (0, focusRing_styles_1.focusVisibleStyles)(theme.colors), children: isOpen ? ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronUp, { size: s.chevronSize })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: s.chevronSize })) }))] })] }) }), isOpen && ((0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.jsxs)(react_2.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 && ((0, jsx_runtime_1.jsxs)(react_2.Box, { py:
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
539
|
+
}, "aria-label": "toggle-dropdown", display: "flex", alignItems: "center", borderRadius: "sm", color: theme.colors.textMuted, cursor: isDisabled ? "not-allowed" : "pointer", sx: (0, focusRing_styles_1.focusVisibleStyles)(theme.colors), children: isOpen ? ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronUp, { size: s.chevronSize })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: s.chevronSize })) }))] })] }) }), isOpen && ((0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.jsxs)(react_2.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 && ((0, jsx_runtime_1.jsxs)(react_2.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: [(0, jsx_runtime_1.jsx)(Checkbox_1.default, { isChecked: allFilteredSelected, sx: { pointerEvents: "none" }, size: s.checkboxSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: allFilteredSelected ? "Unselect All" : "Select All" })] })), (0, jsx_runtime_1.jsx)(react_2.Box, { flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", borderBottom: "0.063rem solid", borderColor: theme.colors.boxborder[200], children: isOptionLoading && filteredOptions.length === 0 ? ((0, jsx_runtime_1.jsxs)(react_2.Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: s.spinnerSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: loadingText })] })) : !isOptionLoading && filteredOptions.length === 0 ? ((0, jsx_runtime_1.jsx)(react_2.Text, { p: 3, fontSize: spec.fontSize, color: theme.colors.textMuted, children: "No results found" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [filteredOptions.map((option, index) => {
|
|
540
|
+
const selected = isSelected(option.id);
|
|
541
|
+
const singleSelected = selected && !isMultiple;
|
|
542
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: getOptionId(index), role: "option", "aria-selected": selected, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", bg: selected
|
|
543
|
+
? index === activeIndex
|
|
544
|
+
? rowBackgrounds.selectedActive
|
|
545
|
+
: rowBackgrounds.selected
|
|
546
|
+
: index === activeIndex
|
|
547
|
+
? rowBackgrounds.active
|
|
548
|
+
: "transparent", _hover: {
|
|
549
|
+
bg: selected ? rowBackgrounds.selectedActive : rowBackgrounds.active,
|
|
550
|
+
}, onClick: () => handleSelect(option), onMouseEnter: () => setActiveIndex(index), display: "flex", alignItems: "center", gap: spec.gap, color: (0, optionRowInk_1.optionRowInk)(theme.colors, singleSelected),
|
|
551
|
+
// `view` is consumer JSX with no size of its own; it inherited the 16px body font.
|
|
552
|
+
fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(singleSelected), borderBottom: `0.063rem solid ${theme.colors.boxborder[200]}`, children: [isMultiple && ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { isChecked: selected, sx: { pointerEvents: "none" }, size: s.checkboxSize })), option.view ? ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: option.view })) : ((0, jsx_runtime_1.jsx)(react_2.Text, { isTruncated: true, children: option.label })), singleSelected && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 }))] }, option.id));
|
|
553
|
+
}), ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && ((0, jsx_runtime_1.jsxs)(react_2.Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", justifyContent: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: s.spinnerSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: loadingText })] }))] })) }), (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && ((0, jsx_runtime_1.jsxs)(react_2.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 || (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: s.addNewIconSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: addNew.text || "Add New" })] }))] }) })), (0, jsx_runtime_1.jsx)(FieldMessage_1.default, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
549
554
|
};
|
|
550
555
|
exports.default = SearchSelect;
|
|
@@ -19,11 +19,6 @@ export type SearchSelectSizeStyles = {
|
|
|
19
19
|
addNewIconSize: number;
|
|
20
20
|
/** Offset for the nested select segment (aligns with field padding). */
|
|
21
21
|
insideSelectMarginLeft: string;
|
|
22
|
-
/** “Select all” / option row text in the dropdown. */
|
|
23
|
-
dropdownTextFontSize: "xs" | "sm" | "md";
|
|
24
|
-
/** Min row height for main option list items. */
|
|
25
|
-
optionRowMinH: string;
|
|
26
|
-
optionRowPy: number;
|
|
27
22
|
/** Chakra `Checkbox` only supports sm | md | lg. */
|
|
28
23
|
checkboxSize: "sm" | "md" | "lg";
|
|
29
24
|
/** Chakra `Input` font token per `size` (unstyled still needs explicit fontSize). */
|
|
@@ -21,9 +21,6 @@ const SIZE_STYLES = {
|
|
|
21
21
|
clearIconSize: 10,
|
|
22
22
|
addNewIconSize: 14,
|
|
23
23
|
insideSelectMarginLeft: "-0.5rem",
|
|
24
|
-
dropdownTextFontSize: "xs",
|
|
25
|
-
optionRowMinH: "1.5rem",
|
|
26
|
-
optionRowPy: 1.5,
|
|
27
24
|
checkboxSize: "sm",
|
|
28
25
|
},
|
|
29
26
|
sm: {
|
|
@@ -42,9 +39,6 @@ const SIZE_STYLES = {
|
|
|
42
39
|
clearIconSize: 12,
|
|
43
40
|
addNewIconSize: 16,
|
|
44
41
|
insideSelectMarginLeft: "-0.625rem",
|
|
45
|
-
dropdownTextFontSize: "sm",
|
|
46
|
-
optionRowMinH: "2rem",
|
|
47
|
-
optionRowPy: 2,
|
|
48
42
|
checkboxSize: "sm",
|
|
49
43
|
},
|
|
50
44
|
md: {
|
|
@@ -63,9 +57,6 @@ const SIZE_STYLES = {
|
|
|
63
57
|
clearIconSize: 12,
|
|
64
58
|
addNewIconSize: 16,
|
|
65
59
|
insideSelectMarginLeft: "-0.75rem",
|
|
66
|
-
dropdownTextFontSize: "sm",
|
|
67
|
-
optionRowMinH: "2.5rem",
|
|
68
|
-
optionRowPy: 2,
|
|
69
60
|
checkboxSize: "md",
|
|
70
61
|
},
|
|
71
62
|
lg: {
|
|
@@ -84,9 +75,6 @@ const SIZE_STYLES = {
|
|
|
84
75
|
clearIconSize: 14,
|
|
85
76
|
addNewIconSize: 18,
|
|
86
77
|
insideSelectMarginLeft: "-0.875rem",
|
|
87
|
-
dropdownTextFontSize: "md",
|
|
88
|
-
optionRowMinH: "2.75rem",
|
|
89
|
-
optionRowPy: 2.5,
|
|
90
78
|
checkboxSize: "lg",
|
|
91
79
|
},
|
|
92
80
|
};
|
|
@@ -41,7 +41,9 @@ function SegmentedControl({ options, value, onChange, size = "sm", isFullWidth =
|
|
|
41
41
|
const innerRadius = isUnderlineSlide ? "none" : `calc(${radii.lg} - ${TRACK_PADDING})`;
|
|
42
42
|
const focusShadow = (0, segmentedInk_1.segmentedFocusShadow)(colors, trackSurface);
|
|
43
43
|
// Inset, never a border: a border consumes the content box and the absolute pill stops centring.
|
|
44
|
-
const plateEdge = isBrandSlide
|
|
44
|
+
const plateEdge = isBrandSlide
|
|
45
|
+
? `inset 0 0 0 1px ${(0, segmentedInk_1.segmentedPlateEdge)(colors, surface)}`
|
|
46
|
+
: undefined;
|
|
45
47
|
// The transparent outline replaces the UA ring and is what Windows high-contrast repaints.
|
|
46
48
|
const segmentFocus = {
|
|
47
49
|
outline: "2px solid transparent",
|
|
@@ -88,7 +90,7 @@ function SegmentedControl({ options, value, onChange, size = "sm", isFullWidth =
|
|
|
88
90
|
selectAt(enabled[enabled.length - 1]);
|
|
89
91
|
}
|
|
90
92
|
};
|
|
91
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.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:
|
|
93
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.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 && ((0, jsx_runtime_1.jsx)(react_2.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: (0, motion_2.reducedMotionSx)() })), isUnderlineSlide && pill && ((0, jsx_runtime_1.jsx)(react_2.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: (0, motion_2.reducedMotionSx)() })), options.map((option, index) => {
|
|
92
94
|
const isSelected = option.value === value;
|
|
93
95
|
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "button", type: "button", ref: (node) => {
|
|
94
96
|
refs.current[index] = node;
|
|
@@ -27,7 +27,7 @@ export type SegmentedControlProps<T extends string = string> = {
|
|
|
27
27
|
isFullWidth?: boolean;
|
|
28
28
|
/** brandSlide = primary view switcher; underlineSlide = dense toolbars/headers. */
|
|
29
29
|
variant?: SegmentedControlVariant;
|
|
30
|
-
/** Background this sits on.
|
|
30
|
+
/** 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. */
|
|
31
31
|
surface?: string;
|
|
32
32
|
"aria-label": string;
|
|
33
33
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { PaletteProps } from "../../Theme/tokens/types";
|
|
2
2
|
import type { SegmentedControlVariant } from "./SegmentedControlProps";
|
|
3
|
-
/** The surface a segment's label really sits on
|
|
3
|
+
/** The surface a segment's label really sits on. */
|
|
4
4
|
export declare const segmentedTrackSurface: (variant: SegmentedControlVariant, colors: PaletteProps, surface?: string) => string;
|
|
5
5
|
/** Unselected label ink, lifted to AA against the track it is drawn on. */
|
|
6
6
|
export declare const segmentedRestInk: (colors: PaletteProps, trackSurface: string) => string;
|
|
7
7
|
/** A radiogroup is one tab stop, so the group wears the ring: a brand hairline plus the field bloom. */
|
|
8
8
|
export declare const segmentedFocusShadow: (colors: PaletteProps, trackSurface: string) => string;
|
|
9
|
-
/**
|
|
9
|
+
/** The plate can match its backdrop's lightness, so its edge carries the boundary.
|
|
10
10
|
* Drawn inset by the caller: a real border consumes the content box and decentres the sliding pill. */
|
|
11
|
-
export declare const segmentedPlateEdge: (colors: PaletteProps, surface
|
|
11
|
+
export declare const segmentedPlateEdge: (colors: PaletteProps, surface?: string) => string;
|
|
@@ -2,19 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.segmentedPlateEdge = exports.segmentedFocusShadow = exports.segmentedRestInk = exports.segmentedTrackSurface = void 0;
|
|
4
4
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
5
|
-
const color_1 = require("../../Theme/tokens/builders/color");
|
|
6
5
|
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
7
6
|
const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
8
|
-
/** The surface a segment's label really sits on
|
|
7
|
+
/** The surface a segment's label really sits on. */
|
|
9
8
|
const segmentedTrackSurface = (variant, colors, surface) => {
|
|
10
|
-
const canvas = surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main;
|
|
11
9
|
if (variant === "underlineSlide")
|
|
12
|
-
return
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
if (variant === "brandSlide")
|
|
16
|
-
return
|
|
17
|
-
}
|
|
10
|
+
return surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main;
|
|
11
|
+
// brandSlide paints an opaque plate on every backdrop: a tint composites into an unknown
|
|
12
|
+
// parent, so its ink was derived from a canvas the control was not actually on.
|
|
13
|
+
if (variant === "brandSlide")
|
|
14
|
+
return colors.surfaceRaised;
|
|
18
15
|
return colors.background[600];
|
|
19
16
|
};
|
|
20
17
|
exports.segmentedTrackSurface = segmentedTrackSurface;
|
|
@@ -24,7 +21,7 @@ exports.segmentedRestInk = segmentedRestInk;
|
|
|
24
21
|
/** A radiogroup is one tab stop, so the group wears the ring: a brand hairline plus the field bloom. */
|
|
25
22
|
const segmentedFocusShadow = (colors, trackSurface) => `inset 0 0 0 1px ${(0, outlineRung_1.liftToContrast)(colors.primary[500], trackSurface, outlineRung_1.AA_NON_TEXT)}, ${(0, focusRing_styles_1.focusGlowShadow)(colors)}`;
|
|
26
23
|
exports.segmentedFocusShadow = segmentedFocusShadow;
|
|
27
|
-
/**
|
|
24
|
+
/** The plate can match its backdrop's lightness, so its edge carries the boundary.
|
|
28
25
|
* Drawn inset by the caller: a real border consumes the content box and decentres the sliding pill. */
|
|
29
|
-
const segmentedPlateEdge = (colors, surface) => (0, outlineRung_1.liftToContrast)(colors.boxborder[500], surface, outlineRung_1.AA_NON_TEXT);
|
|
26
|
+
const segmentedPlateEdge = (colors, surface) => (0, outlineRung_1.liftToContrast)(colors.boxborder[500], surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main, outlineRung_1.AA_NON_TEXT);
|
|
30
27
|
exports.segmentedPlateEdge = segmentedPlateEdge;
|
|
@@ -43,6 +43,8 @@ const lucide_react_1 = require("lucide-react");
|
|
|
43
43
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
44
44
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
45
45
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
46
|
+
const optionRowInk_1 = require("./optionRowInk");
|
|
47
|
+
const optionRowSizes_1 = require("../../Theme/tokens/primitives/optionRowSizes");
|
|
46
48
|
const scrollbar_1 = require("../../Theme/tokens/builders/scrollbar");
|
|
47
49
|
const controlSizes_1 = require("../../Theme/tokens/primitives/controlSizes");
|
|
48
50
|
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
@@ -58,8 +60,6 @@ const CHEVRON_TRANSITION = (0, motion_1.transitionFor)(["transform"], "base");
|
|
|
58
60
|
const PAGE_STEP = 8;
|
|
59
61
|
const LOAD_MORE_THRESHOLD_PX = 8;
|
|
60
62
|
const DEFAULT_MAX_VISIBLE_CHIPS = 6;
|
|
61
|
-
// NOTE: approximate single-row height (px 2.5 + ~13px text); used only to window `virtualizeAfter`, never for layout.
|
|
62
|
-
const VIRTUAL_ROW_HEIGHT_PX = 34;
|
|
63
63
|
const VIRTUAL_OVERSCAN = 4;
|
|
64
64
|
const CHIP_TAG_HEIGHT_PX = { xs: 20, sm: 24 };
|
|
65
65
|
const controlHeightPx = (size) => parseFloat(controlSizes_1.CONTROL_SIZES[size].height) * 16;
|
|
@@ -76,11 +76,11 @@ const parseRemToPx = (value, fallbackPx) => {
|
|
|
76
76
|
const match = /^(\d*\.?\d+)rem$/.exec(value);
|
|
77
77
|
return match ? parseFloat(match[1]) * 16 : fallbackPx;
|
|
78
78
|
};
|
|
79
|
-
const OptionRow = react_1.default.memo(function OptionRow({ option, index, listboxId, isSelected, isActive, isMulti, theme, backgrounds, renderOption, onSelect, onActivate, }) {
|
|
79
|
+
const OptionRow = react_1.default.memo(function OptionRow({ option, index, listboxId, isSelected, isActive, isMulti, theme, backgrounds, spec, renderOption, onSelect, onActivate, }) {
|
|
80
80
|
const isSingleSelected = isSelected && !isMulti;
|
|
81
81
|
const selectedBg = isActive ? backgrounds.selectedActive : backgrounds.selected;
|
|
82
82
|
const restBg = isActive ? backgrounds.active : "transparent";
|
|
83
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: optionDomId(listboxId, option.id), "data-index": index, role: "option", "aria-selected": isSelected, "aria-disabled": option.isDisabled, onMouseEnter: () => onActivate(index), onClick: () => onSelect(option), mx:
|
|
83
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: optionDomId(listboxId, option.id), "data-index": index, role: "option", "aria-selected": isSelected, "aria-disabled": option.isDisabled, onMouseEnter: () => onActivate(index), onClick: () => onSelect(option), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: option.isDisabled ? "not-allowed" : "pointer", opacity: option.isDisabled ? 0.4 : 1, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(isSingleSelected), color: (0, optionRowInk_1.optionRowInk)(theme.colors, isSingleSelected), bg: isSingleSelected ? selectedBg : restBg, transition: ROW_TRANSITION, sx: (0, motion_2.reducedMotionSx)(), children: [isMulti ? ((0, jsx_runtime_1.jsx)(react_2.Checkbox, { id: `${optionDomId(listboxId, option.id)}-checkbox`, isChecked: isSelected, isDisabled: option.isDisabled, pointerEvents: "none", tabIndex: -1 })) : null, renderOption ? ((0, jsx_runtime_1.jsx)(react_2.Box, { flex: 1, minW: 0, children: renderOption(option, { isSelected, isActive }) })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [option.icon ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, children: option.icon })) : null, (0, jsx_runtime_1.jsxs)(react_2.Box, { flex: 1, minW: 0, isTruncated: true, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", isTruncated: true, children: option.label }), option.description ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "block", fontSize: spec.descriptionFontSize, color: (0, optionRowInk_1.optionRowDescriptionInk)(theme.colors), isTruncated: true, children: option.description })) : null] })] })), isSingleSelected ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 })) : null] }));
|
|
84
84
|
});
|
|
85
85
|
const buildGroupedRows = (options) => {
|
|
86
86
|
const rows = [];
|
|
@@ -214,27 +214,28 @@ const SelectPopup = (0, react_1.forwardRef)(function SelectPopup(props, ref) {
|
|
|
214
214
|
const frame = requestAnimationFrame(() => { var _a; return (_a = filterInputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); });
|
|
215
215
|
return () => cancelAnimationFrame(frame);
|
|
216
216
|
}, [isOpen, isSearchable]);
|
|
217
|
+
const spec = (0, react_1.useMemo)(() => (0, optionRowSizes_1.optionRowSpec)(size), [size]);
|
|
217
218
|
const maxMenuHeightPx = (0, react_1.useMemo)(() => parseRemToPx(maxMenuHeight, 256), [maxMenuHeight]);
|
|
218
219
|
const shouldVirtualize = !!virtualizeAfter && !hasGroups && filteredOptions.length > virtualizeAfter;
|
|
219
220
|
const [scrollTop, setScrollTop] = (0, react_1.useState)(0);
|
|
220
|
-
const visibleCount = Math.ceil(maxMenuHeightPx /
|
|
221
|
+
const visibleCount = Math.ceil(maxMenuHeightPx / spec.virtualRowHeightPx) + VIRTUAL_OVERSCAN * 2;
|
|
221
222
|
const startIndex = shouldVirtualize
|
|
222
|
-
? Math.max(0, Math.floor(scrollTop /
|
|
223
|
+
? Math.max(0, Math.floor(scrollTop / spec.virtualRowHeightPx) - VIRTUAL_OVERSCAN)
|
|
223
224
|
: 0;
|
|
224
225
|
const endIndex = shouldVirtualize
|
|
225
226
|
? Math.min(filteredOptions.length, startIndex + visibleCount)
|
|
226
227
|
: filteredOptions.length;
|
|
227
228
|
const renderedOptions = shouldVirtualize ? filteredOptions.slice(startIndex, endIndex) : filteredOptions;
|
|
228
|
-
const topPadPx = shouldVirtualize ? startIndex *
|
|
229
|
-
const bottomPadPx = shouldVirtualize ? (filteredOptions.length - endIndex) *
|
|
229
|
+
const topPadPx = shouldVirtualize ? startIndex * spec.virtualRowHeightPx : 0;
|
|
230
|
+
const bottomPadPx = shouldVirtualize ? (filteredOptions.length - endIndex) * spec.virtualRowHeightPx : 0;
|
|
230
231
|
(0, react_1.useEffect)(() => {
|
|
231
232
|
var _a, _b;
|
|
232
233
|
if (activeIndex < 0 || !listRef.current)
|
|
233
234
|
return;
|
|
234
235
|
const el = listRef.current;
|
|
235
236
|
if (shouldVirtualize) {
|
|
236
|
-
const targetTop = activeIndex *
|
|
237
|
-
const targetBottom = targetTop +
|
|
237
|
+
const targetTop = activeIndex * spec.virtualRowHeightPx;
|
|
238
|
+
const targetBottom = targetTop + spec.virtualRowHeightPx;
|
|
238
239
|
if (targetTop < el.scrollTop) {
|
|
239
240
|
el.scrollTop = targetTop;
|
|
240
241
|
setScrollTop(targetTop);
|
|
@@ -247,7 +248,7 @@ const SelectPopup = (0, react_1.forwardRef)(function SelectPopup(props, ref) {
|
|
|
247
248
|
return;
|
|
248
249
|
}
|
|
249
250
|
(_b = (_a = el.querySelector(`[data-index="${activeIndex}"]`)) === null || _a === void 0 ? void 0 : _a.scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a, { block: "nearest" });
|
|
250
|
-
}, [activeIndex, shouldVirtualize, maxMenuHeightPx]);
|
|
251
|
+
}, [activeIndex, shouldVirtualize, maxMenuHeightPx, spec.virtualRowHeightPx]);
|
|
251
252
|
const moveActive = (delta) => {
|
|
252
253
|
if (!filteredOptions.length)
|
|
253
254
|
return;
|
|
@@ -410,14 +411,14 @@ const SelectPopup = (0, react_1.forwardRef)(function SelectPopup(props, ref) {
|
|
|
410
411
|
const selectedValueIds = selectedOptions.map((option) => option.id.toString());
|
|
411
412
|
const hiddenValues = !name ? [] : props.isMulti ? selectedValueIds : [(_c = selectedValueIds[0]) !== null && _c !== void 0 ? _c : ""];
|
|
412
413
|
const groupedRows = (0, react_1.useMemo)(() => (hasGroups ? buildGroupedRows(filteredOptions) : []), [hasGroups, filteredOptions]);
|
|
413
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.FormControl, { isInvalid: error, isDisabled: isDisabled, isReadOnly: isReadOnly, style: formControlStyle, width: width, children: [label && ((0, jsx_runtime_1.jsx)(FormLabel_1.TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), (0, jsx_runtime_1.jsxs)(react_2.Popover, { isOpen: isOpen, onClose: () => resetAndClose(), placement: "bottom-start", autoFocus: false, returnFocusOnClose: false, gutter: 4, children: [(0, jsx_runtime_1.jsx)(react_2.PopoverAnchor, { children: (0, jsx_runtime_1.jsxs)(react_2.Box, { as: "div", ref: mergedTriggerRef, ...ids.controlProps, role: "combobox", tabIndex: isDisabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-owns": `${listboxId} ${filterInputId}`, "aria-activedescendant": activeDescendantId, "aria-disabled": isDisabled || undefined, "data-readonly": isReadOnly || undefined, "aria-required": isRequired || undefined, "aria-label": triggerAriaLabel(), "aria-labelledby": ariaLabelledBy, onClick: handleOpen, onKeyDown: handleTriggerKeyDown, display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", textAlign: "left", cursor: isDisabled ? "not-allowed" : isReadOnly ? "default" : "pointer", "data-testid": dataTestId, sx: triggerSx, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexWrap: showChips ? "wrap" : "nowrap", alignItems: "center", gap: showChips ? 1 : 1.5, minW: 0, flex: 1, isTruncated: !showChips, children: showChips ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [visibleChipOptions.map((option) => ((0, jsx_runtime_1.jsx)(Tag_1.default, { label: String(option.label), size: chipSize, variant: "subtle", icon: isInteractive ? lucide_react_1.X : undefined, onIconClick: isInteractive ? () => handleSelect(option) : undefined }, option.id))), overflowChipCount > 0 && ((0, jsx_runtime_1.jsx)(Tag_1.default, { label: `+${overflowChipCount}`, size: chipSize, variant: "subtle", colorScheme: "gray" }))] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [triggerIcon ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, children: triggerIcon })) : null, (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", isTruncated: true, children: renderValue ? renderValue(selectedOptions) : defaultTriggerContent() })] })) }), showClear && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", "aria-label": "Clear selection", onClick: handleClear, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: controlSizes_1.CONTROL_SIZES[size].iconSize }) })), (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, ml: 2, transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", color: colors.textMuted, sx: (0, motion_2.reducedMotionSx)(), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: controlSizes_1.CONTROL_SIZES[size].iconSize }) })] }) }), (0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.jsxs)(react_2.PopoverContent, { width: "max-content", minWidth: triggerWidth ? `${triggerWidth}px` : undefined, maxWidth: "min(24rem, calc(100vw - 2rem))", borderRadius: "xl", boxShadow: theme.shadows.lg, overflow: "hidden", onKeyDown: handleListKeyDown, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { p: 2, borderBottom: `0.063rem solid ${colors.boxborder[200]}`, display: isSearchable ? undefined : "none", children: (0, jsx_runtime_1.jsx)(react_2.Input, { ref: filterInputRef, id: filterInputId, size: "sm", placeholder: "Type to filter...", value: currentSearch, onChange: handleFilterChange, "aria-activedescendant": activeDescendantId, "aria-controls": listboxId, "aria-label": "Filter options", tabIndex: isSearchable ? undefined : -1 }) }), (0, jsx_runtime_1.jsxs)(react_2.Box, { id: listboxId, ref: listRef, role: "listbox", "aria-label": (_d = label !== null && label !== void 0 ? label : ariaLabel) !== null && _d !== void 0 ? _d : placeholder, "aria-multiselectable": props.isMulti || undefined, "aria-busy": isLoading || undefined, maxH: maxMenuHeight, overflowY: "auto", py: 1, sx: listScrollStyles, onScroll: handleListScroll, children: [filteredOptions.length === 0 && !isLoading && !showCreateRow && ((0, jsx_runtime_1.jsx)(react_2.Box, { px:
|
|
414
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.FormControl, { isInvalid: error, isDisabled: isDisabled, isReadOnly: isReadOnly, style: formControlStyle, width: width, children: [label && ((0, jsx_runtime_1.jsx)(FormLabel_1.TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), (0, jsx_runtime_1.jsxs)(react_2.Popover, { isOpen: isOpen, onClose: () => resetAndClose(), placement: "bottom-start", autoFocus: false, returnFocusOnClose: false, gutter: 4, children: [(0, jsx_runtime_1.jsx)(react_2.PopoverAnchor, { children: (0, jsx_runtime_1.jsxs)(react_2.Box, { as: "div", ref: mergedTriggerRef, ...ids.controlProps, role: "combobox", tabIndex: isDisabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-owns": `${listboxId} ${filterInputId}`, "aria-activedescendant": activeDescendantId, "aria-disabled": isDisabled || undefined, "data-readonly": isReadOnly || undefined, "aria-required": isRequired || undefined, "aria-label": triggerAriaLabel(), "aria-labelledby": ariaLabelledBy, onClick: handleOpen, onKeyDown: handleTriggerKeyDown, display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", textAlign: "left", cursor: isDisabled ? "not-allowed" : isReadOnly ? "default" : "pointer", "data-testid": dataTestId, sx: triggerSx, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexWrap: showChips ? "wrap" : "nowrap", alignItems: "center", gap: showChips ? 1 : 1.5, minW: 0, flex: 1, isTruncated: !showChips, children: showChips ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [visibleChipOptions.map((option) => ((0, jsx_runtime_1.jsx)(Tag_1.default, { label: String(option.label), size: chipSize, variant: "subtle", icon: isInteractive ? lucide_react_1.X : undefined, onIconClick: isInteractive ? () => handleSelect(option) : undefined }, option.id))), overflowChipCount > 0 && ((0, jsx_runtime_1.jsx)(Tag_1.default, { label: `+${overflowChipCount}`, size: chipSize, variant: "subtle", colorScheme: "gray" }))] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [triggerIcon ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, children: triggerIcon })) : null, (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", isTruncated: true, children: renderValue ? renderValue(selectedOptions) : defaultTriggerContent() })] })) }), showClear && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", "aria-label": "Clear selection", onClick: handleClear, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: controlSizes_1.CONTROL_SIZES[size].iconSize }) })), (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, ml: 2, transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", color: colors.textMuted, sx: (0, motion_2.reducedMotionSx)(), children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: controlSizes_1.CONTROL_SIZES[size].iconSize }) })] }) }), (0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.jsxs)(react_2.PopoverContent, { width: "max-content", minWidth: triggerWidth ? `${triggerWidth}px` : undefined, maxWidth: "min(24rem, calc(100vw - 2rem))", borderRadius: "xl", boxShadow: theme.shadows.lg, overflow: "hidden", onKeyDown: handleListKeyDown, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { p: 2, borderBottom: `0.063rem solid ${colors.boxborder[200]}`, display: isSearchable ? undefined : "none", children: (0, jsx_runtime_1.jsx)(react_2.Input, { ref: filterInputRef, id: filterInputId, size: "sm", placeholder: "Type to filter...", value: currentSearch, onChange: handleFilterChange, "aria-activedescendant": activeDescendantId, "aria-controls": listboxId, "aria-label": "Filter options", tabIndex: isSearchable ? undefined : -1 }) }), (0, jsx_runtime_1.jsxs)(react_2.Box, { id: listboxId, ref: listRef, role: "listbox", "aria-label": (_d = label !== null && label !== void 0 ? label : ariaLabel) !== null && _d !== void 0 ? _d : placeholder, "aria-multiselectable": props.isMulti || undefined, "aria-busy": isLoading || undefined, maxH: maxMenuHeight, overflowY: "auto", py: 1, sx: listScrollStyles, onScroll: handleListScroll, children: [filteredOptions.length === 0 && !isLoading && !showCreateRow && ((0, jsx_runtime_1.jsx)(react_2.Box, { mx: spec.mx, px: spec.px, py: spec.py, fontSize: spec.fontSize, color: colors.textMuted, children: noOptionsMessage !== null && noOptionsMessage !== void 0 ? noOptionsMessage : "No Options" })), showCreateRow && ((0, jsx_runtime_1.jsx)(react_2.Box, { id: createRowId, role: "option", "aria-selected": false, "data-index": -1, onClick: handleCreate, mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", fontSize: spec.fontSize, fontWeight: 600, color: colors.primary[600], bg: activeDescendantId === createRowId ? rowBackgrounds.active : "transparent", children: createLabel ? createLabel(currentSearch.trim()) : `Create "${currentSearch.trim()}"` })), hasGroups
|
|
414
415
|
? groupedRows.map((row) => {
|
|
415
416
|
var _a;
|
|
416
|
-
return row.kind === "header" ? ((0, jsx_runtime_1.jsx)(react_2.Box, { px:
|
|
417
|
+
return row.kind === "header" ? ((0, jsx_runtime_1.jsx)(react_2.Box, { mx: spec.mx, px: spec.px, pt: 2, pb: 1, fontSize: "0.6875rem", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.03em", color: colors.textMuted, children: row.group }, row.key)) : ((0, jsx_runtime_1.jsx)(OptionRow, { option: row.option, index: row.index, listboxId: listboxId, isSelected: selectedIds.has(row.option.id.toString()), isActive: row.index === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, spec: spec, renderOption: renderOption, onSelect: handleSelect, onActivate: setActiveIndex }, row.key));
|
|
417
418
|
})
|
|
418
419
|
: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [topPadPx > 0 && (0, jsx_runtime_1.jsx)(react_2.Box, { height: `${topPadPx}px` }), renderedOptions.map((option, i) => {
|
|
419
420
|
var _a;
|
|
420
|
-
return ((0, jsx_runtime_1.jsx)(OptionRow, { option: option, index: startIndex + i, listboxId: listboxId, isSelected: selectedIds.has(option.id.toString()), isActive: startIndex + i === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, renderOption: renderOption, onSelect: handleSelect, onActivate: setActiveIndex }, option.id));
|
|
421
|
-
}), bottomPadPx > 0 && (0, jsx_runtime_1.jsx)(react_2.Box, { height: `${bottomPadPx}px` })] })), isLoading && ((0, jsx_runtime_1.jsxs)(react_2.Box, { px:
|
|
421
|
+
return ((0, jsx_runtime_1.jsx)(OptionRow, { option: option, index: startIndex + i, listboxId: listboxId, isSelected: selectedIds.has(option.id.toString()), isActive: startIndex + i === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, spec: spec, renderOption: renderOption, onSelect: handleSelect, onActivate: setActiveIndex }, option.id));
|
|
422
|
+
}), bottomPadPx > 0 && (0, jsx_runtime_1.jsx)(react_2.Box, { height: `${bottomPadPx}px` })] })), isLoading && ((0, jsx_runtime_1.jsxs)(react_2.Box, { mx: spec.mx, px: spec.px, py: spec.py, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: colors.textMuted, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: "xs" }), loadingText] }))] })] }) })] }), hiddenValues.map((value) => ((0, jsx_runtime_1.jsx)("input", { type: "hidden", name: name, value: value }, value))), (0, jsx_runtime_1.jsx)(FieldMessage_1.default, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
422
423
|
});
|
|
423
424
|
exports.default = SelectPopup;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PaletteProps } from "../../Theme/tokens/types";
|
|
2
|
+
/** Selection is carried by weight and a trailing brand check, so the label itself stays neutral. */
|
|
3
|
+
export declare const optionRowInk: (colors: PaletteProps, isSingleSelected: boolean) => string;
|
|
4
|
+
export declare const optionRowDescriptionInk: (colors: PaletteProps) => string;
|
|
5
|
+
export declare const OPTION_ROW_REST_WEIGHT = 400;
|
|
6
|
+
export declare const OPTION_ROW_SELECTED_WEIGHT = 500;
|
|
7
|
+
export declare const optionRowWeight: (isSingleSelected: boolean) => number;
|