pixelize-design-library 2.4.2-beta.63 → 2.4.2-beta.64

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/COMPONENT-GOTCHAS.md +26 -3
  3. package/DESIGN-GUIDE.md +1 -0
  4. package/dist/Components/Button/Button.styles.js +7 -2
  5. package/dist/Components/Dropdown/DropDown.js +2 -9
  6. package/dist/Components/FilePreview/FilePreview.js +2 -1
  7. package/dist/Components/FileUploader/FileUploader.js +4 -0
  8. package/dist/Components/FileUploader/FileUploaderProps.d.ts +1 -1
  9. package/dist/Components/Input/TextInput.styles.js +10 -6
  10. package/dist/Components/KanbanBoard/KanbanBoard.js +6 -5
  11. package/dist/Components/NavigationBar/NavigationBar.js +2 -1
  12. package/dist/Components/OrgSwitcher/OrgSwitcher.js +2 -4
  13. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +6 -5
  14. package/dist/Components/ScrollToTop/ScrollToTop.js +4 -3
  15. package/dist/Components/SearchSelect/SearchSelect.js +4 -16
  16. package/dist/Components/Select/SelectPopup.js +1 -3
  17. package/dist/Components/Select/optionRowInk.d.ts +12 -0
  18. package/dist/Components/Select/optionRowInk.js +10 -1
  19. package/dist/Components/SelectSearch/SelectSearch.js +1 -10
  20. package/dist/Components/Table/Table.js +3 -2
  21. package/dist/Components/Table/TableProps.d.ts +1 -1
  22. package/dist/Components/Table/components/TableBody.js +9 -1
  23. package/dist/Components/Table/components/TableHeader.js +7 -2
  24. package/dist/Components/Table/components/TableLoading.d.ts +4 -1
  25. package/dist/Components/Table/components/TableLoading.js +6 -5
  26. package/dist/Components/Table/filters/CompactSelect.js +1 -10
  27. package/dist/Components/Table/variants/TableVariantContext.d.ts +1 -1
  28. package/dist/Components/Table/variants/TableVariantContext.js +3 -3
  29. package/dist/Components/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
  30. package/dist/Components/WorkspaceWindow/WorkspaceWindow.d.ts +2 -2
  31. package/dist/Components/WorkspaceWindow/WorkspaceWindow.js +25 -18
  32. package/dist/Components/WorkspaceWindow/WorkspaceWindowProps.d.ts +38 -2
  33. package/dist/Theme/chakra/Menu.styles.d.ts +8 -0
  34. package/dist/Theme/chakra/Menu.styles.js +7 -0
  35. package/dist/Theme/chakra/componentStyles.d.ts +8 -0
  36. package/dist/Theme/chakra/componentStyles.js +2 -2
  37. package/dist/Theme/tokens/builders/accentText.d.ts +2 -0
  38. package/dist/Theme/tokens/builders/accentText.js +7 -1
  39. package/dist/Theme/tokens/builders/filledSurface.d.ts +5 -1
  40. package/dist/Theme/tokens/builders/filledSurface.js +17 -4
  41. package/dist/Theme/tokens/builders/planeStep.d.ts +1 -0
  42. package/dist/Theme/tokens/builders/planeStep.js +5 -4
  43. package/dist/Utils/table.js +2 -2
  44. package/dist/esm/Components/Button/Button.styles.js +8 -3
  45. package/dist/esm/Components/Dropdown/DropDown.js +2 -9
  46. package/dist/esm/Components/FilePreview/FilePreview.js +3 -2
  47. package/dist/esm/Components/FileUploader/FileUploader.js +4 -0
  48. package/dist/esm/Components/Input/TextInput.styles.js +10 -6
  49. package/dist/esm/Components/KanbanBoard/KanbanBoard.js +6 -5
  50. package/dist/esm/Components/NavigationBar/NavigationBar.js +2 -1
  51. package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +2 -4
  52. package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +7 -6
  53. package/dist/esm/Components/ScrollToTop/ScrollToTop.js +5 -4
  54. package/dist/esm/Components/SearchSelect/SearchSelect.js +6 -18
  55. package/dist/esm/Components/Select/SelectPopup.js +2 -4
  56. package/dist/esm/Components/Select/optionRowInk.js +7 -0
  57. package/dist/esm/Components/SelectSearch/SelectSearch.js +2 -11
  58. package/dist/esm/Components/Table/Table.js +4 -3
  59. package/dist/esm/Components/Table/components/TableBody.js +10 -2
  60. package/dist/esm/Components/Table/components/TableHeader.js +9 -4
  61. package/dist/esm/Components/Table/components/TableLoading.js +5 -4
  62. package/dist/esm/Components/Table/filters/CompactSelect.js +2 -11
  63. package/dist/esm/Components/Table/variants/TableVariantContext.js +3 -3
  64. package/dist/esm/Components/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
  65. package/dist/esm/Components/WorkspaceWindow/WorkspaceWindow.js +26 -19
  66. package/dist/esm/Theme/chakra/Menu.styles.js +7 -0
  67. package/dist/esm/Theme/chakra/componentStyles.js +2 -2
  68. package/dist/esm/Theme/tokens/builders/accentText.js +5 -0
  69. package/dist/esm/Theme/tokens/builders/filledSurface.js +18 -5
  70. package/dist/esm/Theme/tokens/builders/planeStep.js +3 -3
  71. package/dist/esm/Utils/table.js +2 -2
  72. package/dist/esm/index.js +4 -4
  73. package/dist/index.d.ts +5 -5
  74. package/dist/index.js +11 -6
  75. package/package.json +1 -1
@@ -1,7 +1,19 @@
1
+ import { type RowStateBackgrounds } from "../../Theme/tokens/builders/rowStates";
1
2
  import type { PaletteProps } from "../../Theme/tokens/types";
2
3
  /** Selection is carried by weight and a trailing brand check, so the label itself stays neutral. */
3
4
  export declare const optionRowInk: (colors: PaletteProps, isSingleSelected: boolean) => string;
4
5
  export declare const optionRowDescriptionInk: (colors: PaletteProps) => string;
6
+ /** The one fill an option row paints: the cursor's (hover or keyboard). Selection never fills a row. */
7
+ export declare const optionRowFill: (rows: RowStateBackgrounds, isActive: boolean) => string;
8
+ /** Fill for rows that take real DOM focus (roving tabindex) — `:hover` and `:focus` are the cursor. */
9
+ export declare const optionRowCursorSx: (rows: RowStateBackgrounds) => {
10
+ _hover: {
11
+ bg: string;
12
+ };
13
+ _focus: {
14
+ bg: string;
15
+ };
16
+ };
5
17
  export declare const OPTION_ROW_REST_WEIGHT = 400;
6
18
  export declare const OPTION_ROW_SELECTED_WEIGHT = 500;
7
19
  export declare const optionRowWeight: (isSingleSelected: boolean) => number;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.optionRowWeight = exports.OPTION_ROW_SELECTED_WEIGHT = exports.OPTION_ROW_REST_WEIGHT = exports.optionRowDescriptionInk = exports.optionRowInk = void 0;
3
+ exports.optionRowWeight = exports.OPTION_ROW_SELECTED_WEIGHT = exports.OPTION_ROW_REST_WEIGHT = exports.optionRowCursorSx = exports.optionRowFill = exports.optionRowDescriptionInk = exports.optionRowInk = void 0;
4
4
  const accentText_1 = require("../../Theme/tokens/builders/accentText");
5
5
  const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
6
6
  const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
@@ -10,6 +10,15 @@ exports.optionRowInk = optionRowInk;
10
10
  // A 12px sub-line reached only 4.00:1 on the hovered selected row in dark.
