pixelize-design-library 2.4.2-beta.62 → 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 (207) hide show
  1. package/CHANGELOG.md +122 -2
  2. package/COMPONENT-GOTCHAS.md +51 -9
  3. package/DESIGN-GUIDE.md +13 -5
  4. package/dist/Components/Button/Button.styles.js +58 -23
  5. package/dist/Components/Card/CardProps.d.ts +2 -2
  6. package/dist/Components/ConfirmDialog/ConfirmDialog.js +2 -1
  7. package/dist/Components/CustomModulesTable/CustomModulesTable.js +6 -4
  8. package/dist/Components/CustomModulesTable/EditModuleModal.js +2 -1
  9. package/dist/Components/DatePicker/RangeDatePicker.js +2 -1
  10. package/dist/Components/DatePicker/SingleDatePicker.js +2 -1
  11. package/dist/Components/DatePicker/TimeOnlyPicker.js +2 -1
  12. package/dist/Components/DatePicker/dayStates.js +2 -1
  13. package/dist/Components/Drawer/Drawer.js +2 -1
  14. package/dist/Components/Dropdown/DropDown.js +4 -10
  15. package/dist/Components/FilePreview/FilePreview.js +2 -1
  16. package/dist/Components/FileUpload/FileUpload.js +9 -2
  17. package/dist/Components/FileUploader/FileUploader.js +4 -0
  18. package/dist/Components/FileUploader/FileUploaderProps.d.ts +1 -1
  19. package/dist/Components/FilterSidebar/FilterSidebar.js +7 -4
  20. package/dist/Components/Input/TextInput.styles.js +10 -6
  21. package/dist/Components/KanbanBoard/AccountCard.js +4 -2
  22. package/dist/Components/KanbanBoard/KanbanBoard.js +6 -5
  23. package/dist/Components/NavigationBar/NavigationBar.js +5 -3
  24. package/dist/Components/Notification/Notification.js +4 -3
  25. package/dist/Components/NotificationBell/NotificationPanel.js +4 -3
  26. package/dist/Components/OrgSwitcher/OrgSwitcher.js +2 -4
  27. package/dist/Components/OrganizationDetails/CreateOrgModal.js +2 -1
  28. package/dist/Components/OrganizationDetails/OrganizationDetails.js +5 -3
  29. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +12 -7
  30. package/dist/Components/ScrollToTop/ScrollToTop.js +4 -3
  31. package/dist/Components/SearchSelect/SearchSelect.js +44 -35
  32. package/dist/Components/SearchSelect/searchSelectNav.d.ts +13 -0
  33. package/dist/Components/SearchSelect/searchSelectNav.js +40 -0
  34. package/dist/Components/Select/SelectPopup.js +3 -4
  35. package/dist/Components/Select/optionRowInk.d.ts +12 -0
  36. package/dist/Components/Select/optionRowInk.js +10 -1
  37. package/dist/Components/SelectSearch/SelectSearch.js +3 -11
  38. package/dist/Components/SettingRow/SettingRow.js +2 -1
  39. package/dist/Components/SettingRow/SettingsSection.js +2 -1
  40. package/dist/Components/SideBar/SecondaryBar.js +4 -3
  41. package/dist/Components/SideBar/SideBar.js +16 -13
  42. package/dist/Components/SideBar/SideBarProps.d.ts +1 -1
  43. package/dist/Components/SideBar/components/MenuItemBox.js +7 -4
  44. package/dist/Components/SideBar/components/MenuPopoverContent.js +2 -1
  45. package/dist/Components/SideBar/components/OtherApps.js +2 -1
  46. package/dist/Components/SideBar/components/PinnedSection.js +7 -4
  47. package/dist/Components/SideBar/sidebarMotion.d.ts +2 -0
  48. package/dist/Components/SideBar/sidebarMotion.js +6 -0
  49. package/dist/Components/SignInActivityTable/SignInActivityTable.js +6 -4
  50. package/dist/Components/StageProgress/StageItem.js +5 -2
  51. package/dist/Components/StageProgress/StepperStage.js +2 -2
  52. package/dist/Components/StatCard/StatCard.js +2 -1
  53. package/dist/Components/Table/Table.js +9 -4
  54. package/dist/Components/Table/TableProps.d.ts +1 -1
  55. package/dist/Components/Table/components/TableActions.js +3 -2
  56. package/dist/Components/Table/components/TableBody.js +9 -1
  57. package/dist/Components/Table/components/TableHeader.js +7 -2
  58. package/dist/Components/Table/components/TableLoading.d.ts +4 -1
  59. package/dist/Components/Table/components/TableLoading.js +6 -5
  60. package/dist/Components/Table/filters/CompactSelect.js +3 -11
  61. package/dist/Components/Table/settings/TableSettings.js +7 -2
  62. package/dist/Components/Table/variants/TableVariantContext.d.ts +1 -1
  63. package/dist/Components/Table/variants/TableVariantContext.js +3 -3
  64. package/dist/Components/Table/variants/classic.js +3 -2
  65. package/dist/Components/Timeline/Timeline.js +11 -4
  66. package/dist/Components/Toaster/Toaster.js +19 -8
  67. package/dist/Components/Toggle/TableToggle.js +5 -2
  68. package/dist/Components/UserDetails/AddUserModal.js +2 -1
  69. package/dist/Components/UserDetails/ChangeRoleModal.js +2 -1
  70. package/dist/Components/UserDetails/UserDetails.js +3 -1
  71. package/dist/Components/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
  72. package/dist/Components/WorkspaceWindow/WorkspaceWindow.d.ts +2 -2
  73. package/dist/Components/WorkspaceWindow/WorkspaceWindow.js +25 -18
  74. package/dist/Components/WorkspaceWindow/WorkspaceWindowProps.d.ts +38 -2
  75. package/dist/Theme/chakra/Card.styles.d.ts +7 -1
  76. package/dist/Theme/chakra/Card.styles.js +4 -1
  77. package/dist/Theme/chakra/Menu.styles.d.ts +11 -5
  78. package/dist/Theme/chakra/Menu.styles.js +10 -0
  79. package/dist/Theme/chakra/Popover.styles.d.ts +2 -11
  80. package/dist/Theme/chakra/Popover.styles.js +2 -0
  81. package/dist/Theme/chakra/common.web.d.ts +4 -0
  82. package/dist/Theme/chakra/componentStyles.d.ts +19 -3
  83. package/dist/Theme/chakra/componentStyles.js +12 -8
  84. package/dist/Theme/chakra/motion.d.ts +1 -0
  85. package/dist/Theme/chakra/motion.js +2 -1
  86. package/dist/Theme/chakra/overlay.styles.d.ts +5 -0
  87. package/dist/Theme/chakra/overlay.styles.js +4 -1
  88. package/dist/Theme/themeProps.d.ts +4 -0
  89. package/dist/Theme/tokens/brands/index.d.ts +1 -0
  90. package/dist/Theme/tokens/brands/index.js +17 -1
  91. package/dist/Theme/tokens/brands/radiant/palette.light.js +15 -0
  92. package/dist/Theme/tokens/brands/rosewood/palette.light.js +10 -10
  93. package/dist/Theme/tokens/brands/zinc/palette.dark.d.ts +7 -29
  94. package/dist/Theme/tokens/brands/zinc/palette.dark.js +3 -35
  95. package/dist/Theme/tokens/builders/accentText.d.ts +2 -0
  96. package/dist/Theme/tokens/builders/accentText.js +7 -1
  97. package/dist/Theme/tokens/builders/buildDarkPalette.d.ts +6 -1
  98. package/dist/Theme/tokens/builders/buildDarkPalette.js +37 -8
  99. package/dist/Theme/tokens/builders/filledSurface.d.ts +5 -1
  100. package/dist/Theme/tokens/builders/filledSurface.js +17 -4
  101. package/dist/Theme/tokens/builders/mutedText.d.ts +1 -1
  102. package/dist/Theme/tokens/builders/mutedText.js +13 -3
  103. package/dist/Theme/tokens/builders/planeStep.d.ts +5 -0
  104. package/dist/Theme/tokens/builders/planeStep.js +21 -0
  105. package/dist/Theme/tokens/builders/planes.d.ts +4 -0
  106. package/dist/Theme/tokens/builders/planes.js +33 -0
  107. package/dist/Theme/tokens/builders/skeletonShimmer.d.ts +9 -0
  108. package/dist/Theme/tokens/builders/skeletonShimmer.js +25 -0
  109. package/dist/Theme/tokens/builders/surfaceRaised.d.ts +3 -1
  110. package/dist/Theme/tokens/builders/surfaceRaised.js +18 -8
  111. package/dist/Theme/tokens/builders/surfaceRungs.js +3 -1
  112. package/dist/Theme/tokens/primitives/index.d.ts +2 -2
  113. package/dist/Theme/tokens/primitives/index.js +4 -1
  114. package/dist/Theme/tokens/primitives/motion.d.ts +1 -0
  115. package/dist/Theme/tokens/primitives/motion.js +2 -1
  116. package/dist/Theme/tokens/primitives/shadows.d.ts +16 -0
  117. package/dist/Theme/tokens/primitives/shadows.js +56 -6
  118. package/dist/Theme/tokens/primitives/typography.js +1 -1
  119. package/dist/Utils/table.js +2 -2
  120. package/dist/esm/Components/Button/Button.styles.js +62 -27
  121. package/dist/esm/Components/ConfirmDialog/ConfirmDialog.js +2 -1
  122. package/dist/esm/Components/CustomModulesTable/CustomModulesTable.js +6 -4
  123. package/dist/esm/Components/CustomModulesTable/EditModuleModal.js +2 -1
  124. package/dist/esm/Components/DatePicker/RangeDatePicker.js +2 -1
  125. package/dist/esm/Components/DatePicker/SingleDatePicker.js +2 -1
  126. package/dist/esm/Components/DatePicker/TimeOnlyPicker.js +2 -1
  127. package/dist/esm/Components/DatePicker/dayStates.js +2 -1
  128. package/dist/esm/Components/Drawer/Drawer.js +2 -1
  129. package/dist/esm/Components/Dropdown/DropDown.js +4 -10
  130. package/dist/esm/Components/FilePreview/FilePreview.js +3 -2
  131. package/dist/esm/Components/FileUpload/FileUpload.js +10 -3
  132. package/dist/esm/Components/FileUploader/FileUploader.js +4 -0
  133. package/dist/esm/Components/FilterSidebar/FilterSidebar.js +7 -4
  134. package/dist/esm/Components/Input/TextInput.styles.js +10 -6
  135. package/dist/esm/Components/KanbanBoard/AccountCard.js +4 -2
  136. package/dist/esm/Components/KanbanBoard/KanbanBoard.js +6 -5
  137. package/dist/esm/Components/NavigationBar/NavigationBar.js +5 -3
  138. package/dist/esm/Components/Notification/Notification.js +4 -3
  139. package/dist/esm/Components/NotificationBell/NotificationPanel.js +4 -3
  140. package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +2 -4
  141. package/dist/esm/Components/OrganizationDetails/CreateOrgModal.js +2 -1
  142. package/dist/esm/Components/OrganizationDetails/OrganizationDetails.js +5 -3
  143. package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +13 -8
  144. package/dist/esm/Components/ScrollToTop/ScrollToTop.js +5 -4
  145. package/dist/esm/Components/SearchSelect/SearchSelect.js +46 -37
  146. package/dist/esm/Components/SearchSelect/searchSelectNav.js +32 -0
  147. package/dist/esm/Components/Select/SelectPopup.js +4 -5
  148. package/dist/esm/Components/Select/optionRowInk.js +7 -0
  149. package/dist/esm/Components/SelectSearch/SelectSearch.js +4 -12
  150. package/dist/esm/Components/SettingRow/SettingRow.js +2 -1
  151. package/dist/esm/Components/SettingRow/SettingsSection.js +2 -1
  152. package/dist/esm/Components/SideBar/SecondaryBar.js +4 -3
  153. package/dist/esm/Components/SideBar/SideBar.js +17 -14
  154. package/dist/esm/Components/SideBar/components/MenuItemBox.js +7 -4
  155. package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +2 -1
  156. package/dist/esm/Components/SideBar/components/OtherApps.js +2 -1
  157. package/dist/esm/Components/SideBar/components/PinnedSection.js +7 -4
  158. package/dist/esm/Components/SideBar/sidebarMotion.js +3 -0
  159. package/dist/esm/Components/SignInActivityTable/SignInActivityTable.js +6 -4
  160. package/dist/esm/Components/StageProgress/StageItem.js +5 -2
  161. package/dist/esm/Components/StageProgress/StepperStage.js +2 -2
  162. package/dist/esm/Components/StatCard/StatCard.js +2 -1
  163. package/dist/esm/Components/Table/Table.js +12 -7
  164. package/dist/esm/Components/Table/components/TableActions.js +3 -2
  165. package/dist/esm/Components/Table/components/TableBody.js +10 -2
  166. package/dist/esm/Components/Table/components/TableHeader.js +9 -4
  167. package/dist/esm/Components/Table/components/TableLoading.js +5 -4
  168. package/dist/esm/Components/Table/filters/CompactSelect.js +4 -12
  169. package/dist/esm/Components/Table/settings/TableSettings.js +8 -3
  170. package/dist/esm/Components/Table/variants/TableVariantContext.js +3 -3
  171. package/dist/esm/Components/Table/variants/classic.js +3 -2
  172. package/dist/esm/Components/Timeline/Timeline.js +11 -4
  173. package/dist/esm/Components/Toaster/Toaster.js +20 -9
  174. package/dist/esm/Components/Toggle/TableToggle.js +5 -2
  175. package/dist/esm/Components/UserDetails/AddUserModal.js +2 -1
  176. package/dist/esm/Components/UserDetails/ChangeRoleModal.js +2 -1
  177. package/dist/esm/Components/UserDetails/UserDetails.js +3 -1
  178. package/dist/esm/Components/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
  179. package/dist/esm/Components/WorkspaceWindow/WorkspaceWindow.js +26 -19
  180. package/dist/esm/Theme/chakra/Card.styles.js +4 -1
  181. package/dist/esm/Theme/chakra/Menu.styles.js +10 -0
  182. package/dist/esm/Theme/chakra/Popover.styles.js +2 -0
  183. package/dist/esm/Theme/chakra/componentStyles.js +12 -8
  184. package/dist/esm/Theme/chakra/motion.js +1 -0
  185. package/dist/esm/Theme/chakra/overlay.styles.js +3 -0
  186. package/dist/esm/Theme/tokens/brands/index.js +16 -0
  187. package/dist/esm/Theme/tokens/brands/radiant/palette.light.js +15 -0
  188. package/dist/esm/Theme/tokens/brands/rosewood/palette.light.js +10 -10
  189. package/dist/esm/Theme/tokens/brands/zinc/palette.dark.js +3 -35
  190. package/dist/esm/Theme/tokens/builders/accentText.js +5 -0
  191. package/dist/esm/Theme/tokens/builders/buildDarkPalette.js +38 -9
  192. package/dist/esm/Theme/tokens/builders/filledSurface.js +18 -5
  193. package/dist/esm/Theme/tokens/builders/mutedText.js +12 -2
  194. package/dist/esm/Theme/tokens/builders/planeStep.js +14 -0
  195. package/dist/esm/Theme/tokens/builders/planes.js +28 -0
  196. package/dist/esm/Theme/tokens/builders/skeletonShimmer.js +21 -0
  197. package/dist/esm/Theme/tokens/builders/surfaceRaised.js +16 -7
  198. package/dist/esm/Theme/tokens/builders/surfaceRungs.js +3 -1
  199. package/dist/esm/Theme/tokens/primitives/index.js +1 -1
  200. package/dist/esm/Theme/tokens/primitives/motion.js +1 -0
  201. package/dist/esm/Theme/tokens/primitives/shadows.js +54 -5
  202. package/dist/esm/Theme/tokens/primitives/typography.js +1 -1
  203. package/dist/esm/Utils/table.js +2 -2
  204. package/dist/esm/index.js +4 -3
  205. package/dist/index.d.ts +5 -4
  206. package/dist/index.js +15 -6
  207. package/package.json +1 -1
