pixelize-design-library 2.4.2-beta.58 → 2.4.2-beta.60

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 (244) hide show
  1. package/CHANGELOG.md +149 -5
  2. package/COMPONENT-GOTCHAS.md +205 -18
  3. package/DESIGN-GUIDE.md +43 -9
  4. package/dist/Components/AppSwitcher/AppSwitcher.js +5 -1
  5. package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +2 -0
  6. package/dist/Components/AppSwitcher/AppSwitcherProps.js +7 -4
  7. package/dist/Components/AudioRecorder/AudioRecorder.d.ts +8 -0
  8. package/dist/Components/AudioRecorder/AudioRecorder.js +169 -0
  9. package/dist/Components/AudioRecorder/AudioRecorderProps.d.ts +20 -0
  10. package/dist/Components/AudioRecorder/AudioRecorderProps.js +2 -0
  11. package/dist/Components/Breadcrumbs/Breadcrumbs.d.ts +1 -1
  12. package/dist/Components/Breadcrumbs/Breadcrumbs.js +33 -10
  13. package/dist/Components/Breadcrumbs/BreadcrumbsProps.d.ts +16 -2
  14. package/dist/Components/BulkSelectionBar/BulkSelectionBar.d.ts +3 -0
  15. package/dist/Components/BulkSelectionBar/BulkSelectionBar.js +43 -0
  16. package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.d.ts +35 -0
  17. package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.js +2 -0
  18. package/dist/Components/BulkSelectionBar/index.d.ts +2 -0
  19. package/dist/Components/BulkSelectionBar/index.js +8 -0
  20. package/dist/Components/CalendarGrid/CalendarGrid.d.ts +3 -0
  21. package/dist/Components/CalendarGrid/CalendarGrid.js +173 -0
  22. package/dist/Components/CalendarGrid/CalendarGridProps.d.ts +54 -0
  23. package/dist/Components/CalendarGrid/CalendarGridProps.js +2 -0
  24. package/dist/Components/CalendarGrid/calendarMath.d.ts +49 -0
  25. package/dist/Components/CalendarGrid/calendarMath.js +178 -0
  26. package/dist/Components/Card/Card.d.ts +3 -1
  27. package/dist/Components/Card/Card.js +12 -2
  28. package/dist/Components/Card/CardProps.d.ts +5 -1
  29. package/dist/Components/CircularProgress/CircularProgress.d.ts +4 -0
  30. package/dist/Components/CircularProgress/CircularProgress.js +39 -0
  31. package/dist/Components/CircularProgress/CircularProgressProps.d.ts +22 -0
  32. package/dist/Components/CircularProgress/CircularProgressProps.js +2 -0
  33. package/dist/Components/CircularProgress/arc.d.ts +12 -0
  34. package/dist/Components/CircularProgress/arc.js +16 -0
  35. package/dist/Components/Common/fieldStyles.d.ts +1 -0
  36. package/dist/Components/Common/fieldStyles.js +2 -1
  37. package/dist/Components/Drawer/Drawer.d.ts +1 -1
  38. package/dist/Components/Drawer/Drawer.js +4 -2
  39. package/dist/Components/Drawer/DrawerProps.d.ts +3 -1
  40. package/dist/Components/Editor/Editor.js +50 -2
  41. package/dist/Components/Editor/EditorFieldBar.d.ts +15 -0
  42. package/dist/Components/Editor/EditorFieldBar.js +19 -0
  43. package/dist/Components/Editor/EditorProps.d.ts +74 -1
  44. package/dist/Components/Editor/MakeDynamicDialog.d.ts +12 -0
  45. package/dist/Components/Editor/MakeDynamicDialog.js +59 -0
  46. package/dist/Components/Editor/editorLabels.d.ts +4 -0
  47. package/dist/Components/Editor/editorLabels.js +24 -0
  48. package/dist/Components/Editor/editorTokenStyles.d.ts +28 -0
  49. package/dist/Components/Editor/editorTokenStyles.js +30 -0
  50. package/dist/Components/Editor/editorTokens.d.ts +26 -0
  51. package/dist/Components/Editor/editorTokens.js +134 -0
  52. package/dist/Components/Editor/useEditorBridge.d.ts +40 -0
  53. package/dist/Components/Editor/useEditorBridge.js +141 -0
  54. package/dist/Components/EmptyState/EmptyState.js +2 -1
  55. package/dist/Components/EmptyState/EmptyStateProps.d.ts +2 -2
  56. package/dist/Components/FormDrawer/FormDrawer.d.ts +2 -0
  57. package/dist/Components/FormDrawer/FormDrawer.js +97 -0
  58. package/dist/Components/FormDrawer/FormDrawerProps.d.ts +55 -0
  59. package/dist/Components/FormDrawer/FormDrawerProps.js +2 -0
  60. package/dist/Components/Kbd/Kbd.d.ts +6 -0
  61. package/dist/Components/Kbd/Kbd.js +33 -0
  62. package/dist/Components/Kbd/KbdProps.d.ts +17 -0
  63. package/dist/Components/Kbd/KbdProps.js +2 -0
  64. package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +4 -0
  65. package/dist/Components/LauncherTrigger/LauncherTrigger.js +48 -0
  66. package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +18 -0
  67. package/dist/Components/LauncherTrigger/LauncherTriggerProps.js +2 -0
  68. package/dist/Components/Modal/Modal.d.ts +1 -1
  69. package/dist/Components/Modal/Modal.js +4 -2
  70. package/dist/Components/Modal/ModalProps.d.ts +3 -1
  71. package/dist/Components/Modal/warnDroppedSlotChildren.d.ts +2 -0
  72. package/dist/Components/Modal/warnDroppedSlotChildren.js +45 -0
  73. package/dist/Components/NavLauncher/NavLauncher.d.ts +3 -0
  74. package/dist/Components/NavLauncher/NavLauncher.js +452 -0
  75. package/dist/Components/NavLauncher/NavLauncherProps.d.ts +187 -0
  76. package/dist/Components/NavLauncher/NavLauncherProps.js +2 -0
  77. package/dist/Components/NavLauncher/NewTabLink.d.ts +3 -0
  78. package/dist/Components/NavLauncher/NewTabLink.js +14 -0
  79. package/dist/Components/NavLauncher/PinToggle.d.ts +3 -0
  80. package/dist/Components/NavLauncher/PinToggle.js +21 -0
  81. package/dist/Components/NavLauncher/launcherParts.d.ts +152 -0
  82. package/dist/Components/NavLauncher/launcherParts.js +212 -0
  83. package/dist/Components/NavLauncher/match.d.ts +53 -0
  84. package/dist/Components/NavLauncher/match.js +178 -0
  85. package/dist/Components/NavLauncher/revealStyles.d.ts +4 -0
  86. package/dist/Components/NavLauncher/revealStyles.js +19 -0
  87. package/dist/Components/NavigationBar/NavigationBar.d.ts +1 -1
  88. package/dist/Components/NavigationBar/NavigationBar.js +2 -2
  89. package/dist/Components/NavigationBar/NavigationBarProps.d.ts +2 -0
  90. package/dist/Components/PageHeader/PageHeader.d.ts +1 -1
  91. package/dist/Components/PageHeader/PageHeader.js +9 -2
  92. package/dist/Components/PageHeader/PageHeaderProps.d.ts +8 -0
  93. package/dist/Components/PdfViewer/PdfViewer.d.ts +21 -4
  94. package/dist/Components/PdfViewer/PdfViewer.js +124 -3
  95. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
  96. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewerProps.d.ts +1 -1
  97. package/dist/Components/QueryState/QueryStateBoundary.d.ts +2 -2
  98. package/dist/Components/QueryState/QueryStateBoundary.js +9 -3
  99. package/dist/Components/QueryState/QueryStateProps.d.ts +19 -2
  100. package/dist/Components/Search/Search.js +2 -1
  101. package/dist/Components/SectionRail/SectionRail.d.ts +3 -0
  102. package/dist/Components/SectionRail/SectionRail.js +49 -0
  103. package/dist/Components/SectionRail/SectionRailProps.d.ts +23 -0
  104. package/dist/Components/SectionRail/SectionRailProps.js +2 -0
  105. package/dist/Components/SectionRail/index.d.ts +2 -0
  106. package/dist/Components/SectionRail/index.js +8 -0
  107. package/dist/Components/SegmentedControl/SegmentedControl.js +9 -14
  108. package/dist/Components/SegmentedControl/segmentedInk.d.ts +0 -2
  109. package/dist/Components/SegmentedControl/segmentedInk.js +1 -5
  110. package/dist/Components/SideBar/SecondaryBar.js +4 -3
  111. package/dist/Components/SideBar/SideBar.d.ts +1 -1
  112. package/dist/Components/SideBar/SideBar.js +30 -9
  113. package/dist/Components/SideBar/SideBarProps.d.ts +32 -0
  114. package/dist/Components/SideBar/components/MenuItemBox.d.ts +6 -0
  115. package/dist/Components/SideBar/components/MenuItemBox.js +8 -4
  116. package/dist/Components/SideBar/components/MenuPopoverContent.js +3 -2
  117. package/dist/Components/SideBar/components/OtherApps.js +2 -1
  118. package/dist/Components/SideBar/components/PinnedSection.d.ts +17 -0
  119. package/dist/Components/SideBar/components/PinnedSection.js +98 -0
  120. package/dist/Components/SideBar/sidebarFocus.d.ts +23 -0
  121. package/dist/Components/SideBar/sidebarFocus.js +20 -0
  122. package/dist/Components/StageProgress/StageProgress.js +2 -2
  123. package/dist/Components/StageProgress/StageProgressProps.d.ts +2 -0
  124. package/dist/Components/StageProgress/StepperStage.js +5 -2
  125. package/dist/Components/StatCard/StatCard.d.ts +1 -1
  126. package/dist/Components/StatCard/StatCard.js +6 -2
  127. package/dist/Components/StatCard/StatCardProps.d.ts +3 -0
  128. package/dist/Components/StatusTag/StatusTag.d.ts +4 -0
  129. package/dist/Components/StatusTag/StatusTag.js +24 -0
  130. package/dist/Components/StatusTag/StatusTagProps.d.ts +20 -0
  131. package/dist/Components/StatusTag/StatusTagProps.js +2 -0
  132. package/dist/Components/Table/Table.d.ts +1 -1
  133. package/dist/Components/Table/Table.js +6 -2
  134. package/dist/Components/Table/TableProps.d.ts +20 -0
  135. package/dist/Components/Table/components/TableBody.d.ts +1 -1
  136. package/dist/Components/Table/components/TableBody.js +43 -18
  137. package/dist/Components/Table/components/TableHeader.d.ts +1 -1
  138. package/dist/Components/Table/components/TableHeader.js +7 -5
  139. package/dist/Components/Table/components/rowDetail.d.ts +32 -0
  140. package/dist/Components/Table/components/rowDetail.js +20 -0
  141. package/dist/Components/Table/hooks/useRowExpansion.d.ts +7 -0
  142. package/dist/Components/Table/hooks/useRowExpansion.js +27 -0
  143. package/dist/Components/Tabs/Tabs.d.ts +1 -1
  144. package/dist/Components/Tabs/Tabs.js +15 -2
  145. package/dist/Components/Tabs/TabsProps.d.ts +6 -2
  146. package/dist/Components/Tabs/components/Tab.js +4 -33
  147. package/dist/Components/Tabs/components/TabLinkList.d.ts +7 -0
  148. package/dist/Components/Tabs/components/TabLinkList.js +34 -0
  149. package/dist/Components/Tabs/useTabAppearance.d.ts +17 -0
  150. package/dist/Components/Tabs/useTabAppearance.js +67 -0
  151. package/dist/Components/Tag/Tag.js +11 -1
  152. package/dist/Components/Tag/TagProps.d.ts +4 -0
  153. package/dist/Components/Tag/tagDot.d.ts +3 -0
  154. package/dist/Components/Tag/tagDot.js +21 -0
  155. package/dist/Components/WizardDialog/WizardDialog.d.ts +9 -0
  156. package/dist/Components/WizardDialog/WizardDialog.js +102 -0
  157. package/dist/Components/WizardDialog/WizardDialogProps.d.ts +48 -0
  158. package/dist/Components/WizardDialog/WizardDialogProps.js +2 -0
  159. package/dist/Components/WizardDialog/useWizardFlow.d.ts +22 -0
  160. package/dist/Components/WizardDialog/useWizardFlow.js +47 -0
  161. package/dist/Components/WizardDialog/wizardSteps.d.ts +31 -0
  162. package/dist/Components/WizardDialog/wizardSteps.js +40 -0
  163. package/dist/esm/Components/AppSwitcher/AppSwitcher.js +2 -1
  164. package/dist/esm/Components/AppSwitcher/AppSwitcherProps.js +5 -3
  165. package/dist/esm/Components/AudioRecorder/AudioRecorder.js +161 -0
  166. package/dist/esm/Components/AudioRecorder/AudioRecorderProps.js +1 -0
  167. package/dist/esm/Components/Breadcrumbs/Breadcrumbs.js +35 -12
  168. package/dist/esm/Components/BulkSelectionBar/BulkSelectionBar.js +38 -0
  169. package/dist/esm/Components/BulkSelectionBar/BulkSelectionBarProps.js +1 -0
  170. package/dist/esm/Components/BulkSelectionBar/index.js +1 -0
  171. package/dist/esm/Components/CalendarGrid/CalendarGrid.js +168 -0
  172. package/dist/esm/Components/CalendarGrid/CalendarGridProps.js +1 -0
  173. package/dist/esm/Components/CalendarGrid/calendarMath.js +155 -0
  174. package/dist/esm/Components/Card/Card.js +12 -2
  175. package/dist/esm/Components/CircularProgress/CircularProgress.js +37 -0
  176. package/dist/esm/Components/CircularProgress/CircularProgressProps.js +1 -0
  177. package/dist/esm/Components/CircularProgress/arc.js +11 -0
  178. package/dist/esm/Components/Common/fieldStyles.js +2 -1
  179. package/dist/esm/Components/Drawer/Drawer.js +4 -2
  180. package/dist/esm/Components/Editor/Editor.js +48 -3
  181. package/dist/esm/Components/Editor/EditorFieldBar.js +13 -0
  182. package/dist/esm/Components/Editor/MakeDynamicDialog.js +20 -0
  183. package/dist/esm/Components/Editor/editorLabels.js +20 -0
  184. package/dist/esm/Components/Editor/editorTokenStyles.js +25 -0
  185. package/dist/esm/Components/Editor/editorTokens.js +121 -0
  186. package/dist/esm/Components/Editor/useEditorBridge.js +138 -0
  187. package/dist/esm/Components/EmptyState/EmptyState.js +2 -1
  188. package/dist/esm/Components/FormDrawer/FormDrawer.js +58 -0
  189. package/dist/esm/Components/FormDrawer/FormDrawerProps.js +1 -0
  190. package/dist/esm/Components/Kbd/Kbd.js +28 -0
  191. package/dist/esm/Components/Kbd/KbdProps.js +1 -0
  192. package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +41 -0
  193. package/dist/esm/Components/LauncherTrigger/LauncherTriggerProps.js +1 -0
  194. package/dist/esm/Components/Modal/Modal.js +4 -2
  195. package/dist/esm/Components/Modal/warnDroppedSlotChildren.js +38 -0
  196. package/dist/esm/Components/NavLauncher/NavLauncher.js +413 -0
  197. package/dist/esm/Components/NavLauncher/NavLauncherProps.js +1 -0
  198. package/dist/esm/Components/NavLauncher/NewTabLink.js +11 -0
  199. package/dist/esm/Components/NavLauncher/PinToggle.js +18 -0
  200. package/dist/esm/Components/NavLauncher/launcherParts.js +187 -0
  201. package/dist/esm/Components/NavLauncher/match.js +172 -0
  202. package/dist/esm/Components/NavLauncher/revealStyles.js +15 -0
  203. package/dist/esm/Components/NavigationBar/NavigationBar.js +3 -3
  204. package/dist/esm/Components/PageHeader/PageHeader.js +9 -2
  205. package/dist/esm/Components/PdfViewer/PdfViewer.js +123 -2
  206. package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
  207. package/dist/esm/Components/QueryState/QueryStateBoundary.js +10 -4
  208. package/dist/esm/Components/Search/Search.js +3 -2
  209. package/dist/esm/Components/SectionRail/SectionRail.js +44 -0
  210. package/dist/esm/Components/SectionRail/SectionRailProps.js +1 -0
  211. package/dist/esm/Components/SectionRail/index.js +1 -0
  212. package/dist/esm/Components/SegmentedControl/SegmentedControl.js +11 -16
  213. package/dist/esm/Components/SegmentedControl/segmentedInk.js +0 -3
  214. package/dist/esm/Components/SideBar/SecondaryBar.js +4 -3
  215. package/dist/esm/Components/SideBar/SideBar.js +31 -10
  216. package/dist/esm/Components/SideBar/components/MenuItemBox.js +4 -2
  217. package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +3 -2
  218. package/dist/esm/Components/SideBar/components/OtherApps.js +2 -1
  219. package/dist/esm/Components/SideBar/components/PinnedSection.js +92 -0
  220. package/dist/esm/Components/SideBar/sidebarFocus.js +15 -0
  221. package/dist/esm/Components/StageProgress/StageProgress.js +2 -2
  222. package/dist/esm/Components/StageProgress/StepperStage.js +5 -2
  223. package/dist/esm/Components/StatCard/StatCard.js +6 -2
  224. package/dist/esm/Components/StatusTag/StatusTag.js +19 -0
  225. package/dist/esm/Components/StatusTag/StatusTagProps.js +1 -0
  226. package/dist/esm/Components/Table/Table.js +7 -3
  227. package/dist/esm/Components/Table/components/TableBody.js +43 -18
  228. package/dist/esm/Components/Table/components/TableHeader.js +7 -5
  229. package/dist/esm/Components/Table/components/rowDetail.js +14 -0
  230. package/dist/esm/Components/Table/hooks/useRowExpansion.js +23 -0
  231. package/dist/esm/Components/Tabs/Tabs.js +15 -2
  232. package/dist/esm/Components/Tabs/components/Tab.js +4 -33
  233. package/dist/esm/Components/Tabs/components/TabLinkList.js +29 -0
  234. package/dist/esm/Components/Tabs/useTabAppearance.js +63 -0
  235. package/dist/esm/Components/Tag/Tag.js +12 -2
  236. package/dist/esm/Components/Tag/tagDot.js +17 -0
  237. package/dist/esm/Components/WizardDialog/WizardDialog.js +58 -0
  238. package/dist/esm/Components/WizardDialog/WizardDialogProps.js +1 -0
  239. package/dist/esm/Components/WizardDialog/useWizardFlow.js +44 -0
  240. package/dist/esm/Components/WizardDialog/wizardSteps.js +35 -0
  241. package/dist/esm/index.js +14 -0
  242. package/dist/index.d.ts +35 -6
  243. package/dist/index.js +38 -4
  244. package/package.json +1 -1
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useEffect, useRef } from "react";
3
- import { Input, InputGroup, InputLeftElement, InputRightElement, Icon, IconButton, Kbd, FormControl, useMergeRefs, } from "@chakra-ui/react";
3
+ import { Input, InputGroup, InputLeftElement, InputRightElement, Icon, IconButton, FormControl, useMergeRefs, } from "@chakra-ui/react";
4
4
  import { Search as SearchIconComponent, X } from "lucide-react";