11
11
  const optionRowDescriptionInk = (colors) => (0, outlineRung_1.liftToContrast)(colors.textMuted, (0, rowStates_1.listRowBackgrounds)(colors).selectedActive, accentText_1.AA_NORMAL_TEXT);
12
12
  exports.optionRowDescriptionInk = optionRowDescriptionInk;
13
+ /** The one fill an option row paints: the cursor's (hover or keyboard). Selection never fills a row. */
14
+ const optionRowFill = (rows, isActive) => isActive ? rows.active : "transparent";
15
+ exports.optionRowFill = optionRowFill;
16
+ /** Fill for rows that take real DOM focus (roving tabindex) — `:hover` and `:focus` are the cursor. */
17
+ const optionRowCursorSx = (rows) => ({
18
+ _hover: { bg: rows.active },
19
+ _focus: { bg: rows.active },
20
+ });
21
+ exports.optionRowCursorSx = optionRowCursorSx;
13
22
  exports.OPTION_ROW_REST_WEIGHT = 400;
14
23
  // NOTE: 600 beside a tinted fill and a check read as three signals for one state.
15
24
  exports.OPTION_ROW_SELECTED_WEIGHT = 500;
@@ -36,18 +36,9 @@ const RenderOptions = ({ isOptionLoading, filteredOptions, loadingText, handleOp
36
36
  const selected = isOptionSelected(option);
37
37
  const isSingleSelected = selected && !isMultipleSelect;
38
38
  const isActive = index === activeIndex;
39
- const rowBg = selected
40
- ? isActive
41
- ? rowBackgrounds.selectedActive
42
- : rowBackgrounds.selected
43
- : isActive
44
- ? rowBackgrounds.active
45
- : "transparent";
46
39
  return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "button", type: "button", ref: (el) => {
47
40
  optionRefs.current[index] = el;
48
- }, role: "option", "aria-selected": selected, tabIndex: isActive ? 0 : -1, w: `calc(100% - 2 * ${spec.mx})`, minH: spec.minH, textAlign: "left", border: "none", fontFamily: "inherit", bg: rowBg, mx: spec.mx, px: spec.px, py: spec.py, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, fontSize: spec.fontSize, cursor: "pointer", color: (0, optionRowInk_1.optionRowInk)(theme.colors, isSingleSelected), fontWeight: (0, optionRowInk_1.optionRowWeight)(isSingleSelected), transition: ROW_TRANSITION, sx: (0, motion_2.reducedMotionSx)(), _hover: {
49
- bg: selected ? rowBackgrounds.selectedActive : rowBackgrounds.active,
50
- }, onClick: () => handleOptionClick(option), onKeyDown: (e) => {
41
+ }, role: "option", "aria-selected": selected, tabIndex: isActive ? 0 : -1, w: `calc(100% - 2 * ${spec.mx})`, minH: spec.minH, textAlign: "left", border: "none", fontFamily: "inherit", bg: "transparent", mx: spec.mx, px: spec.px, py: spec.py, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, fontSize: spec.fontSize, cursor: "pointer", color: (0, optionRowInk_1.optionRowInk)(theme.colors, isSingleSelected), fontWeight: (0, optionRowInk_1.optionRowWeight)(isSingleSelected), transition: ROW_TRANSITION, sx: (0, motion_2.reducedMotionSx)(), ...(0, optionRowInk_1.optionRowCursorSx)(rowBackgrounds), onClick: () => handleOptionClick(option), onKeyDown: (e) => {
51
42
  if (e.key === "Enter" || e.key === " ") {
52
43
  e.preventDefault();
53
44
  handleOptionClick(option);
@@ -49,11 +49,12 @@ function Table({ data, columns, onSelection, isLoading, isCheckbox = false, free
49
49
  name: "",
50
50
  page: "",
51
51
  orgId: "",
52
- }, paginationMode = "client", infiniteScroll = false, hasMore, isLoadingMore = false, groupLoadMore = false, loadMoreText = "Load more", loadMorePosition = "top", loadMoreChunkSize, noOfRowsPerPage = 50, totalRecords = 0, onPagination, isTableSettings = false, headerActions, onGlobalSearch, onNoOfRowsPerPageChange, paginationSelectOptions, tableMaxHeight, minVisibleRows, maxVisibleRows, autoFitViewport, tableSettings, filterSidebar, loadingSkeletonRows, defaultVisibleColumns, density = "normal", variant = "classic", stripe = false, renderRowDetail, expandedRows, onExpandedRowsChange, groupColors, onAddItem, emptyState, onRefresh, isGlobalSearch = true, }) {
52
+ }, paginationMode = "client", infiniteScroll = false, hasMore, isLoadingMore = false, groupLoadMore = false, loadMoreText = "Load more", loadMorePosition = "top", loadMoreChunkSize, noOfRowsPerPage = 50, totalRecords = 0, onPagination, isTableSettings = false, headerActions, onGlobalSearch, onNoOfRowsPerPageChange, paginationSelectOptions, tableMaxHeight, minVisibleRows, maxVisibleRows, autoFitViewport, tableSettings, filterSidebar, loadingSkeletonRows, defaultVisibleColumns, density = "normal", variant = "studio", stripe = false, renderRowDetail, expandedRows, onExpandedRowsChange, groupColors, onAddItem, emptyState, onRefresh, isGlobalSearch = true, }) {
53
53
  var _a, _b, _c, _d, _e;
54
54
  const theme = (0, useCustomTheme_1.useCustomTheme)();
55
55
  const prefersReducedMotion = (0, motion_2.usePrefersReducedMotion)();
56
56
  const clearFiltersTone = (0, react_1.useMemo)(() => (0, filledSurface_1.resolveFilledSurface)(theme.colors.semantic.error, theme.colors), [theme.colors]);
57
+ const clearBadge = (0, react_1.useMemo)(() => (0, filledSurface_1.resolveFilledSurface)(theme.colors.primary, theme.colors), [theme.colors]);
57
58
  const variantTokens = (0, react_1.useMemo)(() => variants_1.TABLE_VARIANTS[variant](theme.colors, (0, accentText_1.isDarkCanvas)(theme.colors.backgroundColor.main)), [variant, theme.colors]);
58
59
  const [columnsSearch, setColumnsSearch] = (0, react_1.useState)({});
59
60
  const detailIdPrefix = (0, react_1.useId)();
@@ -397,7 +398,7 @@ function Table({ data, columns, onSelection, isLoading, isCheckbox = false, free
397
398
  ? isFilterModalOpen
398
399
  : !!(filterSidebar === null || filterSidebar === void 0 ? void 0 : filterSidebar.isFilterSidebar), display: "inline-flex", alignItems: "center", justifyContent: "center", p: 0, bg: "transparent", border: "none", borderRadius: "md", cursor: "pointer", onClick: filterMode === "modal" ? onFilterModalOpen : filterSidebar === null || filterSidebar === void 0 ? void 0 : filterSidebar.filterSidebarToggle, _hover: {
399
400
  color: theme.colors.accentText,
400
- }, sx: (0, focusRing_styles_1.focusVisibleStyles)(theme.colors), children: (0, jsx_runtime_1.jsx)(react_3.Icon, { as: lucide_react_1.Filter, transform: "scaleX(-1)", boxSize: 4, color: sidebarActiveCount > 0 ? theme.colors.accentText : theme.colors.text[500] }) }), sidebarActiveCount > 0 && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", "aria-label": "Clear filters", position: "absolute", top: "-6px", right: "-10px", bg: theme.colors.primary[(0, filledSurface_1.solidFillRung)(theme.colors.primary, theme.colors.white)], color: (0, filledSurface_1.labelOnFill)(theme.colors.primary[(0, filledSurface_1.solidFillRung)(theme.colors.primary, theme.colors.white)], theme.colors.white, theme.colors.black), border: "none", borderRadius: "full", p: "1px", cursor: "pointer", onClick: (e) => {
401
+ }, sx: (0, focusRing_styles_1.focusVisibleStyles)(theme.colors), children: (0, jsx_runtime_1.jsx)(react_3.Icon, { as: lucide_react_1.Filter, transform: "scaleX(-1)", boxSize: 4, color: sidebarActiveCount > 0 ? theme.colors.accentText : theme.colors.text[500] }) }), sidebarActiveCount > 0 && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", "aria-label": "Clear filters", position: "absolute", top: "-6px", right: "-10px", bg: clearBadge.fill, color: clearBadge.color, border: "none", borderRadius: "full", p: "1px", cursor: "pointer", onClick: (e) => {
401
402
  var _a;