@@ -5,13 +5,17 @@ import { motion } from "framer-motion";
5
5
  import ManageColumns from "./ManageColumns.js";
6
6
  import Button from "../../Button/Button.js";
7
7
  import { useCustomTheme } from "../../../Theme/useCustomTheme.js";
8
+ import { raisedRule } from "../../../Theme/tokens/builders/surfaceRaised.js";
8
9
  import { scrollbarStyles } from "../../../Theme/tokens/builders/scrollbar.js";
9
10
  import { columnLabelText } from "../columnLabel.js";
10
11
  import { Settings, Check } from "lucide-react";
11
12
  import ToolTip from "../../ToolTip/ToolTip.js";
12
13
  import { listRowBackgrounds } from "../../../Theme/tokens/builders/rowStates.js";
13
- import { transitionFor } from "../../../Theme/tokens/primitives/motion.js";
14
+ import { motionTransition, transitionFor } from "../../../Theme/tokens/primitives/motion.js";
15
+ import { usePrefersReducedMotion } from "../../../Theme/chakra/motion.js";
14
16
  const MotionModalContent = motion(ModalContent);
17
+ const SHEET_ENTER = motionTransition("enter", "out");
18
+ const SHEET_EXIT = motionTransition("exit", "in");
15
19
  const DENSITY_OPTIONS = [
16
20
  { value: "compact", label: "Compact", desc: "Tight rows — fit more data on screen" },
17
21
  { value: "normal", label: "Normal", desc: "Balanced default spacing" },
@@ -19,6 +23,7 @@ const DENSITY_OPTIONS = [
19
23
  ];
20
24
  const TableSettings = ({ columns, onSave, density = "normal", onDensityChange, groupBy, }) => {
21
25
  const theme = useCustomTheme();
26
+ const sheetOffset = usePrefersReducedMotion() ? 0 : "-100%";
22
27
  const rowBackgrounds = useMemo(() => listRowBackgrounds(theme.colors), [theme.colors]);
23
28
  const childInputMethodsRef = useRef({});
24
29
  const [settingsOpen, setSettingsOpen] = useState(false);
@@ -52,7 +57,7 @@ const TableSettings = ({ columns, onSave, density = "normal", onDensityChange, g
52
57
  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: transitionFor(["color", "transform"], "base"), color: theme.colors.text[500], _hover: {
53
58
  transform: "scale(1.1)",
54
59
  color: theme.colors.accentText
55
- } }) }), _jsxs(Modal, { isOpen: settingsOpen, onClose: () => setSettingsOpen(false), size: { base: "full", md: "md" }, children: [_jsx(ModalOverlay, {}), _jsxs(MotionModalContent, { mt: "0", top: "0", left: { base: 0, md: "auto" }, right: { base: 0, md: "auto" }, 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: { base: "auto", md: "620px" }, minH: { base: "auto", md: "520px" }, maxH: "90vh", display: "flex", flexDirection: "column", bg: theme.colors.surfaceRaised, 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) => {
60
+ } }) }), _jsxs(Modal, { isOpen: settingsOpen, onClose: () => setSettingsOpen(false), size: { base: "full", md: "md" }, children: [_jsx(ModalOverlay, {}), _jsxs(MotionModalContent, { mt: "0", top: "0", left: { base: 0, md: "auto" }, right: { base: 0, md: "auto" }, position: "absolute", borderTopRadius: "none", initial: { y: sheetOffset, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: sheetOffset, opacity: 0, transition: SHEET_EXIT }, transition: SHEET_ENTER, padding: "0", minW: { base: "auto", md: "620px" }, minH: { base: "auto", md: "520px" }, maxH: "90vh", display: "flex", flexDirection: "column", bg: theme.colors.surfaceRaised, 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: raisedRule(theme.colors), 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) => {
56
61
  const selected = density === opt.value;
57
62
  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 ? rowBackgrounds.selected : theme.colors.transparent, transition: transitionFor(["background-color", "border-color"]), _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));
58
63
  }) }) }), _jsx(TabPanel, { px: 0, py: 0, children: _jsx(Box, { sx: {
@@ -73,6 +78,6 @@ const TableSettings = ({ columns, onSave, density = "normal", onDensityChange, g
73
78
  var _a;
74
79
  const selected = selectedGroup === opt.id;
75
80
  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 ? rowBackgrounds.selected : theme.colors.transparent, transition: transitionFor(["background-color", "border-color"]), _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__")));
76
- }) })] })] }) })] }), _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 })] })] })] })] }));
81
+ }) })] })] }) })] }), _jsxs(ModalFooter, { gap: "0.5rem", borderTop: "1px solid", borderColor: raisedRule(theme.colors), 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 })] })] })] })] }));
77
82
  };