5
+ import Kbd from "../Kbd/Kbd.js";
5
6
  import { TextLabel } from "../Common/FormLabel.js";
6
7
  import FieldMessage from "../Common/FieldMessage.js";
7
8
  import { useFieldIds } from "../Common/useFieldIds.js";
@@ -44,6 +45,6 @@ const Search = forwardRef(function Search({ placeholder = "Search...", onSearch,
44
45
  ps: spec.height,
45
46
  pe: rightPadding,
46
47
  ...reducedMotionSx(),
47
- } }), (showClear || showHint) && (_jsxs(InputRightElement, { h: spec.height, w: "auto", pe: spec.paddingX, gap: 1, children: [showClear && (_jsx(IconButton, { "aria-label": "Clear search", icon: _jsx(X, { size: spec.iconSize }), variant: "ghost", size: "xs", h: clearSize, minW: clearSize, color: theme.colors.textMuted, onClick: handleClear, sx: focusVisibleStyles(theme.colors) })), showHint && (_jsx(Kbd, { "aria-hidden": true, fontSize: "0.6875rem", fontWeight: 500, color: theme.colors.textMuted, bg: theme.colors.backgroundColor.subtle, borderColor: theme.colors.boxborder[200], whiteSpace: "nowrap", children: shortcut }))] }))] }), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
48
+ } }), (showClear || showHint) && (_jsxs(InputRightElement, { h: spec.height, w: "auto", pe: spec.paddingX, gap: 1, children: [showClear && (_jsx(IconButton, { "aria-label": "Clear search", icon: _jsx(X, { size: spec.iconSize }), variant: "ghost", size: "xs", h: clearSize, minW: clearSize, color: theme.colors.textMuted, onClick: handleClear, sx: focusVisibleStyles(theme.colors) })), showHint && (_jsx("span", { "aria-hidden": true, children: _jsx(Kbd, { size: "xs", children: shortcut }) }))] }))] }), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
48
49
  });
49
50
  export default Search;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Flex, Text, VisuallyHidden, useTheme } from "@chakra-ui/react";