402
403
  e.stopPropagation();
403
404
  if (filterSidebar === null || filterSidebar === void 0 ? void 0 : filterSidebar.onClearAllHandler) {
@@ -26,7 +26,7 @@ export type TableProps = {
26
26
  onAddItem?: (groupValue: string | number) => void;
27
27
  /** Seeds the initial row height only — Table Settings > Density (or saved preferences.density) takes over after the first change. */
28
28
  density?: TableDensity;
29
- /** Visual preset. Defaults to `classic` — the table exactly as it shipped. */
29
+ /** Visual preset. Defaults to `studio`; pass `"classic"` for the banded header and per-column rules. */
30
30
  variant?: TableVariant;
31
31
  /** Content shown under a row when expanded; return null for rows with no detail and they get no chevron. Memoize it: an inline function re-renders every row. */
32
32
  renderRowDetail?: (row: DataObject) => ReactNode | null;
@@ -424,7 +424,15 @@ const TableBody = ({ data, isCheckbox, columns, startRow, rowsPerPage, columnWid
424
424
  });
425
425
  }, [data]);
426
426
  if (isLoading && data.length === 0) {
427
- return ((0, jsx_runtime_1.jsx)(TableLoading_1.TableBodyLoading, { noOfColumns: totalVisibleColumns, borderColor: theme.colors.boxborder[200], rowsCount: typeof loadingSkeletonRows === "number" ? loadingSkeletonRows : 4, rowHeight: rowHeight }));
427
+ const dataColumns = columns.filter((o) => !o.isHidden).length;
428
+ const cells = [
429
+ ...(isContent ? ["blank"] : []),
430
+ ...(isCheckbox ? ["checkbox"] : []),
431
+ ...(rowDetail ? ["blank"] : []),
432
+ ...Array.from({ length: dataColumns || TableLoading_1.LOADING_PLACEHOLDER_COLUMNS }, () => "bar"),
433
+ ...(isLink ? ["blank"] : []),
434
+ ];
435
+ return ((0, jsx_runtime_1.jsx)(TableLoading_1.TableBodyLoading, { noOfColumns: cells.length, cells: cells, borderColor: variant.row.ruleColor, rowsCount: typeof loadingSkeletonRows === "number" ? loadingSkeletonRows : 4, rowHeight: rowHeight }));
428
436
  }