78
83
  export default TableSettings;
@@ -1,13 +1,13 @@
1
1
  import { createContext, useContext, useMemo } from "react";
2
2
  import { useCustomTheme } from "../../../Theme/useCustomTheme.js";
3
3
  import { isDarkCanvas } from "../../../Theme/tokens/builders/accentText.js";
4
- import { classicPreset } from "./classic.js";
4
+ import { studioPreset } from "./studio.js";
5
5
  const TableVariantContext = createContext(null);
6
6
  export const TableVariantProvider = TableVariantContext.Provider;
7
- /** No provider means the table as it always was — the default is a variant, not a gap. */
7
+ /** No provider means the default variant — studio, the same preset `Table` falls back to. */
8
8
  export function useTableVariant() {
9
9
  const provided = useContext(TableVariantContext);
10
10
  const theme = useCustomTheme();
11
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]);
12
+ return useMemo(() => provided !== null && provided !== void 0 ? provided : studioPreset(theme.colors, isDark), [provided, theme.colors, isDark]);
13
13
  }
@@ -1,4 +1,5 @@
1
1
  import { accentTextOnCanvas } from "../../../Theme/tokens/builders/accentText.js";
2
+ import { cardRule } from "../../../Theme/tokens/builders/planeStep.js";
2
3
  import { getTableRowHoverStyle } from "../components/tableRowHoverStyle.js";
3
4
  import { stripeFill } from "./stripe.js";