4
+ import Select from "../Select/Select.js";
5
+ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
+ import { focusVisibleRing } from "../../Theme/chakra/focusRing.styles.js";
7
+ import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
8
+ import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
9
+ import { reducedMotionSx } from "../../Theme/chakra/motion.js";
10
+ const FALLBACK_BREAKPOINTS = { sm: "30em", md: "48em", lg: "62em" };
11
+ const matchesMinWidth = (query) => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(query).matches : true;
12
+ const useMinWidth = (minWidth) => {
13
+ const query = `(min-width: ${minWidth})`;
14
+ const [matches, setMatches] = useState(() => matchesMinWidth(query));
15
+ useEffect(() => {
16
+ var _a;
17
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function")
18
+ return undefined;
19
+ const list = window.matchMedia(query);
20
+ const update = () => setMatches(list.matches);
21
+ update();
22
+ (_a = list.addEventListener) === null || _a === void 0 ? void 0 : _a.call(list, "change", update);
23
+ return () => { var _a; return (_a = list.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(list, "change", update); };
24
+ }, [query]);
25
+ return matches;
26
+ };
27
+ const SectionRail = ({ sections, activeId, onSelect, "aria-label": ariaLabel, dirtyHint, collapseBelow = "md", }) => {
28
+ var _a;
29
+ const { colors } = useCustomTheme();
30
+ const { breakpoints } = useTheme();
31
+ const isRail = useMinWidth((_a = breakpoints === null || breakpoints === void 0 ? void 0 : breakpoints[collapseBelow]) !== null && _a !== void 0 ? _a : FALLBACK_BREAKPOINTS[collapseBelow]);
32
+ const rows = listRowBackgrounds(colors);
33
+ if (!isRail) {
34
+ return (_jsx(Select, { "aria-label": ariaLabel, width: "100%", value: activeId, options: sections.map((section) => ({
35
+ id: section.id,
36
+ label: section.isDirty ? `${section.label} (${dirtyHint})` : section.label,
37
+ })), onChange: (option) => option && onSelect(String(option.id)) }));
38
+ }
39
+ return (_jsx(Flex, { as: "nav", "aria-label": ariaLabel, direction: "column", gap: "2px", children: sections.map((section) => {
40
+ const isActive = section.id === activeId;
41
+ return (_jsxs(Flex, { as: "button", type: "button", align: "center", gap: 2, minH: "2rem", px: 3, py: 1.5, borderRadius: "md", fontSize: "sm", fontWeight: isActive ? 600 : 500, textAlign: "left", "aria-current": isActive ? "true" : undefined, bg: isActive ? rows.selected : "transparent", color: isActive ? colors.accentText : colors.text[800], transition: transitionFor(["background-color", "color"]), _hover: { bg: isActive ? rows.selectedActive : rows.active }, _focusVisible: focusVisibleRing(colors), sx: reducedMotionSx(), onClick: () => onSelect(section.id), children: [section.icon ? (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", flexShrink: 0, children: section.icon })) : null, _jsx(Text, { as: "span", noOfLines: 1, flex: "1", minW: 0, children: section.label }), section.isDirty ? (_jsxs(_Fragment, { children: [_jsx(Box, { as: "span", "aria-hidden": true, boxSize: "6px", borderRadius: "full", flexShrink: 0, bg: colors.semanticText.warning }), _jsx(VisuallyHidden, { children: dirtyHint })] })) : null] }, section.id));
42
+ }) }));
43
+ };
44
+ export default SectionRail;
@@ -0,0 +1 @@
1
+ export { default as SectionRail } from "./SectionRail.js";
@@ -5,13 +5,13 @@ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
5
5
  import { resolveFilledSurface } from "../../Theme/tokens/builders/filledSurface.js";
6
6
  import { BUTTON_SIZES } from "../../Theme/tokens/primitives/controlSizes.js";
7
7
  import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
8
- import { FOCUS_VISIBLE_OVER_STATE } from "../../Theme/chakra/focusRing.styles.js";
8
+ import { FOCUS_VISIBLE_OVER_STATE, focusRingShadow, focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
9
9
  import { reducedMotionSx } from "../../Theme/chakra/motion.js";
10
10
  import { AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
11
11
  import { liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
12
12
  import Tag from "../Tag/Tag.js";
13
13
  import { useSlidingPill } from "./useSlidingPill.js";
14
- import { segmentedFocusShadow, segmentedPlateEdge, segmentedRestInk, segmentedSelectedSurface, segmentedTrackSurface, } from "./segmentedInk.js";
14
+ import { segmentedPlateEdge, segmentedRestInk, segmentedSelectedSurface, segmentedTrackSurface, } from "./segmentedInk.js";
15
15
  const TRACK_PADDING = "0.125rem";
16
16
  const UNDERLINE_WIDTH = "0.0625rem";
17
17
  const SLIDE = transitionFor(["transform", "width"], "base");
@@ -37,18 +37,17 @@ export default function SegmentedControl({ options, value, onChange, size = "sm"
37
37
  const segmentH = `calc(${control.height} - ${isUnderlineSlide ? UNDERLINE_WIDTH : `2 * ${TRACK_PADDING}`})`;
38
38
  // Concentric: an inset child keeping the parent's radius bulges at the corners.
39
39
  const innerRadius = isUnderlineSlide ? "none" : `calc(${radii.lg} - ${TRACK_PADDING})`;
40
- const focusShadow = segmentedFocusShadow(colors, trackSurface);
41
40
  // Inset, never a border: a border consumes the content box and the absolute pill stops centring.
42
41
  const plateEdge = isBrandSlide
43
42
  ? `inset 0 0 0 1px ${segmentedPlateEdge(colors, surface)}`
44
43
  : undefined;
45
- // The transparent outline replaces the UA ring and is what Windows high-contrast repaints.
46
- const segmentFocus = {
47
- outline: "2px solid transparent",
48
- outlineOffset: "2px",
49
- // underlineSlide has no track chrome to carry the indicator, so there the segment wears it.
50
- ...(isUnderlineSlide ? { boxShadow: focusShadow } : {}),
51
- };
44
+ // underlineSlide has no track chrome to carry the ring, so there the segment wears Button's own.
45
+ const noRing = { outline: "2px solid transparent", outlineOffset: "2px" };
46
+ const segmentFocus = isUnderlineSlide
47
+ ? focusVisibleStyles(colors)
48
+ : { _focusVisible: noRing, [FOCUS_VISIBLE_OVER_STATE]: noRing };
49
+ const ring = focusRingShadow(colors);
50
+ const trackFocusShadow = plateEdge ? `${plateEdge}, ${ring}` : ring;
52
51
  const [showFocus, setShowFocus] = useState(false);
53
52
  const selectedIndex = options.findIndex((o) => o.value === value);
54
53
  const enabled = options.reduce((acc, o, i) => (o.isDisabled ? acc : [...acc, i]), []);
@@ -90,7 +89,7 @@ export default function SegmentedControl({ options, value, onChange, size = "sm"
90
89
  selectAt(enabled[enabled.length - 1]);
91
90
  }
92
91
  };
93
- return (_jsxs(Flex, { ref: trackRef, role: "radiogroup", "aria-label": ariaLabel, "aria-orientation": isVertical ? "vertical" : undefined, onKeyDown: handleKeyDown, onFocus: (event) => setShowFocus(event.target.matches(":focus-visible")), onBlur: () => setShowFocus(false), boxShadow: showFocus && !isUnderlineSlide ? focusShadow : plateEdge, position: "relative", display: "inline-flex", flexDirection: isVertical ? "column" : "row", align: isVertical ? "stretch" : "center", gap: isUnderlineSlide ? "0" : TRACK_PADDING, p: isUnderlineSlide ? "0" : TRACK_PADDING, h: isVertical ? "fit-content" : control.height, boxSizing: "border-box", borderRadius: isUnderlineSlide ? "none" : "lg", borderBottom: isUnderlineSlide && !isVertical ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, borderLeft: isUnderlineSlide && isVertical ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, bg: isUnderlineSlide ? "transparent" : trackSurface, w: isFullWidth ? "100%" : "fit-content", minW: "max-content", children: [isBrandSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", top: isVertical ? 0 : TRACK_PADDING, left: isVertical ? TRACK_PADDING : 0, right: isVertical ? TRACK_PADDING : undefined, h: segmentH, w: isVertical ? undefined : `${pill.width}px`, borderRadius: innerRadius, bg: brandFill.fill, boxShadow: `0 0.125rem 0.5rem ${colors.boxShadow.primary}`, transform: isVertical ? `translateY(${pill.top}px)` : `translateX(${pill.left}px)`, transition: SLIDE, sx: reducedMotionSx() })), isUnderlineSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", bottom: isVertical ? undefined : "-0.0625rem", left: isVertical ? "-0.0625rem" : 0, top: isVertical ? 0 : undefined, h: isVertical ? segmentH : "0.125rem", w: isVertical ? "0.125rem" : `${pill.width}px`, borderRadius: "full", bg: colors.primary[500], transform: isVertical ? `translateY(${pill.top}px)` : `translateX(${pill.left}px)`, transition: SLIDE, sx: reducedMotionSx() })), options.map((option, index) => {
92
+ return (_jsxs(Flex, { ref: trackRef, role: "radiogroup", "aria-label": ariaLabel, "aria-orientation": isVertical ? "vertical" : undefined, onKeyDown: handleKeyDown, onFocus: (event) => setShowFocus(event.target.matches(":focus-visible")), onBlur: () => setShowFocus(false), boxShadow: showFocus && !isUnderlineSlide ? trackFocusShadow : plateEdge, position: "relative", display: "inline-flex", flexDirection: isVertical ? "column" : "row", align: isVertical ? "stretch" : "center", gap: isUnderlineSlide ? "0" : TRACK_PADDING, p: isUnderlineSlide ? "0" : TRACK_PADDING, h: isVertical ? "fit-content" : control.height, boxSizing: "border-box", borderRadius: isUnderlineSlide ? "none" : "lg", borderBottom: isUnderlineSlide && !isVertical ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, borderLeft: isUnderlineSlide && isVertical ? `${UNDERLINE_WIDTH} solid ${colors.boxborder[200]}` : undefined, bg: isUnderlineSlide ? "transparent" : trackSurface, w: isFullWidth ? "100%" : "fit-content", minW: "max-content", children: [isBrandSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", top: isVertical ? 0 : TRACK_PADDING, left: isVertical ? TRACK_PADDING : 0, right: isVertical ? TRACK_PADDING : undefined, h: segmentH, w: isVertical ? undefined : `${pill.width}px`, borderRadius: innerRadius, bg: brandFill.fill, boxShadow: `0 0.125rem 0.5rem ${colors.boxShadow.primary}`, transform: isVertical ? `translateY(${pill.top}px)` : `translateX(${pill.left}px)`, transition: SLIDE, sx: reducedMotionSx() })), isUnderlineSlide && pill && (_jsx(Box, { "aria-hidden": "true", position: "absolute", bottom: isVertical ? undefined : "-0.0625rem", left: isVertical ? "-0.0625rem" : 0, top: isVertical ? 0 : undefined, h: isVertical ? segmentH : "0.125rem", w: isVertical ? "0.125rem" : `${pill.width}px`, borderRadius: "full", bg: colors.primary[500], transform: isVertical ? `translateY(${pill.top}px)` : `translateX(${pill.left}px)`, transition: SLIDE, sx: reducedMotionSx() })), options.map((option, index) => {
94
93
  const isSelected = option.value === value;
95
94
  const showShortLabel = isCompact && !!option.shortLabel;
96
95
  const displayLabel = showShortLabel ? option.shortLabel : option.label;
@@ -110,10 +109,6 @@ export default function SegmentedControl({ options, value, onChange, size = "sm"
110
109
  : restInk
111
110
  : isSelected
112
111
  ? selectedInk
113
- : restInk, fontSize: control.fontSize, fontWeight: isSelected ? 600 : 500, whiteSpace: "nowrap", opacity: option.isDisabled ? 0.5 : 1, cursor: option.isDisabled ? "not-allowed" : "pointer", transition: transitionFor(["color"]), _hover: option.isDisabled || isSelected ? undefined : { color: hoverInk }, sx: {
114
- _focusVisible: segmentFocus,
115
- [FOCUS_VISIBLE_OVER_STATE]: segmentFocus,
116
- ...reducedMotionSx(),
117
- }, children: [option.icon, displayLabel, option.count != null && (_jsx(Tag, { label: formatCount(option.count), size: COUNT_TAG_SIZE[size], variant: "subtle", colorScheme: "gray" }))] }, option.value));
112
+ : restInk, fontSize: control.fontSize, fontWeight: isSelected ? 600 : 500, whiteSpace: "nowrap", opacity: option.isDisabled ? 0.5 : 1, cursor: option.isDisabled ? "not-allowed" : "pointer", transition: transitionFor(["color"]), _hover: option.isDisabled || isSelected ? undefined : { color: hoverInk }, sx: { ...segmentFocus, ...reducedMotionSx() }, children: [option.icon, displayLabel, option.count != null && (_jsx(Tag, { label: formatCount(option.count), size: COUNT_TAG_SIZE[size], variant: "subtle", colorScheme: "gray" }))] }, option.value));
118
113
  })] }));
119
114
  }
@@ -1,4 +1,3 @@
1
- import { focusGlowShadow } from "../../Theme/chakra/focusRing.styles.js";
2
1
  import { AA_NON_TEXT, liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
3
2
  import { AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
4
3
  /** The surface a segment's label really sits on. */
@@ -15,8 +14,6 @@ export const segmentedTrackSurface = (variant, colors, surface) => {
15
14
  export const segmentedSelectedSurface = (variant, colors, trackSurface) => (variant === "default" ? colors.backgroundColor.light : trackSurface);
16
15
  /** Unselected label ink, lifted to AA against the track it is drawn on. */
17
16
  export const segmentedRestInk = (colors, trackSurface) => liftToContrast(colors.text[600], trackSurface, AA_NORMAL_TEXT);
18
- /** A radiogroup is one tab stop, so the group wears the ring: a brand hairline plus the field bloom. */
19
- export const segmentedFocusShadow = (colors, trackSurface) => `inset 0 0 0 1px ${liftToContrast(colors.primary[500], trackSurface, AA_NON_TEXT)}, ${focusGlowShadow(colors)}`;
20
17
  /** The plate can match its backdrop's lightness, so its edge carries the boundary.
21
18
  * Drawn inset by the caller: a real border consumes the content box and decentres the sliding pill. */
22
19
  export const segmentedPlateEdge = (colors, surface) => liftToContrast(colors.boxborder[500], surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main, AA_NON_TEXT);
@@ -4,6 +4,7 @@ import { Box, Flex, Icon, VStack, Badge, Popover, PopoverTrigger, PopoverContent
4
4
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
5
5
  import { getSidebarActiveAccent } from "./sidebarAccentColors.js";
6
6
  import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
7
+ import { sidebarFocusStyles } from "./sidebarFocus.js";
7
8
  import { menuDisabled } from "./menuDisabled.js";
8
9
  import { ChevronLeft, ChevronRight, LockKeyhole } from "lucide-react";
9
10
  import OverflowTooltipText from "./components/OverflowTooltipText.js";
@@ -46,7 +47,7 @@ const CollapsedItem = ({ item, isActive, itemFontSize, onItemClick }) => {
46
47
  e.preventDefault();
47
48
  if (!blocked)
48
49
  onItemClick === null || onItemClick === void 0 ? void 0 : onItemClick(item);
49
- }, bg: isActive ? "whiteAlpha.300" : "transparent", transition: "background 0.12s", children: _jsxs(Box, { position: "relative", display: "flex", alignItems: "center", children: [_jsx(Icon, { as: item.icon, fontSize: SIDEBAR_ROW.iconSize, color: isActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" }), badgeVal != null && (_jsx(Box, { position: "absolute", top: "0", right: "0", w: "0.375rem", h: "0.375rem", borderRadius: "full", bg: theme.colors.sidebar.semanticText.error }))] }) }) }), _jsx(Portal, { children: _jsx(PopoverContent, { borderRadius: "lg", bg: theme.colors.sidebar.elevated, boxShadow: "lg", minW: "10rem", maxW: "14rem", py: "0.125rem", ml: "-0.5rem", cursor: blocked ? "not-allowed" : "pointer", zIndex: 1500, color: theme.colors.white, border: 'none', children: _jsx(PopoverBody, { p: 3, children: _jsxs(Flex, { align: "center", gap: 2, justify: "space-between", children: [_jsxs(Flex, { align: "center", gap: 2, flex: "1", minW: 0, children: [_jsx(Icon, { as: item.icon, fontSize: "md", flexShrink: 0 }), _jsx(OverflowTooltipText, { placement: "right", maxWidth: "100%", fontSize: itemFontSize, fontWeight: "medium", as: "p", children: item.title })] }), item.locked ? (item.lockedReason ? (_jsx(ToolTip, { label: item.lockedReason, placement: "top", children: _jsx(Icon, { as: LockKeyhole, fontSize: "sm", flexShrink: 0, color: theme.colors.sidebar.mutedIcon }) })) : (_jsx(Icon, { as: LockKeyhole, fontSize: "sm", flexShrink: 0, color: theme.colors.sidebar.mutedIcon }))) : (badgeVal != null && (_jsx(Badge, { ...softBadge(theme.colors, colorScheme, sidebarRowPeak(theme.colors.sidebar.elevated)), fontSize: "xs", variant: variant, borderRadius: radius, children: badgeVal })))] }) }) }) })] }));
50
+ }, bg: isActive ? "whiteAlpha.300" : "transparent", transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), children: _jsxs(Box, { position: "relative", display: "flex", alignItems: "center", children: [_jsx(Icon, { as: item.icon, fontSize: SIDEBAR_ROW.iconSize, color: isActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" }), badgeVal != null && (_jsx(Box, { position: "absolute", top: "0", right: "0", w: "0.375rem", h: "0.375rem", borderRadius: "full", bg: theme.colors.sidebar.semanticText.error }))] }) }) }), _jsx(Portal, { children: _jsx(PopoverContent, { borderRadius: "lg", bg: theme.colors.sidebar.elevated, boxShadow: "lg", minW: "10rem", maxW: "14rem", py: "0.125rem", ml: "-0.5rem", cursor: blocked ? "not-allowed" : "pointer", zIndex: 1500, color: theme.colors.white, border: 'none', children: _jsx(PopoverBody, { p: 3, children: _jsxs(Flex, { align: "center", gap: 2, justify: "space-between", children: [_jsxs(Flex, { align: "center", gap: 2, flex: "1", minW: 0, children: [_jsx(Icon, { as: item.icon, fontSize: "md", flexShrink: 0 }), _jsx(OverflowTooltipText, { placement: "right", maxWidth: "100%", fontSize: itemFontSize, fontWeight: "medium", as: "p", children: item.title })] }), item.locked ? (item.lockedReason ? (_jsx(ToolTip, { label: item.lockedReason, placement: "top", children: _jsx(Icon, { as: LockKeyhole, fontSize: "sm", flexShrink: 0, color: theme.colors.sidebar.mutedIcon }) })) : (_jsx(Icon, { as: LockKeyhole, fontSize: "sm", flexShrink: 0, color: theme.colors.sidebar.mutedIcon }))) : (badgeVal != null && (_jsx(Badge, { ...softBadge(theme.colors, colorScheme, sidebarRowPeak(theme.colors.sidebar.elevated)), fontSize: "xs", variant: variant, borderRadius: radius, children: badgeVal })))] }) }) }) })] }));
50
51
  };
51
52
  const ExpandedItem = ({ item, isActive, itemFontSize, onItemClick }) => {
52
53
  var _a;
@@ -60,7 +61,7 @@ const ExpandedItem = ({ item, isActive, itemFontSize, onItemClick }) => {
60
61
  e.preventDefault();
61
62
  if (!blocked)
62
63
  onItemClick === null || onItemClick === void 0 ? void 0 : onItemClick(item);
63
- }, borderLeft: "2px solid", borderLeftColor: isActive ? theme.colors.sidebar.activeIcon : "transparent", transition: "background 0.12s", children: [_jsxs(Flex, { align: "center", gap: 2, flex: "1", minW: 0, overflow: "hidden", children: [_jsx(Icon, { as: item.icon, fontSize: SIDEBAR_ROW.iconSize, flexShrink: 0, color: isActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" }), _jsx(Box, { flex: "1", minW: 0, children: _jsx(OverflowTooltipText, { placement: "right", maxWidth: "100%", fontSize: itemFontSize, fontWeight: isActive ? 600 : 500, color: isActive ? activeAccent.label : theme.colors.white, _groupHover: isActive ? undefined : { color: theme.colors.white }, as: "p", children: item.title }) })] }), item.locked ? (_jsx(Icon, { as: LockKeyhole, fontSize: "sm", flexShrink: 0, color: theme.colors.sidebar.mutedIcon })) : (badgeVal != null && (_jsx(Badge, { ...softBadge(theme.colors, colorScheme, sidebarRowPeak(theme.colors.sidebar.background[500])), fontSize: "xs", variant: variant, borderRadius: radius, children: badgeVal })))] }));
64
+ }, borderLeft: "2px solid", borderLeftColor: isActive ? theme.colors.sidebar.activeIcon : "transparent", transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), children: [_jsxs(Flex, { align: "center", gap: 2, flex: "1", minW: 0, overflow: "hidden", children: [_jsx(Icon, { as: item.icon, fontSize: SIDEBAR_ROW.iconSize, flexShrink: 0, color: isActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" }), _jsx(Box, { flex: "1", minW: 0, children: _jsx(OverflowTooltipText, { placement: "right", maxWidth: "100%", fontSize: itemFontSize, fontWeight: isActive ? 600 : 500, color: isActive ? activeAccent.label : theme.colors.white, _groupHover: isActive ? undefined : { color: theme.colors.white }, as: "p", children: item.title }) })] }), item.locked ? (_jsx(Icon, { as: LockKeyhole, fontSize: "sm", flexShrink: 0, color: theme.colors.sidebar.mutedIcon })) : (badgeVal != null && (_jsx(Badge, { ...softBadge(theme.colors, colorScheme, sidebarRowPeak(theme.colors.sidebar.background[500])), fontSize: "xs", variant: variant, borderRadius: radius, children: badgeVal })))] }));
64
65
  return item.locked && item.lockedReason ? (_jsx(ToolTip, { label: item.lockedReason, placement: "top", children: row })) : (row);
65
66
  };
66
67
  const SecondaryBar = ({ items = [], defaultCollapsed = true, collapsedWidth = "3.5rem", expandedWidth = "10rem", headerFontSize = SIDEBAR_ROW.fontSize, itemFontSize = SIDEBAR_ROW.fontSize, selected, onItemClick, title, icon, }) => {
@@ -69,7 +70,7 @@ const SecondaryBar = ({ items = [], defaultCollapsed = true, collapsedWidth = "3
69
70
  const scrollContainerId = sanitizeId(useId());
70
71
  const [collapsed, setCollapsed] = useState(defaultCollapsed);
71
72
  const width = collapsed ? collapsedWidth : expandedWidth;
72
- return (_jsx(Box, { as: "nav", "aria-label": title ? `${title} navigation` : "Secondary navigation", transition: transitionFor(["width"], "base"), pos: "sticky", top: 0, sx: fullViewportHeight, w: width, bg: theme.colors.sidebar.background[500], borderLeft: "1px solid", borderColor: theme.colors.sidebar.divider, boxShadow: theme.shadows.md, zIndex: 1, children: _jsxs(VStack, { align: "stretch", spacing: 0, h: "100%", children: [_jsxs(Flex, { align: "center", justify: collapsed ? "center" : "space-between", px: collapsed ? 0 : "10px", gap: "7px", h: HEADER_HEIGHT, overflow: "hidden", borderBottom: "1px solid", borderColor: theme.colors.sidebar.divider, flexShrink: 0, children: [!collapsed && (_jsxs(_Fragment, { children: [icon && (_jsx(Icon, { as: icon, fontSize: SIDEBAR_ROW.iconSize, color: activeAccent.icon, flexShrink: 0 })), _jsx(Box, { flex: "1", minW: 0, children: _jsx(OverflowTooltipText, { placement: "right", maxWidth: "100%", fontSize: headerFontSize, fontWeight: 600, color: theme.colors.white, as: "p", children: title !== null && title !== void 0 ? title : "" }) })] })), _jsx(Box, { as: "button", type: "button", "aria-label": collapsed ? "Expand navigation panel" : "Collapse navigation panel", "aria-expanded": !collapsed, border: "none", bg: "transparent", w: "24px", h: "24px", borderRadius: "lg", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", flexShrink: 0, onClick: () => setCollapsed((c) => !c), _hover: { bg: "whiteAlpha.200" }, transition: "background 0.15s", children: collapsed ? (_jsx(ToolTip, { label: title !== null && title !== void 0 ? title : "", placement: "right", children: _jsx(ChevronRight, { size: 13, color: theme.colors.sidebar.mutedIcon }) })) : (_jsx(ChevronLeft, { size: 13, color: theme.colors.sidebar.mutedIcon })) })] }), _jsx(Box, { id: scrollContainerId, flex: "1", minH: 0, overflowY: "auto", overflowX: "hidden", py: 2, px: "6px", sx: cssForScrollBar(theme), children: items.map((item) => {
73
+ return (_jsx(Box, { as: "nav", "aria-label": title ? `${title} navigation` : "Secondary navigation", transition: transitionFor(["width"], "base"), pos: "sticky", top: 0, sx: fullViewportHeight, w: width, bg: theme.colors.sidebar.background[500], borderLeft: "1px solid", borderColor: theme.colors.sidebar.divider, boxShadow: theme.shadows.md, zIndex: 1, children: _jsxs(VStack, { align: "stretch", spacing: 0, h: "100%", children: [_jsxs(Flex, { align: "center", justify: collapsed ? "center" : "space-between", px: collapsed ? 0 : "10px", gap: "7px", h: HEADER_HEIGHT, overflow: "hidden", borderBottom: "1px solid", borderColor: theme.colors.sidebar.divider, flexShrink: 0, children: [!collapsed && (_jsxs(_Fragment, { children: [icon && (_jsx(Icon, { as: icon, fontSize: SIDEBAR_ROW.iconSize, color: activeAccent.icon, flexShrink: 0 })), _jsx(Box, { flex: "1", minW: 0, children: _jsx(OverflowTooltipText, { placement: "right", maxWidth: "100%", fontSize: headerFontSize, fontWeight: 600, color: theme.colors.white, as: "p", children: title !== null && title !== void 0 ? title : "" }) })] })), _jsx(Box, { as: "button", type: "button", "aria-label": collapsed ? "Expand navigation panel" : "Collapse navigation panel", "aria-expanded": !collapsed, border: "none", bg: "transparent", w: "24px", h: "24px", borderRadius: "lg", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", flexShrink: 0, onClick: () => setCollapsed((c) => !c), _hover: { bg: "whiteAlpha.200" }, transition: "background 0.15s", sx: sidebarFocusStyles(theme.colors), children: collapsed ? (_jsx(ToolTip, { label: title !== null && title !== void 0 ? title : "", placement: "right", children: _jsx(ChevronRight, { size: 13, color: theme.colors.sidebar.mutedIcon }) })) : (_jsx(ChevronLeft, { size: 13, color: theme.colors.sidebar.mutedIcon })) })] }), _jsx(Box, { id: scrollContainerId, flex: "1", minH: 0, overflowY: "auto", overflowX: "hidden", py: 2, px: "6px", sx: cssForScrollBar(theme), children: items.map((item) => {
73
74
  var _a;
74
75
  const isActive = String(selected) === String(item.id) ||
75
76
  String(selected) === String(item.title);
@@ -7,7 +7,8 @@ import { sanitizeId } from "./sidebarId.js";
7
7
  import { cssForScrollBar, HEADER_HEIGHT } from "../../Constants/Sidebar.js";
8
8
  import { overlayScrimStyles } from "../../Theme/chakra/overlay.styles.js";
9
9
  import { REDUCED_MOTION_QUERY } from "../../Theme/chakra/motion.js";
10
- import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
10
+ import { sidebarFocusStyles } from "./sidebarFocus.js";
11
+ import { MOTION_EASE, MOTION_MS } from "../../Theme/tokens/primitives/motion.js";
11
12
  import { ArrowLeftToLine, ArrowRightToLine, ChevronLeft, Maximize2, MoreVertical, PanelLeft, Plus, X } from "lucide-react";
12
13
  // Overlay stacking — mirrors theme.zIndices (overlay=1300 scrim, modal=1400 panel).
13
14
  const Z_SCRIM = 1300;
@@ -18,6 +19,18 @@ const Z_PANEL = 1400;
18
19
  const RAIL_WIDTH = "4.688rem";
19
20
  const PANEL_EASE = "cubic-bezier(0.22, 1, 0.36, 1)";
20
21
  const EXPANDED_WIDTH = "13rem";
22
+ const COLLAPSE_MS = { ...MOTION_MS, slow: 260 };
23
+ const NO_ACTIVE_MENU = "\u0000pinned-active";
24
+ const inlineTransitions = (collapse) => {
25
+ var _a;
26
+ if (collapse === "none")
27
+ return { width: "none", shadow: "none" };
28
+ const ms = COLLAPSE_MS[(_a = collapse.speed) !== null && _a !== void 0 ? _a : "slow"];
29
+ return {
30
+ width: `width ${ms}ms ${PANEL_EASE}`,
31
+ shadow: collapse.animateShadow === false ? "none" : `box-shadow ${ms}ms ${MOTION_EASE}`,
32
+ };
33
+ };
21
34
  const OVERLAY_WIDTH = "14.7rem";
22
35
  const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
23
36
  import MenuItems from "./components/MenuItems.js";
@@ -25,6 +38,7 @@ import DefaultLogo from "../../Assets/defaultLogo.js";
25
38
  import OverflowTooltipText from "./components/OverflowTooltipText.js";
26
39
  import SecondaryBar from "./SecondaryBar.js";
27
40
  import OtherApps from "./components/OtherApps.js";
41
+ import PinnedSection from "./components/PinnedSection.js";
28
42
  import { SIDEBAR_CHROME, SIDEBAR_ROW } from "./sidebarScale.js";
29
43
  import { fullViewportHeight } from "../../Theme/tokens/builders/viewportHeight.js";
30
44
  const SidebarHeader = ({ logo, companyName, companySubtitle, toggle, isLoading, onToggle, overlay, onClose, }) => {
@@ -41,6 +55,7 @@ const SidebarHeader = ({ logo, companyName, companySubtitle, toggle, isLoading,
41
55
  // some brands (zinc), making the tile disappear. See THEME-CONTRACT.md sidebar rules.
42
56
  bg: theme.colors.sidebar.activeBg, color: activeAccent.label, borderRadius: "lg", boxSize: "1.75rem", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: "bold", flexShrink: 0, cursor: toggle ? "pointer" : undefined, onClick: toggle ? onToggle : undefined, children: _jsx(DefaultLogo, {}) }));
43
57
  return (_jsxs(Flex, { h: HEADER_HEIGHT, w: "100%", alignItems: "center", justifyContent: toggle ? "center" : "flex-start", gap: 2, px: toggle ? "8px" : "10px", borderBottom: "1px solid", borderColor: theme.colors.sidebar.divider, minW: 0, overflow: "hidden", flexShrink: 0, children: [logoNode, !toggle && (_jsxs(Box, { flex: "1", minW: 0, children: [_jsx(OverflowTooltipText, { placement: "right", fontWeight: 600, fontSize: SIDEBAR_CHROME.titleFontSize, textAlign: "left", color: theme.colors.white, lineHeight: "1.3", as: "p", width: "100%", children: companyName !== null && companyName !== void 0 ? companyName : "" }), companySubtitle && (_jsx(Text, { fontSize: SIDEBAR_CHROME.metaFontSize, color: theme.colors.sidebar.mutedLabel, lineHeight: "1.3", isTruncated: true, children: companySubtitle }))] })), !toggle && (_jsx(Box, { as: "button", type: "button", onClick: overlay ? onClose : onToggle, "aria-label": overlay ? "Close menu" : "Collapse sidebar", w: "22px", h: "22px", borderRadius: "lg", bg: "whiteAlpha.200", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, position: "relative", _hover: { bg: "whiteAlpha.300" }, transition: "background 0.15s", sx: {
58
+ ...sidebarFocusStyles(theme.colors),
44
59
  "&::after": {
45
60
  content: '""',
46
61
  position: "absolute",
@@ -59,7 +74,7 @@ const SkeletonMenuRow = ({ toggle }) => {
59
74
  const AddNewButton = ({ toggle, onClick, overlay }) => {
60
75
  const theme = useCustomTheme();
61
76
  const activeAccent = getSidebarActiveAccent(theme.colors);
62
- return (_jsxs(Flex, { as: "button", type: "button", "data-group": "", "aria-label": "Add New", alignItems: "center", gap: "9px", px: "10px", py: overlay ? "11px" : "7px", borderRadius: "lg", cursor: "pointer", border: "none", bg: "transparent", w: "100%", mb: "2px", justifyContent: toggle ? "center" : "flex-start", _hover: { backgroundColor: "whiteAlpha.200" }, transition: "background 0.12s", onClick: onClick, children: [_jsx(Box, { w: "18px", h: "18px", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: _jsx(Plus, { size: 16, color: activeAccent.icon }) }), !toggle && (_jsx(Text, { fontSize: SIDEBAR_ROW.fontSize, color: theme.colors.white, fontWeight: "medium", children: "Add New" }))] }));
77
+ return (_jsxs(Flex, { as: "button", type: "button", "data-group": "", "aria-label": "Add New", alignItems: "center", gap: "9px", px: "10px", py: overlay ? "11px" : "7px", borderRadius: "lg", cursor: "pointer", border: "none", bg: "transparent", w: "100%", mb: "2px", justifyContent: toggle ? "center" : "flex-start", _hover: { backgroundColor: "whiteAlpha.200" }, transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), onClick: onClick, children: [_jsx(Box, { w: "18px", h: "18px", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: _jsx(Plus, { size: 16, color: activeAccent.icon }) }), !toggle && (_jsx(Text, { fontSize: SIDEBAR_ROW.fontSize, color: theme.colors.white, fontWeight: "medium", children: "Add New" }))] }));
63
78
  };
64
79
  const deriveInitials = (user) => {
65
80
  if (user.initials)
@@ -77,13 +92,13 @@ const UserFooter = ({ user, toggle, overlay }) => {
77
92
  const avatar = user.avatarUrl ? (_jsx(Image, { src: user.avatarUrl, alt: user.name, boxSize: "26px", borderRadius: "full", objectFit: "cover", flexShrink: 0 })) : (_jsx(Box, { boxSize: "26px", borderRadius: "full",
78
93
  // Same rail-safe pairing as the logo tile — primary.opacity[32] was invisible on zinc/skyline.
79
94
  bg: theme.colors.sidebar.activeBg, color: activeAccent.label, display: "flex", alignItems: "center", justifyContent: "center", fontSize: SIDEBAR_CHROME.metaFontSize, fontWeight: 600, flexShrink: 0, children: deriveInitials(user) }));
80
- return (_jsx(Box, { borderTop: "1px solid", borderColor: theme.colors.sidebar.divider, py: 2, px: "6px", flexShrink: 0, children: _jsxs(Flex, { as: "button", type: "button", onClick: user.onClick, align: "center", gap: 2, px: 2, py: overlay ? "10px" : "6px", w: "100%", borderRadius: "lg", cursor: "pointer", bg: "transparent", border: "none", justify: toggle ? "center" : "flex-start", _hover: { bg: "whiteAlpha.200" }, transition: "background 0.12s", children: [avatar, !toggle && (_jsxs(Box, { flex: "1", minW: 0, textAlign: "left", children: [_jsx(Text, { fontSize: SIDEBAR_CHROME.labelFontSize, fontWeight: 500, color: theme.colors.white, isTruncated: true, children: user.name }), user.role && (_jsx(Text, { fontSize: SIDEBAR_CHROME.metaFontSize, color: theme.colors.sidebar.mutedLabel, isTruncated: true, children: user.role }))] })), !toggle && _jsx(MoreVertical, { size: 14, color: theme.colors.sidebar.mutedIcon })] }) }));
95
+ return (_jsx(Box, { borderTop: "1px solid", borderColor: theme.colors.sidebar.divider, py: 2, px: "6px", flexShrink: 0, children: _jsxs(Flex, { as: "button", type: "button", onClick: user.onClick, align: "center", gap: 2, px: 2, py: overlay ? "10px" : "6px", w: "100%", borderRadius: "lg", cursor: "pointer", bg: "transparent", border: "none", justify: toggle ? "center" : "flex-start", _hover: { bg: "whiteAlpha.200" }, transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), children: [avatar, !toggle && (_jsxs(Box, { flex: "1", minW: 0, textAlign: "left", children: [_jsx(Text, { fontSize: SIDEBAR_CHROME.labelFontSize, fontWeight: 500, color: theme.colors.white, isTruncated: true, children: user.name }), user.role && (_jsx(Text, { fontSize: SIDEBAR_CHROME.metaFontSize, color: theme.colors.sidebar.mutedLabel, isTruncated: true, children: user.role }))] })), !toggle && _jsx(MoreVertical, { size: 14, color: theme.colors.sidebar.mutedIcon })] }) }));
81
96
  };
82
97
  // Footer "cell" — one segment of the Collapse | Hide/Show control.
83
98
  const ControlCell = ({ onClick, ariaLabel, label, accent, children }) => {
84
99
  const theme = useCustomTheme();
85
100
  const activeAccent = getSidebarActiveAccent(theme.colors);
86
- return (_jsxs(Flex, { as: "button", type: "button", "aria-label": ariaLabel, flex: "1", minW: 0, alignItems: "center", justifyContent: "center", gap: "7px", px: "10px", py: "14px", cursor: "pointer", border: "none", bg: accent ? "whiteAlpha.300" : "transparent", onClick: onClick, _hover: { bg: accent ? "whiteAlpha.400" : "whiteAlpha.200" }, transition: "background 0.15s", children: [children, label && (_jsx(Text, { fontSize: SIDEBAR_CHROME.labelFontSize, color: accent ? activeAccent.label : theme.colors.sidebar.mutedLabel, fontWeight: "medium", children: label }))] }));
101
+ return (_jsxs(Flex, { as: "button", type: "button", "aria-label": ariaLabel, flex: "1", minW: 0, alignItems: "center", justifyContent: "center", gap: "7px", px: "10px", py: "14px", cursor: "pointer", border: "none", bg: accent ? "whiteAlpha.300" : "transparent", onClick: onClick, _hover: { bg: accent ? "whiteAlpha.400" : "whiteAlpha.200" }, transition: "background 0.15s", sx: sidebarFocusStyles(theme.colors), children: [children, label && (_jsx(Text, { fontSize: SIDEBAR_CHROME.labelFontSize, color: accent ? activeAccent.label : theme.colors.sidebar.mutedLabel, fontWeight: "medium", children: label }))] }));
87
102
  };
88
103
  // Collapse (mini-rail) sits next to the Hide/Show navbar-mode toggle. In the
89
104
  // desktop mobile-nav overlay only "Show" (return to the docked sidebar) is shown;
@@ -102,7 +117,7 @@ const FooterControls = ({ toggle, overlay, mobileNav, onToggleCollapse, onToggle
102
117
  return shell(_jsxs(_Fragment, { children: [_jsx(ControlCell, { onClick: onToggleCollapse, ariaLabel: toggle ? "Expand sidebar" : undefined, label: toggle ? undefined : "Collapse", children: toggle ? (_jsx(ArrowRightToLine, { size: 14, color: iconColor })) : (_jsx(ArrowLeftToLine, { size: 14, color: iconColor })) }), onToggleMobileNav && (_jsx(ControlCell, { onClick: onToggleMobileNav, ariaLabel: toggle ? "Show sidebar in overlay mode" : undefined, label: toggle ? undefined : "Hide", children: _jsx(Maximize2, { size: 14, color: iconColor }) }))] }));
103
118
  };
104
119
  // --- Main Sidebar component ---
105
- export default function Sidebar({ menus, activeMenu, handleMenuClick, handleSecondaryNavClick, toggle, changeToggle, logo, companyName, companySubtitle, user, isDisableAddNew = false, onAddNewClick, selectedSubMenu, selectedSecondaryItem, isLoading, loadingLines, open = true, otherApps, mobileOpen = false, onMobileClose, mobileNav = false, onToggleMobileNav, peekOpen = false, onPeekEnd, }) {
120
+ export default function Sidebar({ menus, activeMenu, handleMenuClick, handleSecondaryNavClick, toggle, changeToggle, logo, companyName, companySubtitle, user, isDisableAddNew = false, onAddNewClick, selectedSubMenu, selectedSecondaryItem, isLoading, loadingLines, open = true, otherApps, mobileOpen = false, onMobileClose, mobileNav = false, onToggleMobileNav, peekOpen = false, onPeekEnd, pinnedItems, pinnedLabel, onUnpin, activePinnedId, pinnedLimit, pinnedLabels, collapseTransition = { speed: "slow" }, autoCollapse = true, }) {
106
121
  var _a, _b;
107
122
  const theme = useCustomTheme();
108
123
  const scrollContainerId = sanitizeId(useId());
@@ -142,20 +157,24 @@ export default function Sidebar({ menus, activeMenu, handleMenuClick, handleSeco
142
157
  const activeTop = menus.find((m) => String(m.title) === String(activeMenu)) ||
143
158
  menus.find((m) => String(m.id) === String(activeMenu));
144
159
  const secondaryItems = (_b = activeTop === null || activeTop === void 0 ? void 0 : activeTop.secondaryItems) !== null && _b !== void 0 ? _b : [];
160
+ const inline = inlineTransitions(collapseTransition);
161
+ const pinnedActive = activePinnedId != null && !!(pinnedItems === null || pinnedItems === void 0 ? void 0 : pinnedItems.some((p) => String(p.id) === String(activePinnedId)));
145
162
  // Sync toggle with external `open` prop.
146
163
  // `open=false` → collapsed (toggle=true); `open=true` → expanded (toggle=false).
147
164
  useEffect(() => {
165
+ if (!autoCollapse)
166
+ return;
148
167
  const shouldBeCollapsed = !open;
149
168
  if (shouldBeCollapsed !== toggleRef.current) {
150
169
  changeToggleRef.current();
151
170
  }
152
- }, [open]);
171
+ }, [open, autoCollapse]);
153
172
  // Auto-collapse the main sidebar whenever a secondary panel is active.
154
173
  useEffect(() => {
155
- if (secondaryItems.length > 0 && !toggleRef.current) {
174
+ if (autoCollapse && secondaryItems.length > 0 && !toggleRef.current) {
156
175
  changeToggleRef.current();
157
176
  }
158
- }, [secondaryItems.length]);
177
+ }, [secondaryItems.length, autoCollapse]);
159
178
  // Esc closes the overlay.
160
179
  useEffect(() => {
161
180
  if (!isOverlayOpen)
@@ -257,12 +276,14 @@ export default function Sidebar({ menus, activeMenu, handleMenuClick, handleSeco
257
276
  base: dragging
258
277
  ? "none"
259
278
  : "transform 0.28s cubic-bezier(0.22, 1, 0.36, 1)",
260
- lg: transitionFor(["box-shadow"], "base"),
279
+ lg: inline.shadow,
261
280
  }, borderRadius: "none", boxShadow: mobileNav ? theme.shadows["2xl"] : { base: theme.shadows["2xl"], lg: theme.shadows.lg }, display: "flex", background: theme.colors.sidebar.background[500], borderRight: "1px solid", borderColor: theme.colors.sidebar.divider, role: overlay ? "dialog" : undefined, "aria-modal": overlay ? true : undefined, "aria-label": overlay ? "Main navigation" : undefined, ...(overlay && !isOverlayOpen ? { inert: "" } : {}), sx: {
262
281
  ...fullViewportHeight,
263
282
  touchAction: overlay ? "pan-y" : "auto",
264
283
  [REDUCED_MOTION_QUERY]: { "&, & *": { transition: "none", animation: "none" } },
265
284
  }, onMouseLeave: overlayPeeking ? onPeekEnd : undefined, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerUp, children: [_jsx(Box, { width: mobileNav
266
285
  ? OVERLAY_WIDTH
267
- : { base: OVERLAY_WIDTH, lg: effToggle ? RAIL_WIDTH : EXPANDED_WIDTH }, transition: `width 0.26s ${PANEL_EASE}`, display: "flex", flexDirection: "column", justifyContent: "space-between", sx: { "@media (prefers-reduced-motion: reduce)": { transition: "none" } }, children: _jsxs(Flex, { flexDir: "column", w: "100%", as: "nav", "aria-label": "Main navigation", h: "100%", children: [_jsx(SidebarHeader, { logo: logo, companyName: companyName, companySubtitle: companySubtitle, toggle: effToggle, isLoading: isLoading, onToggle: changeToggle, overlay: overlay, onClose: onMobileClose }), _jsx(Box, { id: scrollContainerId, flex: "1", minH: 0, w: "100%", overflowY: "auto", overflowX: "hidden", py: 2, px: "6px", sx: cssForScrollBar(theme), children: isLoading ? (Array.from({ length: loadingLines !== null && loadingLines !== void 0 ? loadingLines : 15 }).map((_, i) => (_jsx(SkeletonMenuRow, { toggle: effToggle }, i)))) : (_jsx(MenuItems, { activeMenu: activeMenu, menus: menus, handleMenuClick: handleMenuClickInternal, toggle: effToggle, selectedSubMenu: selectedSubMenu, overlay: overlay, containerId: scrollContainerId })) }), _jsxs(Box, { flexShrink: 0, children: [!isDisableAddNew && (_jsx(Box, { borderTop: "1px solid", borderColor: theme.colors.sidebar.divider, py: 2, px: "6px", children: _jsx(AddNewButton, { toggle: effToggle, onClick: onAddNewClick, overlay: overlay }) })), _jsx(OtherApps, { toggle: effToggle, otherApps: otherApps !== null && otherApps !== void 0 ? otherApps : [] }), user && _jsx(UserFooter, { user: user, toggle: effToggle, overlay: overlay }), _jsx(FooterControls, { toggle: effToggle, overlay: overlay, mobileNav: mobileNav, onToggleCollapse: changeToggle, onToggleMobileNav: onToggleMobileNav })] })] }) }), secondaryItems.length > 0 && (_jsx(SecondaryBar, { items: secondaryItems, defaultCollapsed: effToggle, collapsedWidth: "3.5rem", expandedWidth: "10rem", selected: selectedSecondaryItem, onItemClick: handleSecondaryNavClick, title: activeTop === null || activeTop === void 0 ? void 0 : activeTop.title, icon: activeTop === null || activeTop === void 0 ? void 0 : activeTop.icon })), overlay && (_jsx(Box, { display: mobileOpen ? "block" : "none", position: "absolute", top: "50%", right: "6px", transform: "translateY(-50%)", w: "4px", h: "42px", borderRadius: "full", bg: theme.colors.sidebar.mutedIcon, "aria-hidden": "true" }))] })] }));
286
+ : { base: OVERLAY_WIDTH, lg: effToggle ? RAIL_WIDTH : EXPANDED_WIDTH }, transition: mobileNav
287
+ ? `width 0.26s ${PANEL_EASE}`
288
+ : { base: `width 0.26s ${PANEL_EASE}`, lg: inline.width }, display: "flex", flexDirection: "column", justifyContent: "space-between", sx: { "@media (prefers-reduced-motion: reduce)": { transition: "none" } }, children: _jsxs(Flex, { flexDir: "column", w: "100%", as: "nav", "aria-label": "Main navigation", h: "100%", children: [_jsx(SidebarHeader, { logo: logo, companyName: companyName, companySubtitle: companySubtitle, toggle: effToggle, isLoading: isLoading, onToggle: changeToggle, overlay: overlay, onClose: onMobileClose }), _jsx(Box, { id: scrollContainerId, flex: "1", minH: 0, w: "100%", overflowY: "auto", overflowX: "hidden", py: 2, px: "6px", sx: cssForScrollBar(theme), children: isLoading ? (Array.from({ length: loadingLines !== null && loadingLines !== void 0 ? loadingLines : 15 }).map((_, i) => (_jsx(SkeletonMenuRow, { toggle: effToggle }, i)))) : (_jsxs(_Fragment, { children: [pinnedItems && pinnedItems.length > 0 && (_jsx(PinnedSection, { items: pinnedItems, label: pinnedLabel, limit: pinnedLimit, toggle: effToggle, overlay: overlay, activeId: activePinnedId, labels: pinnedLabels, onItemClick: handleMenuClickInternal, onUnpin: onUnpin })), _jsx(MenuItems, { activeMenu: pinnedActive ? NO_ACTIVE_MENU : activeMenu, menus: menus, handleMenuClick: handleMenuClickInternal, toggle: effToggle, selectedSubMenu: selectedSubMenu, overlay: overlay, containerId: scrollContainerId })] })) }), _jsxs(Box, { flexShrink: 0, children: [!isDisableAddNew && (_jsx(Box, { borderTop: "1px solid", borderColor: theme.colors.sidebar.divider, py: 2, px: "6px", children: _jsx(AddNewButton, { toggle: effToggle, onClick: onAddNewClick, overlay: overlay }) })), _jsx(OtherApps, { toggle: effToggle, otherApps: otherApps !== null && otherApps !== void 0 ? otherApps : [] }), user && _jsx(UserFooter, { user: user, toggle: effToggle, overlay: overlay }), _jsx(FooterControls, { toggle: effToggle, overlay: overlay, mobileNav: mobileNav, onToggleCollapse: changeToggle, onToggleMobileNav: onToggleMobileNav })] })] }) }), secondaryItems.length > 0 && (_jsx(SecondaryBar, { items: secondaryItems, defaultCollapsed: effToggle, collapsedWidth: "3.5rem", expandedWidth: "10rem", selected: selectedSecondaryItem, onItemClick: handleSecondaryNavClick, title: activeTop === null || activeTop === void 0 ? void 0 : activeTop.title, icon: activeTop === null || activeTop === void 0 ? void 0 : activeTop.icon })), overlay && (_jsx(Box, { display: mobileOpen ? "block" : "none", position: "absolute", top: "50%", right: "6px", transform: "translateY(-50%)", w: "4px", h: "42px", borderRadius: "full", bg: theme.colors.sidebar.mutedIcon, "aria-hidden": "true" }))] })] }));
268
289
  }
@@ -12,13 +12,14 @@ import { scaleFor } from "../../../Theme/chakra/paletteFromTheme.js";
12
12
  import TruncatedTextWithTooltip from "./TextTruncation.js";
13
13
  import { sanitizeId } from "../sidebarId.js";
14
14
  import ToolTip from "../../ToolTip/ToolTip.js";
15
+ import { sidebarFocusStyles } from "../sidebarFocus.js";
15
16
  import { SIDEBAR_ROW } from "../sidebarScale.js";
16
17
  /**
17
18
  * These rows are real `<a href>`s now, so the browser's own gestures have to keep working —
18
19
  * cmd/ctrl-click for a new tab, shift for a window, middle-click. Only a plain left click is
19
20
  * routed through the in-app handler.
20
21
  */
21
- const isBrowserNavigationGesture = (e) => e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0;
22
+ export const isBrowserNavigationGesture = (e) => e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0;
22
23
  const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle, overlay = false, }) => {
23
24
  var _a, _b, _c, _d, _e;
24
25
  const theme = useCustomTheme();
@@ -86,6 +87,7 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
86
87
  cursor: isBlocked ? "not-allowed" : "pointer",
87
88
  opacity: isBlocked ? 0.6 : 1,
88
89
  transition: "background 0.12s",
90
+ sx: sidebarFocusStyles(theme.colors),
89
91
  };
90
92
  const buttonContent = (_jsxs(Flex, { align: "center", gap: "9px", justify: toggle ? "center" : "space-between", w: "100%", children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", children: [_jsxs(Box, { position: "relative", display: "flex", alignItems: "center", children: [_jsx(Icon, { as: icon, fontSize: "md", verticalAlign: "middle", color: isAnyActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isAnyActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" }), toggle && (badgeValue != null || hasSubmenuBadge) && (_jsx(Box, { position: "absolute", top: "0", right: "0", w: "0.375rem", h: "0.375rem", borderRadius: "full", bg: theme.colors.sidebar.semanticText.error }))] }), !toggle && (_jsx(TruncatedTextWithTooltip
91
93
  // open={isOpen}
@@ -138,7 +140,7 @@ const MenuItemBox = ({ menu, activeMenu, toggle, handleClick, isOpen, onToggle,
138
140
  e.preventDefault();
139
141
  if (!isSubBlocked)
140
142
  handleClick === null || handleClick === void 0 ? void 0 : handleClick(sub);
141
- }, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2, transition: "background 0.12s", children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [sub.icon ? (_jsx(Icon, { as: sub.icon, fontSize: SIDEBAR_ROW.iconSize, flexShrink: 0, color: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" })) : (_jsx(Box, { w: "5px", h: "5px", borderRadius: "full", flexShrink: 0, bg: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { bg: theme.colors.white }, transition: "background 0.12s" })), _jsx(TruncatedTextWithTooltip, { fontSize: SIDEBAR_ROW.fontSize, fontWeight: isSubActive ? 600 : 500, lineHeight: "1", color: isSubActive ? activeAccent.label : theme.colors.sidebar.mutedLabel, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, children: sub.title })] }), sub.locked ? (_jsx(Icon, { as: LockKeyhole, fontSize: "md", color: theme.colors.sidebar.mutedIcon })) : (() => {
143
+ }, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2, transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [sub.icon ? (_jsx(Icon, { as: sub.icon, fontSize: SIDEBAR_ROW.iconSize, flexShrink: 0, color: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" })) : (_jsx(Box, { w: "5px", h: "5px", borderRadius: "full", flexShrink: 0, bg: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { bg: theme.colors.white }, transition: "background 0.12s" })), _jsx(TruncatedTextWithTooltip, { fontSize: SIDEBAR_ROW.fontSize, fontWeight: isSubActive ? 600 : 500, lineHeight: "1", color: isSubActive ? activeAccent.label : theme.colors.sidebar.mutedLabel, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, children: sub.title })] }), sub.locked ? (_jsx(Icon, { as: LockKeyhole, fontSize: "md", color: theme.colors.sidebar.mutedIcon })) : (() => {
142
144
  var _a, _b, _c, _d;
143
145
  const subCfg = typeof sub.badge === "object" && sub.badge !== null
144
146
  ? sub.badge
@@ -9,6 +9,7 @@ import { sidebarRowPeak } from "../../../Theme/tokens/builders/sidebarAccent.js"
9
9
  import { scaleFor } from "../../../Theme/chakra/paletteFromTheme.js";
10
10
  import TruncatedTextWithTooltip from "./TextTruncation.js";
11
11
  import ToolTip from "../../ToolTip/ToolTip.js";
12
+ import { sidebarFocusStyles } from "../sidebarFocus.js";
12
13
  import { SIDEBAR_ROW } from "../sidebarScale.js";
13
14
  const MenuPopoverContent = ({ menu, handleMenuClick, activeMenu, }) => {
14
15
  var _a;
@@ -26,7 +27,7 @@ const MenuPopoverContent = ({ menu, handleMenuClick, activeMenu, }) => {
26
27
  if (!isBlocked)
27
28
  handleMenuClick === null || handleMenuClick === void 0 ? void 0 : handleMenuClick(menu);
28
29
  }
29
- }, cursor: !hasSubMenu ? (isBlocked ? "not-allowed" : "pointer") : "default", "aria-disabled": !hasSubMenu && isBlocked ? true : undefined, children: [_jsx(Icon, { as: icon, fontSize: "md", flexShrink: 0, color: !hasSubMenu && isParentActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon }), _jsx(TruncatedTextWithTooltip, { fontWeight: 600, fontSize: SIDEBAR_ROW.fontSize, flex: "1", color: theme.colors.white, children: title }), (() => {
30
+ }, cursor: !hasSubMenu ? (isBlocked ? "not-allowed" : "pointer") : "default", "aria-disabled": !hasSubMenu && isBlocked ? true : undefined, sx: hasSubMenu ? undefined : sidebarFocusStyles(theme.colors, theme.colors.sidebar.elevated), children: [_jsx(Icon, { as: icon, fontSize: "md", flexShrink: 0, color: !hasSubMenu && isParentActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon }), _jsx(TruncatedTextWithTooltip, { fontWeight: 600, fontSize: SIDEBAR_ROW.fontSize, flex: "1", color: theme.colors.white, children: title }), (() => {
30
31
  var _a, _b, _c, _d;
31
32
  const cfg = typeof badge === "object" && badge !== null
32
33
  ? badge
@@ -50,7 +51,7 @@ const MenuPopoverContent = ({ menu, handleMenuClick, activeMenu, }) => {
50
51
  }, px: 2, h: SIDEBAR_ROW.subHeight, mb: "2px", borderRadius: "lg", align: "center", justify: "space-between", gap: 2, borderLeft: "2px solid", borderLeftColor: isSubActive ? theme.colors.sidebar.activeIcon : "transparent", bg: isSubActive ? "whiteAlpha.300" : "transparent", _hover: {
51
52
  bg: isSubBlocked ? "transparent" : isSubActive ? "whiteAlpha.400" : "whiteAlpha.200",
52
53
  cursor: !isSubBlocked ? "pointer" : "not-allowed",
53
- }, opacity: isSubBlocked ? 0.5 : 1, pointerEvents: isSubBlocked ? "none" : "auto", transition: "background 0.12s", children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [sub.icon ? (_jsx(Icon, { as: sub.icon, fontSize: SIDEBAR_ROW.iconSize, flexShrink: 0, color: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" })) : (_jsx(Box, { w: "5px", h: "5px", borderRadius: "full", flexShrink: 0, bg: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { bg: theme.colors.white }, transition: "background 0.12s" })), _jsx(TruncatedTextWithTooltip, { fontSize: SIDEBAR_ROW.fontSize, fontWeight: isSubActive ? 600 : 500, flex: 1, color: isSubActive ? activeAccent.label : theme.colors.white, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, children: sub.title })] }), sub.locked ? (sub.lockedReason ? (_jsx(ToolTip, { label: sub.lockedReason, placement: "top", children: _jsx(Icon, { as: LockKeyhole, fontSize: "md", color: theme.colors.sidebar.mutedIcon }) })) : (_jsx(Icon, { as: LockKeyhole, fontSize: "md", color: theme.colors.sidebar.mutedIcon }))) : (() => {
54
+ }, opacity: isSubBlocked ? 0.5 : 1, pointerEvents: isSubBlocked ? "none" : "auto", transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors, theme.colors.sidebar.elevated), children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", flex: "1", children: [sub.icon ? (_jsx(Icon, { as: sub.icon, fontSize: SIDEBAR_ROW.iconSize, flexShrink: 0, color: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" })) : (_jsx(Box, { w: "5px", h: "5px", borderRadius: "full", flexShrink: 0, bg: isSubActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isSubActive ? undefined : { bg: theme.colors.white }, transition: "background 0.12s" })), _jsx(TruncatedTextWithTooltip, { fontSize: SIDEBAR_ROW.fontSize, fontWeight: isSubActive ? 600 : 500, flex: 1, color: isSubActive ? activeAccent.label : theme.colors.white, _groupHover: isSubActive ? undefined : { color: theme.colors.white }, children: sub.title })] }), sub.locked ? (sub.lockedReason ? (_jsx(ToolTip, { label: sub.lockedReason, placement: "top", children: _jsx(Icon, { as: LockKeyhole, fontSize: "md", color: theme.colors.sidebar.mutedIcon }) })) : (_jsx(Icon, { as: LockKeyhole, fontSize: "md", color: theme.colors.sidebar.mutedIcon }))) : (() => {
54
55
  var _a, _b, _c, _d;
55
56
  const cfg = typeof sub.badge === "object" && sub.badge !== null
56
57
  ? sub.badge
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Flex, Text, Popover, PopoverTrigger, PopoverContent, PopoverBody, Portal, } from "@chakra-ui/react";
3
3
  import { useCustomTheme } from "../../../Theme/useCustomTheme.js";
4
+ import { sidebarFocusStyles } from "../sidebarFocus.js";
4
5
  const OtherApps = ({ toggle, otherApps }) => {
5
6
  const theme = useCustomTheme();
6
7
  if (!otherApps || otherApps.length === 0)
@@ -10,7 +11,7 @@ const OtherApps = ({ toggle, otherApps }) => {
10
11
  return (_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", color: theme.colors.sidebar.mutedIcon, fontSize: "15px", flexShrink: 0, children: (_a = app.icon) !== null && _a !== void 0 ? _a : (_jsx(Box, { w: "0.5rem", h: "0.5rem", borderRadius: "full", bg: app.color, boxShadow: `0 0 5px ${app.color}`, flexShrink: 0 })) }));
11
12
  };
12
13
  // Grid cell: icon on top, truncated label below (label hidden when collapsed).
13
- const appCell = (app) => (_jsxs(Flex, { as: "button", type: "button", direction: "column", align: "center", justify: "center", gap: "3px", px: "2px", py: "6px", borderRadius: "lg", cursor: "pointer", border: "none", bg: "transparent", w: "100%", _hover: { backgroundColor: "whiteAlpha.200" }, transition: "background 0.12s", onClick: app.onClick, children: [appIcon(app), !toggle && (_jsx(Text, { fontSize: "9.5px", color: theme.colors.sidebar.mutedLabel, textAlign: "center", maxW: "44px", isTruncated: true, children: app.name }))] }));
14
+ const appCell = (app) => (_jsxs(Flex, { as: "button", type: "button", direction: "column", align: "center", justify: "center", gap: "3px", px: "2px", py: "6px", borderRadius: "lg", cursor: "pointer", border: "none", bg: "transparent", w: "100%", _hover: { backgroundColor: "whiteAlpha.200" }, transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), onClick: app.onClick, children: [appIcon(app), !toggle && (_jsx(Text, { fontSize: "9.5px", color: theme.colors.sidebar.mutedLabel, textAlign: "center", maxW: "44px", isTruncated: true, children: app.name }))] }));
14
15
  const flyoutBody = (app) => {
15
16
  var _a;
16
17
  return (_jsxs(_Fragment, { children: [_jsxs(Flex, { alignItems: "center", gap: 2, mb: 1, onClick: app.onClick, children: [(_a = app.icon) !== null && _a !== void 0 ? _a : (_jsx(Box, { w: "0.5rem", h: "0.5rem", borderRadius: "full", bg: app.color, boxShadow: `0 0 5px ${app.color}`, flexShrink: 0 })), _jsx(Text, { fontSize: "sm", fontWeight: 700, color: theme.colors.white, children: app.name })] }), app.description && (_jsx(Text, { fontSize: "xs", color: theme.colors.sidebar.mutedLabel, children: app.description }))] }));
@@ -0,0 +1,92 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useState } from "react";
3
+ import { Badge, Box, Flex, Icon } from "@chakra-ui/react";
4
+ import { ChevronDown, ChevronUp, PinOff } from "lucide-react";
5
+ import { useCustomTheme } from "../../../Theme/useCustomTheme.js";
6
+ import { sidebarFocusStyles } from "../sidebarFocus.js";
7
+ import { resolveSoftTone } from "../../../Theme/tokens/builders/tone.js";
8
+ import { sidebarRowPeak } from "../../../Theme/tokens/builders/sidebarAccent.js";
9
+ import { scaleFor } from "../../../Theme/chakra/paletteFromTheme.js";
10
+ import { getSidebarActiveAccent } from "../sidebarAccentColors.js";
11
+ import { sanitizeId } from "../sidebarId.js";
12
+ import { SIDEBAR_ROW } from "../sidebarScale.js";
13
+ import ToolTip from "../../ToolTip/ToolTip.js";
14
+ import TruncatedTextWithTooltip from "./TextTruncation.js";
15
+ import { isBrowserNavigationGesture } from "./MenuItemBox.js";
16
+ export const DEFAULT_PINNED_LABEL = "Pinned";
17
+ export const DEFAULT_PINNED_LIMIT = 5;
18
+ const DEFAULT_LABELS = {
19
+ showMore: "Show {count} more",
20
+ showLess: "Show less",
21
+ unpin: "Unpin {title}",
22
+ };
23
+ const UNPIN_SIZE = "24px";
24
+ const LADDER = { hover: "whiteAlpha.200", active: "whiteAlpha.300", activeHover: "whiteAlpha.400" };
25
+ const VISUALLY_HIDDEN = {
26
+ position: "absolute",
27
+ width: "1px",
28
+ height: "1px",
29
+ padding: 0,
30
+ margin: "-1px",
31
+ clip: "rect(0, 0, 0, 0)",
32
+ whiteSpace: "nowrap",
33
+ border: 0,
34
+ };
35
+ const readBadge = (badge) => {
36
+ var _a;
37
+ const cfg = typeof badge === "object" && badge !== null ? badge : null;
38
+ const value = (_a = cfg === null || cfg === void 0 ? void 0 : cfg.count) !== null && _a !== void 0 ? _a : (typeof badge !== "object" ? badge : undefined);
39
+ return { cfg, value };
40
+ };
41
+ const PinnedSection = ({ items, label = DEFAULT_PINNED_LABEL, limit = DEFAULT_PINNED_LIMIT, toggle, overlay, activeId, labels, onItemClick, onUnpin, }) => {
42
+ const theme = useCustomTheme();
43
+ const activeAccent = getSidebarActiveAccent(theme.colors);
44
+ const text = { ...DEFAULT_LABELS, ...labels };
45
+ const headingId = `pinned-${sanitizeId(useId())}`;
46
+ const cap = Math.max(1, limit);
47
+ const rowHeight = overlay ? SIDEBAR_ROW.overlayHeight : SIDEBAR_ROW.height;
48
+ const activeIndex = activeId == null ? -1 : items.findIndex((i) => String(i.id) === String(activeId));
49
+ const [showAll, setShowAll] = useState(activeIndex >= cap);
50
+ useEffect(() => {
51
+ if (activeIndex >= cap)
52
+ setShowAll(true);
53
+ }, [activeIndex, cap]);
54
+ if (items.length === 0)
55
+ return null;
56
+ const hiddenCount = Math.max(0, items.length - cap);
57
+ const visible = showAll ? items : items.slice(0, cap);
58
+ const badgeSurface = sidebarRowPeak(theme.colors.sidebar.background[500]);
59
+ const unpinReplacesBadge = !!onUnpin && !overlay;
60
+ const renderBadge = (badge) => {
61
+ var _a, _b, _c;
62
+ const { cfg, value } = readBadge(badge);
63
+ if (value == null)
64
+ return null;
65
+ const tone = resolveSoftTone(theme.colors, scaleFor(theme.colors, (_a = cfg === null || cfg === void 0 ? void 0 : cfg.color) !== null && _a !== void 0 ? _a : "teal"), badgeSurface);
66
+ return (_jsx(Badge, { bg: tone.bg, color: tone.fg, fontSize: "xs", variant: (_b = cfg === null || cfg === void 0 ? void 0 : cfg.variant) !== null && _b !== void 0 ? _b : "subtle", borderRadius: (_c = cfg === null || cfg === void 0 ? void 0 : cfg.radius) !== null && _c !== void 0 ? _c : "full", ...(unpinReplacesBadge
67
+ ? { _groupHover: { opacity: 0 }, _groupFocusWithin: { opacity: 0 } }
68
+ : {}), children: value }));
69
+ };
70
+ const renderRow = (item) => {
71
+ const isActive = activeIndex >= 0 && String(item.id) === String(activeId);
72
+ const anchor = (_jsx(Box, { as: "a", href: item.url, "data-group": "", "data-pinned-id": String(item.id), "aria-label": toggle ? item.title : undefined, "aria-current": isActive ? "page" : undefined, display: "flex", alignItems: "center", textAlign: "left", textDecoration: "none", w: "100%", h: rowHeight, borderRadius: "lg", pl: "10px", pr: !toggle && onUnpin ? `calc(10px + ${UNPIN_SIZE})` : "10px", fontWeight: "medium", borderLeft: "2px solid", borderLeftColor: isActive ? theme.colors.sidebar.activeIcon : "transparent", backgroundColor: isActive ? LADDER.active : "transparent", color: theme.colors.sidebar.mutedIcon, _hover: { backgroundColor: isActive ? LADDER.activeHover : LADDER.hover }, cursor: "pointer", transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), onClick: (e) => {
73
+ if (item.url && isBrowserNavigationGesture(e))
74
+ return;
75
+ e.preventDefault();
76
+ onItemClick(item);
77
+ }, children: _jsxs(Flex, { align: "center", gap: "9px", justify: toggle ? "center" : "space-between", w: "100%", children: [_jsxs(Flex, { align: "center", gap: "9px", overflow: "hidden", flex: toggle ? undefined : 1, minW: 0, children: [_jsxs(Box, { position: "relative", display: "flex", alignItems: "center", children: [_jsx(Icon, { as: item.icon, fontSize: "md", color: isActive ? activeAccent.icon : theme.colors.sidebar.mutedIcon, _groupHover: isActive ? undefined : { color: theme.colors.white }, transition: "color 0.12s" }), toggle && readBadge(item.badge).value != null && (_jsx(Box, { position: "absolute", top: "0", right: "0", w: "0.375rem", h: "0.375rem", borderRadius: "full", bg: theme.colors.sidebar.semanticText.error }))] }), !toggle && (_jsx(TruncatedTextWithTooltip, { fontSize: SIDEBAR_ROW.fontSize, lineHeight: "1", flex: "1", color: isActive ? activeAccent.label : theme.colors.white, fontWeight: isActive ? 600 : 500, _groupHover: isActive ? undefined : { color: theme.colors.white }, children: item.title }))] }), !toggle && renderBadge(item.badge)] }) }));
78
+ return (_jsxs(Box, { as: "li", listStyleType: "none", position: "relative", "data-group": "", children: [toggle ? (_jsx(ToolTip, { label: item.title, placement: "right", children: anchor })) : (anchor), onUnpin && !toggle && (_jsx(Box, { as: "button", type: "button", "aria-label": text.unpin.replace("{title}", item.title), onClick: () => onUnpin(item), position: "absolute", top: "50%", right: "6px", transform: "translateY(-50%)", w: UNPIN_SIZE, h: UNPIN_SIZE, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "lg", border: "none", bg: "transparent", cursor: "pointer", color: theme.colors.sidebar.mutedIcon, opacity: overlay ? 1 : 0, _groupHover: { opacity: 1 }, _groupFocusWithin: { opacity: 1 }, _hover: {
79
+ bg: LADDER.active,
80
+ color: theme.colors.white,
81
+ }, transition: "opacity 0.12s, background 0.12s", sx: sidebarFocusStyles(theme.colors), children: _jsx(Icon, { as: PinOff, fontSize: "sm" }) }))] }, String(item.id)));
82
+ };
83
+ const disclosureLabel = showAll
84
+ ? text.showLess
85
+ : text.showMore.replace("{count}", String(hiddenCount));
86
+ const disclosureButton = (_jsxs(Box, { as: "button", type: "button", "aria-expanded": showAll, "aria-label": toggle ? disclosureLabel : undefined, onClick: () => setShowAll((v) => !v), display: "flex", alignItems: "center", justifyContent: toggle ? "center" : "flex-start", gap: "9px", w: "100%", h: rowHeight, px: "10px", borderRadius: "lg", border: "none", borderLeft: "2px solid transparent", bg: "transparent", cursor: "pointer", color: theme.colors.sidebar.mutedLabel, fontSize: SIDEBAR_ROW.fontSize, fontWeight: "medium", _hover: {
87
+ backgroundColor: LADDER.hover,
88
+ color: theme.colors.white,
89
+ }, transition: "background 0.12s", sx: sidebarFocusStyles(theme.colors), children: [_jsx(Icon, { as: showAll ? ChevronUp : ChevronDown, fontSize: "md", flexShrink: 0 }), !toggle && _jsx("span", { children: disclosureLabel })] }));
90
+ return (_jsxs(Box, { mb: toggle ? "5px" : "3px", "data-testid": "sidebar-pinned", children: [_jsx(Box, { id: headingId, as: "h2", px: "10px", pt: overlay ? "6px" : "10px", pb: "3px", fontSize: "9.5px", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.1em", color: theme.colors.sidebar.mutedLabel, whiteSpace: "nowrap", overflow: "hidden", sx: toggle ? VISUALLY_HIDDEN : undefined, children: label }), _jsxs(Box, { as: "ul", "aria-labelledby": headingId, display: "flex", flexDirection: "column", gap: "2px", m: 0, p: 0, children: [visible.map(renderRow), hiddenCount > 0 && (_jsx(Box, { as: "li", listStyleType: "none", children: toggle ? (_jsx(ToolTip, { label: disclosureLabel, placement: "right", children: disclosureButton })) : (disclosureButton) }))] }), _jsx(Box, { h: "1px", bg: theme.colors.sidebar.divider, mx: 2, mt: overlay ? "6px" : "8px", mb: overlay ? "3px" : "5px" })] }));
91
+ };
92
+ export default PinnedSection;
@@ -0,0 +1,15 @@
1
+ import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
2
+ import { AA_NON_TEXT, liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
3
+ const cache = new Map();
4
+ /** The ring is derived for the canvas; the rail is near-black in both modes, so it is re-settled against the rail. */
5
+ export const sidebarFocusRing = (colors, surface = colors.sidebar.background[500]) => {
6
+ const key = `${colors.focusRing.ring}|${surface}`;
7
+ let tokens = cache.get(key);
8
+ if (!tokens) {
9
+ tokens = { ring: liftToContrast(colors.focusRing.ring, surface, AA_NON_TEXT), halo: surface };
10
+ cache.set(key, tokens);
11
+ }
12
+ return { focusRing: tokens };
13
+ };
14
+ /** `focusVisibleStyles` for a control sitting on the rail (or on `surface`, e.g. the flyout). */
15
+ export const sidebarFocusStyles = (colors, surface) => focusVisibleStyles(sidebarFocusRing(colors, surface));