429
437
  if (!isLoading && data.length === 0) {
430
438
  return ((0, jsx_runtime_1.jsx)(react_1.Tr, { backgroundColor: theme.colors.background[50], children: (0, jsx_runtime_1.jsx)(react_1.Td, { colSpan: totalVisibleColumns, p: 0, border: "none", children: (0, jsx_runtime_1.jsx)(TableEmpty, { emptyState: emptyState }) }) }));
@@ -18,11 +18,12 @@ const motion_1 = require("../../../Theme/tokens/primitives/motion");
18
18
  const motion_2 = require("../../../Theme/chakra/motion");
19
19
  const focusRing_styles_1 = require("../../../Theme/chakra/focusRing.styles");
20
20
  const rowDetail_1 = require("./rowDetail");
21
+ const TableLoading_1 = require("./TableLoading");
21
22
  const LABEL_JUSTIFY = { start: "space-between", center: "center", end: "flex-start" };
22
23
  const TableHeader = ({ columns, isCheckbox, handleSort, headerRefs, columnWidths, slotRef, slotWidth, columnsSort, freezedTextColor, noBorders, handleCheckbox, checked, isLoading, isContent, isLink, hasDetail = false, isActionFreeze, setColumnsSearch, columnsSearch, isSelecting, }) => {
23
24
  const [openFilterId, setOpenFilterId] = (0, react_2.useState)(null);
24
25
  const variant = (0, TableVariantContext_1.useTableVariant)();
25
- return ((0, jsx_runtime_1.jsxs)(react_1.Tr, { pr: 0, "aria-rowindex": 1, children: [isContent && (0, jsx_runtime_1.jsx)(ContentSpacerCell, { noBorders: noBorders }), isCheckbox && ((0, jsx_runtime_1.jsx)(SelectAllCell, { slotRef: slotRef, freezedTextColor: freezedTextColor, noBorders: noBorders, isSelecting: isSelecting, isLoading: isLoading, checked: checked, handleCheckbox: handleCheckbox })), hasDetail && ((0, jsx_runtime_1.jsx)(rowDetail_1.RowDetailHeaderCell, { left: isCheckbox ? slotWidth : 0, bg: variant.header.bg, ruleColor: variant.header.ruleColor, noBorders: noBorders })), columns.map((header, index) => {
26
+ return ((0, jsx_runtime_1.jsxs)(react_1.Tr, { pr: 0, "aria-rowindex": 1, children: [isContent && (0, jsx_runtime_1.jsx)(ContentSpacerCell, { noBorders: noBorders }), isCheckbox && ((0, jsx_runtime_1.jsx)(SelectAllCell, { slotRef: slotRef, freezedTextColor: freezedTextColor, noBorders: noBorders, isSelecting: isSelecting, isLoading: isLoading, checked: checked, handleCheckbox: handleCheckbox })), hasDetail && ((0, jsx_runtime_1.jsx)(rowDetail_1.RowDetailHeaderCell, { left: isCheckbox ? slotWidth : 0, bg: variant.header.bg, ruleColor: variant.header.ruleColor, noBorders: noBorders })), isLoading && !columns.some((header) => !(header === null || header === void 0 ? void 0 : header.isHidden)) && ((0, jsx_runtime_1.jsx)(LoadingHeaderCells, { noBorders: noBorders })), columns.map((header, index) => {
26
27
  if (header === null || header === void 0 ? void 0 : header.isHidden)
27
28
  return null;
28
29
  return ((0, jsx_runtime_1.jsx)(ColumnHeader, { header: header, index: index, columnWidths: columnWidths, slotWidth: slotWidth, detailWidth: hasDetail ? rowDetail_1.DETAIL_SLOT_PX : 0, handleSort: handleSort, isCheckbox: isCheckbox, noBorders: noBorders, columnsSort: columnsSort, headerRefs: headerRefs, columnsSearch: columnsSearch, setColumnsSearch: setColumnsSearch, openFilterId: openFilterId, setOpenFilterId: setOpenFilterId }, (0, columnLabel_1.columnLabelText)(header)));
@@ -32,8 +33,12 @@ exports.default = (0, react_2.memo)(TableHeader);
32
33
  const SelectAllCell = (0, react_2.memo)(({ slotRef, freezedTextColor, noBorders, isSelecting, isLoading, checked, handleCheckbox, }) => {
33
34
  const theme = (0, useCustomTheme_1.useCustomTheme)();
34
35
  const variant = (0, TableVariantContext_1.useTableVariant)();
35
- return ((0, jsx_runtime_1.jsx)(react_1.Th, { ref: slotRef, 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: (0, jsx_runtime_1.jsx)(react_1.Box, { display: "flex", alignItems: "center", justifyContent: "center", children: isSelecting ? ((0, jsx_runtime_1.jsx)(react_1.Spinner, { size: "sm", color: theme.colors.accentText })) : ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { "aria-label": "Select all rows", variant: "outline", onChange: () => handleCheckbox(0), isChecked: isLoading ? false : checked === true, isIndeterminate: checked === "indeterminate" })) }) }));
36
+ return ((0, jsx_runtime_1.jsx)(react_1.Th, { ref: slotRef, 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: (0, jsx_runtime_1.jsx)(react_1.Box, { display: "flex", alignItems: "center", justifyContent: "center", children: isSelecting ? ((0, jsx_runtime_1.jsx)(react_1.Spinner, { size: "sm", color: theme.colors.accentText })) : ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { "aria-label": "Select all rows", variant: "outline", onChange: () => handleCheckbox(0), isChecked: isLoading ? false : checked === true, isDisabled: isLoading, isIndeterminate: checked === "indeterminate" })) }) }));
36
37
  });
38
+ const LoadingHeaderCells = ({ noBorders }) => {
39
+ const variant = (0, TableVariantContext_1.useTableVariant)();
40
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: Array.from({ length: TableLoading_1.LOADING_PLACEHOLDER_COLUMNS }, (_, i) => ((0, jsx_runtime_1.jsx)(react_1.Th, { scope: "col", "aria-hidden": "true", px: 3, backgroundColor: variant.header.bg, borderBottom: noBorders ? "none" : `0.063rem solid ${variant.header.ruleColor}`, children: (0, jsx_runtime_1.jsx)(react_1.Skeleton, { height: "0.75rem", width: i % 2 ? "50%" : "70%", borderRadius: "full", variant: "shine" }) }, i))) }));
41
+ };
37
42
  const ContentSpacerCell = (0, react_2.memo)(({ noBorders }) => {
38
43
  const variant = (0, TableVariantContext_1.useTableVariant)();
39
44
  return ((0, jsx_runtime_1.jsx)(react_1.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 }));
@@ -1,7 +1,10 @@
1
1
  declare const TableLoading: () => import("react/jsx-runtime").JSX.Element;
2
2
  export default TableLoading;
3
- export declare const TableBodyLoading: ({ noOfColumns, borderColor, rowsCount, rowHeight, }: {
3
+ export declare const LOADING_PLACEHOLDER_COLUMNS = 4;
4
+ export type LoadingCell = "bar" | "checkbox" | "blank";
5
+ export declare const TableBodyLoading: ({ noOfColumns, cells, borderColor, rowsCount, rowHeight, }: {
4
6
  noOfColumns: number;
7
+ cells?: LoadingCell[];
5
8
  borderColor: string;
6
9
  rowsCount?: number;
7
10
  rowHeight?: number;
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TableBodyLoading = void 0;
3
+ exports.TableBodyLoading = exports.LOADING_PLACEHOLDER_COLUMNS = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("@chakra-ui/react");
6
6
  const TableLoading = () => {
7
7
  return ((0, jsx_runtime_1.jsx)(react_1.Box, { overflowX: "auto", p: 4, children: (0, jsx_runtime_1.jsxs)(react_1.Table, { variant: "simple", width: "full", children: [(0, jsx_runtime_1.jsx)(react_1.Thead, { children: (0, jsx_runtime_1.jsxs)(react_1.Tr, { children: [(0, jsx_runtime_1.jsx)(react_1.Th, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.Skeleton, { height: "20px" }) }), (0, jsx_runtime_1.jsx)(react_1.Th, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.Skeleton, { height: "20px" }) }), (0, jsx_runtime_1.jsx)(react_1.Th, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.Skeleton, { height: "20px" }) }), (0, jsx_runtime_1.jsx)(react_1.Th, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.Skeleton, { height: "20px" }) })] }) }), (0, jsx_runtime_1.jsx)(react_1.Tbody, { children: Array.from({ length: 5 }).map((_, rowIndex) => ((0, jsx_runtime_1.jsxs)(react_1.Tr, { children: [(0, jsx_runtime_1.jsx)(react_1.Td, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.SkeletonText, { noOfLines: 1, skeletonHeight: "2" }) }), (0, jsx_runtime_1.jsx)(react_1.Td, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.SkeletonText, { noOfLines: 1, skeletonHeight: "2" }) }), (0, jsx_runtime_1.jsx)(react_1.Td, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.SkeletonText, { noOfLines: 1, skeletonHeight: "2" }) }), (0, jsx_runtime_1.jsx)(react_1.Td, { p: 2, children: (0, jsx_runtime_1.jsx)(react_1.SkeletonText, { noOfLines: 1, skeletonHeight: "2" }) })] }, rowIndex))) })] }) }));
8
8
  };
9
9
  exports.default = TableLoading;
10
- const TableBodyLoading = ({ noOfColumns, borderColor, rowsCount = 4, rowHeight = 45, }) => {
11
- return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: Array.from({ length: rowsCount }).map((_, index) => {
12
- return ((0, jsx_runtime_1.jsx)(react_1.Tr, { children: (0, jsx_runtime_1.jsx)(react_1.Td, { colSpan: noOfColumns, textAlign: "center", border: `1px solid ${borderColor}`, px: 2, pt: 2, pb: 0, height: `${rowHeight}px`, boxSizing: "border-box", children: (0, jsx_runtime_1.jsx)(react_1.Skeleton, { height: "15px", variant: "shine" }) }) }, index));
13
- }) }));
10
+ exports.LOADING_PLACEHOLDER_COLUMNS = 4;
11
+ const BAR_WIDTHS = ["72%", "58%", "84%", "46%", "66%", "78%"];
12
+ const TableBodyLoading = ({ noOfColumns, cells, borderColor, rowsCount = 4, rowHeight = 45, }) => {
13
+ const layout = cells !== null && cells !== void 0 ? cells : Array.from({ length: noOfColumns }, () => "bar");
14
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: Array.from({ length: rowsCount }).map((_, row) => ((0, jsx_runtime_1.jsx)(react_1.Tr, { "aria-hidden": "true", children: layout.map((kind, col) => ((0, jsx_runtime_1.jsxs)(react_1.Td, { px: kind === "checkbox" ? 0 : 3, py: 0, height: `${rowHeight}px`, boxSizing: "border-box", border: "none", borderBottom: `0.0625rem solid ${borderColor}`, textAlign: kind === "checkbox" ? "center" : undefined, children: [kind === "checkbox" && ((0, jsx_runtime_1.jsx)(react_1.Skeleton, { boxSize: "1rem", borderRadius: "sm", variant: "shine", display: "inline-block", verticalAlign: "middle" })), kind === "bar" && ((0, jsx_runtime_1.jsx)(react_1.Skeleton, { height: "0.75rem", width: BAR_WIDTHS[(row * 3 + col) % BAR_WIDTHS.length], borderRadius: "full", variant: "shine" }))] }, col))) }, row))) }));
14
15
  };
15
16
  exports.TableBodyLoading = TableBodyLoading;
