pixelize-design-library 2.4.2-beta.5 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Components/Button/Button.d.ts +2 -2
- package/dist/Components/Button/Button.js +3 -3
- package/dist/Components/Button/Button.styles.js +29 -3
- package/dist/Components/Button/ButtonProps.d.ts +6 -1
- package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -1
- package/dist/Components/Card/Card.d.ts +1 -1
- package/dist/Components/Card/Card.js +2 -2
- package/dist/Components/Card/CardProps.d.ts +0 -1
- package/dist/Components/Checkbox/Checkbox.js +1 -1
- package/dist/Components/Checkbox/Checkbox.styles.js +2 -0
- package/dist/Components/DatePicker/RangeDatePicker.js +1 -1
- package/dist/Components/DatePicker/SingleDatePicker.js +1 -1
- package/dist/Components/FileUpload/FileUpload.js +3 -2
- package/dist/Components/FileUpload/FileUploadProps.d.ts +3 -1
- package/dist/Components/FileUpload/constants.js +1 -0
- package/dist/Components/FilterSidebar/FilterSidebar.js +2 -2
- package/dist/Components/Form/FormWrapper.js +1 -1
- package/dist/Components/Input/TextInput.styles.js +6 -8
- package/dist/Components/InputTextArea/InputTextArea.style.js +5 -4
- package/dist/Components/NavigationBar/NavigationBar.js +3 -3
- package/dist/Components/NoteTextArea/NoteTextArea.js +5 -3
- package/dist/Components/NumberInput/NumberInput.js +3 -1
- package/dist/Components/NumberInput/NumberInput.styles.js +5 -4
- package/dist/Components/Primitives/Primitives.d.ts +6 -0
- package/dist/Components/Primitives/Primitives.js +113 -0
- package/dist/Components/ProfileCard/ProfileCard.js +1 -1
- package/dist/Components/RadioButton/RadioButton.js +1 -1
- package/dist/Components/Search/Search.js +2 -2
- package/dist/Components/SearchSelect/SearchSelect.js +2 -1
- package/dist/Components/Select/Select.d.ts +1 -1
- package/dist/Components/Select/Select.js +4 -2
- package/dist/Components/Select/Select.styles.js +2 -1
- package/dist/Components/Select/SelectProps.d.ts +3 -0
- package/dist/Components/SelectSearch/SelectSearch.js +1 -1
- package/dist/Components/SelectV2/SelectV2.js +3 -2
- package/dist/Components/SideBar/SecondaryBar.js +1 -1
- package/dist/Components/SideBar/SideBar.js +1 -1
- package/dist/Components/Table/Table.d.ts +1 -1
- package/dist/Components/Table/Table.js +10 -4
- package/dist/Components/Table/TableProps.d.ts +4 -0
- package/dist/Components/Table/components/Pagination.js +1 -1
- package/dist/Components/Table/components/TableActions.js +2 -2
- package/dist/Components/Table/components/TableBody.js +40 -11
- package/dist/Components/Table/components/TableGroupRow.js +1 -1
- package/dist/Components/Table/components/TableHeader.js +21 -8
- package/dist/Components/Table/components/TableSearch.js +1 -1
- package/dist/Components/Table/settings/ManageColumns.js +1 -1
- package/dist/Components/Table/settings/TableSettings.js +2 -2
- package/dist/Components/Table/variants/TableVariantContext.d.ts +4 -0
- package/dist/Components/Table/variants/TableVariantContext.js +17 -0
- package/dist/Components/Table/variants/classic.d.ts +6 -0
- package/dist/Components/Table/variants/classic.js +47 -0
- package/dist/Components/Table/variants/index.d.ts +7 -0
- package/dist/Components/Table/variants/index.js +27 -0
- package/dist/Components/Table/variants/pillInk.d.ts +6 -0
- package/dist/Components/Table/variants/pillInk.js +21 -0
- package/dist/Components/Table/variants/studio.d.ts +6 -0
- package/dist/Components/Table/variants/studio.js +50 -0
- package/dist/Components/Table/variants/tokens.d.ts +49 -0
- package/dist/Components/Table/variants/tokens.js +2 -0
- package/dist/Components/Trail/TrailWorks.js +1 -1
- package/dist/Theme/chakra/Menu.styles.d.ts +2 -0
- package/dist/Theme/chakra/Menu.styles.js +5 -3
- package/dist/Theme/chakra/Popover.styles.d.ts +3 -0
- package/dist/Theme/chakra/Popover.styles.js +5 -1
- package/dist/Theme/chakra/componentStyles.d.ts +5 -0
- package/dist/Theme/provider/ThemeSwitcher.js +5 -2
- package/dist/Theme/tokens/builders/chartColorsFromTheme.js +1 -1
- package/dist/Theme/tokens/builders/index.d.ts +1 -0
- package/dist/Theme/tokens/builders/index.js +1 -0
- package/dist/Theme/tokens/builders/outlineRung.d.ts +27 -3
- package/dist/Theme/tokens/builders/outlineRung.js +55 -5
- package/dist/Theme/tokens/builders/placeholderInk.d.ts +19 -0
- package/dist/Theme/tokens/builders/placeholderInk.js +35 -0
- package/dist/Theme/tokens/builders/resolvedRungs.d.ts +5 -1
- package/dist/Theme/tokens/builders/resolvedRungs.js +7 -2
- package/dist/Theme/tokens/builders/surfaceRungs.d.ts +5 -2
- package/dist/Theme/tokens/builders/surfaceRungs.js +19 -9
- package/dist/esm/Components/Button/Button.js +4 -4
- package/dist/esm/Components/Button/Button.styles.js +29 -3
- package/dist/esm/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -1
- package/dist/esm/Components/Card/Card.js +2 -2
- package/dist/esm/Components/Checkbox/Checkbox.js +1 -1
- package/dist/esm/Components/Checkbox/Checkbox.styles.js +2 -0
- package/dist/esm/Components/DatePicker/RangeDatePicker.js +1 -1
- package/dist/esm/Components/DatePicker/SingleDatePicker.js +1 -1
- package/dist/esm/Components/FileUpload/FileUpload.js +4 -3
- package/dist/esm/Components/FileUpload/constants.js +1 -0
- package/dist/esm/Components/FilterSidebar/FilterSidebar.js +2 -2
- package/dist/esm/Components/Form/FormWrapper.js +1 -1
- package/dist/esm/Components/Input/TextInput.styles.js +6 -8
- package/dist/esm/Components/InputTextArea/InputTextArea.style.js +5 -4
- package/dist/esm/Components/NavigationBar/NavigationBar.js +3 -3
- package/dist/esm/Components/NoteTextArea/NoteTextArea.js +5 -3
- package/dist/esm/Components/NumberInput/NumberInput.js +4 -2
- package/dist/esm/Components/NumberInput/NumberInput.styles.js +5 -4
- package/dist/esm/Components/Primitives/Primitives.js +6 -0
- package/dist/esm/Components/ProfileCard/ProfileCard.js +1 -1
- package/dist/esm/Components/RadioButton/RadioButton.js +1 -1
- package/dist/esm/Components/Search/Search.js +2 -2
- package/dist/esm/Components/SearchSelect/SearchSelect.js +2 -1
- package/dist/esm/Components/Select/Select.js +5 -3
- package/dist/esm/Components/Select/Select.styles.js +2 -1
- package/dist/esm/Components/SelectSearch/SelectSearch.js +1 -1
- package/dist/esm/Components/SelectV2/SelectV2.js +3 -2
- package/dist/esm/Components/SideBar/SecondaryBar.js +1 -1
- package/dist/esm/Components/SideBar/SideBar.js +1 -1
- package/dist/esm/Components/Table/Table.js +10 -4
- package/dist/esm/Components/Table/components/Pagination.js +1 -1
- package/dist/esm/Components/Table/components/TableActions.js +2 -2
- package/dist/esm/Components/Table/components/TableBody.js +41 -12
- package/dist/esm/Components/Table/components/TableGroupRow.js +1 -1
- package/dist/esm/Components/Table/components/TableHeader.js +21 -8
- package/dist/esm/Components/Table/components/TableSearch.js +1 -1
- package/dist/esm/Components/Table/settings/ManageColumns.js +1 -1
- package/dist/esm/Components/Table/settings/TableSettings.js +2 -2
- package/dist/esm/Components/Table/variants/TableVariantContext.js +13 -0
- package/dist/esm/Components/Table/variants/classic.js +43 -0
- package/dist/esm/Components/Table/variants/index.js +9 -0
- package/dist/esm/Components/Table/variants/pillInk.js +18 -0
- package/dist/esm/Components/Table/variants/studio.js +46 -0
- package/dist/esm/Components/Table/variants/tokens.js +1 -0
- package/dist/esm/Components/Trail/TrailWorks.js +1 -1
- package/dist/esm/Theme/chakra/Menu.styles.js +5 -3
- package/dist/esm/Theme/chakra/Popover.styles.js +5 -1
- package/dist/esm/Theme/provider/ThemeSwitcher.js +5 -2
- package/dist/esm/Theme/tokens/builders/chartColorsFromTheme.js +1 -1
- package/dist/esm/Theme/tokens/builders/index.js +1 -0
- package/dist/esm/Theme/tokens/builders/outlineRung.js +52 -4
- package/dist/esm/Theme/tokens/builders/placeholderInk.js +29 -0
- package/dist/esm/Theme/tokens/builders/resolvedRungs.js +7 -2
- package/dist/esm/Theme/tokens/builders/surfaceRungs.js +20 -10
- package/dist/esm/index.js +2 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +9 -1
- package/package.json +4 -2
|
@@ -3,13 +3,14 @@ import { Box, IconButton, Spinner, Td, Tr } from "@chakra-ui/react";
|
|
|
3
3
|
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { normalizeTableCellValue, buildTablePalette, pickTableColor } from "../../../Utils/table.js";
|
|
5
5
|
import { useCustomTheme } from "../../../Theme/useCustomTheme.js";
|
|
6
|
-
import { onFilled
|
|
6
|
+
import { onFilled } from "../../../Theme/tokens/builders/accentText.js";
|
|
7
7
|
import { TableBodyLoading } from "./TableLoading.js";
|
|
8
8
|
import TableActions from "./TableActions.js";
|
|
9
|
-
import { getTableRowHoverStyle } from "./tableRowHoverStyle.js";
|
|
10
9
|
import { ChevronDown, ChevronRight } from "lucide-react";
|
|
11
10
|
import CheckBox from "../../Checkbox/Checkbox.js";
|
|
12
11
|
import TableGroupRow from "./TableGroupRow.js";
|
|
12
|
+
import { useTableVariant } from "../variants/TableVariantContext.js";
|
|
13
|
+
import { resolvePillInk } from "../variants/pillInk.js";
|
|
13
14
|
// Density → row height (px) + cell vertical padding. The padding (not just the
|
|
14
15
|
// min-height) is what drives perceived row size, so compact tightens both.
|
|
15
16
|
const DENSITY_CONFIG = {
|
|
@@ -25,6 +26,9 @@ const getRowContent = (row) => typeof row.content === "function"
|
|
|
25
26
|
// the label is centered in whichever of white/black clears AA against that fill —
|
|
26
27
|
// the fill can be a consumer hex we can't know in advance is light or dark.
|
|
27
28
|
const StatusCell = ({ value, onSolid }) => (_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", w: "100%", h: "100%", px: "0.5rem", fontSize: "0.75rem", fontWeight: 600, color: onSolid, textAlign: "center", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", children: value }));
|
|
29
|
+
// Studio's status cell: a contained chip on the tone's soft rung. Same tone pair
|
|
30
|
+
// the group header already uses, so an override hex behaves identically in both.
|
|
31
|
+
const StatusPill = ({ value, soft, ink, radius, }) => (_jsx(Box, { display: "inline-flex", alignItems: "center", maxW: "100%", px: "0.5rem", py: "0.125rem", borderRadius: radius, bg: soft, color: ink, fontSize: "0.75rem", fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", children: value }));
|
|
28
32
|
const EMPTY_HOVER = {};
|
|
29
33
|
const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent, isLink, isCheckbox, isLoading, isActionFreeze, columns, leftOffsets, noBorders, freezedBgColor, freezedTextColor, theme, borderStyle, selectedBg, selectedFrozenBg, hoverStyle, rowHeight, cellPy, palette, accentColor, ariaRowIndex, handleCheckbox, onRowClick, toggleRowExpansion, onMeasure, }) => {
|
|
30
34
|
// Measured-height virtualization: report this row's real height (main + any
|
|
@@ -63,15 +67,38 @@ const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent,
|
|
|
63
67
|
onRowClick(row, { label: header.label, id: header.id });
|
|
64
68
|
}
|
|
65
69
|
}, [row, onRowClick]);
|
|
70
|
+
const variant = useTableVariant();
|
|
66
71
|
const firstDataColIndex = columns.findIndex((c) => !c.isHidden);
|
|
67
72
|
const accentSx = accentColor ? `inset 3px 0 0 ${accentColor}` : undefined;
|
|
73
|
+
// A selected row earns the leading bar only where a group accent isn't already
|
|
74
|
+
// claiming that edge — the group's identity is the scarcer signal.
|
|
75
|
+
const leadSx = variant.row.showSelectedBar && isChecked && !accentColor
|
|
76
|
+
? `inset 3px 0 0 ${variant.row.selectedBarColor}`
|
|
77
|
+
: accentSx;
|
|
78
|
+
const restsOnIndex = variant.slot.rest === "index";
|
|
79
|
+
const rowSx = {
|
|
80
|
+
...(variant.row.hoverScope === "row"
|
|
81
|
+
? { "&:hover > td[data-hoverable='true']": { backgroundColor: variant.row.hoverBg } }
|
|
82
|
+
: {}),
|
|
83
|
+
// opacity, not display: a hidden-by-display checkbox leaves the a11y tree and
|
|
84
|
+
// cannot be tabbed to, so `:focus-within` would never fire to bring it back.
|
|
85
|
+
...(restsOnIndex
|
|
86
|
+
? {
|
|
87
|
+
"& .table-slot-box": { opacity: isChecked ? 1 : 0 },
|
|
88
|
+
"& .table-slot-index": { opacity: isChecked ? 0 : 1 },
|
|
89
|
+
"&:hover .table-slot-box, &:focus-within .table-slot-box": { opacity: 1 },
|
|
90
|
+
"&:hover .table-slot-index, &:focus-within .table-slot-index": { opacity: 0 },
|
|
91
|
+
}
|
|
92
|
+
: {}),
|
|
93
|
+
};
|
|
68
94
|
// Pinned/frozen columns get a tinted, opaque background so the frozen pane reads
|
|
69
95
|
// as one piece with its header (and so sticky cells cover scrolling content).
|
|
70
96
|
const frozenCellBg = freezedBgColor !== null && freezedBgColor !== void 0 ? freezedBgColor : theme.colors.table.hover[100];
|
|
71
97
|
const cellStyles = row.cellStyle;
|
|
72
98
|
const rowStyle = row.rowStyle;
|
|
99
|
+
const chromeHoverable = rowStyle ? "false" : "true";
|
|
73
100
|
const rowContent = getRowContent(row);
|
|
74
|
-
return (_jsxs(React.Fragment, { children: [_jsxs(Tr, { ref: mainRowRef, "aria-rowindex": ariaRowIndex, opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: "opacity 0.2s", children: [isContent && (_jsx(Td, { w: "6", p: 0, fontSize: 14, background: isChecked ? selectedFrozenBg : frozenCellBg, color: freezedTextColor, position: "sticky", borderBottom: borderStyle, boxShadow:
|
|
101
|
+
return (_jsxs(React.Fragment, { children: [_jsxs(Tr, { ref: mainRowRef, "aria-rowindex": ariaRowIndex, opacity: isLoading ? 0.4 : 1, pointerEvents: isLoading ? "none" : "auto", transition: "opacity 0.2s", sx: rowSx, children: [isContent && (_jsx(Td, { w: "6", p: 0, fontSize: 14, 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: !!(row === null || row === void 0 ? void 0 : row.content) && (_jsx(IconButton, { "aria-label": isExpanded ? "Collapse row" : "Expand row", color: theme.colors.textMuted, icon: isExpanded ? (_jsx(ChevronDown, { fontSize: 16 })) : (_jsx(ChevronRight, { fontSize: 16 })), _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: 14, 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: "opacity 0.15s ease-in-out", sx: { fontVariantNumeric: "tabular-nums" }, 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: "opacity 0.15s ease-in-out", children: _jsx(CheckBox, { "aria-label": `Select row ${rowIndex + 1}`, onChange: () => handleCheckbox(getRowId(row)), isChecked: isChecked }) })] }) })), columns.map((header, headerIndex) => {
|
|
75
102
|
var _a, _b, _c;
|
|
76
103
|
if (header.isHidden)
|
|
77
104
|
return null;
|
|
@@ -86,13 +113,15 @@ const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent,
|
|
|
86
113
|
const statusOnSolid = statusColor
|
|
87
114
|
? (_c = statusColor.onSolid) !== null && _c !== void 0 ? _c : onFilled(statusColor.solid, theme.colors.white, theme.colors.black)
|
|
88
115
|
: undefined;
|
|
89
|
-
|
|
116
|
+
const isBleed = variant.cells.statusFill === "bleed" && !!statusColor;
|
|
117
|
+
const canHover = !isBleed && !((cellStyles === null || cellStyles === void 0 ? void 0 : cellStyles[header.id]) || rowStyle);
|
|
118
|
+
return (_jsx(Td, { maxWidth: 500, minWidth: 120, height: `${rowHeight}px`, py: isBleed ? 0 : cellPy, px: isBleed ? 0 : undefined, onClick: () => handleCellClick(header), fontSize: 14, fontWeight: 400, position: isFrozen ? "sticky" : undefined, left: isFrozen ? leftOffsets[headerIndex] : undefined, zIndex: isFrozen ? 1 : 0, background: isBleed ? statusColor.solid : isChecked ? (isFrozen ? selectedFrozenBg : selectedBg) : isFrozen ? frozenCellBg : theme.colors.background[50], textOverflow: "ellipsis", boxShadow: isFirstDataCell ? leadSx : undefined, borderBottom: noBorders
|
|
90
119
|
? "none"
|
|
91
|
-
: `0.063rem solid ${
|
|
120
|
+
: `0.063rem solid ${isFrozen || isChecked
|
|
121
|
+
? variant.row.frozenRuleColor
|
|
122
|
+
: variant.row.ruleColor}`, className: `columns ${isFrozen ? "sticky-columns" : "scrollable-columns"}`, boxSizing: "border-box", color: isBleed ? statusOnSolid : variant.cells.ink, "data-hoverable": canHover ? "true" : "false", _hover: variant.row.hoverScope === "row" || !canHover
|
|
92
123
|
? EMPTY_HOVER
|
|
93
|
-
: (
|
|
94
|
-
? EMPTY_HOVER
|
|
95
|
-
: hoverStyle, children: isStatus && statusColor ? (_jsx(StatusCell, { value: normalizeTableCellValue(row[header.id]), onSolid: statusOnSolid })) : (_jsx(Box, { display: "block", overflow: "hidden", whiteSpace: "normal", overflowWrap: "break-word", children: normalizeTableCellValue(header.node ? header.node(row) : row[header.id]) })) }, headerIndex + 1));
|
|
124
|
+
: hoverStyle, children: isBleed ? (_jsx(StatusCell, { value: normalizeTableCellValue(row[header.id]), onSolid: statusOnSolid })) : isStatus && statusColor ? (_jsx(StatusPill, { value: normalizeTableCellValue(row[header.id]), soft: statusColor.soft, ink: resolvePillInk(statusColor.text, statusColor.soft, theme.colors.background[50]), radius: variant.cells.pillRadius })) : (_jsx(Box, { display: "block", overflow: "hidden", whiteSpace: "normal", overflowWrap: "break-word", children: normalizeTableCellValue(header.node ? header.node(row) : row[header.id]) })) }, headerIndex + 1));
|
|
96
125
|
}), isLink && (_jsx(Td, { w: 2, p: 0, fontSize: 14, background: isChecked ? (isActionFreeze ? selectedFrozenBg : selectedBg) : isActionFreeze ? frozenCellBg : theme.colors.background[50], color: freezedTextColor, position: isActionFreeze ? "sticky" : "relative", borderBottom: borderStyle, boxSizing: "border-box", right: 0, zIndex: 1, className: `columns sticky-columns${isActionFreeze ? "-right" : ""}`, children: (row.onLink || row.onEdit || row.onDelete) ? (_jsx(TableActions, { row: row })) : null }))] }), rowContent && isExpanded && (_jsx(Tr, { ref: expandedRowRef, children: _jsx(Td, { colSpan: columns.length +
|
|
97
126
|
(isCheckbox ? 1 : 0) +
|
|
98
127
|
(isContent ? 1 : 0) +
|
|
@@ -101,6 +130,7 @@ const TableRow = React.memo(({ row, rowIndex, isChecked, isExpanded, isContent,
|
|
|
101
130
|
const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedBgColor, freezedTextColor, noBorders, handleCheckbox, selections, isLoading, onRowClick, isContent, isLink, isActionFreeze, loadingSkeletonRows, scrollContainerRef, density, accentColors, groups, onAddItem, emptyState, }) => {
|
|
102
131
|
var _a, _b, _c;
|
|
103
132
|
const theme = useCustomTheme();
|
|
133
|
+
const variant = useTableVariant();
|
|
104
134
|
const [expandedRows, setExpandedRows] = useState(new Set());
|
|
105
135
|
const [collapsedGroups, setCollapsedGroups] = useState(new Set());
|
|
106
136
|
const toggleGroup = useCallback((value) => {
|
|
@@ -246,17 +276,16 @@ const TableBody = ({ data, isCheckbox, columns, startRow, columnWidths, freezedB
|
|
|
246
276
|
const checkboxOffset = isCheckbox ? 50 : 0;
|
|
247
277
|
return columnWidths.map((_, idx) => columnWidths.slice(0, idx).reduce((sum, w) => sum + w, 0) + checkboxOffset);
|
|
248
278
|
}, [columnWidths, isCheckbox]);
|
|
249
|
-
const borderStyle = useMemo(() => noBorders ? "none" : `0.063rem solid ${
|
|
279
|
+
const borderStyle = useMemo(() => noBorders ? "none" : `0.063rem solid ${variant.row.ruleColor}`, [noBorders, variant.row.ruleColor]);
|
|
250
280
|
// Selected-row tint applied per-cell (NOT a blanket `& td` override) so status
|
|
251
281
|
// cells keep their solid color + white text instead of white-on-tint.
|
|
252
|
-
const selectedBg =
|
|
282
|
+
const selectedBg = variant.row.selectedBg;
|
|
253
283
|
// Sticky/frozen cells overlay scrolling content, so their selected background
|
|
254
284
|
// must be OPAQUE — paint the (translucent) tint over an opaque base.
|
|
255
285
|
const selectedFrozenBg = `linear-gradient(0deg, ${selectedBg}, ${selectedBg}), ${theme.colors.background[50]}`;
|
|
256
286
|
// Dark mode lifts the hover fill to `table.hover[500]`; `[300]` sits ~1.2:1 from the canvas.
|
|
257
287
|
// No ink flip needed — see `tableRowHoverStyle.ts`.
|
|
258
|
-
const
|
|
259
|
-
const hoverStyle = useMemo(() => getTableRowHoverStyle(theme.colors, isDark), [theme.colors, isDark]);
|
|
288
|
+
const hoverStyle = useMemo(() => ({ backgroundColor: variant.row.hoverBg }), [variant.row.hoverBg]);
|
|
260
289
|
// Flatten groups → one sequence of header/row items so it can be windowed.
|
|
261
290
|
// Independent of scrollTop, so scrolling reuses it instead of re-flattening.
|
|
262
291
|
const groupItems = useMemo(() => {
|
|
@@ -5,6 +5,6 @@ import { useCustomTheme } from "../../../Theme/useCustomTheme.js";
|
|
|
5
5
|
/** Monday-style colored, collapsible group header row. */
|
|
6
6
|
export const TableGroupRow = ({ label, value, count, color, collapsed, colSpan, ariaRowIndex, onToggle, onAddItem, }) => {
|
|
7
7
|
const theme = useCustomTheme();
|
|
8
|
-
return (_jsx(Tr, { "aria-rowindex": ariaRowIndex, children: _jsx(Td, { colSpan: colSpan, p: 0, border: "none", children: _jsxs(Flex, { align: "center", gap: 2, px: 3, py: 2, bg: color.soft, boxShadow: `inset 4px 0 0 ${color.solid}`, borderBottom: `0.063rem solid ${theme.colors.
|
|
8
|
+
return (_jsx(Tr, { "aria-rowindex": ariaRowIndex, children: _jsx(Td, { colSpan: colSpan, p: 0, border: "none", children: _jsxs(Flex, { align: "center", gap: 2, px: 3, py: 2, bg: color.soft, boxShadow: `inset 4px 0 0 ${color.solid}`, borderBottom: `0.063rem solid ${theme.colors.boxborder[200]}`, children: [_jsx(IconButton, { "aria-label": collapsed ? "Expand group" : "Collapse group", size: "xs", variant: "ghost", color: color.text, onClick: onToggle, icon: collapsed ? _jsx(ChevronRight, { size: 16 }) : _jsx(ChevronDown, { size: 16 }) }), _jsx(Text, { fontSize: "0.8125rem", fontWeight: 700, color: color.text, isTruncated: true, maxW: "20rem", children: label }), _jsx(Box, { as: "span", fontSize: "0.6875rem", fontWeight: 600, color: color.text, bg: theme.colors.background[50], borderRadius: "full", px: "0.5rem", py: "0.0625rem", children: count }), onAddItem && (_jsxs(Flex, { as: "button", type: "button", align: "center", gap: 1, ml: "auto", px: 2, py: 1, borderRadius: "md", fontSize: "0.75rem", fontWeight: 500, color: theme.colors.text[500], _hover: { bg: theme.colors.background[50], color: color.text }, onClick: () => onAddItem(value), children: [_jsx(Plus, { size: 14 }), " Add item"] }))] }) }) }));
|
|
9
9
|
};
|
|
10
10
|
export default TableGroupRow;
|
|
@@ -7,6 +7,7 @@ import { accentTextOnCanvas } from "../../../Theme/tokens/builders/accentText.js
|
|
|
7
7
|
import TableFilters from "../filters/TableFilters.js";
|
|
8
8
|
import { ChevronDown, ChevronsUpDown, ChevronUp } from "lucide-react";
|
|
9
9
|
import CheckBox from "../../Checkbox/Checkbox.js";
|
|
10
|
+
import { useTableVariant } from "../variants/TableVariantContext.js";
|
|
10
11
|
const TableHeader = ({ columns, isCheckbox, handleSort, headerRefs, columnWidths, columnsSort, headerBgColor, freezedBgColor, freezedTextColor, noBorders, handleCheckbox, checked, isLoading, isContent, isLink, isActionFreeze, setColumnsSearch, columnsSearch, isSelecting, }) => {
|
|
11
12
|
const [openFilterId, setOpenFilterId] = useState(null);
|
|
12
13
|
return (_jsxs(Tr, { pr: 0, "aria-rowindex": 1, children: [isContent && _jsx(ContentSpacerCell, { noBorders: noBorders }), isCheckbox && (_jsx(SelectAllCell, { freezedTextColor: freezedTextColor, noBorders: noBorders, isSelecting: isSelecting, isLoading: isLoading, checked: checked, handleCheckbox: handleCheckbox })), columns.map((header, index) => {
|
|
@@ -18,30 +19,33 @@ const TableHeader = ({ columns, isCheckbox, handleSort, headerRefs, columnWidths
|
|
|
18
19
|
export default memo(TableHeader);
|
|
19
20
|
const SelectAllCell = memo(({ freezedTextColor, noBorders, isSelecting, isLoading, checked, handleCheckbox, }) => {
|
|
20
21
|
const theme = useCustomTheme();
|
|
21
|
-
|
|
22
|
+
const variant = useTableVariant();
|
|
23
|
+
return (_jsx(Th, { scope: "col", w: variant.slot.width, minW: variant.slot.width, px: variant.slot.centered ? 0 : undefined, textAlign: variant.slot.centered ? "center" : undefined, fontSize: 14, fontWeight: 600, color: freezedTextColor, textTransform: "capitalize", backgroundColor: variant.header.bg, position: "sticky", className: `columns sticky-columns`, left: "0px", zIndex: 9, borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, children: _jsx(Box, { display: "flex", alignItems: "center", justifyContent: variant.slot.centered ? "center" : "flex-start", children: isSelecting ? (_jsx(Spinner, { size: "sm", color: theme.colors.accentText })) : (_jsx(CheckBox, { "aria-label": "Select all rows", variant: "outline", onChange: () => handleCheckbox(0), isChecked: isLoading ? false : checked === true, isIndeterminate: checked === "indeterminate" })) }) }));
|
|
22
24
|
});
|
|
23
25
|
const ContentSpacerCell = memo(({ noBorders }) => {
|
|
24
|
-
const
|
|
25
|
-
return (_jsx(Th, { scope: "col", w: 6, minW: "35px", p: 0, backgroundColor:
|
|
26
|
+
const variant = useTableVariant();
|
|
27
|
+
return (_jsx(Th, { scope: "col", w: 6, minW: "35px", p: 0, backgroundColor: variant.header.bg, borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, position: "sticky", className: `columns sticky-columns`, left: "0px", zIndex: 1 }));
|
|
26
28
|
});
|
|
27
29
|
const ViewSpacerCell = memo(({ noBorders, isActionFreeze, }) => {
|
|
28
|
-
const
|
|
29
|
-
return (_jsx(Th, { scope: "col", w: 2, p: 0, minW: "2.065rem", backgroundColor:
|
|
30
|
+
const variant = useTableVariant();
|
|
31
|
+
return (_jsx(Th, { scope: "col", w: 2, p: 0, minW: "2.065rem", backgroundColor: variant.header.bg, borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, position: isActionFreeze ? "sticky" : "relative", className: `columns sticky-columns`, right: "0px", zIndex: 1 }));
|
|
30
32
|
});
|
|
31
33
|
const SortingIcon = memo(({ label, sortState, isVisible, handleSortClick, }) => {
|
|
32
34
|
const theme = useCustomTheme();
|
|
35
|
+
const variant = useTableVariant();
|
|
33
36
|
return (_jsxs(Box, { as: "button", type: "button", "aria-label": `Sort by ${label}`, display: "inline-flex", alignItems: "center", justifyContent: "center", cursor: "pointer", ml: 2, p: 0, border: "none", bg: "transparent", lineHeight: "1", onClick: handleSortClick, transition: "color 0.2s ease-in-out, opacity 0.2s ease-in-out",
|
|
34
37
|
// Kept out of view until hover/focus so the header looks exactly as it did,
|
|
35
38
|
// while staying in the DOM and reachable by Tab.
|
|
36
39
|
opacity: isVisible ? 1 : 0, _focus: { opacity: 1 }, _focusVisible: { opacity: 1 }, color: theme.colors.textMuted, _hover: {
|
|
37
40
|
opacity: 1,
|
|
38
|
-
color: accentTextOnCanvas(theme.colors.secondary,
|
|
41
|
+
color: accentTextOnCanvas(theme.colors.secondary, variant.header.bg),
|
|
39
42
|
}, children: [sortState === "none" && _jsx(ChevronsUpDown, { size: 14 }), sortState === "asc" && _jsx(ChevronUp, { size: 14 }), sortState === "desc" && _jsx(ChevronDown, { size: 14 })] }));
|
|
40
43
|
});
|
|
41
44
|
const ColumnHeader = memo(({ header, index, columnWidths, isCheckbox, noBorders, columnsSort, headerRefs, columnsSearch, setColumnsSearch, handleSort, openFilterId, setOpenFilterId, }) => {
|
|
42
45
|
var _a;
|
|
43
46
|
const [isHovered, setIsHovered] = useState(false);
|
|
44
47
|
const theme = useCustomTheme();
|
|
48
|
+
const variant = useTableVariant();
|
|
45
49
|
const isFrozen = header.isFreeze;
|
|
46
50
|
const leftOffset = isFrozen
|
|
47
51
|
? calculateLeftOffset(columnWidths, index) + (isCheckbox ? 50 : 0)
|
|
@@ -77,7 +81,16 @@ const ColumnHeader = memo(({ header, index, columnWidths, isCheckbox, noBorders,
|
|
|
77
81
|
document.addEventListener("mouseup", onMouseUp);
|
|
78
82
|
};
|
|
79
83
|
const isSortActive = !!(isSorting === null || isSorting === void 0 ? void 0 : isSorting.direction) && isSorting.direction !== "none";
|
|
80
|
-
|
|
84
|
+
// Studio keeps the kebab out of the resting header — nine of them read louder than
|
|
85
|
+
// the labels. A column that is filtering or sorting keeps its control on show.
|
|
86
|
+
const showActions = variant.header.actions === "always" ||
|
|
87
|
+
isHovered ||
|
|
88
|
+
isSortActive ||
|
|
89
|
+
openFilterId === header.id ||
|
|
90
|
+
!!columnsSearch[header.id];
|
|
91
|
+
return (_jsx(Th, { scope: "col", ref: (el) => (headerRefs.current[index] = el), "aria-sort": ariaSort, textTransform: variant.header.textTransform, fontSize: variant.header.fontSize, fontWeight: variant.header.fontWeight, letterSpacing: variant.header.letterSpacing, position: isFrozen ? "sticky" : undefined, left: isFrozen ? leftOffset : undefined, minWidth: 120, backgroundColor: variant.header.bg, maxWidth: 500, zIndex: isFrozen ? 2 : 1, py: 2, pr: 0, overflow: "hidden", borderTop: "none", borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, borderRight: noBorders || !variant.header.showColumnRule
|
|
92
|
+
? "none"
|
|
93
|
+
: `0.063rem solid ${variant.header.ruleColor}`, className: `columns ${isFrozen ? "sticky-columns" : "scrollable-columns"}`, height: variant.header.height, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: _jsxs(Box, { display: "flex", color: variant.header.ink, lineHeight: "1.25rem", justifyContent: "space-between", alignItems: "center", position: "relative", children: [_jsx(Box, { textOverflow: "ellipsis", whiteSpace: "nowrap", display: "inline-block", overflow: "hidden", children: header.label }), _jsxs(Box, { display: "inline-flex", alignItems: "center", opacity: showActions ? 1 : 0, transition: "opacity 0.2s ease-in-out", _focusWithin: { opacity: 1 }, children: [header.isSort && (_jsx(SortingIcon, { label: String(header.label), sortState: (_a = isSorting === null || isSorting === void 0 ? void 0 : isSorting.direction) !== null && _a !== void 0 ? _a : "none", isVisible: isHovered || isSortActive, handleSortClick: () => {
|
|
81
94
|
let direction = "asc";
|
|
82
95
|
if ((isSorting === null || isSorting === void 0 ? void 0 : isSorting.direction) === "asc") {
|
|
83
96
|
direction = "desc";
|
|
@@ -86,7 +99,7 @@ const ColumnHeader = memo(({ header, index, columnWidths, isCheckbox, noBorders,
|
|
|
86
99
|
direction = "none";
|
|
87
100
|
}
|
|
88
101
|
handleSort(header.id, direction);
|
|
89
|
-
} })), sortPriority > 0 && (_jsx(Box, { as: "span", ml: 1, fontSize: "0.625rem", fontWeight: 700, lineHeight: "1", borderRadius: "full", px: "0.3rem", py: "0.1rem", bg: theme.colors.primary.opacity[16], color: accentTextOnCanvas(theme.colors.primary,
|
|
102
|
+
} })), sortPriority > 0 && (_jsx(Box, { as: "span", ml: 1, fontSize: "0.625rem", fontWeight: 700, lineHeight: "1", borderRadius: "full", px: "0.3rem", py: "0.1rem", bg: theme.colors.primary.opacity[16], color: accentTextOnCanvas(theme.colors.primary, variant.header.bg), children: sortPriority })), _jsx(TableFilters, { header: header, setColumnsSearch: (searchVal) => setColumnsSearch((prev) => ({
|
|
90
103
|
...prev,
|
|
91
104
|
[header.id]: searchVal,
|
|
92
105
|
})), columnsSearch: columnsSearch[header.id] || "", onClose: () => {
|
|
@@ -25,6 +25,6 @@ const TableSearch = ({ onSearch }) => {
|
|
|
25
25
|
setQuery(value);
|
|
26
26
|
handleDebounce(value);
|
|
27
27
|
};
|
|
28
|
-
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: "2", children: [!showInput && (_jsx(ToolTip, { label: "Search", placement: "top", children: _jsx(IconButton, { "aria-label": "Search", icon: _jsx(Icon, { as: Search, transform: "scaleX(-1)", boxSize: 5 }), variant: "unstyled", minW: "auto", h: "auto", display: "flex", cursor: "pointer", color: colors.text[500], onClick: handleSearchClick, _hover: { color: colors.accentText } }) })), showInput ? (_jsxs(InputGroup, { maxW: "12.5rem", transition: "all 0.3s ease", children: [_jsx(Input, { placeholder: "Search...", value: query, onChange: handleInputChange, size: "sm", borderRadius: "md", borderColor: colors.
|
|
28
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: "2", children: [!showInput && (_jsx(ToolTip, { label: "Search", placement: "top", children: _jsx(IconButton, { "aria-label": "Search", icon: _jsx(Icon, { as: Search, transform: "scaleX(-1)", boxSize: 5 }), variant: "unstyled", minW: "auto", h: "auto", display: "flex", cursor: "pointer", color: colors.text[500], onClick: handleSearchClick, _hover: { color: colors.accentText } }) })), showInput ? (_jsxs(InputGroup, { maxW: "12.5rem", transition: "all 0.3s ease", children: [_jsx(Input, { placeholder: "Search...", value: query, onChange: handleInputChange, size: "sm", borderRadius: "md", borderColor: colors.boxborder[500], _focus: { borderColor: colors.accentText }, autoFocus: true }), _jsx(InputRightElement, { display: "flex", alignItems: "center", height: "2rem", children: _jsx(IconButton, { "aria-label": "Clear search", icon: _jsx(Icon, { as: X }), variant: "unstyled", minW: "auto", h: "auto", display: "flex", color: colors.text[500], cursor: "pointer", onClick: handleSearchClick }) })] })) : null] }));
|
|
29
29
|
};
|
|
30
30
|
export default TableSearch;
|
|
@@ -82,7 +82,7 @@ const ManageColumns = ({ columns, items, setItems, childInputMethodsRef, }) => {
|
|
|
82
82
|
? theme.colors.primary[400]
|
|
83
83
|
: "transparent", cursor: "grab", fontSize: "13px", px: 2, py: "3px", mb: "3px", transition: "all 0.15s ease", _hover: {
|
|
84
84
|
bg: theme.colors.gray[50],
|
|
85
|
-
borderColor: theme.colors.
|
|
85
|
+
borderColor: theme.colors.boxborder[200],
|
|
86
86
|
}, _active: { cursor: "grabbing" }, children: [_jsx(Box, { as: "span", "aria-hidden": "true", display: "inline-flex", cursor: "grab", mr: 1, _hover: { color: theme.colors.gray[500] }, children: _jsx(GripVertical, { size: 14 }) }), _jsx(Checkbox, { isChecked: !item.isHidden, label: String(item.label), onChange: () => toggleCheckbox(item.id), size: "sm", labelSx: {
|
|
87
87
|
color: item.isHidden ? theme.colors.gray[500] : theme.colors.gray[700],
|
|
88
88
|
} }), _jsx(Text, { fontSize: "12px", color: theme.colors.gray[500], ml: 'auto', children: index + 1 }), _jsx(IconButton, { "aria-label": `Move ${item.label} up`, icon: _jsx(ChevronUp, { size: 14 }), variant: "ghost", size: "xs", ml: 1, minW: "auto", h: "auto", p: 0, color: theme.colors.gray[500], isDisabled: realIndex <= 0, onClick: () => moveItem(item.id, -1) }), _jsx(IconButton, { "aria-label": `Move ${item.label} down`, icon: _jsx(ChevronDown, { size: 14 }), variant: "ghost", size: "xs", ml: 1, minW: "auto", h: "auto", p: 0, color: theme.colors.gray[500], isDisabled: realIndex === -1 || realIndex >= items.length - 1, onClick: () => moveItem(item.id, 1) })] }, item.id));
|
|
@@ -48,7 +48,7 @@ const TableSettings = ({ columns, onSave, density = "normal", onDensityChange, g
|
|
|
48
48
|
return (_jsxs(Box, { children: [_jsx(ToolTip, { label: "Table Settings", placement: "top", children: _jsx(IconButton, { "aria-label": "Table settings", icon: _jsx(Settings, { size: "1.25rem" }), onClick: handleOpen, variant: "unstyled", minW: "auto", h: "auto", display: "flex", justifyContent: "flex-end", cursor: "pointer", transition: "all 0.2s ease", color: theme.colors.text[500], _hover: {
|
|
49
49
|
transform: "scale(1.1)",
|
|
50
50
|
color: theme.colors.accentText
|
|
51
|
-
} }) }), _jsxs(Modal, { isOpen: settingsOpen, onClose: () => setSettingsOpen(false), size: "md", children: [_jsx(ModalOverlay, {}), _jsxs(MotionModalContent, { mt: "0", top: "0", position: "absolute", borderTopRadius: "none", initial: { y: "-100%", opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: "-100%", opacity: 0 }, transition: { duration: 0.4, ease: "easeOut" }, padding: "0", minW: "620px", minH: "520px", maxH: "90vh", display: "flex", flexDirection: "column", bg: theme.colors.background[50], overflow: "hidden", children: [_jsx(ModalCloseButton, { top: 3, right: 3, zIndex: 2 }), _jsxs(Tabs, { index: selectedIndex, onChange: setSelectedIndex, variant: "unstyled", display: "flex", flexDirection: "column", flex: "1", minH: 0, children: [_jsxs(Box, { pt: 4, px: 4, children: [_jsx(Text, { fontWeight: 600, fontSize: "md", mb: 3, children: "Table Settings" }), _jsxs(TabList, { gap: 0, borderBottom: "1px solid", borderColor: theme.colors.
|
|
51
|
+
} }) }), _jsxs(Modal, { isOpen: settingsOpen, onClose: () => setSettingsOpen(false), size: "md", children: [_jsx(ModalOverlay, {}), _jsxs(MotionModalContent, { mt: "0", top: "0", position: "absolute", borderTopRadius: "none", initial: { y: "-100%", opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: "-100%", opacity: 0 }, transition: { duration: 0.4, ease: "easeOut" }, padding: "0", minW: "620px", minH: "520px", maxH: "90vh", display: "flex", flexDirection: "column", bg: theme.colors.background[50], overflow: "hidden", children: [_jsx(ModalCloseButton, { top: 3, right: 3, zIndex: 2 }), _jsxs(Tabs, { index: selectedIndex, onChange: setSelectedIndex, variant: "unstyled", display: "flex", flexDirection: "column", flex: "1", minH: 0, children: [_jsxs(Box, { pt: 4, px: 4, children: [_jsx(Text, { fontWeight: 600, fontSize: "md", mb: 3, children: "Table Settings" }), _jsxs(TabList, { gap: 0, borderBottom: "1px solid", borderColor: theme.colors.boxborder[200], children: [_jsx(Tab, { ...tabStyle, children: "Density" }), _jsx(Tab, { ...tabStyle, children: "Manage Columns" }), _jsx(Tab, { ...tabStyle, children: "Group" })] })] }), _jsx(ModalBody, { px: 4, py: 3, flex: "1", overflowY: "auto", sx: scrollbarStyles(theme.colors), children: _jsxs(TabPanels, { children: [_jsx(TabPanel, { px: 0, py: 1, children: _jsx(Flex, { direction: "column", gap: 2, children: DENSITY_OPTIONS.map((opt) => {
|
|
52
52
|
const selected = density === opt.value;
|
|
53
53
|
return (_jsxs(Flex, { as: "button", type: "button", align: "center", gap: 3, px: 3, py: 2.5, borderRadius: "lg", border: "1px solid", borderColor: selected ? theme.colors.accentText : theme.colors.boxborder[200], bg: selected ? theme.colors.primary.opacity[8] : theme.colors.transparent, transition: "all 0.15s", _hover: { borderColor: theme.colors.accentText }, onClick: () => onDensityChange === null || onDensityChange === void 0 ? void 0 : onDensityChange(opt.value), children: [_jsx(Box, { boxSize: "1.1rem", borderRadius: "full", border: "2px solid", borderColor: selected ? theme.colors.accentText : theme.colors.boxborder[500], display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: selected && _jsx(Check, { size: 11, color: theme.colors.accentText, strokeWidth: 3 }) }), _jsxs(Box, { textAlign: "left", children: [_jsx(Text, { fontSize: "13px", fontWeight: 600, color: theme.colors.text[700], children: opt.label }), _jsx(Text, { fontSize: "11px", color: theme.colors.textMuted, children: opt.desc })] })] }, opt.value));
|
|
54
54
|
}) }) }), _jsx(TabPanel, { px: 0, py: 0, children: _jsx(Box, { sx: {
|
|
@@ -69,6 +69,6 @@ const TableSettings = ({ columns, onSave, density = "normal", onDensityChange, g
|
|
|
69
69
|
var _a;
|
|
70
70
|
const selected = selectedGroup === opt.id;
|
|
71
71
|
return (_jsxs(Flex, { as: "button", type: "button", align: "center", gap: 3, px: 3, py: 2.5, borderRadius: "lg", border: "1px solid", borderColor: selected ? theme.colors.accentText : theme.colors.boxborder[200], bg: selected ? theme.colors.primary.opacity[8] : theme.colors.transparent, transition: "all 0.15s", _hover: { borderColor: theme.colors.accentText }, onClick: () => setSelectedGroup(opt.id), children: [_jsx(Box, { boxSize: "1.1rem", borderRadius: "full", border: "2px solid", borderColor: selected ? theme.colors.accentText : theme.colors.boxborder[500], display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: selected && _jsx(Check, { size: 11, color: theme.colors.accentText, strokeWidth: 3 }) }), _jsx(Text, { fontSize: "13px", fontWeight: 600, color: theme.colors.text[700], textAlign: "left", children: opt.label })] }, String((_a = opt.id) !== null && _a !== void 0 ? _a : "__none__")));
|
|
72
|
-
}) })] })] }) })] }), _jsxs(ModalFooter, { gap: "0.5rem", borderTop: "1px solid", borderColor: theme.colors.
|
|
72
|
+
}) })] })] }) })] }), _jsxs(ModalFooter, { gap: "0.5rem", borderTop: "1px solid", borderColor: theme.colors.boxborder[200], pt: 3, pb: 3, px: 4, children: [_jsx(Button, { label: "Close", size: "sm", onClick: () => setSettingsOpen(false), variant: "outline", colorScheme: "red" }), _jsx(Button, { label: "Save", size: "sm", onClick: handleSave })] })] })] })] }));
|
|
73
73
|
};
|
|
74
74
|
export default TableSettings;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo } from "react";
|
|
2
|
+
import { useCustomTheme } from "../../../Theme/useCustomTheme.js";
|
|
3
|
+
import { isDarkCanvas } from "../../../Theme/tokens/builders/accentText.js";
|
|
4
|
+
import { classicPreset } from "./classic.js";
|
|
5
|
+
const TableVariantContext = createContext(null);
|
|
6
|
+
export const TableVariantProvider = TableVariantContext.Provider;
|
|
7
|
+
/** No provider means the table as it always was — the default is a variant, not a gap. */
|
|
8
|
+
export function useTableVariant() {
|
|
9
|
+
const provided = useContext(TableVariantContext);
|
|
10
|
+
const theme = useCustomTheme();
|
|
11
|
+
const isDark = isDarkCanvas(theme.colors.backgroundColor.main);
|
|
12
|
+
return useMemo(() => provided !== null && provided !== void 0 ? provided : classicPreset(theme.colors, isDark), [provided, theme.colors, isDark]);
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { accentTextOnCanvas } from "../../../Theme/tokens/builders/accentText.js";
|
|
2
|
+
import { getTableRowHoverStyle } from "../components/tableRowHoverStyle.js";
|
|
3
|
+
/**
|
|
4
|
+
* The table as it shipped. Pinned by snapshot: every value here is read off the
|
|
5
|
+
* live component, so adding a variant cannot move it.
|
|
6
|
+
*/
|
|
7
|
+
export const classicPreset = (colors, isDark) => {
|
|
8
|
+
const headerBg = colors.table.hover[200];
|
|
9
|
+
return {
|
|
10
|
+
header: {
|
|
11
|
+
bg: headerBg,
|
|
12
|
+
ink: accentTextOnCanvas(colors.secondary, headerBg),
|
|
13
|
+
fontSize: 13,
|
|
14
|
+
fontWeight: 600,
|
|
15
|
+
textTransform: "capitalize",
|
|
16
|
+
letterSpacing: "normal",
|
|
17
|
+
height: "45px",
|
|
18
|
+
ruleColor: colors.boxborder[200],
|
|
19
|
+
showColumnRule: true,
|
|
20
|
+
actions: "always",
|
|
21
|
+
},
|
|
22
|
+
row: {
|
|
23
|
+
hoverBg: getTableRowHoverStyle(colors, isDark).backgroundColor,
|
|
24
|
+
hoverScope: "cell",
|
|
25
|
+
ruleColor: colors.boxborder[200],
|
|
26
|
+
frozenRuleColor: colors.boxborder[300],
|
|
27
|
+
selectedBg: colors.primary.opacity[16],
|
|
28
|
+
selectedBarColor: colors.primary[500],
|
|
29
|
+
showSelectedBar: false,
|
|
30
|
+
},
|
|
31
|
+
slot: {
|
|
32
|
+
width: "6",
|
|
33
|
+
rest: "checkbox",
|
|
34
|
+
indexInk: colors.textMuted,
|
|
35
|
+
centered: false,
|
|
36
|
+
},
|
|
37
|
+
cells: {
|
|
38
|
+
ink: colors.text[500],
|
|
39
|
+
statusFill: "bleed",
|
|
40
|
+
pillRadius: "0",
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { classicPreset } from "./classic.js";
|
|
2
|
+
import { studioPreset } from "./studio.js";
|
|
3
|
+
/** Adding a variant is one file and one union member — no component changes. */
|
|
4
|
+
export const TABLE_VARIANTS = {
|
|
5
|
+
classic: classicPreset,
|
|
6
|
+
studio: studioPreset,
|
|
7
|
+
};
|
|
8
|
+
export * from "./tokens.js";
|
|
9
|
+
export { classicPreset, studioPreset };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AA_NORMAL_TEXT } from "../../../Theme/tokens/builders/accentText.js";
|
|
2
|
+
import { compositeOver } from "../../../Theme/tokens/builders/color.js";
|
|
3
|
+
import { liftToContrast } from "../../../Theme/tokens/builders/outlineRung.js";
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
/**
|
|
6
|
+
* A consumer `statusColors` hex is a FILL, and `pickTableColor` hands it straight back as ink
|
|
7
|
+
* over a 13%-alpha tint of itself — legible reversed out of a solid cell, ~1.9:1 on a pill.
|
|
8
|
+
* The palette path is already resolved by `resolveStatusTone`; this rescues the override path.
|
|
9
|
+
*/
|
|
10
|
+
export function resolvePillInk(ink, soft, surface) {
|
|
11
|
+
const key = `${ink}|${soft}|${surface}`;
|
|
12
|
+
const hit = cache.get(key);
|
|
13
|
+
if (hit !== undefined)
|
|
14
|
+
return hit;
|
|
15
|
+
const resolved = liftToContrast(ink, compositeOver(soft, surface), AA_NORMAL_TEXT);
|
|
16
|
+
cache.set(key, resolved);
|
|
17
|
+
return resolved;
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CHROME_RULE, MIN_PERCEPTIBLE_STEP, liftToContrast, } from "../../../Theme/tokens/builders/outlineRung.js";
|
|
2
|
+
import { getTableRowHoverStyle } from "../components/tableRowHoverStyle.js";
|
|
3
|
+
/**
|
|
4
|
+
* Quieter chrome, one rule instead of a grid, and a slot that rests on the row
|
|
5
|
+
* number. Every colour is lifted against the surface it actually sits on.
|
|
6
|
+
*/
|
|
7
|
+
export const studioPreset = (colors, isDark) => {
|
|
8
|
+
const surface = colors.background[50];
|
|
9
|
+
// Studio drops the per-column rule, so the one line left has to be visible on
|
|
10
|
+
// every brand — `boxborder[200]` measures as low as 1.09:1 on some of them.
|
|
11
|
+
const rule = liftToContrast(colors.boxborder[200], surface, CHROME_RULE);
|
|
12
|
+
return {
|
|
13
|
+
header: {
|
|
14
|
+
bg: surface,
|
|
15
|
+
ink: colors.text[500],
|
|
16
|
+
fontSize: 11,
|
|
17
|
+
fontWeight: 600,
|
|
18
|
+
textTransform: "uppercase",
|
|
19
|
+
letterSpacing: "0.06em",
|
|
20
|
+
height: "40px",
|
|
21
|
+
ruleColor: rule,
|
|
22
|
+
showColumnRule: false,
|
|
23
|
+
actions: "hover",
|
|
24
|
+
},
|
|
25
|
+
row: {
|
|
26
|
+
hoverBg: getTableRowHoverStyle(colors, isDark).backgroundColor,
|
|
27
|
+
hoverScope: "row",
|
|
28
|
+
ruleColor: rule,
|
|
29
|
+
frozenRuleColor: rule,
|
|
30
|
+
selectedBg: liftToContrast(colors.primary[50], surface, MIN_PERCEPTIBLE_STEP),
|
|
31
|
+
selectedBarColor: colors.primary[500],
|
|
32
|
+
showSelectedBar: true,
|
|
33
|
+
},
|
|
34
|
+
slot: {
|
|
35
|
+
width: "3.5rem",
|
|
36
|
+
rest: "index",
|
|
37
|
+
indexInk: colors.textMuted,
|
|
38
|
+
centered: true,
|
|
39
|
+
},
|
|
40
|
+
cells: {
|
|
41
|
+
ink: colors.text[500],
|
|
42
|
+
statusFill: "pill",
|
|
43
|
+
pillRadius: "0.375rem",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -3,7 +3,7 @@ import { Box, Thead, Table, Tr, Th, Tbody, Td, Checkbox, } from "@chakra-ui/reac
|
|
|
3
3
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
4
4
|
const TrailWorks = () => {
|
|
5
5
|
const { colors } = useCustomTheme();
|
|
6
|
-
const cellBorder = colors.
|
|
6
|
+
const cellBorder = colors.boxborder[200];
|
|
7
7
|
const data = [
|
|
8
8
|
{
|
|
9
9
|
contact: "John Doe",
|
|
@@ -28,9 +28,11 @@ const baseStyle = definePartsStyle(({ theme }) => {
|
|
|
28
28
|
[$bg.variable]: colors.backgroundColor.light,
|
|
29
29
|
bg: $bg.reference,
|
|
30
30
|
color: colors.text[900],
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
31
|
+
// A rule at CHROME_RULE weight, not Chakra's gray.200: that default read as a hard edge,
|
|
32
|
+
// which is why this shipped borderless. At 1.31:1 the edge defines the surface instead.
|
|
33
|
+
borderWidth: "1px",
|
|
34
|
+
borderStyle: "solid",
|
|
35
|
+
borderColor: colors.boxborder[200],
|
|
34
36
|
},
|
|
35
37
|
// Own CSS var (`--menu-item-bg`), NOT the list's `--menu-bg` — Chakra's stock item
|
|
36
38
|
// baseStyle rewrites `--menu-bg` itself on hover/focus, so reusing that same var name
|
|
@@ -18,8 +18,12 @@ const baseStyle = definePartsStyle(({ theme }) => {
|
|
|
18
18
|
content: {
|
|
19
19
|
[$popperBg.variable]: colors.backgroundColor.light,
|
|
20
20
|
bg: $popperBg.reference,
|
|
21
|
-
[$arrowShadowColor.variable]: colors.
|
|
21
|
+
[$arrowShadowColor.variable]: colors.boxborder[200],
|
|
22
22
|
color: colors.text[900],
|
|
23
|
+
// Matches Menu: a CHROME_RULE-weight edge, not Chakra's gray.200 hard edge.
|
|
24
|
+
borderWidth: "1px",
|
|
25
|
+
borderStyle: "solid",
|
|
26
|
+
borderColor: colors.boxborder[200],
|
|
23
27
|
},
|
|
24
28
|
};
|
|
25
29
|
});
|
|
@@ -23,7 +23,10 @@ const toBrandLabel = (brand) => brand.charAt(0).toUpperCase() + brand.slice(1);
|
|
|
23
23
|
* regardless of which brand/mode is currently active.
|
|
24
24
|
*/
|
|
25
25
|
const brandSwatchColor = (brand) => brandThemes[brand].light.colors.primary[500];
|
|
26
|
-
const BrandSwatch = ({ brand }) =>
|
|
26
|
+
const BrandSwatch = ({ brand }) => {
|
|
27
|
+
const { colors } = useCustomTheme();
|
|
28
|
+
return (_jsx(Box, { as: "span", display: "inline-block", w: "0.625rem", h: "0.625rem", borderRadius: "full", bg: brandSwatchColor(brand), border: "1px solid", borderColor: colors.boxborder[200], flexShrink: 0 }));
|
|
29
|
+
};
|
|
27
30
|
/**
|
|
28
31
|
* Palette values already differ per color mode (`brandThemes[brand].light|dark`), so tokens
|
|
29
32
|
* are read straight from the active theme — no `useColorModeValue` pairing needed here.
|
|
@@ -36,7 +39,7 @@ const useSwitcherColors = () => {
|
|
|
36
39
|
activeColor: colors.primary[500],
|
|
37
40
|
mutedColor: colors.text[600],
|
|
38
41
|
hoverBg: colors.gray[100],
|
|
39
|
-
borderColor: colors.
|
|
42
|
+
borderColor: colors.boxborder[200],
|
|
40
43
|
// NOTE: menuBg lives in Menu.styles.ts — re-pushing it via `sx` flickers on every render.
|
|
41
44
|
menuColor: colors.text[900],
|
|
42
45
|
};
|
|
@@ -7,6 +7,29 @@ import { contrastRatio, hexToHsl, relativeLuminance, withLightness } from "./col
|
|
|
7
7
|
export const MIN_PERCEPTIBLE_STEP = 1.15;
|
|
8
8
|
/** WCAG 1.4.11 — a control outline is a UI component boundary, held to 3:1. */
|
|
9
9
|
export const AA_NON_TEXT = 3;
|
|
10
|
+
/**
|
|
11
|
+
* Resting chrome — card, popover and field outlines — settles here, not at `AA_NON_TEXT`.
|
|
12
|
+
* At 3:1 a container border is text-weight and the UI reads as a wireframe; every mainstream
|
|
13
|
+
* system draws this line far lighter (Tailwind gray-200 1.24, Chakra gray.200 1.30, Material
|
|
14
|
+
* outline-variant 1.61). 1.4.11 asks that a control be identifiable, not that its resting
|
|
15
|
+
* border carry the whole job: the fill, the label and the focus ring do that, and the focus
|
|
16
|
+
* ring is still held to `AA_NON_TEXT`.
|
|
17
|
+
*/
|
|
18
|
+
export const CHROME_BORDER = 1.5;
|
|
19
|
+
// The resting rule between two surfaces — a card edge, a divider, a menu outline. Lighter than
|
|
20
|
+
// CHROME_BORDER, which outlines a control the user acts on.
|
|
21
|
+
export const CHROME_RULE = 1.31;
|
|
22
|
+
/**
|
|
23
|
+
* A rule is decorative, so it has a ceiling and no floor: soften it until the surface it reads
|
|
24
|
+
* hardest against sits at `target`, and leave a already-fainter rung alone. Never `settleAcross`
|
|
25
|
+
* here — that clears a floor on EVERY surface, so one tinted surface drags the rule darker.
|
|
26
|
+
*/
|
|
27
|
+
export function ruleInk(hex, surfaces, target) {
|
|
28
|
+
const loudest = surfaces.reduce((worst, s) => contrastRatio(hex, s) > contrastRatio(hex, worst) ? s : worst);
|
|
29
|
+
return contrastRatio(hex, loudest) > target
|
|
30
|
+
? softenToContrast(hex, loudest, target)
|
|
31
|
+
: hex;
|
|
32
|
+
}
|
|
10
33
|
/**
|
|
11
34
|
* Walks a hex toward `target` contrast against `backdrop`, keeping hue and saturation.
|
|
12
35
|
* Direction is DERIVED from the backdrop, never passed: a mode argument here was redundant
|
|
@@ -50,9 +73,10 @@ export function softenToContrast(hex, backdrop, target) {
|
|
|
50
73
|
}
|
|
51
74
|
/**
|
|
52
75
|
* `boxborder[500]` is the rung every control outline reads (Checkbox, SearchSelect, the date
|
|
53
|
-
* pickers, the editor's inputs).
|
|
54
|
-
*
|
|
55
|
-
*
|
|
76
|
+
* pickers, the editor's inputs). Settled onto `CHROME_BORDER` — lifted when a brand authored it
|
|
77
|
+
* invisibly pale, softened when it overshoots. It was previously lifted to `AA_NON_TEXT`, which
|
|
78
|
+
* shipped 3.85-4.95:1 outlines: a text-weight box around every card and popover.
|
|
79
|
+
* Resolved here rather than hand-pinned per brand: a copied hex goes stale the moment a canvas moves.
|
|
56
80
|
*
|
|
57
81
|
* `border[*]` is deliberately untouched — that ramp draws table gridlines, which are decorative
|
|
58
82
|
* and outside 1.4.11.
|
|
@@ -65,7 +89,9 @@ export function withOutlineRung(palette) {
|
|
|
65
89
|
contrastRatio(current, palette.background[50])
|
|
66
90
|
? palette.backgroundColor.main
|
|
67
91
|
: palette.background[50];
|
|
68
|
-
const resolved =
|
|
92
|
+
const resolved = contrastRatio(current, backdrop) > CHROME_BORDER
|
|
93
|
+
? softenToContrast(current, backdrop, CHROME_BORDER)
|
|
94
|
+
: liftToContrast(current, backdrop, CHROME_BORDER);
|
|
69
95
|
if (resolved === current)
|
|
70
96
|
return palette;
|
|
71
97
|
return { ...palette, boxborder: { ...palette.boxborder, 500: resolved } };
|
|
@@ -81,3 +107,25 @@ export function withOutlineRung(palette) {
|
|
|
81
107
|
export function resolveHoverTint(seed, restSurface) {
|
|
82
108
|
return liftToContrast(seed, restSurface, MIN_PERCEPTIBLE_STEP);
|
|
83
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Settles ink into `target` against EVERY surface at once. Softening one step can change which
|
|
112
|
+
* surface is hardest, so resolving against a single pre-computed "hardest" one lands under the
|
|
113
|
+
* floor on another — that is how radiant and rosewood ended up with an invisible outline on
|
|
114
|
+
* their muted surface.
|
|
115
|
+
*/
|
|
116
|
+
export function settleAcross(hex, surfaces, target) {
|
|
117
|
+
const clearsAll = (c) => surfaces.every((s) => contrastRatio(c, s) >= target);
|
|
118
|
+
if (!clearsAll(hex)) {
|
|
119
|
+
return surfaces.reduce((ink, s) => liftToContrast(ink, s, target), hex);
|
|
120
|
+
}
|
|
121
|
+
const { s: sat, l: start } = hexToHsl(hex);
|
|
122
|
+
const toward = relativeLuminance(surfaces[0]) < 0.5 ? -1 : 1;
|
|
123
|
+
let settled = hex;
|
|
124
|
+
for (let l = start; l > 0 && l < 100; l += toward) {
|
|
125
|
+
const next = withLightness(hex, l, sat);
|
|
126
|
+
if (!clearsAll(next))
|
|
127
|
+
break;
|
|
128
|
+
settled = next;
|
|
129
|
+
}
|
|
130
|
+
return settled;
|
|
131
|
+
}
|