pixelize-design-library 2.4.2-beta.66 → 2.4.2-beta.67
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 +10 -0
- package/COMPONENT-GOTCHAS.md +8 -8
- package/dist/Components/NavLauncher/NavLauncher.js +3 -0
- package/dist/Components/NavLauncher/launcherMotion.d.ts +19 -0
- package/dist/Components/NavLauncher/launcherMotion.js +15 -0
- package/dist/Components/Table/TableProps.d.ts +1 -1
- package/dist/Components/Table/components/TableBody.js +10 -1
- package/dist/Components/Table/components/rowFocusRing.d.ts +21 -0
- package/dist/Components/Table/components/rowFocusRing.js +20 -0
- package/dist/esm/Components/NavLauncher/NavLauncher.js +4 -1
- package/dist/esm/Components/NavLauncher/launcherMotion.js +11 -0
- package/dist/esm/Components/Table/components/TableBody.js +10 -1
- package/dist/esm/Components/Table/components/rowFocusRing.js +16 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -139,6 +139,7 @@ next major.
|
|
|
139
139
|
- **`ChoiceCard` / `ChoiceCardGroup`**: radio cards for picking one of a few options that each need a line of
|
|
140
140
|
explanation. Group: `value` / `defaultValue` / `onValueChange`; card: `label`, `description`, `icon`,
|
|
141
141
|
`disabled`. One tab stop; arrows, Home and End move focus and selection. Same look as `PaymentOption`.
|
|
142
|
+
`Pagination` and `ChoiceCard`: reviewed on screen by the owner 2026-10-15, take as-is.
|
|
142
143
|
- **Plain HTML attributes on the page primitives**: `PageHeader`, `StatCard`, `SaveBar`, `EmptyState`,
|
|
143
144
|
`DescriptionList`, `FilterBar` and `Skeletons` pass `id`, `className`, `style`, `data-*`, `aria-*` and
|
|
144
145
|
event handlers to their root element; the component's own props still win and Chakra style props are
|
|
@@ -430,6 +431,11 @@ next major.
|
|
|
430
431
|
|
|
431
432
|
### Changed
|
|
432
433
|
|
|
434
|
+
- **`Table` rows are keyboard-reachable when `onRowClick` is set.** Each row becomes a tab stop (`tabIndex` 0) and Enter or Space
|
|
435
|
+
on the focused row fires `onRowClick` with the first visible column that has no `node`, the same call a click on that cell makes.
|
|
436
|
+
Rows of a table without `onRowClick`, with every column on `node`, or while `isLoading` are unchanged. **Consumers that pass
|
|
437
|
+
`onRowClick` gain one Tab stop per row**: update tab-order tests and expect long tables to need more Tab presses to cross.
|
|
438
|
+
|
|
433
439
|
- **`ProfileCard` shares the dropped-slot dev warning with `Modal` and `Drawer`.** The helper takes any owner name and
|
|
434
440
|
warns once per owner and type (previously one set across owners); `ProfileCard` keeps its own message text.
|
|
435
441
|
- **`MediaViewer` image pan no longer re-renders the viewer on every pointer move.** The pan offset is held in a ref and
|
|
@@ -556,6 +562,10 @@ next major.
|
|
|
556
562
|
|
|
557
563
|
### Changed (visual)
|
|
558
564
|
|
|
565
|
+
- **`Table` rows with `onRowClick` show a keyboard focus ring.** A focused row draws the library's two-tone ring inset around its
|
|
566
|
+
cells (sticky cells included), on `:focus-visible` only, so a mouse click draws nothing. It clears 3:1 on every row plane in light and dark.
|
|
567
|
+
|
|
568
|
+
- **`NavLauncher` slides in with the library's 200 ms enter tween instead of Chakra's drawer spring.** The spring took about 570 ms to come to rest on a full-height panel; the tween (`motionTransition("enter", "out")`, instant under `prefers-reduced-motion`) lands in 200 ms. Measured at 4x CPU throttle, 1280x800, 12 runs against an unchanged build: click to panel at rest and search field focused, median 765 to 413 ms (spotlight, -46%), 819 to 481 ms (classic, -41%), 787 to 460 ms (dark spotlight, -42%). The resting layout and the exit are unchanged.
|
|
559
569
|
- **`NavLauncher` text snaps to the type scale.** The trigger, panel and spotlight (`NavLauncher`, `launcherParts`, `spotlightParts`) read
|
|
560
570
|
`xs-plus` (13px) where they drew `12.5px` and `xs-minus` (11px) where they drew `11.5px`; the `13.5px` sizes stay. Each size moves by 0.5px.
|
|
561
571
|
- **Disabled `Button` dims to the shared 0.5.** The base style drew `opacity: 0.6` while every other disabled control reads
|
package/COMPONENT-GOTCHAS.md
CHANGED
|
@@ -708,14 +708,14 @@ accepted, typed, never called.
|
|
|
708
708
|
**Apply:** leave at least one column without a `node`, or put an explicit control inside a `node`
|
|
709
709
|
renderer instead of relying on row click.
|
|
710
710
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
`
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
**Apply:**
|
|
717
|
-
|
|
718
|
-
|
|
711
|
+
Setting `onRowClick` makes every row a tab stop: Enter or Space on the focused row (not on a checkbox,
|
|
712
|
+
button or link inside it) fires `onRowClick` with the first visible column that has no `node`. A table
|
|
713
|
+
without `onRowClick`, one whose every column has a `node`, and a table that is `isLoading` get no tab
|
|
714
|
+
stop. The column resize handle is a bare `div` with a `mousedown` handler and is unreachable by keyboard.
|
|
715
|
+
|
|
716
|
+
**Apply:** expect one extra Tab stop per row once you pass `onRowClick`, and update any tab-order test.
|
|
717
|
+
Controls inside a row keep their own keys; give a `node` column a real `Button` or link for the actions
|
|
718
|
+
that must not depend on the row.
|
|
719
719
|
|
|
720
720
|
### `Table.renderRowDetail` runs for every row on every render, and `expandedRows` ids match strictly
|
|
721
721
|
|
|
@@ -63,6 +63,7 @@ const LauncherTabs_1 = require("./LauncherTabs");
|
|
|
63
63
|
const match_1 = require("./match");
|
|
64
64
|
const NewTabLink_1 = __importDefault(require("./NewTabLink"));
|
|
65
65
|
const PinToggle_1 = __importDefault(require("./PinToggle"));
|
|
66
|
+
const launcherMotion_1 = require("./launcherMotion");
|
|
66
67
|
const resultNavigation_1 = require("./resultNavigation");
|
|
67
68
|
const ScopeBar_1 = require("./ScopeBar");
|
|
68
69
|
const searchLayout_1 = require("./searchLayout");
|
|
@@ -243,6 +244,7 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
243
244
|
const isDesktop = (0, useMatchMedia_1.useMatchMedia)(MD_QUERY, true);
|
|
244
245
|
const isLg = (0, useMatchMedia_1.useMatchMedia)(LG_QUERY, true);
|
|
245
246
|
const isWide = (0, useMatchMedia_1.useMatchMedia)(XL_QUERY, false);
|
|
247
|
+
const isReducedMotion = (0, motion_1.usePrefersReducedMotion)();
|
|
246
248
|
const columns = isWide ? 3 : isDesktop ? 2 : 1;
|
|
247
249
|
const [innerQuery, setInnerQuery] = (0, react_2.useState)("");
|
|
248
250
|
const [activeIndex, setActiveIndex] = (0, react_2.useState)(0);
|
|
@@ -828,6 +830,7 @@ function NavLauncher({ isOpen, onClose, sections: rawSections, pinnedIds, freque
|
|
|
828
830
|
borderBottomRightRadius: { base: 0, lg: "16px" },
|
|
829
831
|
boxShadow: colors.boxShadow.default,
|
|
830
832
|
overflow: "hidden",
|
|
833
|
+
motionProps: (0, launcherMotion_1.launcherSlide)(isReducedMotion),
|
|
831
834
|
onKeyDown: onContentKeyDown,
|
|
832
835
|
...{ "data-testid": testId },
|
|
833
836
|
}, children: [(0, jsx_runtime_1.jsxs)(Drawer_1.DrawerHeader, { p: 0, flexShrink: 0, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { "data-testid": "launcher-header-row", align: "center", gap: { base: "8px", md: "12px" }, minH: { base: "60px", md: "68px" }, pl: { base: "12px", md: "18px" }, pr: { base: "8px", md: "12px" }, children: [(0, jsx_runtime_1.jsx)(react_1.VisuallyHidden, { as: "h2", id: titleId, children: labels.title }), (0, jsx_runtime_1.jsx)(SearchField, { inputRef: inputRef, value: query, onChange: setQuery, onKeyDown: onInputKeyDown, labels: labels, placeholder: (_c = currentScope === null || currentScope === void 0 ? void 0 : currentScope.placeholder) !== null && _c !== void 0 ? _c : labels.searchPlaceholder, hint: searchHint, listboxId: listboxId, activeId: isListView && hasResults ? optionId(active) : undefined, isExpanded: isListView, hasResults: hasResults, minW: inlineScopes ? FIELD_MIN_WIDTH : 0 }), inlineScopes, (0, jsx_runtime_1.jsx)(react_1.Box, { "aria-hidden": true, "data-testid": "launcher-count", display: { base: "none", sm: "block" }, flexShrink: 0, overflow: "hidden", w: countText ? "88px" : "0", opacity: countText ? 1 : 0, textAlign: "right", fontSize: "13px", whiteSpace: "nowrap", color: colors.textMuted, transition: (0, motion_2.transitionFor)(["width", "opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...(0, motion_1.reducedMotionSx)() }, children: countText }), headerActions ? ((0, jsx_runtime_1.jsx)(react_1.Flex, { align: "center", gap: "4px", flexShrink: 0, children: headerActions })) : null, (0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": labels.close, icon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 18, "aria-hidden": true }), variant: "ghost", boxSize: { base: "40px", md: "44px" }, minW: { base: "40px", md: "44px" }, borderRadius: "10px", flexShrink: 0, color: colors.textMuted, onClick: onClose })] }), scopeRow ? ((0, jsx_runtime_1.jsx)(react_1.Box, { "data-testid": "launcher-scope-row", pl: "7px", pr: "3px", mt: "-6px", pb: "4px", children: scopeRow })) : null] }), (0, jsx_runtime_1.jsx)(Drawer_1.DrawerBody, { p: 0, display: "flex", flexDirection: "column", overflowY: "hidden", minH: 0, children: (0, jsx_runtime_1.jsxs)(react_1.Flex, { ref: bodyRef, direction: "column", flex: 1, minH: 0, minW: 0, children: [(0, jsx_runtime_1.jsx)(react_1.VisuallyHidden, { "data-testid": "launcher-live", "aria-live": "polite", "aria-atomic": "true", children: countText }), (0, jsx_runtime_1.jsx)(react_1.VisuallyHidden, { "data-testid": "launcher-announce", "aria-live": "polite", "aria-atomic": "true", children: (0, jsx_runtime_1.jsx)("span", { children: announcement.text }, announcement.n) }), isTabbed ? ((0, jsx_runtime_1.jsx)(LauncherTabs_1.LauncherTabs, { tabs: tabs, selected: activeTab, onSelect: setTab, idBase: baseId, label: labels.tabsLabel, onLeaveUp: () => focusField(), onLeaveDown: () => moveFocus("first") })) : null, isLg ? ((0, jsx_runtime_1.jsx)(react_1.Flex, { direction: "row", flex: 1, minH: 0, minW: 0, children: body })) : (compactBody), dock] }) }), (0, jsx_runtime_1.jsxs)(Drawer_1.DrawerFooter, { p: 0, minH: "32px", flexShrink: 0, px: "18px", py: "6px", gap: "6px 14px", flexWrap: "wrap", bg: colors.backgroundColor.subtle, fontSize: "xs-minus", color: colors.textMuted, whiteSpace: "nowrap", justifyContent: "flex-start", children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { display: { base: "none", md: "flex" }, gap: "6px 14px", align: "center", wrap: "wrap", "aria-hidden": true, sx: { "@media (hover: none)": { display: "none" } }, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(react_1.Flex, { as: "span", gap: "2px", children: ARROW_KEYS.map((arrow) => ((0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: arrow }, arrow))) }), labels.hintMove] }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.KbdShortcut, { shortcut: "enter", size: "xs" }), (0, jsx_runtime_1.jsx)(launcherParts_1.HintLabel, { text: enterLabel, reserve: enterReserve })] }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", opacity: canOpenInTab ? 1 : 0.45, transition: (0, motion_2.transitionFor)(["opacity"]), sx: (0, motion_1.reducedMotionSx)(), children: [modKeyLabel ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", gap: "4px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: modKeyLabel }), (0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: ENTER_KEY })] })) : ((0, jsx_runtime_1.jsx)(Kbd_1.KbdShortcut, { shortcut: "mod+enter", size: "xs" })), labels.hintNewTab] }), (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", display: { base: "none", lg: "flex" }, children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: modKeyLabel !== null && modKeyLabel !== void 0 ? modKeyLabel : MOD_KEY }), labels.hintClickNewTab] }), onTogglePin ? ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: "P" }), labels.hintPin] })) : null, (0, jsx_runtime_1.jsxs)(react_1.Flex, { as: "span", align: "center", gap: "5px", children: [(0, jsx_runtime_1.jsx)(Kbd_1.KbdShortcut, { shortcut: "esc", size: "xs" }), labels.hintClose] })] }), (0, jsx_runtime_1.jsx)(react_1.Text, { as: "span", ml: "auto", children: labels.footerNote })] })] }) }));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const launcherSlide: (isReducedMotion: boolean) => {
|
|
2
|
+
variants: {
|
|
3
|
+
enter: {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
transition: {
|
|
7
|
+
duration: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
exit: {
|
|
11
|
+
x: string;
|
|
12
|
+
y: number;
|
|
13
|
+
transition: {
|
|
14
|
+
duration: number;
|
|
15
|
+
ease: (0 | 1 | 0.2 | 0.4)[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.launcherSlide = void 0;
|
|
4
|
+
const react_1 = require("@chakra-ui/react");
|
|
5
|
+
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
6
|
+
const ENTER = (0, motion_1.motionTransition)("enter", "out");
|
|
7
|
+
const INSTANT = { duration: 0 };
|
|
8
|
+
const EXIT = { duration: 0.15, ease: [...react_1.EASINGS.easeInOut] };
|
|
9
|
+
const launcherSlide = (isReducedMotion) => ({
|
|
10
|
+
variants: {
|
|
11
|
+
enter: { x: 0, y: 0, transition: isReducedMotion ? INSTANT : ENTER },
|
|
12
|
+
exit: { x: "-100%", y: 0, transition: EXIT },
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
exports.launcherSlide = launcherSlide;
|
|
@@ -68,7 +68,7 @@ export type TableProps = {
|
|
|
68
68
|
isPagination?: boolean;
|
|
69
69
|
/** Controlled selected row ids; a new array replaces the internal selection. */
|
|
70
70
|
selections?: (string | number)[];
|
|
71
|
-
/** Fires per cell, gated on the column: a column with `node` swallows its clicks. A node `label` arrives as its plain text. */
|
|
71
|
+
/** Fires per cell, gated on the column: a column with `node` swallows its clicks. Also makes rows tab stops; Enter or Space fires it for the first column without `node`. A node `label` arrives as its plain text. */
|
|
72
72
|
onRowClick?: (row: DataObject, header: Record<string | number, string | number>) => void;
|
|
73
73
|
/** Sticky-positions the trailing action/link column during horizontal scroll. Defaults true. */
|
|
74
74
|
isActionFreeze?: boolean;
|
|
@@ -58,6 +58,8 @@ const motion_2 = require("../../../Theme/chakra/motion");
|
|
|
58
58
|
const rowDetail_1 = require("./rowDetail");
|
|
59
59
|
const rowMeasurer_1 = require("./rowMeasurer");
|
|
60
60
|
const borderWidths_1 = require("../../../Theme/tokens/primitives/borderWidths");
|
|
61
|
+
const activationKey_1 = require("../../Common/activationKey");
|
|
62
|
+
const rowFocusRing_1 = require("./rowFocusRing");
|
|
61
63
|
const getRowId = (row) => row.id;
|
|
62
64
|
const getRowContent = (row) => typeof row.content === "function"
|
|
63
65
|
? row.content
|
|
@@ -117,6 +119,12 @@ const TableRow = react_2.default.memo(({ row, rowIndex, isChecked, isExpanded, i
|
|
|
117
119
|
});
|
|
118
120
|
}
|
|
119
121
|
}, [row, onRowClick]);
|
|
122
|
+
const keyboardHeader = onRowClick && !isLoading ? columns.find((c) => !c.isHidden && !c.node) : undefined;
|
|
123
|
+
const handleRowKeyDown = (0, react_2.useCallback)((event) => {
|
|
124
|
+
if (event.target !== event.currentTarget || event.repeat || !keyboardHeader)
|
|
125
|
+
return;
|
|
126
|
+
(0, activationKey_1.onActivationKey)(() => handleCellClick(keyboardHeader))(event);
|
|
127
|
+
}, [keyboardHeader, handleCellClick]);
|
|
120
128
|
const variant = (0, TableVariantContext_1.useTableVariant)();
|
|
121
129
|
const firstDataColIndex = columns.findIndex((c) => !c.isHidden);
|
|
122
130
|
const accentSx = accentColor ? `inset 3px 0 0 ${accentColor}` : undefined;
|
|
@@ -127,6 +135,7 @@ const TableRow = react_2.default.memo(({ row, rowIndex, isChecked, isExpanded, i
|
|
|
127
135
|
: accentSx;
|
|
128
136
|
const restsOnIndex = variant.slot.rest === "index";
|
|
129
137
|
const rowSx = {
|
|
138
|
+
...(keyboardHeader ? (0, rowFocusRing_1.rowFocusRingSx)(theme.colors) : {}),
|
|
130
139
|
...(variant.row.hoverScope === "row"
|
|
131
140
|
? { "&:hover > td[data-hoverable='true']": { backgroundColor: variant.row.hoverBg } }
|
|
132
141
|
: {}),
|
|
@@ -153,7 +162,7 @@ const TableRow = react_2.default.memo(({ row, rowIndex, isChecked, isExpanded, i
|
|
|
153
162
|
const chromeHoverable = rowStyle ? "false" : "true";
|
|
154
163
|
const rowContent = getRowContent(row);
|
|
155
164
|
const restBg = isStriped ? variant.row.stripeBg : theme.colors.background[50];
|
|
156
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.default.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Tr, { ref: mainRowRef, "aria-rowindex": ariaRowIndex, opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: (0, motion_1.transitionFor)(["opacity"], "base"), sx: { ...rowSx, ...(0, motion_2.reducedMotionSx)() }, children: [isContent && ((0, jsx_runtime_1.jsx)(react_1.Td, { w: "6", p: 0, fontSize: "sm", background: isChecked ? selectedFrozenBg : frozenCellBg, color: freezedTextColor, position: "sticky", borderBottom: borderStyle, boxShadow: leadSx, boxSizing: "border-box", "data-hoverable": chromeHoverable, left: "0px", zIndex: 1, className: "columns sticky-columns", children: hasContent && ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": isExpanded ? "Collapse row" : "Expand row", color: theme.colors.textMuted, icon: isExpanded ? ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, {})) : ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, {})), _hover: { transform: "scale(1.1)" }, size: "sm", onClick: () => toggleRowExpansion(rowIndex), variant: "ghost" })) })), isCheckbox && ((0, jsx_runtime_1.jsx)(react_1.Td, { w: variant.slot.width, minW: variant.slot.width, px: variant.slot.centered ? 0 : undefined, textAlign: variant.slot.centered ? "center" : undefined, fontSize: "sm", fontWeight: 600, color: theme.colors.background[50], textTransform: "capitalize", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: "0px", zIndex: 1, className: "columns sticky-columns", children: (0, jsx_runtime_1.jsxs)(react_1.Box, { display: restsOnIndex ? "grid" : undefined, placeItems: "center", children: [restsOnIndex && ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", className: "table-slot-index", gridArea: "1 / 1", color: variant.slot.indexInk, fontSize: "0.6875rem", fontWeight: 500, transition: (0, motion_1.transitionFor)(["opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...(0, motion_2.reducedMotionSx)() }, children: rowIndex + 1 })), (0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", className: restsOnIndex ? "table-slot-box" : undefined, gridArea: restsOnIndex ? "1 / 1" : undefined, display: "flex", alignItems: "center", justifyContent: "center", transition: (0, motion_1.transitionFor)(["opacity"]), sx: (0, motion_2.reducedMotionSx)(), children: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { "aria-label": `Select row ${rowIndex + 1}`, onChange: () => handleCheckbox(getRowId(row)), isChecked: isChecked }) })] }) })), rowDetail && detailIds && ((0, jsx_runtime_1.jsx)(react_1.Td, { w: `${rowDetail_1.DETAIL_SLOT_PX}px`, minW: `${rowDetail_1.DETAIL_SLOT_PX}px`, maxW: `${rowDetail_1.DETAIL_SLOT_PX}px`, p: 0, textAlign: "center", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent && !isCheckbox ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: `${detailLeft}px`, zIndex: 1, className: "columns sticky-columns", children: hasDetail && ((0, jsx_runtime_1.jsx)(rowDetail_1.RowDetailToggle, { isOpen: detailOpen, detailId: detailIds.detailId, color: theme.colors.textMuted, theme: theme, onToggle: () => rowDetail.toggle(getRowId(row)) })) })), columns.map((header, headerIndex) => {
|
|
165
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.default.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_1.Tr, { ref: mainRowRef, "aria-rowindex": ariaRowIndex, tabIndex: keyboardHeader ? 0 : undefined, onKeyDown: keyboardHeader ? handleRowKeyDown : undefined, opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: (0, motion_1.transitionFor)(["opacity"], "base"), sx: { ...rowSx, ...(0, motion_2.reducedMotionSx)() }, children: [isContent && ((0, jsx_runtime_1.jsx)(react_1.Td, { w: "6", p: 0, fontSize: "sm", background: isChecked ? selectedFrozenBg : frozenCellBg, color: freezedTextColor, position: "sticky", borderBottom: borderStyle, boxShadow: leadSx, boxSizing: "border-box", "data-hoverable": chromeHoverable, left: "0px", zIndex: 1, className: "columns sticky-columns", children: hasContent && ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": isExpanded ? "Collapse row" : "Expand row", color: theme.colors.textMuted, icon: isExpanded ? ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, {})) : ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, {})), _hover: { transform: "scale(1.1)" }, size: "sm", onClick: () => toggleRowExpansion(rowIndex), variant: "ghost" })) })), isCheckbox && ((0, jsx_runtime_1.jsx)(react_1.Td, { w: variant.slot.width, minW: variant.slot.width, px: variant.slot.centered ? 0 : undefined, textAlign: variant.slot.centered ? "center" : undefined, fontSize: "sm", fontWeight: 600, color: theme.colors.background[50], textTransform: "capitalize", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: "0px", zIndex: 1, className: "columns sticky-columns", children: (0, jsx_runtime_1.jsxs)(react_1.Box, { display: restsOnIndex ? "grid" : undefined, placeItems: "center", children: [restsOnIndex && ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", className: "table-slot-index", gridArea: "1 / 1", color: variant.slot.indexInk, fontSize: "0.6875rem", fontWeight: 500, transition: (0, motion_1.transitionFor)(["opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...(0, motion_2.reducedMotionSx)() }, children: rowIndex + 1 })), (0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", className: restsOnIndex ? "table-slot-box" : undefined, gridArea: restsOnIndex ? "1 / 1" : undefined, display: "flex", alignItems: "center", justifyContent: "center", transition: (0, motion_1.transitionFor)(["opacity"]), sx: (0, motion_2.reducedMotionSx)(), children: (0, jsx_runtime_1.jsx)(Checkbox_1.default, { "aria-label": `Select row ${rowIndex + 1}`, onChange: () => handleCheckbox(getRowId(row)), isChecked: isChecked }) })] }) })), rowDetail && detailIds && ((0, jsx_runtime_1.jsx)(react_1.Td, { w: `${rowDetail_1.DETAIL_SLOT_PX}px`, minW: `${rowDetail_1.DETAIL_SLOT_PX}px`, maxW: `${rowDetail_1.DETAIL_SLOT_PX}px`, p: 0, textAlign: "center", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent && !isCheckbox ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: `${detailLeft}px`, zIndex: 1, className: "columns sticky-columns", children: hasDetail && ((0, jsx_runtime_1.jsx)(rowDetail_1.RowDetailToggle, { isOpen: detailOpen, detailId: detailIds.detailId, color: theme.colors.textMuted, theme: theme, onToggle: () => rowDetail.toggle(getRowId(row)) })) })), columns.map((header, headerIndex) => {
|
|
157
166
|
var _a, _b, _c, _d;
|
|
158
167
|
if (header.isHidden)
|
|
159
168
|
return null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FocusRingTokens } from "../../../Theme/tokens/types";
|
|
2
|
+
export declare const rowFocusRingSx: (colors: {
|
|
3
|
+
focusRing: FocusRingTokens;
|
|
4
|
+
}) => {
|
|
5
|
+
"&:focus-visible": {
|
|
6
|
+
outline: string;
|
|
7
|
+
outlineOffset: string;
|
|
8
|
+
};
|
|
9
|
+
"&:focus-visible > td": {
|
|
10
|
+
boxShadow: string;
|
|
11
|
+
};
|
|
12
|
+
"&:focus-visible > td:first-of-type": {
|
|
13
|
+
boxShadow: string;
|
|
14
|
+
};
|
|
15
|
+
"&:focus-visible > td:last-of-type": {
|
|
16
|
+
boxShadow: string;
|
|
17
|
+
};
|
|
18
|
+
"&:focus-visible > td:only-of-type": {
|
|
19
|
+
boxShadow: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rowFocusRingSx = void 0;
|
|
4
|
+
const focusRing_styles_1 = require("../../../Theme/chakra/focusRing.styles");
|
|
5
|
+
const TOP = [0, 1];
|
|
6
|
+
const BOTTOM = [0, -1];
|
|
7
|
+
const LEFT = [1, 0];
|
|
8
|
+
const RIGHT = [-1, 0];
|
|
9
|
+
const RING_BAND = focusRing_styles_1.FOCUS_RING_WIDTH - focusRing_styles_1.FOCUS_HALO_WIDTH;
|
|
10
|
+
const band = ([x, y], width, color) => `inset ${x * width}px ${y * width}px 0 0 ${color}`;
|
|
11
|
+
const edgeShadow = (edges, { ring, halo }) => [...edges.map((edge) => band(edge, RING_BAND, ring)), ...edges.map((edge) => band(edge, focusRing_styles_1.FOCUS_RING_WIDTH, halo))].join(", ");
|
|
12
|
+
// NOTE: each cell draws its slice, so sticky cells keep the ring above scrolled content.
|
|
13
|
+
const rowFocusRingSx = (colors) => ({
|
|
14
|
+
"&:focus-visible": { outline: "2px solid transparent", outlineOffset: "-2px" },
|
|
15
|
+
"&:focus-visible > td": { boxShadow: edgeShadow([TOP, BOTTOM], colors.focusRing) },
|
|
16
|
+
"&:focus-visible > td:first-of-type": { boxShadow: edgeShadow([TOP, BOTTOM, LEFT], colors.focusRing) },
|
|
17
|
+
"&:focus-visible > td:last-of-type": { boxShadow: edgeShadow([TOP, BOTTOM, RIGHT], colors.focusRing) },
|
|
18
|
+
"&:focus-visible > td:only-of-type": { boxShadow: edgeShadow([TOP, BOTTOM, LEFT, RIGHT], colors.focusRing) },
|
|
19
|
+
});
|
|
20
|
+
exports.rowFocusRingSx = rowFocusRingSx;
|
|
@@ -4,7 +4,7 @@ import { Search as SearchIcon, X } from "lucide-react";
|
|
|
4
4
|
import { memo, useCallback, useEffect, useId, useMemo, useRef, useState, } from "react";
|
|
5
5
|
import { useMatchMedia } from "../../Hooks/useMatchMedia.js";
|
|
6
6
|
import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
|
|
7
|
-
import { reducedMotionSx } from "../../Theme/chakra/motion.js";
|
|
7
|
+
import { reducedMotionSx, usePrefersReducedMotion } from "../../Theme/chakra/motion.js";
|
|
8
8
|
import { chartMode } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
|
|
9
9
|
import { placeholderInk } from "../../Theme/tokens/builders/placeholderInk.js";
|
|
10
10
|
import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
@@ -24,6 +24,7 @@ import { LauncherTabs, panelId, tabId, } from "./LauncherTabs.js";
|
|
|
24
24
|
import { filterItems, searchLauncherSections, } from "./match.js";
|
|
25
25
|
import NewTabLink from "./NewTabLink.js";
|
|
26
26
|
import PinToggle from "./PinToggle.js";
|
|
27
|
+
import { launcherSlide } from "./launcherMotion.js";
|
|
27
28
|
import { moveResult } from "./resultNavigation.js";
|
|
28
29
|
import { ScopeBar } from "./ScopeBar.js";
|
|
29
30
|
import { copiesOnEnter, countByScope, inScope, layoutSearch, scopeGroups, takeTopHit, } from "./searchLayout.js";
|
|
@@ -204,6 +205,7 @@ export default function NavLauncher({ isOpen, onClose, sections: rawSections, pi
|
|
|
204
205
|
const isDesktop = useMatchMedia(MD_QUERY, true);
|
|
205
206
|
const isLg = useMatchMedia(LG_QUERY, true);
|
|
206
207
|
const isWide = useMatchMedia(XL_QUERY, false);
|
|
208
|
+
const isReducedMotion = usePrefersReducedMotion();
|
|
207
209
|
const columns = isWide ? 3 : isDesktop ? 2 : 1;
|
|
208
210
|
const [innerQuery, setInnerQuery] = useState("");
|
|
209
211
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
@@ -789,6 +791,7 @@ export default function NavLauncher({ isOpen, onClose, sections: rawSections, pi
|
|
|
789
791
|
borderBottomRightRadius: { base: 0, lg: "16px" },
|
|
790
792
|
boxShadow: colors.boxShadow.default,
|
|
791
793
|
overflow: "hidden",
|
|
794
|
+
motionProps: launcherSlide(isReducedMotion),
|
|
792
795
|
onKeyDown: onContentKeyDown,
|
|
793
796
|
...{ "data-testid": testId },
|
|
794
797
|
}, children: [_jsxs(DrawerHeader, { p: 0, flexShrink: 0, children: [_jsxs(Flex, { "data-testid": "launcher-header-row", align: "center", gap: { base: "8px", md: "12px" }, minH: { base: "60px", md: "68px" }, pl: { base: "12px", md: "18px" }, pr: { base: "8px", md: "12px" }, children: [_jsx(VisuallyHidden, { as: "h2", id: titleId, children: labels.title }), _jsx(SearchField, { inputRef: inputRef, value: query, onChange: setQuery, onKeyDown: onInputKeyDown, labels: labels, placeholder: (_c = currentScope === null || currentScope === void 0 ? void 0 : currentScope.placeholder) !== null && _c !== void 0 ? _c : labels.searchPlaceholder, hint: searchHint, listboxId: listboxId, activeId: isListView && hasResults ? optionId(active) : undefined, isExpanded: isListView, hasResults: hasResults, minW: inlineScopes ? FIELD_MIN_WIDTH : 0 }), inlineScopes, _jsx(Box, { "aria-hidden": true, "data-testid": "launcher-count", display: { base: "none", sm: "block" }, flexShrink: 0, overflow: "hidden", w: countText ? "88px" : "0", opacity: countText ? 1 : 0, textAlign: "right", fontSize: "13px", whiteSpace: "nowrap", color: colors.textMuted, transition: transitionFor(["width", "opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...reducedMotionSx() }, children: countText }), headerActions ? (_jsx(Flex, { align: "center", gap: "4px", flexShrink: 0, children: headerActions })) : null, _jsx(IconButton, { "aria-label": labels.close, icon: _jsx(X, { size: 18, "aria-hidden": true }), variant: "ghost", boxSize: { base: "40px", md: "44px" }, minW: { base: "40px", md: "44px" }, borderRadius: "10px", flexShrink: 0, color: colors.textMuted, onClick: onClose })] }), scopeRow ? (_jsx(Box, { "data-testid": "launcher-scope-row", pl: "7px", pr: "3px", mt: "-6px", pb: "4px", children: scopeRow })) : null] }), _jsx(DrawerBody, { p: 0, display: "flex", flexDirection: "column", overflowY: "hidden", minH: 0, children: _jsxs(Flex, { ref: bodyRef, direction: "column", flex: 1, minH: 0, minW: 0, children: [_jsx(VisuallyHidden, { "data-testid": "launcher-live", "aria-live": "polite", "aria-atomic": "true", children: countText }), _jsx(VisuallyHidden, { "data-testid": "launcher-announce", "aria-live": "polite", "aria-atomic": "true", children: _jsx("span", { children: announcement.text }, announcement.n) }), isTabbed ? (_jsx(LauncherTabs, { tabs: tabs, selected: activeTab, onSelect: setTab, idBase: baseId, label: labels.tabsLabel, onLeaveUp: () => focusField(), onLeaveDown: () => moveFocus("first") })) : null, isLg ? (_jsx(Flex, { direction: "row", flex: 1, minH: 0, minW: 0, children: body })) : (compactBody), dock] }) }), _jsxs(DrawerFooter, { p: 0, minH: "32px", flexShrink: 0, px: "18px", py: "6px", gap: "6px 14px", flexWrap: "wrap", bg: colors.backgroundColor.subtle, fontSize: "xs-minus", color: colors.textMuted, whiteSpace: "nowrap", justifyContent: "flex-start", children: [_jsxs(Flex, { display: { base: "none", md: "flex" }, gap: "6px 14px", align: "center", wrap: "wrap", "aria-hidden": true, sx: { "@media (hover: none)": { display: "none" } }, children: [_jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(Flex, { as: "span", gap: "2px", children: ARROW_KEYS.map((arrow) => (_jsx(Kbd, { size: "xs", children: arrow }, arrow))) }), labels.hintMove] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(KbdShortcut, { shortcut: "enter", size: "xs" }), _jsx(HintLabel, { text: enterLabel, reserve: enterReserve })] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", opacity: canOpenInTab ? 1 : 0.45, transition: transitionFor(["opacity"]), sx: reducedMotionSx(), children: [modKeyLabel ? (_jsxs(Flex, { as: "span", gap: "4px", children: [_jsx(Kbd, { size: "xs", children: modKeyLabel }), _jsx(Kbd, { size: "xs", children: ENTER_KEY })] })) : (_jsx(KbdShortcut, { shortcut: "mod+enter", size: "xs" })), labels.hintNewTab] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", display: { base: "none", lg: "flex" }, children: [_jsx(Kbd, { size: "xs", children: modKeyLabel !== null && modKeyLabel !== void 0 ? modKeyLabel : MOD_KEY }), labels.hintClickNewTab] }), onTogglePin ? (_jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(Kbd, { size: "xs", children: "P" }), labels.hintPin] })) : null, _jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(KbdShortcut, { shortcut: "esc", size: "xs" }), labels.hintClose] })] }), _jsx(Text, { as: "span", ml: "auto", children: labels.footerNote })] })] }) }));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EASINGS } from "@chakra-ui/react";
|
|
2
|
+
import { motionTransition } from "../../Theme/tokens/primitives/motion.js";
|
|
3
|
+
const ENTER = motionTransition("enter", "out");
|
|
4
|
+
const INSTANT = { duration: 0 };
|
|
5
|
+
const EXIT = { duration: 0.15, ease: [...EASINGS.easeInOut] };
|
|
6
|
+
export const launcherSlide = (isReducedMotion) => ({
|
|
7
|
+
variants: {
|
|
8
|
+
enter: { x: 0, y: 0, transition: isReducedMotion ? INSTANT : ENTER },
|
|
9
|
+
exit: { x: "-100%", y: 0, transition: EXIT },
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -19,6 +19,8 @@ import { reducedMotionSx } from "../../../Theme/chakra/motion.js";
|
|
|
19
19
|
import { DETAIL_SLOT_PX, RowDetailRow, RowDetailToggle, detailDomIds } from "./rowDetail.js";
|
|
20
20
|
import { useRowMeasurer } from "./rowMeasurer.js";
|
|
21
21
|
import { BORDER_WIDTHS } from "../../../Theme/tokens/primitives/borderWidths.js";
|
|
22
|
+
import { onActivationKey } from "../../Common/activationKey.js";
|
|
23
|
+
import { rowFocusRingSx } from "./rowFocusRing.js";
|
|
22
24
|
const getRowId = (row) => row.id;
|
|
23
25
|
const getRowContent = (row) => typeof row.content === "function"
|
|
24
26
|
? row.content
|
|
@@ -77,6 +79,12 @@ const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent,
|
|
|
77
79
|
});
|
|
78
80
|
}
|
|
79
81
|
}, [row, onRowClick]);
|
|
82
|
+
const keyboardHeader = onRowClick && !isLoading ? columns.find((c) => !c.isHidden && !c.node) : undefined;
|
|
83
|
+
const handleRowKeyDown = useCallback((event) => {
|
|
84
|
+
if (event.target !== event.currentTarget || event.repeat || !keyboardHeader)
|
|
85
|
+
return;
|
|
86
|
+
onActivationKey(() => handleCellClick(keyboardHeader))(event);
|
|
87
|
+
}, [keyboardHeader, handleCellClick]);
|
|
80
88
|
const variant = useTableVariant();
|
|
81
89
|
const firstDataColIndex = columns.findIndex((c) => !c.isHidden);
|
|
82
90
|
const accentSx = accentColor ? `inset 3px 0 0 ${accentColor}` : undefined;
|
|
@@ -87,6 +95,7 @@ const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent,
|
|
|
87
95
|
: accentSx;
|
|
88
96
|
const restsOnIndex = variant.slot.rest === "index";
|
|
89
97
|
const rowSx = {
|
|
98
|
+
...(keyboardHeader ? rowFocusRingSx(theme.colors) : {}),
|
|
90
99
|
...(variant.row.hoverScope === "row"
|
|
91
100
|
? { "&:hover > td[data-hoverable='true']": { backgroundColor: variant.row.hoverBg } }
|
|
92
101
|
: {}),
|
|
@@ -113,7 +122,7 @@ const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent,
|
|
|
113
122
|
const chromeHoverable = rowStyle ? "false" : "true";
|
|
114
123
|
const rowContent = getRowContent(row);
|
|
115
124
|
const restBg = isStriped ? variant.row.stripeBg : theme.colors.background[50];
|
|
116
|
-
return (_jsxs(React.Fragment, { children: [_jsxs(Tr, { ref: mainRowRef, "aria-rowindex": ariaRowIndex, opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: transitionFor(["opacity"], "base"), sx: { ...rowSx, ...reducedMotionSx() }, children: [isContent && (_jsx(Td, { w: "6", p: 0, fontSize: "sm", background: isChecked ? selectedFrozenBg : frozenCellBg, color: freezedTextColor, position: "sticky", borderBottom: borderStyle, boxShadow: leadSx, boxSizing: "border-box", "data-hoverable": chromeHoverable, left: "0px", zIndex: 1, className: "columns sticky-columns", children: hasContent && (_jsx(IconButton, { "aria-label": isExpanded ? "Collapse row" : "Expand row", color: theme.colors.textMuted, icon: isExpanded ? (_jsx(ChevronDown, {})) : (_jsx(ChevronRight, {})), _hover: { transform: "scale(1.1)" }, size: "sm", onClick: () => toggleRowExpansion(rowIndex), variant: "ghost" })) })), isCheckbox && (_jsx(Td, { w: variant.slot.width, minW: variant.slot.width, px: variant.slot.centered ? 0 : undefined, textAlign: variant.slot.centered ? "center" : undefined, fontSize: "sm", fontWeight: 600, color: theme.colors.background[50], textTransform: "capitalize", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: "0px", zIndex: 1, className: "columns sticky-columns", children: _jsxs(Box, { display: restsOnIndex ? "grid" : undefined, placeItems: "center", children: [restsOnIndex && (_jsx(Box, { as: "span", className: "table-slot-index", gridArea: "1 / 1", color: variant.slot.indexInk, fontSize: "0.6875rem", fontWeight: 500, transition: transitionFor(["opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...reducedMotionSx() }, children: rowIndex + 1 })), _jsx(Box, { as: "span", className: restsOnIndex ? "table-slot-box" : undefined, gridArea: restsOnIndex ? "1 / 1" : undefined, display: "flex", alignItems: "center", justifyContent: "center", transition: transitionFor(["opacity"]), sx: reducedMotionSx(), children: _jsx(CheckBox, { "aria-label": `Select row ${rowIndex + 1}`, onChange: () => handleCheckbox(getRowId(row)), isChecked: isChecked }) })] }) })), rowDetail && detailIds && (_jsx(Td, { w: `${DETAIL_SLOT_PX}px`, minW: `${DETAIL_SLOT_PX}px`, maxW: `${DETAIL_SLOT_PX}px`, p: 0, textAlign: "center", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent && !isCheckbox ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: `${detailLeft}px`, zIndex: 1, className: "columns sticky-columns", children: hasDetail && (_jsx(RowDetailToggle, { isOpen: detailOpen, detailId: detailIds.detailId, color: theme.colors.textMuted, theme: theme, onToggle: () => rowDetail.toggle(getRowId(row)) })) })), columns.map((header, headerIndex) => {
|
|
125
|
+
return (_jsxs(React.Fragment, { children: [_jsxs(Tr, { ref: mainRowRef, "aria-rowindex": ariaRowIndex, tabIndex: keyboardHeader ? 0 : undefined, onKeyDown: keyboardHeader ? handleRowKeyDown : undefined, opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: transitionFor(["opacity"], "base"), sx: { ...rowSx, ...reducedMotionSx() }, children: [isContent && (_jsx(Td, { w: "6", p: 0, fontSize: "sm", background: isChecked ? selectedFrozenBg : frozenCellBg, color: freezedTextColor, position: "sticky", borderBottom: borderStyle, boxShadow: leadSx, boxSizing: "border-box", "data-hoverable": chromeHoverable, left: "0px", zIndex: 1, className: "columns sticky-columns", children: hasContent && (_jsx(IconButton, { "aria-label": isExpanded ? "Collapse row" : "Expand row", color: theme.colors.textMuted, icon: isExpanded ? (_jsx(ChevronDown, {})) : (_jsx(ChevronRight, {})), _hover: { transform: "scale(1.1)" }, size: "sm", onClick: () => toggleRowExpansion(rowIndex), variant: "ghost" })) })), isCheckbox && (_jsx(Td, { w: variant.slot.width, minW: variant.slot.width, px: variant.slot.centered ? 0 : undefined, textAlign: variant.slot.centered ? "center" : undefined, fontSize: "sm", fontWeight: 600, color: theme.colors.background[50], textTransform: "capitalize", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: "0px", zIndex: 1, className: "columns sticky-columns", children: _jsxs(Box, { display: restsOnIndex ? "grid" : undefined, placeItems: "center", children: [restsOnIndex && (_jsx(Box, { as: "span", className: "table-slot-index", gridArea: "1 / 1", color: variant.slot.indexInk, fontSize: "0.6875rem", fontWeight: 500, transition: transitionFor(["opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...reducedMotionSx() }, children: rowIndex + 1 })), _jsx(Box, { as: "span", className: restsOnIndex ? "table-slot-box" : undefined, gridArea: restsOnIndex ? "1 / 1" : undefined, display: "flex", alignItems: "center", justifyContent: "center", transition: transitionFor(["opacity"]), sx: reducedMotionSx(), children: _jsx(CheckBox, { "aria-label": `Select row ${rowIndex + 1}`, onChange: () => handleCheckbox(getRowId(row)), isChecked: isChecked }) })] }) })), rowDetail && detailIds && (_jsx(Td, { w: `${DETAIL_SLOT_PX}px`, minW: `${DETAIL_SLOT_PX}px`, maxW: `${DETAIL_SLOT_PX}px`, p: 0, textAlign: "center", background: isChecked ? selectedFrozenBg : frozenCellBg, position: "sticky", borderBottom: borderStyle, boxShadow: !isContent && !isCheckbox ? leadSx : undefined, "data-hoverable": chromeHoverable, boxSizing: "border-box", left: `${detailLeft}px`, zIndex: 1, className: "columns sticky-columns", children: hasDetail && (_jsx(RowDetailToggle, { isOpen: detailOpen, detailId: detailIds.detailId, color: theme.colors.textMuted, theme: theme, onToggle: () => rowDetail.toggle(getRowId(row)) })) })), columns.map((header, headerIndex) => {
|
|
117
126
|
var _a, _b, _c, _d;
|
|
118
127
|
if (header.isHidden)
|
|
119
128
|
return null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FOCUS_HALO_WIDTH, FOCUS_RING_WIDTH } from "../../../Theme/chakra/focusRing.styles.js";
|
|
2
|
+
const TOP = [0, 1];
|
|
3
|
+
const BOTTOM = [0, -1];
|
|
4
|
+
const LEFT = [1, 0];
|
|
5
|
+
const RIGHT = [-1, 0];
|
|
6
|
+
const RING_BAND = FOCUS_RING_WIDTH - FOCUS_HALO_WIDTH;
|
|
7
|
+
const band = ([x, y], width, color) => `inset ${x * width}px ${y * width}px 0 0 ${color}`;
|
|
8
|
+
const edgeShadow = (edges, { ring, halo }) => [...edges.map((edge) => band(edge, RING_BAND, ring)), ...edges.map((edge) => band(edge, FOCUS_RING_WIDTH, halo))].join(", ");
|
|
9
|
+
// NOTE: each cell draws its slice, so sticky cells keep the ring above scrolled content.
|
|
10
|
+
export const rowFocusRingSx = (colors) => ({
|
|
11
|
+
"&:focus-visible": { outline: "2px solid transparent", outlineOffset: "-2px" },
|
|
12
|
+
"&:focus-visible > td": { boxShadow: edgeShadow([TOP, BOTTOM], colors.focusRing) },
|
|
13
|
+
"&:focus-visible > td:first-of-type": { boxShadow: edgeShadow([TOP, BOTTOM, LEFT], colors.focusRing) },
|
|
14
|
+
"&:focus-visible > td:last-of-type": { boxShadow: edgeShadow([TOP, BOTTOM, RIGHT], colors.focusRing) },
|
|
15
|
+
"&:focus-visible > td:only-of-type": { boxShadow: edgeShadow([TOP, BOTTOM, LEFT, RIGHT], colors.focusRing) },
|
|
16
|
+
});
|
package/package.json
CHANGED