@@ -60,18 +60,9 @@ const CompactSelect = ({ options, value, placeholder = "Select", height = "22px"
60
60
  }, sx: { fontSize: "0.75rem", height: "22px" } }), (0, jsx_runtime_1.jsxs)(react_2.Box, { role: "listbox", "aria-label": placeholder, maxH: "12rem", overflowY: "auto", borderTop: "1px solid", borderColor: (0, surfaceRaised_1.raisedRule)(theme.colors), onKeyDown: handleListKeyDown, children: [filtered.map((opt, i) => {
61
61
  const selected = (value === null || value === void 0 ? void 0 : value.id) === opt.id;
62
62
  const isActive = i === activeIndex;
63
- const rowBg = selected
64
- ? isActive
65
- ? rowBackgrounds.selectedActive
66
- : rowBackgrounds.selected
67
- : isActive
68
- ? rowBackgrounds.active
69
- : theme.colors.transparent;
70
63
  return ((0, jsx_runtime_1.jsxs)(react_2.Box, { as: "button", type: "button", role: "option", "aria-selected": selected, tabIndex: isActive ? 0 : -1, ref: (el) => {
71
64
  optionRefs.current[i] = el;
72
- }, w: `calc(100% - 2 * ${spec.mx})`, minH: spec.minH, textAlign: "left", border: "none", fontFamily: "inherit", display: "flex", alignItems: "center", gap: spec.gap, mx: spec.mx, px: spec.px, py: spec.py, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, cursor: "pointer", color: (0, optionRowInk_1.optionRowInk)(theme.colors, selected), fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(selected), _hover: {
73
- bg: selected ? rowBackgrounds.selectedActive : rowBackgrounds.active,
74
- }, bg: rowBg, onClick: () => {
65
+ }, w: `calc(100% - 2 * ${spec.mx})`, minH: spec.minH, textAlign: "left", border: "none", fontFamily: "inherit", display: "flex", alignItems: "center", gap: spec.gap, mx: spec.mx, px: spec.px, py: spec.py, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, cursor: "pointer", color: (0, optionRowInk_1.optionRowInk)(theme.colors, selected), fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(selected), bg: "transparent", ...(0, optionRowInk_1.optionRowCursorSx)(rowBackgrounds), onClick: () => {
75
66
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(opt);
76
67
  setIsOpen(false);
77
68
  setQuery("");
@@ -1,4 +1,4 @@
1
1
  import type { TableVariantTokens } from "./tokens";
2
2
  export declare const TableVariantProvider: import("react").Provider<TableVariantTokens | null>;
3
- /** No provider means the table as it always was — the default is a variant, not a gap. */
3
+ /** No provider means the default variant — studio, the same preset `Table` falls back to. */
4
4
  export declare function useTableVariant(): TableVariantTokens;
@@ -5,13 +5,13 @@ exports.useTableVariant = useTableVariant;
5
5
  const react_1 = require("react");
6
6
  const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
7
7
  const accentText_1 = require("../../../Theme/tokens/builders/accentText");
8
- const classic_1 = require("./classic");
8
+ const studio_1 = require("./studio");
9
9
  const TableVariantContext = (0, react_1.createContext)(null);
10
10
  exports.TableVariantProvider = TableVariantContext.Provider;
11
- /** No provider means the table as it always was — the default is a variant, not a gap. */
11
+ /** No provider means the default variant — studio, the same preset `Table` falls back to. */
12
12
  function useTableVariant() {
13
13
  const provided = (0, react_1.useContext)(TableVariantContext);
14
14
  const theme = (0, useCustomTheme_1.useCustomTheme)();
15
15
  const isDark = (0, accentText_1.isDarkCanvas)(theme.colors.backgroundColor.main);
16
- return (0, react_1.useMemo)(() => provided !== null && provided !== void 0 ? provided : (0, classic_1.classicPreset)(theme.colors, isDark), [provided, theme.colors, isDark]);
16
+ return (0, react_1.useMemo)(() => provided !== null && provided !== void 0 ? provided : (0, studio_1.studioPreset)(theme.colors, isDark), [provided, theme.colors, isDark]);
17
17
  }
@@ -21,7 +21,11 @@ function WorkspaceTrayPreview({ children, preview, enabled = true, openDelayMs =
21
21
  const [open, setOpen] = (0, react_1.useState)(false);
22
22
  const openTimer = (0, react_1.useRef)(null);
23
23
  const closeTimer = (0, react_1.useRef)(null);
24
+ const openRef = (0, react_1.useRef)(false);
24
25
  const setOpenState = (0, react_1.useCallback)((next) => {
26
+ if (openRef.current === next)
27
+ return;
28
+ openRef.current = next;
25
29
  setOpen(next);
26
30
  onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
27
31
  }, [onOpenChange]);
@@ -1,3 +1,3 @@
1
1
  import type { WorkspaceTrayProps, WorkspaceWindowProps } from "./WorkspaceWindowProps";
2
- export default function WorkspaceWindow({ title, state, children, onMinimize, onClose, onOpenFullPage, onOverlayClick, headerActions, testId, }: WorkspaceWindowProps): import("react/jsx-runtime").JSX.Element | null;
3
- export declare function WorkspaceTray({ items, onRestore, onClose, testId, renderItem, }: WorkspaceTrayProps): import("react/jsx-runtime").JSX.Element | null;
2
+ export default function WorkspaceWindow({ title, state, children, onMinimize, onClose, onOpenFullPage, onOverlayClick, headerActions, labels, finalFocusRef, testId, }: WorkspaceWindowProps): import("react/jsx-runtime").JSX.Element | null;
3
+ export declare function WorkspaceTray({ items, onRestore, onClose, testId, placement, offset, closeLabel, dirtyLabel, label, renderItem, }: WorkspaceTrayProps): import("react/jsx-runtime").JSX.Element | null;
@@ -12,7 +12,7 @@ const lucide_react_1 = require("lucide-react");
12
12
  const useCustomTheme_1 = require("../../Theme/useCustomTheme");
13
13
  const zIndices_1 = __importDefault(require("../../Theme/tokens/primitives/zIndices"));
14
14
  const accentText_1 = require("../../Theme/tokens/builders/accentText");
15
- // NOTE: no maximize/restore toggle (removed by product decision) — onMedium/onMaximize kept optional for back-compat.
15
+ // NOTE: no maximize/restore toggle (removed by product decision); onMedium/onMaximize stay as deprecated no-ops.
16
16
  const WINDOW_SIZE = { maxW: "88rem", h: "92vh" };
17
17
  const WindowTitle = ({ testId, color, children }) => {
18
18
  const { headerId, setHeaderMounted } = (0, react_1.useModalContext)();
@@ -23,7 +23,8 @@ const WindowTitle = ({ testId, color, children }) => {
23
23
  return ((0, jsx_runtime_1.jsx)(react_1.Text, { id: headerId, fontSize: "sm", fontWeight: 600, color: color, noOfLines: 1, "data-testid": `${testId}-title`, children: children }));
24
24
  };
25
25
  // Presentational floating "workspace window" (Jira-style dialog), headless — the parent owns window state.
26
- function WorkspaceWindow({ title, state, children, onMinimize, onClose, onOpenFullPage, onOverlayClick, headerActions, testId = "workspace-window", }) {
26
+ function WorkspaceWindow({ title, state, children, onMinimize, onClose, onOpenFullPage, onOverlayClick, headerActions, labels, finalFocusRef, testId = "workspace-window", }) {
27
+ var _a, _b, _c;
27
28
  // Must run before the early return: a minimized render would otherwise skip the hook
28
29
  // and change hook order when the window reopens.
29
30
  const theme = (0, useCustomTheme_1.useCustomTheme)();
@@ -33,28 +34,34 @@ function WorkspaceWindow({ title, state, children, onMinimize, onClose, onOpenFu
33
34
  return ((0, jsx_runtime_1.jsxs)(react_1.Modal, { isOpen: true, onClose: () => onClose === null || onClose === void 0 ? void 0 : onClose(), size: "full", isCentered: true, scrollBehavior: "inside",
34
35
  // Don't discard unsaved work on an accidental overlay click / Esc — the
35
36
  // parent decides via the explicit Close control.
36
- closeOnOverlayClick: false, closeOnEsc: false, children: [(0, jsx_runtime_1.jsx)(react_1.ModalOverlay, { backdropFilter: "blur(3px)" }), (0, jsx_runtime_1.jsx)(react_1.ModalContent, { bg: "transparent", boxShadow: "none", m: 0, display: "flex", alignItems: "center", justifyContent: "center",
37
+ closeOnOverlayClick: false, closeOnEsc: false, finalFocusRef: finalFocusRef, children: [(0, jsx_runtime_1.jsx)(react_1.ModalOverlay, { backdropFilter: "blur(3px)" }), (0, jsx_runtime_1.jsx)(react_1.ModalContent, { bg: "transparent", boxShadow: "none", m: 0, display: "flex", alignItems: "center", justifyContent: "center",
37
38
  // NOTE: size="full" blocks Chakra's own closeOnOverlayClick — detect the outside click ourselves instead.
38
39
  onMouseDown: (e) => {
39
40
  if (e.target === e.currentTarget)
40
41
  onOverlayClick === null || onOverlayClick === void 0 ? void 0 : onOverlayClick();
41
- }, "data-testid": `${testId}-backdrop`, children: (0, jsx_runtime_1.jsxs)(react_1.Box, { bg: theme.colors.surfaceRaised, borderRadius: "xl", boxShadow: "2xl", overflow: "hidden", w: "100%", maxW: size.maxW, h: size.h, display: "flex", flexDirection: "column", "data-testid": testId, "data-window-state": state, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", justify: "space-between", px: 3, py: 2, borderBottomWidth: "1px", borderColor: theme.colors.boxborder[200], bg: theme.colors.background[100], flexShrink: 0, children: [(0, jsx_runtime_1.jsx)(WindowTitle, { testId: testId, color: theme.colors.text[700], children: title }), (0, jsx_runtime_1.jsxs)(react_1.HStack, { spacing: 1, children: [headerActions, onOpenFullPage ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": "Open full page", size: "xs", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { size: 14 }), onClick: onOpenFullPage, "data-testid": `${testId}-fullpage` })) : null, onMinimize ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": "Minimize", size: "xs", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Minus, { size: 15 }), onClick: onMinimize, "data-testid": `${testId}-minimize` })) : null, onClose ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": "Close", size: "xs", variant: "ghost", colorScheme: "red", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 15 }), onClick: onClose, "data-testid": `${testId}-close` })) : null] })] }), (0, jsx_runtime_1.jsx)(react_1.Box, { flex: "1", overflow: "auto", px: { base: 3, md: 5 }, py: 4, children: children })] }) })] }));
42
+ }, "data-testid": `${testId}-backdrop`, children: (0, jsx_runtime_1.jsxs)(react_1.Box, { bg: theme.colors.surfaceRaised, borderRadius: "xl", boxShadow: "2xl", overflow: "hidden", w: "100%", maxW: size.maxW, h: size.h, display: "flex", flexDirection: "column", "data-testid": testId, "data-window-state": state, children: [(0, jsx_runtime_1.jsxs)(react_1.Flex, { align: "center", justify: "space-between", px: 3, py: 2, borderBottomWidth: "1px", borderColor: theme.colors.boxborder[200], bg: theme.colors.background[100], flexShrink: 0, children: [(0, jsx_runtime_1.jsx)(WindowTitle, { testId: testId, color: theme.colors.text[700], children: title }), (0, jsx_runtime_1.jsxs)(react_1.HStack, { spacing: 1, children: [headerActions, onOpenFullPage ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": (_a = labels === null || labels === void 0 ? void 0 : labels.openFullPage) !== null && _a !== void 0 ? _a : "Open full page", size: "xs", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { size: 14 }), onClick: onOpenFullPage, "data-testid": `${testId}-fullpage` })) : null, onMinimize ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": (_b = labels === null || labels === void 0 ? void 0 : labels.minimize) !== null && _b !== void 0 ? _b : "Minimize", size: "xs", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Minus, { size: 15 }), onClick: onMinimize, "data-testid": `${testId}-minimize` })) : null, onClose ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": (_c = labels === null || labels === void 0 ? void 0 : labels.close) !== null && _c !== void 0 ? _c : "Close", size: "xs", variant: "ghost", colorScheme: "red", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 15 }), onClick: onClose, "data-testid": `${testId}-close` })) : null] })] }), (0, jsx_runtime_1.jsx)(react_1.Box, { flex: "1", overflow: "auto", px: { base: 3, md: 5 }, py: 4, children: children })] }) })] }));
42
43
  }
43
- // Bottom tray of restore pills for minimized WorkspaceWindows — presentational, parent supplies handlers.
44
- function WorkspaceTray({ items, onRestore, onClose, testId = "workspace-tray", renderItem, }) {
44
+ const cssLength = (value) => (typeof value === "number" ? `${value}px` : value);
45
+ // Portalled above modal overlays; aria-live keeps Chakra's modal aria-hidden off it, data-no-focus-lock keeps the focus trap off it.
46
+ function WorkspaceTray({ items, onRestore, onClose, testId = "workspace-tray", placement = "bottom-end", offset, closeLabel, dirtyLabel = "Unsaved changes", label = "Minimized windows", renderItem, }) {
47
+ var _a, _b;
45
48
  const theme = (0, useCustomTheme_1.useCustomTheme)();
46
49
  if (!items.length)
47
50
  return null;
48
- return (
49
- // NOTE: floating bottom-right cluster (Gmail/Jira pattern), not a docked bar — needs no reserved page gutter.
50
- (0, jsx_runtime_1.jsx)(react_1.Flex, { position: "fixed", bottom: 4, right: 4, zIndex: zIndices_1.default.modal, direction: "row-reverse", wrap: "wrap-reverse", justify: "flex-start", gap: 2, maxW: { base: "calc(100vw - 2rem)", md: "36rem" }, "data-testid": testId, children: items.map((item) => {
51
- const api = {
52
- restore: () => onRestore === null || onRestore === void 0 ? void 0 : onRestore(item.id),
53
- close: () => onClose === null || onClose === void 0 ? void 0 : onClose(item.id),
54
- };
55
- if (renderItem) {
56
- return (0, jsx_runtime_1.jsx)(react_1.Box, { children: renderItem(item, api) }, item.id);
57
- }
58
- return ((0, jsx_runtime_1.jsxs)(react_1.HStack, { spacing: 1, pl: 3, pr: 1, py: 1, borderRadius: "full", bg: theme.colors.primary[100], color: (0, accentText_1.accentTextOnCanvas)(theme.colors.primary, theme.colors.primary[100]), boxShadow: "md", borderWidth: "1px", borderColor: theme.colors.primary[200], "data-testid": `${testId}-pill`, children: [(0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", fontSize: "sm", onClick: api.restore, "data-testid": `${testId}-restore`, maxW: "16rem", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: item.title }), onClose ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": "Close", size: "xs", variant: "ghost", borderRadius: "full", minW: "auto", h: "auto", p: 1, color: "inherit", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 13 }), onClick: api.close, "data-testid": `${testId}-close` })) : null] }, item.id));
59
- }) }));
51
+ const atEnd = placement === "bottom-end";
52
+ const inset = {
53
+ bottom: cssLength((_a = offset === null || offset === void 0 ? void 0 : offset.y) !== null && _a !== void 0 ? _a : "1rem"),
54
+ [atEnd ? "right" : "left"]: cssLength((_b = offset === null || offset === void 0 ? void 0 : offset.x) !== null && _b !== void 0 ? _b : "1rem"),
55
+ };
56
+ const ink = (0, accentText_1.accentTextOnCanvas)(theme.colors.primary, theme.colors.primary[100]);
57
+ return ((0, jsx_runtime_1.jsx)(react_1.Portal, { children: (0, jsx_runtime_1.jsx)(react_1.Flex, { role: "region", "aria-label": label, "aria-live": "polite", "data-no-focus-lock": "true", position: "fixed", ...inset, zIndex: zIndices_1.default.modal + 1, direction: atEnd ? "row-reverse" : "row", wrap: "wrap-reverse", justify: "flex-start", gap: 2, maxW: { base: "calc(100vw - 2rem)", md: "36rem" }, "data-testid": testId, children: items.map((item) => {
58
+ const api = {
59
+ restore: () => onRestore === null || onRestore === void 0 ? void 0 : onRestore(item.id),
60
+ close: () => onClose === null || onClose === void 0 ? void 0 : onClose(item.id),
61
+ };
62
+ if (renderItem) {
63
+ return (0, jsx_runtime_1.jsx)(react_1.Box, { children: renderItem(item, api) }, item.id);
64
+ }
65
+ return ((0, jsx_runtime_1.jsxs)(react_1.HStack, { spacing: 1, pl: 3, pr: 1, py: 1, borderRadius: "full", bg: theme.colors.primary[100], color: ink, boxShadow: "md", borderWidth: "1px", borderColor: theme.colors.primary[200], "data-testid": `${testId}-pill`, children: [item.isDirty ? ((0, jsx_runtime_1.jsx)(react_1.Box, { role: "img", "aria-label": dirtyLabel, boxSize: "0.5rem", borderRadius: "full", bg: "currentColor", flexShrink: 0, "data-testid": `${testId}-dirty` })) : null, (0, jsx_runtime_1.jsxs)(react_1.Box, { as: "button", type: "button", fontSize: "sm", onClick: api.restore, "data-testid": `${testId}-restore`, maxW: "16rem", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: [item.title, item.subtitle ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", fontWeight: 400, ml: 1.5, children: item.subtitle })) : null] }), onClose ? ((0, jsx_runtime_1.jsx)(react_1.IconButton, { "aria-label": closeLabel ? closeLabel(item) : "Close", size: "xs", variant: "ghost", borderRadius: "full", minW: "auto", h: "auto", p: 1, color: "inherit", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 13 }), onClick: api.close, "data-testid": `${testId}-close` })) : null] }, item.id));
66
+ }) }) }));
60
67
  }
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from "react";
1
+ import type { ReactNode, RefObject } from "react";
2
2
  /**
3
3
  * Window state for a WorkspaceWindow.
4
4
  * - "medium": default centered dialog (Jira-style)
@@ -6,6 +6,15 @@ import type { ReactNode } from "react";
6
6
  * - "minimized": collapsed to a tray pill (rendered by WorkspaceTray, not the window)
7
7
  */
8
8
  export type WorkspaceWindowState = "medium" | "maximized" | "minimized";
9
+ /** Accessible names of the window's icon controls; omitted keys keep the English default. */
10
+ export interface WorkspaceWindowLabels {
11
+ /** Default "Minimize". */
12
+ minimize?: string;
13
+ /** Default "Close". */
14
+ close?: string;
15
+ /** Default "Open full page". */
16
+ openFullPage?: string;
17
+ }
9
18
  /** Props for one WorkspaceWindow instance; minimized windows render separately via WorkspaceTrayProps below. */
10
19
  export interface WorkspaceWindowProps {
11
20
  /** Title shown in the window's control bar. */
@@ -29,6 +38,13 @@ export interface WorkspaceWindowProps {
29
38
  * itself (e.g. minimize-on-backdrop-click) via this callback.
30
39
  */
31
40
  onOverlayClick?: () => void;
41
+ /** Translated names for the Minimize, Close and Open full page buttons. */
42
+ labels?: WorkspaceWindowLabels;
43
+ /**
44
+ * Where focus lands when the window closes or minimizes. Without it focus returns to whatever opened the
45
+ * window, which is gone once it was restored from the tray; point it at a stable element (e.g. the page's main region).
46
+ */
47
+ finalFocusRef?: RefObject<HTMLElement>;
32
48
  /** Extra controls rendered in the control bar, left of the window buttons. */
33
49
  headerActions?: ReactNode;
34
50
  /** data-testid for the window container. */
@@ -38,14 +54,34 @@ export interface WorkspaceWindowProps {
38
54
  export interface WorkspaceTrayItem {
39
55
  id: string;
40
56
  title: ReactNode;
57
+ /** Shows an unsaved-changes dot on the built-in pill; named for screen readers by the tray's `dirtyLabel`. */
58
+ isDirty?: boolean;
59
+ /** Secondary text after the title on the built-in pill, e.g. a record type or id. */
60
+ subtitle?: ReactNode;
41
61
  }
42
- /** Props for the bottom-right cluster of minimized-window pills; renders nothing when items is empty. */
62
+ /** Props for the cluster of minimized-window pills; renders nothing when items is empty. */
43
63
  export interface WorkspaceTrayProps {
44
64
  /** Minimized items rendered as restore pills along the bottom. */
45
65
  items: WorkspaceTrayItem[];
46
66
  onRestore?: (id: string) => void;
47
67
  onClose?: (id: string) => void;
48
68
  testId?: string;
69
+ /**
70
+ * Which bottom corner the tray sits in. Default "bottom-end". The tray is portalled above modals and stays
71
+ * reachable while a modal is open.
72
+ */
73
+ placement?: "bottom-end" | "bottom-start";
74
+ /** Distance from the viewport edges, to clear a sticky bar. Numbers are px, strings any CSS length. Default 1rem each. */
75
+ offset?: {
76
+ x?: number | string;
77
+ y?: number | string;
78
+ };
79
+ /** Accessible name for each pill's close button. Default "Close"; pass one that names the item for non-English or per-window labels. */
80
+ closeLabel?: (item: WorkspaceTrayItem) => string;
81
+ /** Accessible name of the unsaved dot on a pill with `isDirty`. Default "Unsaved changes". */
82
+ dirtyLabel?: string;
83
+ /** Accessible name of the tray region. Default "Minimized windows". */
84
+ label?: string;
49
85
  /**
50
86
  * Full override for a single item's pill markup — e.g. to wrap it in an
51
87
  * app-specific hover preview, or to show extra state (a dirty dot) the
@@ -15,6 +15,7 @@ export declare const Menu: {
15
15
  borderColor: string;
16
16
  };
17
17
  item: {
18
+ color: string;
18
19
  minH: string;
19
20
  px: string;
20
21
  py: string;
@@ -39,6 +40,13 @@ export declare const Menu: {
39
40
  [$itemBg.variable]: string;
40
41
  bg: string;
41
42
  };
43
+ "&[aria-checked=true]": {
44
+ color: string;
45
+ fontWeight: number;
46
+ "& .chakra-menu__icon-wrapper": {
47
+ color: string;
48
+ };
49
+ };
42
50
  };
43
51
  }) | undefined;
44
52
  sizes?: {
@@ -4,6 +4,7 @@ exports.Menu = void 0;
4
4
  const react_1 = require("@chakra-ui/react");
5
5
  const anatomy_1 = require("@chakra-ui/anatomy");
6
6
  const rowStates_1 = require("../tokens/builders/rowStates");
7
+ const optionRowInk_1 = require("../../Components/Select/optionRowInk");
7
8
  const optionRowSizes_1 = require("../tokens/primitives/optionRowSizes");
8
9
  const paletteFromTheme_1 = require("./paletteFromTheme");
9
10
  const overlay_styles_1 = require("./overlay.styles");
@@ -28,6 +29,7 @@ const baseStyle = definePartsStyle(({ theme }) => {
28
29
  ...overlay_styles_1.POPUP_SURFACE,
29
30
  },
30
31
  item: {
32
+ color: (0, optionRowInk_1.optionRowInk)(colors, false),
31
33
  minH: itemRowSpec.minH,
32
34
  px: itemRowSpec.px,
33
35
  py: itemRowSpec.py,
@@ -40,6 +42,11 @@ const baseStyle = definePartsStyle(({ theme }) => {
40
42
  _hover: { [$itemBg.variable]: hoverBg, bg: $itemBg.reference },
41
43
  _active: { [$itemBg.variable]: activeBg, bg: $itemBg.reference },
42
44
  _expanded: { [$itemBg.variable]: hoverBg, bg: $itemBg.reference },
45
+ "&[aria-checked=true]": {
46
+ color: (0, optionRowInk_1.optionRowInk)(colors, true),
47
+ fontWeight: (0, optionRowInk_1.optionRowWeight)(true),
48
+ "& .chakra-menu__icon-wrapper": { color: colors.accentText },
49
+ },
43
50
  },
44
51
  };
45
52
  });
@@ -182,6 +182,7 @@ export declare const componentStyles: {
182
182
  borderColor: string;
183
183
  };
184
184
  item: {
185
+ color: string;
185
186
  minH: string;
186
187
  px: string;
187
188
  py: string;
@@ -206,6 +207,13 @@ export declare const componentStyles: {
206
207
  [x: string]: string;
207
208
  bg: string;
208
209
  };
210
+ "&[aria-checked=true]": {
211
+ color: string;
212
+ fontWeight: number;
213
+ "& .chakra-menu__icon-wrapper": {
214
+ color: string;
215
+ };
216
+ };
209
217
  };
210
218
  }) | undefined;
211
219
  sizes?: {
@@ -26,7 +26,7 @@ const Card_styles_1 = require("./Card.styles");
26
26
  // NOTE: guards untethemed scales (blackAlpha/whiteAlpha) whose rgba() rungs would throw in hexToRgb.
27
27
  const isHexColor = (color) => typeof color === "string" && /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color);
28
28
  const solidFillStyle = (palette, swatch) => {
29
- const rung = (0, filledSurface_1.solidFillRung)(swatch, palette.white);
29
+ const rung = (0, filledSurface_1.solidFillRung)(swatch, palette.white, palette.backgroundColor.main);
30
30
  const bg = swatch[rung];
31
31
  return {
32
32
  bg,
@@ -161,7 +161,7 @@ const Radio = {
161
161
  baseStyle: ({ theme, colorScheme }) => {
162
162
  const palette = (0, paletteFromTheme_1.paletteOf)(theme);
163
163
  const swatch = (0, paletteFromTheme_1.scaleFor)(palette, colorScheme);
164
- const fill = swatch[(0, filledSurface_1.solidFillRung)(swatch, palette.white)];
164
+ const fill = swatch[(0, filledSurface_1.solidFillRung)(swatch, palette.white, palette.backgroundColor.main)];
165
165
  const dot = (0, filledSurface_1.labelOnFill)(fill, palette.white, palette.black);
166
166
  const hoverFill = (0, outlineRung_1.resolveHoverTint)(fill, fill);
167
167
  return {
@@ -12,6 +12,8 @@ declare const DARK_CANVAS_LUMINANCE = 0.2;
12
12
  /** True when `canvas` is a dark surface. Lets style configs branch without threading color mode. */
13
13
  export declare const isDarkCanvas: (canvas: string) => boolean;
14
14
  export declare const accentTextOnCanvas: (scale: ColorScale, canvas: string) => string;
15
+ /** Accent ink for chrome that can land on any plane (canvas, card, overlay): the first rung clearing AA on all. */
16
+ export declare const accentTextOnPlanes: (scale: ColorScale, planes: readonly string[]) => string;
15
17
  export declare const onFilled: (fill: string, white: string, black: string) => string;
16
18
  export declare const onGradient: (from: string, to: string, white: string, black: string) => string;
17
19
  export { AA_NORMAL_TEXT, DARK_CANVAS_LUMINANCE };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DARK_CANVAS_LUMINANCE = exports.AA_NORMAL_TEXT = exports.onGradient = exports.onFilled = exports.accentTextOnCanvas = exports.isDarkCanvas = exports.DEFAULT_BLACK = exports.DEFAULT_WHITE = exports.DEFAULT_CANVAS = exports.pickAccentText = void 0;
3
+ exports.DARK_CANVAS_LUMINANCE = exports.AA_NORMAL_TEXT = exports.onGradient = exports.onFilled = exports.accentTextOnPlanes = exports.accentTextOnCanvas = exports.isDarkCanvas = exports.DEFAULT_BLACK = exports.DEFAULT_WHITE = exports.DEFAULT_CANVAS = exports.pickAccentText = void 0;
4
4
  const color_1 = require("./color");
5
5
  /** WCAG AA for normal-size text. Accent-as-text must clear this against the canvas. */
6
6
  const AA_NORMAL_TEXT = 4.5;
@@ -27,6 +27,12 @@ exports.isDarkCanvas = isDarkCanvas;
27
27
  // NOTE: held to AA in both modes now — some brands' light [500] no longer passes through untouched.
28
28
  const accentTextOnCanvas = (scale, canvas) => (0, exports.pickAccentText)(scale, canvas);
29
29
  exports.accentTextOnCanvas = accentTextOnCanvas;
30
+ /** Accent ink for chrome that can land on any plane (canvas, card, overlay): the first rung clearing AA on all. */
31
+ const accentTextOnPlanes = (scale, planes) => {
32
+ const found = SEARCH_ORDER.find((rung) => planes.every((plane) => (0, color_1.contrastRatio)(scale[rung], plane) >= AA_NORMAL_TEXT));
33
+ return scale[found !== null && found !== void 0 ? found : SEARCH_ORDER[SEARCH_ORDER.length - 1]];
34
+ };
35
+ exports.accentTextOnPlanes = accentTextOnPlanes;
30
36
  // NOTE: white/black must be the mode-stable absolutes, not an adaptive token — always clears AA.
31
37
  const onFilled = (fill, white, black) => (0, color_1.contrastRatio)(white, fill) >= AA_NORMAL_TEXT ? white : black;
32
38
  exports.onFilled = onFilled;
@@ -12,7 +12,11 @@ export type FilledSurface = {
12
12
  activeColor: string;
13
13
  };
14
14
  export declare const solidFillRung: (scale: ColorScale, white: string, canvas?: string) => keyof ColorScale;
15
- /** Hover (step 0) or active (step 1) fill rung, one step darker than `solidFillRung`. */
15
+ /**
16
+ * Hover (step 0) or active (step 1) fill rung, one step darker than `solidFillRung`. A step may not
17
+ * flip the label's side of AA (zinc dark: near-white fill, black ink) or fall below `CHROME_BORDER`
18
+ * against the canvas (a pressed warning solid used to reach 1.00:1 on the card).
19
+ */
16
20
  export declare const darkerFillRung: (scale: ColorScale, canvas: string, white: string, step: 0 | 1) => keyof ColorScale;
17
21
  /** Label ink for a specific fill swatch — always `onFilled`, exported for style configs. */
18
22
  export declare const labelOnFill: (fill: string, white: string, black: string) => string;