4
5
  /**
@@ -16,14 +17,14 @@ export const classicPreset = (colors, isDark) => {
16
17
  textTransform: "capitalize",
17
18
  letterSpacing: "normal",
18
19
  height: "45px",
19
- ruleColor: colors.boxborder[200],
20
+ ruleColor: cardRule(colors),
20
21
  showColumnRule: true,
21
22
  actions: "always",
22
23
  },
23
24
  row: {
24
25
  hoverBg: getTableRowHoverStyle(colors, isDark).backgroundColor,
25
26
  hoverScope: "cell",
26
- ruleColor: colors.boxborder[200],
27
+ ruleColor: cardRule(colors),
27
28
  frozenRuleColor: colors.boxborder[300],
28
29
  selectedBg: colors.primary.opacity[16],
29
30
  selectedBarColor: colors.primary[500],
@@ -4,6 +4,7 @@ import { ArrowRight, FileText, IndianRupee, Paperclip, Pencil, PencilLine, Plus,
4
4
  import React from "react";
5
5
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
6
  import { accentTextOnCanvas } from "../../Theme/tokens/builders/accentText.js";
7
+ import { bandOn } from "../../Theme/tokens/builders/planeStep.js";
7
8
  import { AA_NON_TEXT, liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
8
9
  import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
9
10
  import { BORDER_WIDTHS } from "../../Theme/tokens/primitives/borderWidths.js";
@@ -152,12 +153,14 @@ const NoteItem = React.memo(function NoteItem({ note, event, index, colors, acti
152
153
  var _a, _b;
153
154
  const isEditing = activeEditNoteId === note.log_note_id;
154
155
  const isDeleting = deletingNoteId != null && deletingNoteId === note.log_note_id;
156
+ const noteChip = bandOn(colors.primary[50], colors.gray[50]);
157
+ const noteChipInk = accentTextOnCanvas(colors.primary, noteChip);
155
158
  if (isEditing) {
156
159
  return (_jsx(Box, { children: _jsx(NoteTextArea, { isEditable: true, value: (_a = note.note) !== null && _a !== void 0 ? _a : "", title: (_b = note.title) !== null && _b !== void 0 ? _b : "", saveButtonLoading: editNoteLoading, handleSubmit: (n, t, f) => onEditNote === null || onEditNote === void 0 ? void 0 : onEditNote(note.log_note_id, n, t, f, event, index), handleCancel: () => setActiveEditNoteId(null) }) }));
157
160
  }
158
161
  return (_jsxs(Box, { position: "relative", zIndex: 1, bg: colors.gray[50], border: `${BORDER_WIDTHS.hairline} solid ${colors.boxborder[200]}`, borderRadius: "lg", px: 2.5, py: 2, opacity: isDeleting ? 0.6 : 1, _hover: { ".timeline-note-actions": { opacity: 1 } }, _focusWithin: { ".timeline-note-actions": { opacity: 1 } }, children: [_jsxs(Flex, { align: "center", gap: 1.5, mb: note.note ? 1 : 0, children: [_jsx(Box, { as: "span", color: accentTextOnCanvas(colors.primary, colors.gray[50]), display: "inline-flex", children: _jsx(StickyNote, { size: 12 }) }), note.title && (_jsx(Text, { fontSize: fontSizes["xs-minus"], fontWeight: 700, color: colors.text[800], noOfLines: 1, children: note.title })), (onEditNote || onDeleteNote) && (_jsx(Flex, { className: "timeline-note-actions", align: "center", gap: 0.5, ml: "auto", flexShrink: 0, opacity: isDeleting ? 1 : 0, transition: REVEAL_TRANSITION, sx: reducedMotionSx(), children: isDeleting ? (_jsx(Spinner, { size: "xs", color: colors.textMuted })) : (_jsxs(_Fragment, { children: [onEditNote && (_jsx(Tooltip, { label: "Edit", placement: "top", hasArrow: true, children: _jsx(Box, { as: "button", type: "button", "aria-label": "Edit note", sx: focusVisibleStyles(colors), display: "inline-flex", alignItems: "center", justifyContent: "center", p: 1, borderRadius: "md", color: colors.textMuted, bg: "transparent", cursor: "pointer", _hover: {
159
- bg: colors.primary[50],
160
- color: accentTextOnCanvas(colors.primary, colors.primary[50]),
162
+ bg: noteChip,
163
+ color: noteChipInk,
161
164
  }, onClick: (e) => {
162
165
  e.stopPropagation();
163
166
  setActiveEditNoteId(note.log_note_id);
@@ -167,7 +170,7 @@ const NoteItem = React.memo(function NoteItem({ note, event, index, colors, acti
167
170
  }, onClick: (e) => {
168
171
  e.stopPropagation();
169
172
  onDeleteNote === null || onDeleteNote === void 0 ? void 0 : onDeleteNote(note.log_note_id, event, index);
170
- }, children: _jsx(Trash2, { size: 12 }) }) }))] })) }))] }), note.note && (_jsx(Text, { fontSize: fontSizes["xs-minus"], color: colors.text[700], whiteSpace: "pre-wrap", children: note.note })), note.attachments && note.attachments.length > 0 && (_jsx(Flex, { wrap: "wrap", gap: 1.5, mt: 1.5, children: note.attachments.map((att, attIndex) => (_jsxs(Flex, { as: "a", href: att.url, target: "_blank", rel: "noopener noreferrer", onClick: (e) => e.stopPropagation(), align: "center", gap: 1, px: 1.5, py: 0.5, borderRadius: "full", bg: colors.primary[50], color: accentTextOnCanvas(colors.primary, colors.primary[50]), fontSize: MIN_TEXT, maxW: "100%", _hover: { bg: colors.primary[100] }, children: [_jsx(Paperclip, { size: 10 }), _jsx(Box, { as: "span", noOfLines: 1, maxW: "9rem", children: att.name || "attachment" })] }, attIndex))) })), (note.created_by || note.created_at) && (_jsxs(Text, { fontSize: MIN_TEXT, color: colors.textMuted, mt: 1, children: [note.created_by ? formatName(note.created_by) : "", note.created_by && note.created_at ? " · " : "", note.created_at ? formatTimestamp(note.created_at, "time") : ""] }))] }));
173
+ }, children: _jsx(Trash2, { size: 12 }) }) }))] })) }))] }), note.note && (_jsx(Text, { fontSize: fontSizes["xs-minus"], color: colors.text[700], whiteSpace: "pre-wrap", children: note.note })), note.attachments && note.attachments.length > 0 && (_jsx(Flex, { wrap: "wrap", gap: 1.5, mt: 1.5, children: note.attachments.map((att, attIndex) => (_jsxs(Flex, { as: "a", href: att.url, target: "_blank", rel: "noopener noreferrer", onClick: (e) => e.stopPropagation(), align: "center", gap: 1, px: 1.5, py: 0.5, borderRadius: "full", bg: noteChip, color: noteChipInk, fontSize: MIN_TEXT, maxW: "100%", _hover: { bg: colors.primary[100] }, children: [_jsx(Paperclip, { size: 10 }), _jsx(Box, { as: "span", noOfLines: 1, maxW: "9rem", children: att.name || "attachment" })] }, attIndex))) })), (note.created_by || note.created_at) && (_jsxs(Text, { fontSize: MIN_TEXT, color: colors.textMuted, mt: 1, children: [note.created_by ? formatName(note.created_by) : "", note.created_by && note.created_at ? " · " : "", note.created_at ? formatTimestamp(note.created_at, "time") : ""] }))] }));
171
174
  });
172
175
  // The colored event card (shared by both the desktop "tree" and mobile rail).
173
176
  // `accent` decides which edge carries the colored bar (pointing toward the spine).
@@ -177,6 +180,7 @@ const EventCard = React.memo(function EventCard({ event, index, accent, colors,
177
180
  const changes = useMemo(() => parseChanges(event.changes), [event.changes]);
178
181
  const clickable = typeof onEventClick === "function";
179
182
  const actor = formatName(event.action_by);
183
+ const addNoteHover = bandOn(colors.primary[50], colors.background[50]);
180
184
  return (_jsxs(Box, { _hover: { ".timeline-add-note": { opacity: 1 } }, _focusWithin: { ".timeline-add-note": { opacity: 1 } }, children: [_jsxs(Box, { position: "relative", zIndex: 1, bg: colors.background[50], border: `${BORDER_WIDTHS.hairline} solid ${colors.boxborder[200]}`, borderInlineStartWidth: accent === "left" ? "0.1875rem" : undefined, borderInlineEndWidth: accent === "right" ? "0.1875rem" : undefined, borderInlineStartColor: accent === "left" ? scale[400] : undefined, borderInlineEndColor: accent === "right" ? scale[400] : undefined, borderRadius: "xl", px: 3.5, py: 2.5, ...(clickable && {
181
185
  transition: CARD_TRANSITION,
182
186
  _hover: {
@@ -189,7 +193,10 @@ const EventCard = React.memo(function EventCard({ event, index, accent, colors,
189
193
  e.stopPropagation();
190
194
  setActiveNoteEventIndex(isActiveNote ? null : index);
191
195
  }, position: "relative", zIndex: 1, opacity: isActiveNote ? 1 : 0, transition: REVEAL_TRANSITION, sx: { ...focusVisibleStyles(colors), ...reducedMotionSx() }, px: 2, py: 1, borderRadius: "md", fontSize: fontSizes.xs, fontWeight: 600, color: accentTextOnCanvas(colors.primary, colors.background[50]), bg: "transparent", cursor: "pointer", _hover: {
192
- bg: colors.primary[50],
196
+ bg: addNoteHover,
197
+ color: addNoteHover === colors.primary[50]
198
+ ? undefined
199
+ : accentTextOnCanvas(colors.primary, addNoteHover),
193
200
  }, children: isActiveNote ? "Hide note" : "Add note" }))] }), event.notes && event.notes.length > 0 && (_jsx(Flex, { direction: "column", gap: 2, mt: 2, pt: 2, borderTop: `${BORDER_WIDTHS.hairline} solid ${colors.boxborder[200]}`, children: event.notes.map((note, noteIndex) => {
194
201
  var _a;
195
202
  return (_jsx(NoteItem, { note: note, event: event, index: index, colors: colors, activeEditNoteId: activeEditNoteId, setActiveEditNoteId: setActiveEditNoteId, editNoteLoading: editNoteLoading, deletingNoteId: deletingNoteId, onEditNote: onEditNote, onDeleteNote: onDeleteNote }, (_a = note.log_note_id) !== null && _a !== void 0 ? _a : noteIndex));
@@ -7,9 +7,10 @@ import { CheckCircle2, AlertTriangle, Info, XCircle, } from "lucide-react";
7
7
  import Spinner from "../Spinner/Spinner.js";
8
8
  import { toastDuration } from "./toastDuration.js";
9
9
  import { REDUCED_MOTION_QUERY, reducedMotionSx } from "../../Theme/chakra/motion.js";
10
- import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
10
+ import { motionTransition, transitionFor } from "../../Theme/tokens/primitives/motion.js";
11
11
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
12
12
  import { resolveSoftTone } from "../../Theme/tokens/builders/tone.js";
13
+ import { compositeOver } from "../../Theme/tokens/builders/color.js";
13
14
  const ToasterContext = createContext(undefined);
14
15
  const shrink = keyframes `
15
16
  from { transform: scaleX(1); }
@@ -17,11 +18,13 @@ const shrink = keyframes `
17
18
  `;
18
19
  // One-shot ring pulse on the status icon — a subtle "just happened" beat.
19
20
  const iconPulse = keyframes `
20
- 0% { box-shadow: 0 0 0 0 var(--toast-pulse-color); }
21
- 70% { box-shadow: 0 0 0 0.5rem transparent; }
22
- 100% { box-shadow: 0 0 0 0 transparent; }
21
+ 0% { transform: scale(1); opacity: 1; }
22
+ 70% { transform: scale(1.5); opacity: 0; }
23
+ 100% { transform: scale(1.5); opacity: 0; }
23
24
  `;
24
25
  const MotionBox = motion(Box);
26
+ const TOAST_ENTER = motionTransition("enter", "out");
27
+ const TOAST_EXIT = motionTransition("exit", "in");
25
28
  // Presentational toast card — owns hover state so auto-dismiss pauses while the pointer is over it.
26
29
  function ToastCard({ onClose, title, description, actions, isClosable, duration, meta, colors: c, isUrgent, }) {
27
30
  const [paused, setPaused] = useState(false);
@@ -33,15 +36,23 @@ function ToastCard({ onClose, title, description, actions, isClosable, duration,
33
36
  , {
34
37
  // The single live-region root: an explicit aria-live here, on the card's own
35
38
  // root, keeps errors from being announced a second time by a duplicate wrapper.
36
- role: isUrgent ? "alert" : undefined, "aria-live": isUrgent ? "assertive" : undefined, initial: { opacity: 0, x: 24 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: 24 }, transition: { duration: 0.24, ease: [0.22, 1, 0.36, 1] }, onHoverStart: () => setPaused(true), onHoverEnd: () => setPaused(false), position: "relative", bg: c.surfaceRaised, border: `0.0625rem solid ${c.boxborder[200]}`, boxShadow: "md", borderRadius: "xl", overflow: "hidden", minW: "22.5rem", maxW: "26rem", sx: {
39
+ role: isUrgent ? "alert" : undefined, "aria-live": isUrgent ? "assertive" : undefined, initial: { opacity: 0, x: 24 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: 24, transition: TOAST_EXIT }, transition: TOAST_ENTER, onHoverStart: () => setPaused(true), onHoverEnd: () => setPaused(false), position: "relative", bg: c.surfaceRaised, border: `0.0625rem solid ${c.boxborder[200]}`, boxShadow: "md", borderRadius: "xl", overflow: "hidden", minW: "22.5rem", maxW: "26rem", sx: {
37
40
  isolation: "isolate",
38
41
  transition: transitionFor(["box-shadow"]),
39
42
  _hover: { boxShadow: "lg" },
40
43
  ...reducedMotionSx(),
41
- }, children: [_jsxs(Flex, { p: 3.5, gap: 3, align: actions ? "flex-start" : "center", children: [_jsx(Flex, { align: "center", justify: "center", boxSize: "2rem", minW: "2rem", borderRadius: "full", bg: tone.bg, color: tone.fg, sx: {
42
- ["--toast-pulse-color"]: tone.border,
43
- animation: `${iconPulse} 900ms ease-out 1`,
44
- ...reducedMotionSx(),
44
+ }, children: [_jsxs(Flex, { p: 3.5, gap: 3, align: actions ? "flex-start" : "center", children: [_jsx(Flex, { align: "center", justify: "center", boxSize: "2rem", minW: "2rem", borderRadius: "full", position: "relative", bg: compositeOver(tone.bg, c.surfaceRaised), color: tone.fg, sx: {
45
+ "&::after": {
46
+ content: '""',
47
+ position: "absolute",
48
+ inset: 0,
49
+ borderRadius: "full",
50
+ bg: tone.border,
51
+ zIndex: -1,
52
+ opacity: 0,
53
+ animation: `${iconPulse} 900ms ease-out 1`,
54
+ ...reducedMotionSx(),
55
+ },
45
56
  }, children: meta.icon }), _jsxs(VisuallyHidden, { children: [meta.label, ":"] }), _jsxs(Box, { flex: "1", minW: 0, children: [title && (_jsx(Text, { fontWeight: 600, fontSize: "0.875rem", lineHeight: "1.3", color: c.text[800], children: title })), description && (_jsx(Text, { fontSize: "0.8125rem", lineHeight: "1.45", color: c.textMuted, mt: title ? "0.125rem" : 0, children: description })), actions && (_jsx(Flex, { gap: 2, mt: 2.5, align: "center", wrap: "wrap", children: actions(onClose) }))] }), isClosable && (_jsx(CloseButton, { onClick: onClose, size: "sm", color: c.textMuted, borderRadius: "md", alignSelf: "flex-start", _hover: {
46
57
  bg: c.gray[100],
47
58
  color: c.text[700],
@@ -8,9 +8,12 @@ import ToolTip from "../ToolTip/ToolTip.js";
8
8
  import { useSlidingPill } from "../SegmentedControl/useSlidingPill.js";
9
9
  import { BUTTON_SIZES } from "../../Theme/tokens/primitives/controlSizes.js";
10
10
  import { BORDER_WIDTHS } from "../../Theme/tokens/primitives/borderWidths.js";
11
+ import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
11
12
  const MODES = ["grid", "list"];
12
13
  const TRACK_HEIGHT = BUTTON_SIZES.sm.height;
13
14
  const SEGMENT_HEIGHT = `calc(${TRACK_HEIGHT} - 0.25rem)`;
15
+ const INK_TRANSITION = transitionFor(["color"], "base");
16
+ const PILL_TRANSITION = transitionFor(["transform", "width"], "base", "out");
14
17
  /** Two icon-only IconButtons (Grid/List), aria-pressed marks the active one; shares variant with SegmentedControl (see MODULE.md). */
15
18
  const TableToggle = ({ value, onChange, variant = "default" }) => {
16
19
  const theme = useCustomTheme();
@@ -71,10 +74,10 @@ const TableToggle = ({ value, onChange, variant = "default" }) => {
71
74
  ? colors.accentText
72
75
  : hoverInk,
73
76
  },
74
- transition: "color 0.2s ease-in-out",
77
+ transition: INK_TRANSITION,
75
78
  }),
76
79
  };
77
- return (_jsxs(Box, { ref: trackRef, ...styles.container, children: [isBrandSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", top: "0.125rem", left: 0, h: SEGMENT_HEIGHT, w: `${pill.width}px`, borderRadius: "lg", bg: brandFill.fill, boxShadow: `0 0.125rem 0.5rem ${colors.boxShadow.primary}`, transform: `translateX(${pill.left}px)`, transition: "transform 0.24s cubic-bezier(0.2, 0.8, 0.2, 1), width 0.24s cubic-bezier(0.2, 0.8, 0.2, 1)" })), isUnderlineSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", bottom: `-${BORDER_WIDTHS.hairline}`, left: 0, h: "0.125rem", w: `${pill.width}px`, borderRadius: "full", bg: colors.primary[500], transform: `translateX(${pill.left}px)`, transition: "transform 0.24s cubic-bezier(0.2, 0.8, 0.2, 1), width 0.24s cubic-bezier(0.2, 0.8, 0.2, 1)" })), _jsx(ToolTip, { label: "Grid View", placement: "top", fontSize: "xs", hasArrow: true, children: _jsx(IconButton, { ref: (node) => {
80
+ return (_jsxs(Box, { ref: trackRef, ...styles.container, children: [isBrandSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", top: "0.125rem", left: 0, h: SEGMENT_HEIGHT, w: `${pill.width}px`, borderRadius: "lg", bg: brandFill.fill, boxShadow: `0 0.125rem 0.5rem ${colors.boxShadow.primary}`, transform: `translateX(${pill.left}px)`, transition: PILL_TRANSITION })), isUnderlineSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", bottom: `-${BORDER_WIDTHS.hairline}`, left: 0, h: "0.125rem", w: `${pill.width}px`, borderRadius: "full", bg: colors.primary[500], transform: `translateX(${pill.left}px)`, transition: PILL_TRANSITION })), _jsx(ToolTip, { label: "Grid View", placement: "top", fontSize: "xs", hasArrow: true, children: _jsx(IconButton, { ref: (node) => {
78
81
  refs.current[0] = node;
79
82
  }, "aria-label": "Grid View", "aria-pressed": value === "grid", icon: _jsx(Table2, { size: "1.25rem" }), variant: "ghost", onClick: () => onChange("grid"), ...styles.button("grid") }) }), _jsx(ToolTip, { label: "List View", placement: "top", fontSize: "xs", hasArrow: true, children: _jsx(IconButton, { ref: (node) => {
80
83
  refs.current[1] = node;
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, ModalFooter, FormControl, VStack, } from "@chakra-ui/react";
4
4
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
5
+ import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
5
6
  import Button from "../Button/Button.js";
6
7
  import TextInput from "../Input/TextInput.js";
7
8
  import SearchSelect from "../SearchSelect/SearchSelect.js";
@@ -75,7 +76,7 @@ const AddUserModal = ({ isOpen, onClose, roleOptions, isLoading, onSubmit, onAdd
75
76
  type: "email",
76
77
  },
77
78
  ];
78
- return (_jsxs(Modal, { isOpen: isOpen, onClose: onClose, trapFocus: false, isCentered: true, children: [_jsx(ModalOverlay, { backdropFilter: "blur(4px)" }), _jsxs(ModalContent, { bg: colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", children: [_jsxs(ModalHeader, { borderBottom: "1px solid", borderColor: colors.boxborder[200], children: [labels.addNewUser, upgradeNotice] }), _jsx(ModalCloseButton, {}), _jsx(ModalBody, { py: 5, children: _jsxs(VStack, { spacing: 4, align: "stretch", children: [textFields.map((field) => (_jsx(FormControl, { isInvalid: !!errors[field.id], children: _jsx(TextInput, { id: field.id, name: field.id, type: field.type, label: field.label, placeholder: field.placeholder, value: values[field.id], onChange: handleChange(field.id), isRequired: true, error: !!errors[field.id], errorMessage: errors[field.id], width: "100%" }) }, field.id))), _jsx(FormControl, { isInvalid: !!errors.role, isRequired: true, children: _jsx(SearchSelect, { label: labels.role, options: roleOptions.map((item) => ({
79
+ return (_jsxs(Modal, { isOpen: isOpen, onClose: onClose, trapFocus: false, isCentered: true, children: [_jsx(ModalOverlay, { backdropFilter: "blur(4px)" }), _jsxs(ModalContent, { bg: colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", children: [_jsxs(ModalHeader, { borderBottom: "1px solid", borderColor: raisedRule(colors), children: [labels.addNewUser, upgradeNotice] }), _jsx(ModalCloseButton, {}), _jsx(ModalBody, { py: 5, children: _jsxs(VStack, { spacing: 4, align: "stretch", children: [textFields.map((field) => (_jsx(FormControl, { isInvalid: !!errors[field.id], children: _jsx(TextInput, { id: field.id, name: field.id, type: field.type, label: field.label, placeholder: field.placeholder, value: values[field.id], onChange: handleChange(field.id), isRequired: true, error: !!errors[field.id], errorMessage: errors[field.id], width: "100%" }) }, field.id))), _jsx(FormControl, { isInvalid: !!errors.role, isRequired: true, children: _jsx(SearchSelect, { label: labels.role, options: roleOptions.map((item) => ({
79
80
  id: item.id,
80
81
  label: item.label,
81
82
  })), value: (_a = values.role) !== null && _a !== void 0 ? _a : undefined, onSelect: (option) => {
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, ModalFooter, FormControl, VStack, } from "@chakra-ui/react";
4
4
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
5
+ import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
5
6
  import Button from "../Button/Button.js";
6
7
  import TextInput from "../Input/TextInput.js";
7
8
  import SearchSelect from "../SearchSelect/SearchSelect.js";
@@ -31,7 +32,7 @@ const ChangeRoleModal = ({ isOpen, onClose, userId, email, currentRole, roleOpti
31
32
  return;
32
33
  await (onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit({ userId, roleId: selectedRole.id }));
33
34
  };
34
- return (_jsxs(Modal, { isOpen: isOpen, onClose: onClose, trapFocus: false, isCentered: true, children: [_jsx(ModalOverlay, { backdropFilter: "blur(4px)" }), _jsxs(ModalContent, { bg: colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", children: [_jsx(ModalHeader, { borderBottom: "1px solid", borderColor: colors.boxborder[200], children: labels.changeRoleTitle }), _jsx(ModalCloseButton, {}), _jsx(ModalBody, { py: 5, children: _jsxs(VStack, { spacing: 4, align: "stretch", children: [_jsx(TextInput, { label: labels.email, value: email !== null && email !== void 0 ? email : "", isDisabled: true, width: "100%" }), _jsx(FormControl, { isInvalid: !!error, isRequired: true, children: _jsx(SearchSelect, { label: labels.role, options: roleOptions.map((item) => ({
35
+ return (_jsxs(Modal, { isOpen: isOpen, onClose: onClose, trapFocus: false, isCentered: true, children: [_jsx(ModalOverlay, { backdropFilter: "blur(4px)" }), _jsxs(ModalContent, { bg: colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", children: [_jsx(ModalHeader, { borderBottom: "1px solid", borderColor: raisedRule(colors), children: labels.changeRoleTitle }), _jsx(ModalCloseButton, {}), _jsx(ModalBody, { py: 5, children: _jsxs(VStack, { spacing: 4, align: "stretch", children: [_jsx(TextInput, { label: labels.email, value: email !== null && email !== void 0 ? email : "", isDisabled: true, width: "100%" }), _jsx(FormControl, { isInvalid: !!error, isRequired: true, children: _jsx(SearchSelect, { label: labels.role, options: roleOptions.map((item) => ({
35
36
  id: item.id,
36
37
  label: item.label,
37
38
  })), value: selectedRole !== null && selectedRole !== void 0 ? selectedRole : undefined, onSelect: (option) => {
@@ -6,6 +6,7 @@ import { ChevronLeft, RefreshCw, Send, Shield, Trash2, UserPlus, Users, UserRoun
6
6
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
7
7
  import { resolveHoverTint } from "../../Theme/tokens/builders/outlineRung.js";
8
8
  import { accentTextOnCanvas } from "../../Theme/tokens/builders/accentText.js";
9
+ import { bandOn } from "../../Theme/tokens/builders/planeStep.js";
9
10
  import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
10
11
  import { focusVisibleRing } from "../../Theme/chakra/focusRing.styles.js";
11
12
  import EmptyState from "../EmptyState/EmptyState.js";
@@ -60,6 +61,7 @@ const UserDetails = ({ users, selectedUser, selectedUserId, roleOptions = [], ca
60
61
  const [actionUserId, setActionUserId] = useState(null);
61
62
  const border = colors.boxborder[200];
62
63
  const surface = colors.background[50];
64
+ const detailTile = bandOn(colors.primary[50], surface);
63
65
  const rowBackgrounds = useMemo(() => listRowBackgrounds(colors), [colors]);
64
66
  const muted = colors.textMuted;
65
67
  const heading = colors.text[900];
@@ -100,7 +102,7 @@ const UserDetails = ({ users, selectedUser, selectedUserId, roleOptions = [], ca
100
102
  const Icon = (_a = detail.icon) !== null && _a !== void 0 ? _a : UserRound;
101
103
  return (_jsxs(React.Fragment, { children: [_jsxs(Flex, { gap: 3, px: 3, py: 2.5, _hover: {
102
104
  bg: resolveHoverTint(colors.gray[50], surface),
103
- }, alignItems: "center", transition: "background 0.15s ease", children: [_jsx(Flex, { align: "center", justify: "center", boxSize: "30px", bg: colors.primary[50], borderRadius: "lg", color: accentTextOnCanvas(colors.primary, colors.primary[50]), flexShrink: 0, children: _jsx(Icon, { size: 15 }) }), _jsx(Box, { minWidth: "110px", children: _jsx(Text, { fontSize: "xs", fontWeight: 600, color: muted, children: detail.label }) }), _jsx(Box, { flex: 1, children: _jsx(Text, { fontSize: "sm", color: heading, fontWeight: 500, children: detail.value }) })] }), index < selectedUser.details.length - 1 && (_jsx(Divider, { borderColor: border }))] }, index));
105
+ }, alignItems: "center", transition: "background 0.15s ease", children: [_jsx(Flex, { align: "center", justify: "center", boxSize: "30px", bg: detailTile, borderRadius: "lg", color: accentTextOnCanvas(colors.primary, detailTile), flexShrink: 0, children: _jsx(Icon, { size: 15 }) }), _jsx(Box, { minWidth: "110px", children: _jsx(Text, { fontSize: "xs", fontWeight: 600, color: muted, children: detail.label }) }), _jsx(Box, { flex: 1, children: _jsx(Text, { fontSize: "sm", color: heading, fontWeight: 500, children: detail.value }) })] }), index < selectedUser.details.length - 1 && (_jsx(Divider, { borderColor: border }))] }, index));
104
106
  }) })] })] })) : (_jsx(EmptyState, { icon: UserRound, title: labels.selectTeamMember, description: labels.noUserSelected, minH: "300px" })) })] }), _jsx(AddUserModal, { isOpen: isAddUserOpen, onClose: onAddUserClose, roleOptions: roleOptions, isLoading: isAddUserLoading, onSubmit: async (values) => {
105
107
  await (onAddUserSubmit === null || onAddUserSubmit === void 0 ? void 0 : onAddUserSubmit(values));
106
108
  onAddUserClose();
@@ -15,7 +15,11 @@ export default function WorkspaceTrayPreview({ children, preview, enabled = true
15
15
  const [open, setOpen] = useState(false);
16
16
  const openTimer = useRef(null);
17
17
  const closeTimer = useRef(null);
18
+ const openRef = useRef(false);
18
19
  const setOpenState = useCallback((next) => {
20
+ if (openRef.current === next)
21
+ return;
22
+ openRef.current = next;
19
23
  setOpen(next);
20
24
  onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
21
25
  }, [onOpenChange]);
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Flex, HStack, IconButton, Modal, ModalContent, ModalOverlay, Text, useModalContext, } from "@chakra-ui/react";
2
+ import { Box, Flex, HStack, IconButton, Modal, ModalContent, ModalOverlay, Portal, Text, useModalContext, } from "@chakra-ui/react";
3
3
  import { useEffect } from "react";
4
4
  import { ExternalLink, Minus, X } from "lucide-react";
5
5
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
6
  import zIndices from "../../Theme/tokens/primitives/zIndices.js";
7
7
  import { accentTextOnCanvas } from "../../Theme/tokens/builders/accentText.js";
8
- // NOTE: no maximize/restore toggle (removed by product decision) — onMedium/onMaximize kept optional for back-compat.
8
+ // NOTE: no maximize/restore toggle (removed by product decision); onMedium/onMaximize stay as deprecated no-ops.
9
9
  const WINDOW_SIZE = { maxW: "88rem", h: "92vh" };
10
10
  const WindowTitle = ({ testId, color, children }) => {
11
11
  const { headerId, setHeaderMounted } = useModalContext();
@@ -16,7 +16,8 @@ const WindowTitle = ({ testId, color, children }) => {
16
16
  return (_jsx(Text, { id: headerId, fontSize: "sm", fontWeight: 600, color: color, noOfLines: 1, "data-testid": `${testId}-title`, children: children }));
17
17
  };
18
18
  // Presentational floating "workspace window" (Jira-style dialog), headless — the parent owns window state.
19
- export default function WorkspaceWindow({ title, state, children, onMinimize, onClose, onOpenFullPage, onOverlayClick, headerActions, testId = "workspace-window", }) {
19
+ export default function WorkspaceWindow({ title, state, children, onMinimize, onClose, onOpenFullPage, onOverlayClick, headerActions, labels, finalFocusRef, testId = "workspace-window", }) {
20
+ var _a, _b, _c;
20
21
  // Must run before the early return: a minimized render would otherwise skip the hook
21
22
  // and change hook order when the window reopens.
22
23
  const theme = useCustomTheme();
@@ -26,28 +27,34 @@ export default function WorkspaceWindow({ title, state, children, onMinimize, on
26
27
  return (_jsxs(Modal, { isOpen: true, onClose: () => onClose === null || onClose === void 0 ? void 0 : onClose(), size: "full", isCentered: true, scrollBehavior: "inside",
27
28
  // Don't discard unsaved work on an accidental overlay click / Esc — the
28
29
  // parent decides via the explicit Close control.
29
- closeOnOverlayClick: false, closeOnEsc: false, children: [_jsx(ModalOverlay, { backdropFilter: "blur(3px)" }), _jsx(ModalContent, { bg: "transparent", boxShadow: "none", m: 0, display: "flex", alignItems: "center", justifyContent: "center",
30
+ closeOnOverlayClick: false, closeOnEsc: false, finalFocusRef: finalFocusRef, children: [_jsx(ModalOverlay, { backdropFilter: "blur(3px)" }), _jsx(ModalContent, { bg: "transparent", boxShadow: "none", m: 0, display: "flex", alignItems: "center", justifyContent: "center",
30
31
  // NOTE: size="full" blocks Chakra's own closeOnOverlayClick — detect the outside click ourselves instead.
31
32
  onMouseDown: (e) => {
32
33
  if (e.target === e.currentTarget)
33
34
  onOverlayClick === null || onOverlayClick === void 0 ? void 0 : onOverlayClick();
34
- }, "data-testid": `${testId}-backdrop`, children: _jsxs(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: [_jsxs(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: [_jsx(WindowTitle, { testId: testId, color: theme.colors.text[700], children: title }), _jsxs(HStack, { spacing: 1, children: [headerActions, onOpenFullPage ? (_jsx(IconButton, { "aria-label": "Open full page", size: "xs", variant: "ghost", icon: _jsx(ExternalLink, { size: 14 }), onClick: onOpenFullPage, "data-testid": `${testId}-fullpage` })) : null, onMinimize ? (_jsx(IconButton, { "aria-label": "Minimize", size: "xs", variant: "ghost", icon: _jsx(Minus, { size: 15 }), onClick: onMinimize, "data-testid": `${testId}-minimize` })) : null, onClose ? (_jsx(IconButton, { "aria-label": "Close", size: "xs", variant: "ghost", colorScheme: "red", icon: _jsx(X, { size: 15 }), onClick: onClose, "data-testid": `${testId}-close` })) : null] })] }), _jsx(Box, { flex: "1", overflow: "auto", px: { base: 3, md: 5 }, py: 4, children: children })] }) })] }));
35
+ }, "data-testid": `${testId}-backdrop`, children: _jsxs(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: [_jsxs(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: [_jsx(WindowTitle, { testId: testId, color: theme.colors.text[700], children: title }), _jsxs(HStack, { spacing: 1, children: [headerActions, onOpenFullPage ? (_jsx(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: _jsx(ExternalLink, { size: 14 }), onClick: onOpenFullPage, "data-testid": `${testId}-fullpage` })) : null, onMinimize ? (_jsx(IconButton, { "aria-label": (_b = labels === null || labels === void 0 ? void 0 : labels.minimize) !== null && _b !== void 0 ? _b : "Minimize", size: "xs", variant: "ghost", icon: _jsx(Minus, { size: 15 }), onClick: onMinimize, "data-testid": `${testId}-minimize` })) : null, onClose ? (_jsx(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: _jsx(X, { size: 15 }), onClick: onClose, "data-testid": `${testId}-close` })) : null] })] }), _jsx(Box, { flex: "1", overflow: "auto", px: { base: 3, md: 5 }, py: 4, children: children })] }) })] }));
35
36
  }
36
- // Bottom tray of restore pills for minimized WorkspaceWindows — presentational, parent supplies handlers.
37
- export function WorkspaceTray({ items, onRestore, onClose, testId = "workspace-tray", renderItem, }) {
37
+ const cssLength = (value) => (typeof value === "number" ? `${value}px` : value);
38
+ // Portalled above modal overlays; aria-live keeps Chakra's modal aria-hidden off it, data-no-focus-lock keeps the focus trap off it.
39
+ export function WorkspaceTray({ items, onRestore, onClose, testId = "workspace-tray", placement = "bottom-end", offset, closeLabel, dirtyLabel = "Unsaved changes", label = "Minimized windows", renderItem, }) {
40
+ var _a, _b;
38
41
  const theme = useCustomTheme();
39
42
  if (!items.length)
40
43
  return null;
41
- return (
42
- // NOTE: floating bottom-right cluster (Gmail/Jira pattern), not a docked bar — needs no reserved page gutter.
43
- _jsx(Flex, { position: "fixed", bottom: 4, right: 4, zIndex: zIndices.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) => {
44
- const api = {
45
- restore: () => onRestore === null || onRestore === void 0 ? void 0 : onRestore(item.id),
46
- close: () => onClose === null || onClose === void 0 ? void 0 : onClose(item.id),
47
- };
48
- if (renderItem) {
49
- return _jsx(Box, { children: renderItem(item, api) }, item.id);
50
- }
51
- return (_jsxs(HStack, { spacing: 1, pl: 3, pr: 1, py: 1, borderRadius: "full", bg: theme.colors.primary[100], color: accentTextOnCanvas(theme.colors.primary, theme.colors.primary[100]), boxShadow: "md", borderWidth: "1px", borderColor: theme.colors.primary[200], "data-testid": `${testId}-pill`, children: [_jsx(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 ? (_jsx(IconButton, { "aria-label": "Close", size: "xs", variant: "ghost", borderRadius: "full", minW: "auto", h: "auto", p: 1, color: "inherit", icon: _jsx(X, { size: 13 }), onClick: api.close, "data-testid": `${testId}-close` })) : null] }, item.id));
52
- }) }));
44
+ const atEnd = placement === "bottom-end";
45
+ const inset = {
46
+ bottom: cssLength((_a = offset === null || offset === void 0 ? void 0 : offset.y) !== null && _a !== void 0 ? _a : "1rem"),
47
+ [atEnd ? "right" : "left"]: cssLength((_b = offset === null || offset === void 0 ? void 0 : offset.x) !== null && _b !== void 0 ? _b : "1rem"),
48
+ };
49
+ const ink = accentTextOnCanvas(theme.colors.primary, theme.colors.primary[100]);
50
+ return (_jsx(Portal, { children: _jsx(Flex, { role: "region", "aria-label": label, "aria-live": "polite", "data-no-focus-lock": "true", position: "fixed", ...inset, zIndex: zIndices.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) => {
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 _jsx(Box, { children: renderItem(item, api) }, item.id);
57
+ }
58
+ return (_jsxs(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 ? (_jsx(Box, { role: "img", "aria-label": dirtyLabel, boxSize: "0.5rem", borderRadius: "full", bg: "currentColor", flexShrink: 0, "data-testid": `${testId}-dirty` })) : null, _jsxs(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 ? (_jsx(Box, { as: "span", fontWeight: 400, ml: 1.5, children: item.subtitle })) : null] }), onClose ? (_jsx(IconButton, { "aria-label": closeLabel ? closeLabel(item) : "Close", size: "xs", variant: "ghost", borderRadius: "full", minW: "auto", h: "auto", p: 1, color: "inherit", icon: _jsx(X, { size: 13 }), onClick: api.close, "data-testid": `${testId}-close` })) : null] }, item.id));
59
+ }) }) }));
53
60
  }
@@ -36,10 +36,13 @@ const variants = {
36
36
  outline: definePartsStyle({
37
37
  container: { [$border.variable]: "1px", [$shadow.variable]: "none" },
38
38
  }),
39
+ elevated: definePartsStyle({
40
+ container: { [$border.variable]: "0", [$shadow.variable]: "shadows.card" },
41
+ }),
39
42
  };
40
43
  export const Card = defineMultiStyleConfig({
41
44
  baseStyle,
42
45
  sizes,
43
46
  variants,
44
- defaultProps: { variant: "outline", size: "md" },
47
+ defaultProps: { variant: "elevated", size: "md" },
45
48
  });
@@ -1,8 +1,10 @@
1
1
  import { createMultiStyleConfigHelpers, cssVar } from "@chakra-ui/react";
2
2
  import { menuAnatomy } from "@chakra-ui/anatomy";
3
3
  import { listRowBackgrounds } from "../tokens/builders/rowStates.js";
4
+ import { optionRowInk, optionRowWeight } from "../../Components/Select/optionRowInk.js";
4
5
  import { OPTION_ROW_SIZES, OPTION_ROW_RADIUS } from "../tokens/primitives/optionRowSizes.js";
5
6
  import { paletteOf } from "./paletteFromTheme.js";
7
+ import { POPUP_SURFACE } from "./overlay.styles.js";
6
8
  const itemRowSpec = OPTION_ROW_SIZES.sm;
7
9
  const { defineMultiStyleConfig, definePartsStyle } = createMultiStyleConfigHelpers(menuAnatomy.keys);
8
10
  const $bg = cssVar("menu-bg");
@@ -21,12 +23,15 @@ const baseStyle = definePartsStyle(({ theme }) => {
21
23
  borderWidth: "1px",
22
24
  borderStyle: "solid",
23
25
  borderColor: colors.boxborder[200],
26
+ ...POPUP_SURFACE,
24
27
  },
25
28
  item: {
29
+ color: optionRowInk(colors, false),
26
30
  minH: itemRowSpec.minH,
27
31
  px: itemRowSpec.px,
28
32
  py: itemRowSpec.py,
29
33
  mx: itemRowSpec.mx,
34
+ width: `calc(100% - 2 * ${itemRowSpec.mx})`,
30
35
  fontSize: itemRowSpec.fontSize,
31
36
  borderRadius: OPTION_ROW_RADIUS,
32
37
  bg: "transparent",
@@ -34,6 +39,11 @@ const baseStyle = definePartsStyle(({ theme }) => {
34
39
  _hover: { [$itemBg.variable]: hoverBg, bg: $itemBg.reference },
35
40
  _active: { [$itemBg.variable]: activeBg, bg: $itemBg.reference },
36
41
  _expanded: { [$itemBg.variable]: hoverBg, bg: $itemBg.reference },
42
+ "&[aria-checked=true]": {
43
+ color: optionRowInk(colors, true),
44
+ fontWeight: optionRowWeight(true),
45
+ "& .chakra-menu__icon-wrapper": { color: colors.accentText },
46
+ },
37
47
  },
38
48
  };
39
49
  });
@@ -1,6 +1,7 @@
1
1
  import { createMultiStyleConfigHelpers, cssVar } from "@chakra-ui/react";
2
2
  import { popoverAnatomy } from "@chakra-ui/anatomy";
3
3
  import { paletteOf } from "./paletteFromTheme.js";
4
+ import { POPUP_SURFACE } from "./overlay.styles.js";
4
5
  const { defineMultiStyleConfig, definePartsStyle } = createMultiStyleConfigHelpers(popoverAnatomy.keys);
5
6
  const $popperBg = cssVar("popper-bg");
6
7
  const $arrowShadowColor = cssVar("popper-arrow-shadow-color");
@@ -16,6 +17,7 @@ const baseStyle = definePartsStyle(({ theme }) => {
16
17
  borderWidth: "1px",
17
18
  borderStyle: "solid",
18
19
  borderColor: colors.boxborder[200],
20
+ ...POPUP_SURFACE,
19
21
  },
20
22
  };
21
23
  });
@@ -12,6 +12,7 @@ import { AA_NORMAL_TEXT, accentTextOnCanvas, isDarkCanvas } from "../tokens/buil
12
12
  import { labelOnFill, solidFillRung } from "../tokens/builders/filledSurface.js";
13
13
  import { liftToContrast, resolveHoverTint } from "../tokens/builders/outlineRung.js";
14
14
  import { resolveSoftTone } from "../tokens/builders/tone.js";
15
+ import { skeletonShimmer } from "../tokens/builders/skeletonShimmer.js";
15
16
  import { tooltipSurface } from "../tokens/builders/tooltipSurface.js";
16
17
  import { Drawer } from "./Drawer.styles.js";
17
18
  import { Menu } from "./Menu.styles.js";
@@ -22,7 +23,7 @@ import { Card } from "./Card.styles.js";
22
23
  // NOTE: guards untethemed scales (blackAlpha/whiteAlpha) whose rgba() rungs would throw in hexToRgb.
23
24
  const isHexColor = (color) => typeof color === "string" && /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color);
24
25
  const solidFillStyle = (palette, swatch) => {
25
- const rung = solidFillRung(swatch, palette.white);
26
+ const rung = solidFillRung(swatch, palette.white, palette.backgroundColor.main);
26
27
  const bg = swatch[rung];
27
28
  return {
28
29
  bg,
@@ -142,19 +143,22 @@ const $skeletonStart = cssVar("skeleton-start-color");
142
143
  const $skeletonEnd = cssVar("skeleton-end-color");
143
144
  // NOTE: fixes Chakra's Skeleton hardcoding gray.100/.400 via `_dark` (dead here) — an explicit startColor/endColor prop still wins.
144
145
  const Skeleton = {
145
- baseStyle: ({ theme }) => ({
146
- [$skeletonStart.variable]: paletteOf(theme).gray[100],
147
- [$skeletonEnd.variable]: paletteOf(theme).gray[300],
148
- background: $skeletonStart.reference,
149
- borderColor: $skeletonEnd.reference,
150
- }),
146
+ baseStyle: ({ theme }) => {
147
+ const shimmer = skeletonShimmer(paletteOf(theme));
148
+ return {
149
+ [$skeletonStart.variable]: shimmer.start,
150
+ [$skeletonEnd.variable]: shimmer.end,
151
+ background: $skeletonStart.reference,
152
+ borderColor: $skeletonEnd.reference,
153
+ };
154
+ },
151
155
  };
152
156
  // NOTE: uses Chakra's own shorthand style keys (bg/w/h/pos) + !important — extendTheme deep-merges by key, so mismatched keys let Chakra's stock value win.
153
157
  const Radio = {
154
158
  baseStyle: ({ theme, colorScheme }) => {
155
159
  const palette = paletteOf(theme);
156
160
  const swatch = scaleFor(palette, colorScheme);
157
- const fill = swatch[solidFillRung(swatch, palette.white)];
161
+ const fill = swatch[solidFillRung(swatch, palette.white, palette.backgroundColor.main)];
158
162
  const dot = labelOnFill(fill, palette.white, palette.black);
159
163
  const hoverFill = resolveHoverTint(fill, fill);
160
164
  return {
@@ -1,5 +1,6 @@
1
1
  import { useMatchMedia } from "../../Hooks/useMatchMedia.js";
2
2
  export const REDUCED_MOTION_QUERY = "@media (prefers-reduced-motion: reduce)";
3
+ export const MOTION_OK_QUERY = "@media (prefers-reduced-motion: no-preference)";
3
4
  const MEDIA = "(prefers-reduced-motion: reduce)";
4
5
  export const usePrefersReducedMotion = () => useMatchMedia(MEDIA, false);
5
6
  /** Spread into `sx`: drops transitions and animations when the user asks for less motion. */
@@ -1,6 +1,9 @@
1
1
  import { overlayScrim } from "../tokens/builders/scrim.js";
2
2
  import { raisedRule } from "../tokens/builders/surfaceRaised.js";
3
+ import { RADIUS_ROLE } from "../tokens/primitives/radii.js";
3
4
  export const OVERLAY_BLUR = "blur(4px)";
5
+ /** Radius and elevation every anchored popup shares: menus, popovers, selects, dropdowns. */
6
+ export const POPUP_SURFACE = { borderRadius: RADIUS_ROLE.float, boxShadow: "md" };
4
7
  /** The one Modal/Drawer backdrop; reuse it for any custom full-screen scrim. */
5
8
  export const overlayScrimStyles = (colors) => ({
6
9
  bg: overlayScrim(colors),
@@ -27,9 +27,25 @@ import { withSurfaceRungs } from "../builders/surfaceRungs.js";
27
27
  import { buildSurfaceRaised, withSurfaceRaised } from "../builders/surfaceRaised.js";
28
28
  import { buildStar } from "../builders/star.js";
29
29
  import { lazyPalette } from "../builders/lazy.js";
30
+ import { contrastRatio, mix } from "../builders/color.js";
31
+ export const LIGHT_CANVAS_STEP = 1.09;
32
+ const CANVAS_MIX_STEP = 0.0025;
33
+ const CANVAS_MIX_MAX = 0.5;
34
+ const lightCanvas = (canvas, card, ink) => {
35
+ for (let weight = 0; weight < CANVAS_MIX_MAX; weight += CANVAS_MIX_STEP) {
36
+ const next = mix(canvas, ink, weight);
37
+ if (contrastRatio(next, card) >= LIGHT_CANVAS_STEP)
38
+ return next;
39
+ }
40
+ return mix(canvas, ink, CANVAS_MIX_MAX);
41
+ };
30
42
  /** Derived, never hand-written: a copied hex goes stale the moment a canvas moves. */
31
43
  export const finalizeLightPalette = (source) => ({
32
44
  ...source,
45
+ backgroundColor: {
46
+ ...source.backgroundColor,
47
+ base: lightCanvas(source.backgroundColor.base, source.background[50], source.text[900]),
48
+ },
33
49
  sidebar: withSidebarTokens(source),
34
50
  accentText: pickAccentText(source.primary, source.backgroundColor.main),
35
51
  textMuted: buildMutedText(source.text, source.backgroundColor.main),