pixelize-design-library 2.4.2-beta.3 → 2.4.2-beta.31

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 (284) hide show
  1. package/dist/Components/Accordion/Accordion.js +5 -3
  2. package/dist/Components/AppSwitcher/AppMark.d.ts +19 -0
  3. package/dist/Components/AppSwitcher/AppMark.js +62 -0
  4. package/dist/Components/AppSwitcher/AppSwitcher.d.ts +4 -0
  5. package/dist/Components/AppSwitcher/AppSwitcher.js +315 -0
  6. package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +64 -0
  7. package/dist/Components/AppSwitcher/AppSwitcherProps.js +86 -0
  8. package/dist/Components/AppSwitcher/useAppSwitcherShortcut.d.ts +3 -0
  9. package/dist/Components/AppSwitcher/useAppSwitcherShortcut.js +31 -0
  10. package/dist/Components/AppearanceSettings/AppearanceSettings.js +2 -1
  11. package/dist/Components/Button/Button.d.ts +2 -2
  12. package/dist/Components/Button/Button.js +4 -3
  13. package/dist/Components/Button/Button.styles.js +39 -25
  14. package/dist/Components/Button/ButtonProps.d.ts +6 -1
  15. package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.js +6 -6
  16. package/dist/Components/Card/Card.d.ts +1 -1
  17. package/dist/Components/Card/Card.js +2 -2
  18. package/dist/Components/Card/CardProps.d.ts +0 -1
  19. package/dist/Components/Card/PaymentCard/PaymentCard.js +7 -7
  20. package/dist/Components/Checkbox/Checkbox.js +1 -1
  21. package/dist/Components/Checkbox/Checkbox.styles.js +12 -7
  22. package/dist/Components/Common/fieldStyles.js +1 -1
  23. package/dist/Components/CopyButton/CopyButton.js +6 -2
  24. package/dist/Components/CustomModulesTable/EditModuleModal.js +1 -1
  25. package/dist/Components/DatePicker/CalendarPanel.d.ts +5 -0
  26. package/dist/Components/DatePicker/CalendarPanel.js +24 -5
  27. package/dist/Components/DatePicker/DatePickerProps.d.ts +21 -0
  28. package/dist/Components/DatePicker/RangeDatePicker.js +4 -4
  29. package/dist/Components/DatePicker/SingleDatePicker.js +2 -2
  30. package/dist/Components/DatePicker/TimeOnlyPicker.js +1 -1
  31. package/dist/Components/DatePicker/dayStates.d.ts +56 -0
  32. package/dist/Components/DatePicker/dayStates.js +87 -0
  33. package/dist/Components/Dropdown/DropDown.js +7 -15
  34. package/dist/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +1 -1
  35. package/dist/Components/EmptyState/EmptyState.js +2 -1
  36. package/dist/Components/FilePreview/FilePreview.js +1 -1
  37. package/dist/Components/FileUpload/FileUpload.js +5 -4
  38. package/dist/Components/FileUpload/FileUploadProps.d.ts +3 -1
  39. package/dist/Components/FileUpload/constants.js +1 -0
  40. package/dist/Components/FilterSidebar/FilterSidebar.js +4 -4
  41. package/dist/Components/FilterSidebar/filterHoverStyle.d.ts +0 -9
  42. package/dist/Components/FilterSidebar/filterHoverStyle.js +4 -10
  43. package/dist/Components/Form/FormWrapper.js +1 -1
  44. package/dist/Components/Header/components/mobileMenu.js +12 -3
  45. package/dist/Components/Input/TextInput.styles.js +6 -4
  46. package/dist/Components/InputTextArea/InputTextArea.style.js +5 -4
  47. package/dist/Components/KanbanBoard/KanbanBoard.js +5 -45
  48. package/dist/Components/LimitReachedModal/LimitReachedModal.js +4 -5
  49. package/dist/Components/Loading/Loading.js +15 -14
  50. package/dist/Components/MultiSelect/MultiSelect.js +1 -1
  51. package/dist/Components/NavigationBar/NavigationBar.js +3 -3
  52. package/dist/Components/NoteTextArea/NoteTextArea.js +9 -4
  53. package/dist/Components/NumberInput/NumberInput.js +3 -1
  54. package/dist/Components/NumberInput/NumberInput.styles.js +5 -4
  55. package/dist/Components/OrgSwitcher/OrgSwitcher.js +2 -2
  56. package/dist/Components/PdfViewer/PdfViewer.js +6 -1
  57. package/dist/Components/PinInput/PinInput.js +1 -1
  58. package/dist/Components/PlanPill/PlanPill.js +5 -5
  59. package/dist/Components/PlanPill/planPillGradient.d.ts +2 -14
  60. package/dist/Components/PlanPill/planPillGradient.js +3 -15
  61. package/dist/Components/Primitives/Primitives.d.ts +6 -0
  62. package/dist/Components/Primitives/Primitives.js +113 -0
  63. package/dist/Components/ProductCard/ProductPrice.js +1 -1
  64. package/dist/Components/ProductCard/ProductReview.js +6 -1
  65. package/dist/Components/ProductCard/ProductTags.js +1 -1
  66. package/dist/Components/ProfileCard/ProfileCard.js +1 -1
  67. package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +1 -1
  68. package/dist/Components/RadioButton/RadioButton.js +3 -2
  69. package/dist/Components/Reorder/Reorder.js +1 -1
  70. package/dist/Components/ScrollToTop/ScrollToTop.js +2 -0
  71. package/dist/Components/ScrollbarBox/ScrollbarBox.d.ts +13 -0
  72. package/dist/Components/ScrollbarBox/ScrollbarBox.js +11 -0
  73. package/dist/Components/Search/Search.js +2 -2
  74. package/dist/Components/SearchSelect/SearchSelect.js +14 -9
  75. package/dist/Components/SegmentedControl/SegmentedControl.d.ts +2 -0
  76. package/dist/Components/SegmentedControl/SegmentedControl.js +58 -0
  77. package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +16 -0
  78. package/dist/Components/SegmentedControl/SegmentedControlProps.js +2 -0
  79. package/dist/Components/Select/Select.d.ts +1 -1
  80. package/dist/Components/Select/Select.js +4 -2
  81. package/dist/Components/Select/Select.styles.js +3 -2
  82. package/dist/Components/Select/SelectProps.d.ts +3 -0
  83. package/dist/Components/SelectSearch/SelectSearch.js +7 -4
  84. package/dist/Components/SelectV2/SelectV2.js +18 -14
  85. package/dist/Components/SideBar/SecondaryBar.js +1 -1
  86. package/dist/Components/SideBar/SideBar.js +1 -1
  87. package/dist/Components/SideBar/components/MenuPopoverContent.js +2 -2
  88. package/dist/Components/Slider/Slider.js +8 -1
  89. package/dist/Components/StageProgress/StageProgress.js +2 -9
  90. package/dist/Components/StageProgress/StepperStage.js +2 -2
  91. package/dist/Components/Table/Table.d.ts +1 -1
  92. package/dist/Components/Table/Table.js +17 -24
  93. package/dist/Components/Table/TableProps.d.ts +4 -0
  94. package/dist/Components/Table/components/Pagination.js +1 -1
  95. package/dist/Components/Table/components/TableActions.js +2 -2
  96. package/dist/Components/Table/components/TableBody.js +41 -12
  97. package/dist/Components/Table/components/TableGroupRow.js +1 -1
  98. package/dist/Components/Table/components/TableHeader.js +21 -8
  99. package/dist/Components/Table/components/TableSearch.js +1 -1
  100. package/dist/Components/Table/filters/ActiveFilters.js +2 -18
  101. package/dist/Components/Table/settings/ManageColumns.js +4 -17
  102. package/dist/Components/Table/settings/TableSettings.js +5 -11
  103. package/dist/Components/Table/variants/TableVariantContext.d.ts +4 -0
  104. package/dist/Components/Table/variants/TableVariantContext.js +17 -0
  105. package/dist/Components/Table/variants/classic.d.ts +6 -0
  106. package/dist/Components/Table/variants/classic.js +47 -0
  107. package/dist/Components/Table/variants/index.d.ts +7 -0
  108. package/dist/Components/Table/variants/index.js +27 -0
  109. package/dist/Components/Table/variants/pillInk.d.ts +6 -0
  110. package/dist/Components/Table/variants/pillInk.js +21 -0
  111. package/dist/Components/Table/variants/studio.d.ts +6 -0
  112. package/dist/Components/Table/variants/studio.js +50 -0
  113. package/dist/Components/Table/variants/tokens.d.ts +49 -0
  114. package/dist/Components/Table/variants/tokens.js +2 -0
  115. package/dist/Components/Tag/Tag.styles.js +9 -8
  116. package/dist/Components/Timeline/Timeline.js +2 -2
  117. package/dist/Components/Toaster/Toaster.js +1 -1
  118. package/dist/Components/Toggle/TableToggle.js +11 -7
  119. package/dist/Components/ToggleSwitch/ToggleSwitch.d.ts +2 -0
  120. package/dist/Components/ToggleSwitch/ToggleSwitch.js +18 -0
  121. package/dist/Components/ToggleSwitch/ToggleSwitchProps.d.ts +11 -0
  122. package/dist/Components/ToggleSwitch/ToggleSwitchProps.js +2 -0
  123. package/dist/Components/ToolTip/ToolTip.js +6 -6
  124. package/dist/Components/ToolTip/ToolTipProps.d.ts +3 -1
  125. package/dist/Components/Trail/TrailWorks.js +1 -1
  126. package/dist/Components/UpgradeButton/UpgradeButton.js +3 -3
  127. package/dist/Constants/Sidebar.js +3 -2
  128. package/dist/Theme/assertPalette.d.ts +1 -1
  129. package/dist/Theme/assertPalette.js +3 -2
  130. package/dist/Theme/chakra/Menu.styles.d.ts +3 -0
  131. package/dist/Theme/chakra/Menu.styles.js +5 -0
  132. package/dist/Theme/chakra/Popover.styles.d.ts +3 -0
  133. package/dist/Theme/chakra/Popover.styles.js +5 -1
  134. package/dist/Theme/chakra/componentStyles.d.ts +6 -0
  135. package/dist/Theme/chakra/componentStyles.js +5 -5
  136. package/dist/Theme/chakra/createBrandTheme.js +10 -16
  137. package/dist/Theme/chakra/focusRing.styles.d.ts +52 -0
  138. package/dist/Theme/chakra/focusRing.styles.js +46 -0
  139. package/dist/Theme/provider/ThemeSwitcher.js +5 -2
  140. package/dist/Theme/tokens/brands/index.js +6 -1
  141. package/dist/Theme/tokens/brands/zinc/palette.dark.d.ts +1 -0
  142. package/dist/Theme/tokens/builders/buildDarkPalette.js +6 -1
  143. package/dist/Theme/tokens/builders/chartColorsFromTheme.js +1 -1
  144. package/dist/Theme/tokens/builders/focusRing.d.ts +52 -0
  145. package/dist/Theme/tokens/builders/focusRing.js +92 -0
  146. package/dist/Theme/tokens/builders/gradientInk.d.ts +22 -0
  147. package/dist/Theme/tokens/builders/gradientInk.js +28 -0
  148. package/dist/Theme/tokens/builders/index.d.ts +4 -0
  149. package/dist/Theme/tokens/builders/index.js +4 -0
  150. package/dist/Theme/tokens/builders/outlineRung.d.ts +46 -3
  151. package/dist/Theme/tokens/builders/outlineRung.js +101 -5
  152. package/dist/Theme/tokens/builders/placeholderInk.d.ts +19 -0
  153. package/dist/Theme/tokens/builders/placeholderInk.js +35 -0
  154. package/dist/Theme/tokens/builders/resolvedRungs.d.ts +5 -1
  155. package/dist/Theme/tokens/builders/resolvedRungs.js +7 -2
  156. package/dist/Theme/tokens/builders/rowStates.d.ts +13 -0
  157. package/dist/Theme/tokens/builders/rowStates.js +19 -0
  158. package/dist/Theme/tokens/builders/scrollbar.d.ts +12 -0
  159. package/dist/Theme/tokens/builders/scrollbar.js +57 -0
  160. package/dist/Theme/tokens/builders/solidFillRung.d.ts +4 -0
  161. package/dist/Theme/tokens/builders/solidFillRung.js +33 -0
  162. package/dist/Theme/tokens/builders/surfaceRungs.d.ts +5 -0
  163. package/dist/Theme/tokens/builders/surfaceRungs.js +20 -5
  164. package/dist/Theme/tokens/types.d.ts +21 -1
  165. package/dist/esm/Components/Accordion/Accordion.js +5 -3
  166. package/dist/esm/Components/AppSwitcher/AppMark.js +56 -0
  167. package/dist/esm/Components/AppSwitcher/AppSwitcher.js +280 -0
  168. package/dist/esm/Components/AppSwitcher/AppSwitcherProps.js +78 -0
  169. package/dist/esm/Components/AppSwitcher/useAppSwitcherShortcut.js +27 -0
  170. package/dist/esm/Components/AppearanceSettings/AppearanceSettings.js +2 -1
  171. package/dist/esm/Components/Button/Button.js +5 -4
  172. package/dist/esm/Components/Button/Button.styles.js +40 -26
  173. package/dist/esm/Components/ButtonGroupIcon/ButtonGroupIcon.js +6 -6
  174. package/dist/esm/Components/Card/Card.js +2 -2
  175. package/dist/esm/Components/Card/PaymentCard/PaymentCard.js +7 -7
  176. package/dist/esm/Components/Checkbox/Checkbox.js +1 -1
  177. package/dist/esm/Components/Checkbox/Checkbox.styles.js +13 -8
  178. package/dist/esm/Components/Common/fieldStyles.js +1 -1
  179. package/dist/esm/Components/CopyButton/CopyButton.js +7 -3
  180. package/dist/esm/Components/CustomModulesTable/EditModuleModal.js +1 -1
  181. package/dist/esm/Components/DatePicker/CalendarPanel.js +24 -5
  182. package/dist/esm/Components/DatePicker/RangeDatePicker.js +4 -4
  183. package/dist/esm/Components/DatePicker/SingleDatePicker.js +2 -2
  184. package/dist/esm/Components/DatePicker/TimeOnlyPicker.js +1 -1
  185. package/dist/esm/Components/DatePicker/dayStates.js +79 -0
  186. package/dist/esm/Components/Dropdown/DropDown.js +7 -15
  187. package/dist/esm/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +1 -1
  188. package/dist/esm/Components/EmptyState/EmptyState.js +2 -1
  189. package/dist/esm/Components/FilePreview/FilePreview.js +1 -1
  190. package/dist/esm/Components/FileUpload/FileUpload.js +6 -5
  191. package/dist/esm/Components/FileUpload/constants.js +1 -0
  192. package/dist/esm/Components/FilterSidebar/FilterSidebar.js +4 -4
  193. package/dist/esm/Components/FilterSidebar/filterHoverStyle.js +4 -10
  194. package/dist/esm/Components/Form/FormWrapper.js +1 -1
  195. package/dist/esm/Components/Header/components/mobileMenu.js +12 -3
  196. package/dist/esm/Components/Input/TextInput.styles.js +6 -4
  197. package/dist/esm/Components/InputTextArea/InputTextArea.style.js +5 -4
  198. package/dist/esm/Components/KanbanBoard/KanbanBoard.js +5 -45
  199. package/dist/esm/Components/LimitReachedModal/LimitReachedModal.js +4 -5
  200. package/dist/esm/Components/Loading/Loading.js +12 -11
  201. package/dist/esm/Components/MultiSelect/MultiSelect.js +1 -1
  202. package/dist/esm/Components/NavigationBar/NavigationBar.js +3 -3
  203. package/dist/esm/Components/NoteTextArea/NoteTextArea.js +9 -4
  204. package/dist/esm/Components/NumberInput/NumberInput.js +4 -2
  205. package/dist/esm/Components/NumberInput/NumberInput.styles.js +5 -4
  206. package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +2 -2
  207. package/dist/esm/Components/PdfViewer/PdfViewer.js +6 -1
  208. package/dist/esm/Components/PinInput/PinInput.js +1 -1
  209. package/dist/esm/Components/PlanPill/PlanPill.js +6 -6
  210. package/dist/esm/Components/PlanPill/planPillGradient.js +3 -15
  211. package/dist/esm/Components/Primitives/Primitives.js +6 -0
  212. package/dist/esm/Components/ProductCard/ProductPrice.js +1 -1
  213. package/dist/esm/Components/ProductCard/ProductReview.js +6 -1
  214. package/dist/esm/Components/ProductCard/ProductTags.js +1 -1
  215. package/dist/esm/Components/ProfileCard/ProfileCard.js +1 -1
  216. package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +1 -1
  217. package/dist/esm/Components/RadioButton/RadioButton.js +3 -2
  218. package/dist/esm/Components/Reorder/Reorder.js +1 -1
  219. package/dist/esm/Components/ScrollToTop/ScrollToTop.js +2 -0
  220. package/dist/esm/Components/ScrollbarBox/ScrollbarBox.js +9 -0
  221. package/dist/esm/Components/Search/Search.js +2 -2
  222. package/dist/esm/Components/SearchSelect/SearchSelect.js +14 -9
  223. package/dist/esm/Components/SegmentedControl/SegmentedControl.js +55 -0
  224. package/dist/esm/Components/SegmentedControl/SegmentedControlProps.js +1 -0
  225. package/dist/esm/Components/Select/Select.js +5 -3
  226. package/dist/esm/Components/Select/Select.styles.js +3 -2
  227. package/dist/esm/Components/SelectSearch/SelectSearch.js +7 -4
  228. package/dist/esm/Components/SelectV2/SelectV2.js +18 -14
  229. package/dist/esm/Components/SideBar/SecondaryBar.js +1 -1
  230. package/dist/esm/Components/SideBar/SideBar.js +1 -1
  231. package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +2 -2
  232. package/dist/esm/Components/Slider/Slider.js +8 -1
  233. package/dist/esm/Components/StageProgress/StageProgress.js +2 -9
  234. package/dist/esm/Components/StageProgress/StepperStage.js +2 -2
  235. package/dist/esm/Components/Table/Table.js +17 -24
  236. package/dist/esm/Components/Table/components/Pagination.js +1 -1
  237. package/dist/esm/Components/Table/components/TableActions.js +2 -2
  238. package/dist/esm/Components/Table/components/TableBody.js +42 -13
  239. package/dist/esm/Components/Table/components/TableGroupRow.js +1 -1
  240. package/dist/esm/Components/Table/components/TableHeader.js +21 -8
  241. package/dist/esm/Components/Table/components/TableSearch.js +1 -1
  242. package/dist/esm/Components/Table/filters/ActiveFilters.js +2 -18
  243. package/dist/esm/Components/Table/settings/ManageColumns.js +4 -17
  244. package/dist/esm/Components/Table/settings/TableSettings.js +5 -11
  245. package/dist/esm/Components/Table/variants/TableVariantContext.js +13 -0
  246. package/dist/esm/Components/Table/variants/classic.js +43 -0
  247. package/dist/esm/Components/Table/variants/index.js +9 -0
  248. package/dist/esm/Components/Table/variants/pillInk.js +18 -0
  249. package/dist/esm/Components/Table/variants/studio.js +46 -0
  250. package/dist/esm/Components/Table/variants/tokens.js +1 -0
  251. package/dist/esm/Components/Tag/Tag.styles.js +10 -9
  252. package/dist/esm/Components/Timeline/Timeline.js +2 -2
  253. package/dist/esm/Components/Toaster/Toaster.js +1 -1
  254. package/dist/esm/Components/Toggle/TableToggle.js +11 -7
  255. package/dist/esm/Components/ToggleSwitch/ToggleSwitch.js +15 -0
  256. package/dist/esm/Components/ToggleSwitch/ToggleSwitchProps.js +1 -0
  257. package/dist/esm/Components/ToolTip/ToolTip.js +6 -6
  258. package/dist/esm/Components/Trail/TrailWorks.js +1 -1
  259. package/dist/esm/Components/UpgradeButton/UpgradeButton.js +3 -3
  260. package/dist/esm/Constants/Sidebar.js +3 -2
  261. package/dist/esm/Theme/assertPalette.js +3 -2
  262. package/dist/esm/Theme/chakra/Menu.styles.js +5 -0
  263. package/dist/esm/Theme/chakra/Popover.styles.js +5 -1
  264. package/dist/esm/Theme/chakra/componentStyles.js +5 -5
  265. package/dist/esm/Theme/chakra/createBrandTheme.js +10 -16
  266. package/dist/esm/Theme/chakra/focusRing.styles.js +40 -0
  267. package/dist/esm/Theme/provider/ThemeSwitcher.js +5 -2
  268. package/dist/esm/Theme/tokens/brands/index.js +6 -1
  269. package/dist/esm/Theme/tokens/builders/buildDarkPalette.js +6 -1
  270. package/dist/esm/Theme/tokens/builders/chartColorsFromTheme.js +1 -1
  271. package/dist/esm/Theme/tokens/builders/focusRing.js +85 -0
  272. package/dist/esm/Theme/tokens/builders/gradientInk.js +22 -0
  273. package/dist/esm/Theme/tokens/builders/index.js +4 -0
  274. package/dist/esm/Theme/tokens/builders/outlineRung.js +96 -4
  275. package/dist/esm/Theme/tokens/builders/placeholderInk.js +29 -0
  276. package/dist/esm/Theme/tokens/builders/resolvedRungs.js +7 -2
  277. package/dist/esm/Theme/tokens/builders/rowStates.js +15 -0
  278. package/dist/esm/Theme/tokens/builders/scrollbar.js +52 -0
  279. package/dist/esm/Theme/tokens/builders/solidFillRung.js +28 -0
  280. package/dist/esm/Theme/tokens/builders/surfaceRungs.js +20 -5
  281. package/dist/esm/index.js +24 -1
  282. package/dist/index.d.ts +27 -3
  283. package/dist/index.js +64 -4
  284. package/package.json +8 -2
@@ -6,9 +6,14 @@ exports.withTableHoverRung = withTableHoverRung;
6
6
  const outlineRung_1 = require("./outlineRung");
7
7
  Object.defineProperty(exports, "AA_NON_TEXT", { enumerable: true, get: function () { return outlineRung_1.AA_NON_TEXT; } });
8
8
  const accentText_1 = require("./accentText");
9
- /** Placeholder text measured 4.32:1 on a card for 8 of 9 brands — text, so held to 4.5:1. */
9
+ const surfaceRungs_1 = require("./surfaceRungs");
10
+ /**
11
+ * A hint is floored at 3:1, not 4.5:1. Held to body-text contrast it stopped reading as a hint —
12
+ * an empty field looked like one already typed into — and the published library ships this rung
13
+ * as the brand authored it. See CHROME-CONSISTENCY.md.
14
+ */
10
15
  function withPlaceholderRung(palette) {
11
- const resolved = (0, outlineRung_1.liftToContrast)(palette.placeholder[500], palette.background[50], accentText_1.AA_NORMAL_TEXT);
16
+ const resolved = (0, outlineRung_1.liftToContrast)(palette.placeholder[500], palette.background[50], surfaceRungs_1.PLACEHOLDER_TEXT);
12
17
  if (resolved === palette.placeholder[500])
13
18
  return palette;
14
19
  return { ...palette, placeholder: { ...palette.placeholder, 500: resolved } };
@@ -0,0 +1,13 @@
1
+ import type { PaletteProps } from "../types";
2
+ export interface RowStateBackgrounds {
3
+ active: string;
4
+ selected: string;
5
+ selectedActive: string;
6
+ }
7
+ /**
8
+ * The three backgrounds a selectable list row can rest on. Every rung is NEUTRAL and pale, so a
9
+ * row's ink stays the canvas ink `withSurfaceRungs` already settled. Seeding a row state from
10
+ * `primary[500]` instead — the filled-button rung — is what painted SelectV2's hovered rows a
11
+ * solid brand fill under unreadable text.
12
+ */
13
+ export declare const listRowBackgrounds: (colors: PaletteProps) => RowStateBackgrounds;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listRowBackgrounds = void 0;
4
+ const outlineRung_1 = require("./outlineRung");
5
+ /**
6
+ * The three backgrounds a selectable list row can rest on. Every rung is NEUTRAL and pale, so a
7
+ * row's ink stays the canvas ink `withSurfaceRungs` already settled. Seeding a row state from
8
+ * `primary[500]` instead — the filled-button rung — is what painted SelectV2's hovered rows a
9
+ * solid brand fill under unreadable text.
10
+ */
11
+ const listRowBackgrounds = (colors) => {
12
+ const selected = colors.backgroundColor.tertiary;
13
+ return {
14
+ active: (0, outlineRung_1.resolveHoverTint)(colors.gray[50], colors.backgroundColor.light),
15
+ selected,
16
+ selectedActive: (0, outlineRung_1.resolveHoverTint)(selected, selected),
17
+ };
18
+ };
19
+ exports.listRowBackgrounds = listRowBackgrounds;
@@ -0,0 +1,12 @@
1
+ import type { PaletteProps } from "../types";
2
+ export type ScrollbarStyle = Record<string, unknown>;
3
+ /** One width everywhere. The 3px, 4px, 5px and 10px bars in use were all accidents. */
4
+ export declare const SCROLLBAR_WIDTH = "6px";
5
+ export type ScrollbarVariant = "default" | "overlay" | "hover";
6
+ /**
7
+ * The single source of scrollbar chrome. `overlay` drops the track for bars that float over a
8
+ * card; `hover` keeps the thumb invisible until the region is hovered.
9
+ */
10
+ export declare function scrollbarStyles(palette: PaletteProps, variant?: ScrollbarVariant): ScrollbarStyle;
11
+ /** The same chrome for the document itself, where `&` selectors do not apply. */
12
+ export declare function globalScrollbarStyles(palette: PaletteProps): ScrollbarStyle;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SCROLLBAR_WIDTH = void 0;
4
+ exports.scrollbarStyles = scrollbarStyles;
5
+ exports.globalScrollbarStyles = globalScrollbarStyles;
6
+ /** One width everywhere. The 3px, 4px, 5px and 10px bars in use were all accidents. */
7
+ exports.SCROLLBAR_WIDTH = "6px";
8
+ const RADIUS = "6px";
9
+ /**
10
+ * The single source of scrollbar chrome. `overlay` drops the track for bars that float over a
11
+ * card; `hover` keeps the thumb invisible until the region is hovered.
12
+ */
13
+ function scrollbarStyles(palette, variant = "default") {
14
+ const { gray } = palette;
15
+ const track = variant === "default" ? gray[100] : "transparent";
16
+ const thumb = variant === "hover" ? "transparent" : gray[400];
17
+ return {
18
+ scrollbarWidth: "thin",
19
+ scrollbarColor: `${thumb} ${track}`,
20
+ "&::-webkit-scrollbar": {
21
+ width: exports.SCROLLBAR_WIDTH,
22
+ height: exports.SCROLLBAR_WIDTH,
23
+ },
24
+ "&::-webkit-scrollbar-track": {
25
+ background: track,
26
+ borderRadius: RADIUS,
27
+ },
28
+ "&::-webkit-scrollbar-thumb": {
29
+ background: thumb,
30
+ borderRadius: RADIUS,
31
+ transition: "background 0.2s ease",
32
+ },
33
+ "&::-webkit-scrollbar-thumb:hover": {
34
+ background: gray[500],
35
+ },
36
+ "&::-webkit-scrollbar-corner": {
37
+ background: track,
38
+ },
39
+ ...(variant === "hover"
40
+ ? {
41
+ "&:hover": { scrollbarColor: `${gray[400]} ${track}` },
42
+ "&:hover::-webkit-scrollbar-thumb": { background: gray[400] },
43
+ }
44
+ : {}),
45
+ };
46
+ }
47
+ /** The same chrome for the document itself, where `&` selectors do not apply. */
48
+ function globalScrollbarStyles(palette) {
49
+ const { gray } = palette;
50
+ return {
51
+ "::-webkit-scrollbar": { width: exports.SCROLLBAR_WIDTH, height: exports.SCROLLBAR_WIDTH },
52
+ "::-webkit-scrollbar-track": { background: gray[100] },
53
+ "::-webkit-scrollbar-thumb": { background: gray[400], borderRadius: RADIUS },
54
+ "::-webkit-scrollbar-thumb:hover": { background: gray[500] },
55
+ "::-webkit-scrollbar-corner": { background: gray[100] },
56
+ };
57
+ }
@@ -0,0 +1,4 @@
1
+ import type { ColorScale, BrandScale } from "../types";
2
+ export type Rung = keyof ColorScale;
3
+ export declare const solidRung: (scale: ColorScale | BrandScale, white: string) => Rung;
4
+ export declare const darkerFillRung: (scale: ColorScale | BrandScale, white: string, isDark: boolean, step: 0 | 1) => Rung;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.darkerFillRung = exports.solidRung = void 0;
4
+ const color_1 = require("./color");
5
+ const AA_NORMAL_TEXT = 4.5;
6
+ const RUNGS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
7
+ const FILL_STEP_RUNGS = { light: [600, 700], dark: [400, 300] };
8
+ const hasOpacity = (scale) => "opacity" in scale;
9
+ // Brand scales keep [500]; utility scales (red, green, …) step to the closest passing rung.
10
+ const solidRung = (scale, white) => {
11
+ if (hasOpacity(scale))
12
+ return 500;
13
+ if ((0, color_1.contrastRatio)(scale[500], white) >= AA_NORMAL_TEXT)
14
+ return 500;
15
+ const passing = RUNGS.filter((r) => scale[r] && (0, color_1.contrastRatio)(scale[r], white) >= AA_NORMAL_TEXT);
16
+ if (!passing.length)
17
+ return 500;
18
+ return passing.reduce((a, b) => (0, color_1.contrastRatio)(scale[a], white) <= (0, color_1.contrastRatio)(scale[b], white) ? a : b);
19
+ };
20
+ exports.solidRung = solidRung;
21
+ const steppedRung = (base, step, isDark) => {
22
+ const dir = isDark ? -100 : 100;
23
+ return Math.min(900, Math.max(50, base + dir * step));
24
+ };
25
+ const darkerFillRung = (scale, white, isDark, step) => {
26
+ const base = (0, exports.solidRung)(scale, white);
27
+ if (base === 500) {
28
+ const mode = isDark ? "dark" : "light";
29
+ return FILL_STEP_RUNGS[mode][step];
30
+ }
31
+ return steppedRung(base, step === 0 ? 1 : 2, isDark);
32
+ };
33
+ exports.darkerFillRung = darkerFillRung;
@@ -1,4 +1,9 @@
1
1
  import type { PaletteProps } from "../types";
2
+ /**
3
+ * A hint is held to the non-text floor, not the body-text one. Deliberate: at 4.5 an empty field
4
+ * reads as one already typed into. See CHROME-CONSISTENCY.md.
5
+ */
6
+ export declare const PLACEHOLDER_TEXT = 3;
2
7
  export declare const namedSurfaces: (palette: PaletteProps) => string[];
3
8
  /** The surface an ink is closest to — clearing it clears every other one. */
4
9
  export declare const hardestSurface: (ink: string, surfaces: string[]) => string;
@@ -1,11 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hardestSurface = exports.namedSurfaces = void 0;
3
+ exports.hardestSurface = exports.namedSurfaces = exports.PLACEHOLDER_TEXT = void 0;
4
4
  exports.withSurfaceRungs = withSurfaceRungs;
5
5
  const color_1 = require("./color");
6
6
  const outlineRung_1 = require("./outlineRung");
7
7
  const accentText_1 = require("./accentText");
8
8
  const semanticText_1 = require("./semanticText");
9
+ /**
10
+ * A hint is held to the non-text floor, not the body-text one. Deliberate: at 4.5 an empty field
11
+ * reads as one already typed into. See CHROME-CONSISTENCY.md.
12
+ */
13
+ exports.PLACEHOLDER_TEXT = outlineRung_1.AA_NON_TEXT;
9
14
  const namedSurfaces = (palette) => [
10
15
  ...Object.values(palette.backgroundColor),
11
16
  palette.background[50],
@@ -19,6 +24,8 @@ exports.hardestSurface = hardestSurface;
19
24
  function withSurfaceRungs(palette) {
20
25
  const surfaces = (0, exports.namedSurfaces)(palette);
21
26
  const lift = (ink, target) => (0, outlineRung_1.liftToContrast)(ink, (0, exports.hardestSurface)(ink, surfaces), target);
27
+ const settle = (ink, target) => (0, outlineRung_1.settleAcross)(ink, surfaces, target);
28
+ const rule = (ink) => (0, outlineRung_1.ruleInk)(ink, [palette.backgroundColor.main, palette.background[50]], outlineRung_1.CHROME_RULE);
22
29
  const semanticText = semanticText_1.SEMANTIC_TONES.reduce((acc, tone) => {
23
30
  acc[tone] = lift(palette.semanticText[tone], accentText_1.AA_NORMAL_TEXT);
24
31
  return acc;
@@ -30,10 +37,18 @@ function withSurfaceRungs(palette) {
30
37
  semanticText,
31
38
  text: { ...palette.text, 500: lift(palette.text[500], accentText_1.AA_NORMAL_TEXT) },
32
39
  header: { ...palette.header, 500: lift(palette.header[500], accentText_1.AA_NORMAL_TEXT) },
33
- placeholder: {
34
- ...palette.placeholder,
35
- 500: lift(palette.placeholder[500], accentText_1.AA_NORMAL_TEXT),
40
+ // `placeholder` is deliberately absent: the brand authors that rung and the published
41
+ // library ships it untouched. Its floor is enforced in `placeholderInk`, against the
42
+ // surfaces a FIELD renders on — not `table.hover`, which no field sits on.
43
+ // Settled, not lifted: this rung already passed through `withOutlineRung`, and lifting it
44
+ // again here to AA_NON_TEXT is what shipped 3.85-4.95:1 outlines. See CHROME_BORDER.
45
+ boxborder: {
46
+ ...palette.boxborder,
47
+ 200: rule(palette.boxborder[200]),
48
+ 500: settle(palette.boxborder[500], outlineRung_1.CHROME_BORDER),
36
49
  },
37
- boxborder: { ...palette.boxborder, 500: lift(palette.boxborder[500], outlineRung_1.AA_NON_TEXT) },
50
+ // The 200 rung of both ramps is the same job under two names; settle them together or an
51
+ // app picking `border` gets a heavier rule than one picking `boxborder`.
52
+ border: { ...palette.border, 200: rule(palette.border[200]) },
38
53
  };
39
54
  }
@@ -65,6 +65,24 @@ export type SidebarTokens = {
65
65
  /** Status ink resolved against the RAIL. `semanticText` is canvas-relative, not rail. */
66
66
  semanticText: SemanticTextTokens;
67
67
  };
68
+ /**
69
+ * The two-tone focus indicator. `ring` is the brand-toned band that carries WCAG 1.4.11
70
+ * against whatever is BEHIND the control; `halo` is the inner band drawn between the ring
71
+ * and the control's own fill, so the indicator stays visible on a filled button too.
72
+ *
73
+ * Two tones because one band cannot own both adjacencies. `ring` is resolved against three
74
+ * backdrops (canvas, card, `primary[500]`) and clears all three on every brand — but a button
75
+ * can be filled by any `colorScheme`, and widening the ring to cover the mid-tone named surfaces
76
+ * as well leaves `skyline` and `storefront` with no solution at any hue (contrast is luminance
77
+ * alone, and their bands come out empty). `halo` takes the fill-side adjacency instead, so the
78
+ * pair holds whatever the button is filled with and whatever it sits on.
79
+ */
80
+ export type FocusRingTokens = {
81
+ /** Outer band. >=3:1 against the canvas, a card surface, and the control's own fill. */
82
+ ring: string;
83
+ /** Inner band, in the surface tone. >=3:1 against the fill it hugs and against `ring`. */
84
+ halo: string;
85
+ };
68
86
  export type BoxShadowTokens = {
69
87
  primary: string;
70
88
  error: string;
@@ -102,6 +120,8 @@ export type PaletteProps = {
102
120
  header: ColorScale;
103
121
  placeholder: ColorScale;
104
122
  boxShadow: BoxShadowTokens;
123
+ /** Two-tone `:focus-visible` indicator, resolved to 3:1 (WCAG 1.4.11). Never hand-written. */
124
+ focusRing: FocusRingTokens;
105
125
  sidebar: SidebarTokens;
106
126
  boxborder: ColorScale;
107
127
  border: ColorScale;
@@ -125,7 +145,7 @@ export type PaletteProps = {
125
145
  };
126
146
  /** What a brand writes by hand; every token required, so a gap is a compile error. */
127
147
  /** Derived tokens are excluded — `finalizeLightPalette` resolves them per brand. */
128
- export type BrandPaletteSource = Omit<PaletteProps, "accentText" | "textMuted" | "semanticText" | "sidebar" | "onPrimary" | "onSemantic"> & {
148
+ export type BrandPaletteSource = Omit<PaletteProps, "accentText" | "textMuted" | "semanticText" | "sidebar" | "onPrimary" | "onSemantic" | "focusRing"> & {
129
149
  sidebar: {
130
150
  background: ColorScale;
131
151
  };
@@ -4,21 +4,23 @@ import { Accordion as ChakraAccordian, AccordionItem, AccordionButton, Accordion
4
4
  import { ChevronDown } from "lucide-react";
5
5
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
6
  import { isDarkCanvas } from "../../Theme/tokens/builders/accentText.js";
7
+ import { liftToContrast, AA_NON_TEXT } from "../../Theme/tokens/builders/outlineRung.js";
7
8
  import { getAccordionHoverColors } from "./accordionColors.js";
8
9
  const Accordion = ({ allowMultiple, allowToggle, index, onChange, reduceMotion, items, ...rest }) => {
9
10
  const { colors } = useCustomTheme();
10
11
  const isDark = isDarkCanvas(colors.backgroundColor.main);
11
- const border = colors.boxborder[300];
12
+ const border = colors.boxborder[200];
12
13
  const headingColor = colors.text[800];
13
14
  const bodyColor = colors.text[600];
14
15
  const { expandedBg, softHover } = useMemo(() => getAccordionHoverColors(colors, isDark), [colors, isDark]);
16
+ const focusRing = useMemo(() => liftToContrast(colors.primary[300], colors.background[50], AA_NON_TEXT), [colors]);
15
17
  return (_jsx(ChakraAccordian, { allowMultiple: allowMultiple, allowToggle: allowToggle, index: index, onChange: onChange, reduceMotion: reduceMotion, ...rest, children: items.map((item, idx) => {
16
18
  var _a;
17
- return (_jsx(AccordionItem, { border: `0.063rem solid ${border}`, borderRadius: "xl", bg: colors.background[50], mb: 3, overflow: "hidden", transition: "border-color 0.15s ease, box-shadow 0.15s ease", _last: { mb: 0 }, _hover: { borderColor: colors.boxborder[400] }, ...item.itemProps, sx: (_a = item.customStyles) === null || _a === void 0 ? void 0 : _a.itemStyle, children: ({ isExpanded }) => {
19
+ return (_jsx(AccordionItem, { border: `0.063rem solid ${border}`, borderRadius: "xl", bg: colors.background[50], mb: 3, overflow: "hidden", transition: "border-color 0.15s ease, box-shadow 0.15s ease", _last: { mb: 0 }, _hover: { borderColor: colors.boxborder[200] }, ...item.itemProps, sx: (_a = item.customStyles) === null || _a === void 0 ? void 0 : _a.itemStyle, children: ({ isExpanded }) => {
18
20
  var _a, _b, _c;
19
21
  return (_jsxs(_Fragment, { children: [_jsx("h2", { children: _jsxs(AccordionButton, { px: 4, py: 3, gap: 3, fontSize: "0.875rem", fontWeight: 600, color: headingColor, bg: isExpanded ? expandedBg : "transparent", transition: "background 0.15s ease", _hover: { bg: isExpanded ? expandedBg : softHover }, _focusVisible: {
20
22
  boxShadow: "none",
21
- outline: `0.125rem solid ${colors.primary[300]}`,
23
+ outline: `0.125rem solid ${focusRing}`,
22
24
  outlineOffset: "-0.125rem",
23
25
  }, ...item.buttonProps, sx: {
24
26
  ...(_a = item.customStyles) === null || _a === void 0 ? void 0 : _a.buttonStyle,
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { isValidElement } from "react";
3
+ import { Box, Flex, Image } from "@chakra-ui/react";
4
+ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
5
+ import { mix } from "../../Theme/tokens/builders/color.js";
6
+ import { onFilled } from "../../Theme/tokens/builders/accentText.js";
7
+ import { liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
8
+ import { AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
9
+ import { CATEGORICAL_SERIES } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
10
+ import { resolveAvatarFill } from "../../Theme/tokens/builders/statusTone.js";
11
+ import { avatarColor } from "../OrgSwitcher/OrgSwitcherProps.js";
12
+ import { appInitials } from "./AppSwitcherProps.js";
13
+ // The app's own hue at 85% strength — the solid mark, softened just off full saturation.
14
+ const FILL_STRENGTH = 0.85;
15
+ /**
16
+ * One hue per app. `avatarColor` hashes into an 8-hue series, so six apps collide more often than
17
+ * not — Billing and Account shipped identical marks. Assigned over the WHOLE list, never the
18
+ * filtered one, so searching cannot repaint a mark.
19
+ */
20
+ export const assignAppHues = (apps, mode) => {
21
+ const ramp = CATEGORICAL_SERIES[mode].map(resolveAvatarFill);
22
+ const taken = new Set(apps.flatMap((a) => (a.color ? [a.color] : [])));
23
+ return apps.map((app) => {
24
+ if (app.color)
25
+ return app.color;
26
+ const seed = avatarColor(app.id || app.name, mode);
27
+ const start = Math.max(0, ramp.indexOf(seed));
28
+ for (let i = 0; i < ramp.length; i += 1) {
29
+ const next = ramp[(start + i) % ramp.length];
30
+ if (!taken.has(next)) {
31
+ taken.add(next);
32
+ return next;
33
+ }
34
+ }
35
+ // More apps than the series has hues; past this point a repeat is unavoidable.
36
+ return seed;
37
+ });
38
+ };
39
+ // Softening costs the fill contrast, so it is walked back until a WHITE glyph clears AA — every
40
+ // mark keeps the same white icon instead of some flipping to black.
41
+ export const appTint = (hue, surface, white) => liftToContrast(mix(hue, surface, 1 - FILL_STRENGTH), white, AA_NORMAL_TEXT);
42
+ export const appTintInk = (fill, white, black) => onFilled(fill, white, black);
43
+ const AppMark = ({ app, hue, boxSize, iconSize, muted = false, }) => {
44
+ const { colors } = useCustomTheme();
45
+ if (app.iconUrl) {
46
+ return (_jsx(Image, { src: app.iconUrl, alt: "", boxSize: boxSize, borderRadius: "lg", objectFit: "cover", flexShrink: 0, opacity: muted ? 0.55 : 1 }));
47
+ }
48
+ const surface = colors.backgroundColor.light;
49
+ const fill = muted ? colors.backgroundColor.quaternary : appTint(hue, surface, colors.white);
50
+ const ink = muted ? colors.textMuted : appTintInk(fill, colors.white, colors.black);
51
+ // lucide icons are forwardRef objects, not functions — a typeof check silently drops them.
52
+ const isElement = isValidElement(app.icon);
53
+ const Icon = !isElement && app.icon ? app.icon : null;
54
+ return (_jsx(Flex, { align: "center", justify: "center", boxSize: boxSize, borderRadius: "lg", flexShrink: 0, bg: fill, color: ink, fontSize: `${Math.round(iconSize * 0.72)}px`, fontWeight: 700, "aria-hidden": true, children: Icon ? (_jsx(Icon, { size: iconSize, color: ink })) : isValidElement(app.icon) ? (_jsx(Box, { display: "flex", color: ink, children: app.icon })) : (appInitials(app.name)) }));
55
+ };
56
+ export default AppMark;
@@ -0,0 +1,280 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Box, Flex, Input, InputGroup, InputLeftElement, Popover, PopoverBody, PopoverContent, PopoverTrigger, Skeleton, Text, useDisclosure, } from "@chakra-ui/react";
4
+ import { Grip, Lock, Search } from "lucide-react";
5
+ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
+ import { chartMode } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
7
+ import { AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
8
+ import { liftToContrast, neutralStep, resolveHoverTint, ruleInk, AA_NON_TEXT, CHROME_RULE, MIN_PERCEPTIBLE_STEP, } from "../../Theme/tokens/builders/outlineRung.js";
9
+ import AppMark, { assignAppHues } from "./AppMark.js";
10
+ import { filterApps, formatShortcut, } from "./AppSwitcherProps.js";
11
+ import { useAppSwitcherShortcut } from "./useAppSwitcherShortcut.js";
12
+ const COLUMNS = 2;
13
+ const HEADING = "Your apps";
14
+ const s = { trigger: "36px", mark: "32px", icon: 17, name: "0.875rem", sub: "0.75rem", row: "52px" };
15
+ const warned = new Set();
16
+ const warnOnce = (key, message) => {
17
+ if (process.env.NODE_ENV === "production" || warned.has(key))
18
+ return;
19
+ warned.add(key);
20
+ console.error(`[AppSwitcher] ${message}`);
21
+ };
22
+ const validate = (apps) => {
23
+ const seen = new Set();
24
+ apps.forEach((app, i) => {
25
+ var _a;
26
+ if (!app.id)
27
+ warnOnce(`id-${i}`, `apps[${i}] ("${(_a = app.name) !== null && _a !== void 0 ? _a : "unnamed"}") has no id.`);
28
+ else if (seen.has(app.id))
29
+ warnOnce(`dup-${app.id}`, `duplicate app id "${app.id}".`);
30
+ else
31
+ seen.add(app.id);
32
+ if (!app.url && !app.locked)
33
+ warnOnce(`url-${app.id || i}`, `app "${app.name}" has no url; it cannot be opened.`);
34
+ });
35
+ };
36
+ // Past a pointer's pause threshold, so crossing the launcher on the way elsewhere does not open it,
37
+ // but short enough that deliberately resting on it feels instant.
38
+ const HOVER_OPEN_DELAY = 450;
39
+ // Long enough to travel the gap between the launcher and the panel below it.
40
+ const HOVER_CLOSE_GRACE = 250;
41
+ // Flip back to true to restore `searchThreshold` opening the field on its own.
42
+ const SEARCH_OPENS_BY_COUNT = false;
43
+ const AppSwitcher = ({ apps, currentAppId, onSelect, onOpenChange, isSearchable, searchThreshold = 8, shortcut = null, showShortcutHint = true, hideWhenAlone = true, emptyMessage = "No app matches that name.", footer, ariaLabel = "Switch app", panelWidth, maxHeight = "24rem", isLoading = false, }) => {
44
+ const theme = useCustomTheme();
45
+ const { isOpen, onOpen, onClose } = useDisclosure();
46
+ const [query, setQuery] = useState("");
47
+ const [activeIndex, setActiveIndex] = useState(0);
48
+ const searchRef = useRef(null);
49
+ const triggerRef = useRef(null);
50
+ const panelRef = useRef(null);
51
+ const itemRefs = useRef([]);
52
+ const typeahead = useRef({ buffer: "", at: 0 });
53
+ const hoverTimer = useRef();
54
+ const openedByHover = useRef(false);
55
+ useEffect(() => validate(apps), [apps]);
56
+ // Search is off while the panel's design settles: the app count alone no longer opens it. An
57
+ // explicit `isSearchable` still wins, so nothing about the prop changed.
58
+ const showSearch = isSearchable !== null && isSearchable !== void 0 ? isSearchable : (SEARCH_OPENS_BY_COUNT && apps.length >= searchThreshold);
59
+ const hues = useMemo(() => assignAppHues(apps, chartMode(theme.colors)), [apps, theme.colors]);
60
+ const visible = useMemo(() => filterApps(apps, query), [apps, query]);
61
+ const setOpen = useCallback((next) => {
62
+ if (next)
63
+ onOpen();
64
+ else
65
+ onClose();
66
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
67
+ }, [onOpen, onClose, onOpenChange]);
68
+ const cancelHoverTimer = useCallback(() => {
69
+ if (hoverTimer.current)
70
+ clearTimeout(hoverTimer.current);
71
+ hoverTimer.current = undefined;
72
+ }, []);
73
+ useEffect(() => cancelHoverTimer, [cancelHoverTimer]);
74
+ // Touch and stylus report no hover; a timer there would open the panel on a tap-and-hold.
75
+ const pointerHovers = () => {
76
+ var _a;
77
+ return typeof window !== "undefined" &&
78
+ ((_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, "(hover: hover) and (pointer: fine)").matches);
79
+ };
80
+ const handleHoverStart = useCallback(() => {
81
+ if (isOpen || !pointerHovers())
82
+ return;
83
+ cancelHoverTimer();
84
+ hoverTimer.current = setTimeout(() => {
85
+ openedByHover.current = true;
86
+ setOpen(true);
87
+ }, HOVER_OPEN_DELAY);
88
+ }, [isOpen, cancelHoverTimer, setOpen]);
89
+ // A click-opened panel is sticky, and a keyboard user inside it is not "hovering away".
90
+ const handleHoverEnd = useCallback(() => {
91
+ cancelHoverTimer();
92
+ if (!isOpen || !openedByHover.current)
93
+ return;
94
+ hoverTimer.current = setTimeout(() => {
95
+ var _a;
96
+ if ((_a = panelRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement))
97
+ return;
98
+ openedByHover.current = false;
99
+ setOpen(false);
100
+ setQuery("");
101
+ }, HOVER_CLOSE_GRACE);
102
+ }, [isOpen, cancelHoverTimer, setOpen]);
103
+ const closeAndRefocus = useCallback(() => {
104
+ var _a;
105
+ setOpen(false);
106
+ setQuery("");
107
+ (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
108
+ }, [setOpen]);
109
+ useAppSwitcherShortcut(shortcut, () => setOpen(true), !isOpen);
110
+ useEffect(() => {
111
+ if (!isOpen)
112
+ setQuery("");
113
+ setActiveIndex(0);
114
+ }, [isOpen]);
115
+ useEffect(() => setActiveIndex(0), [query]);
116
+ const focusItem = useCallback((next) => {
117
+ var _a;
118
+ setActiveIndex(next);
119
+ (_a = itemRefs.current[next]) === null || _a === void 0 ? void 0 : _a.focus();
120
+ }, []);
121
+ // Read the focused row, not state: two keydowns in one React batch would both compute from a
122
+ // stale activeIndex, so holding an arrow key silently dropped moves.
123
+ const currentIndex = useCallback(() => {
124
+ const focused = itemRefs.current.findIndex((el) => el === document.activeElement);
125
+ return focused === -1 ? activeIndex : focused;
126
+ }, [activeIndex]);
127
+ const step = useCallback((delta) => {
128
+ const count = visible.length;
129
+ if (count === 0)
130
+ return;
131
+ focusItem(((currentIndex() + delta) % count + count) % count);
132
+ }, [currentIndex, visible.length, focusItem]);
133
+ const jumpToLetter = useCallback((char) => {
134
+ const now = Date.now();
135
+ const buffer = now - typeahead.current.at < 600 ? typeahead.current.buffer + char : char;
136
+ typeahead.current = { buffer, at: now };
137
+ const at = currentIndex();
138
+ const from = buffer.length > 1 ? at : at + 1;
139
+ const order = visible.map((_, i) => (from + i) % visible.length);
140
+ const hit = order.find((i) => visible[i].name.toLowerCase().startsWith(buffer));
141
+ if (hit !== undefined)
142
+ focusItem(hit);
143
+ }, [visible, currentIndex, focusItem]);
144
+ // Chakra parks focus on the panel itself, where no arrow handler lives; route the first press in.
145
+ const handlePanelKeyDown = useCallback((e) => {
146
+ if (itemRefs.current.includes(document.activeElement))
147
+ return;
148
+ if (visible.length === 0)
149
+ return;
150
+ if (["ArrowDown", "ArrowRight", "Home"].includes(e.key)) {
151
+ e.preventDefault();
152
+ focusItem(0);
153
+ }
154
+ else if (["ArrowUp", "ArrowLeft", "End"].includes(e.key)) {
155
+ e.preventDefault();
156
+ focusItem(visible.length - 1);
157
+ }
158
+ else if (e.key === "Escape") {
159
+ e.preventDefault();
160
+ closeAndRefocus();
161
+ }
162
+ }, [visible.length, focusItem, closeAndRefocus]);
163
+ const handleItemKeyDown = useCallback((e, app) => {
164
+ if (e.key === "ArrowDown") {
165
+ e.preventDefault();
166
+ step(COLUMNS);
167
+ }
168
+ else if (e.key === "ArrowUp") {
169
+ e.preventDefault();
170
+ step(-COLUMNS);
171
+ }
172
+ else if (e.key === "ArrowRight") {
173
+ e.preventDefault();
174
+ step(1);
175
+ }
176
+ else if (e.key === "ArrowLeft") {
177
+ e.preventDefault();
178
+ step(-1);
179
+ }
180
+ else if (e.key === "Home") {
181
+ e.preventDefault();
182
+ focusItem(0);
183
+ }
184
+ else if (e.key === "End") {
185
+ e.preventDefault();
186
+ focusItem(visible.length - 1);
187
+ }
188
+ else if (e.key === "Escape") {
189
+ e.preventDefault();
190
+ closeAndRefocus();
191
+ }
192
+ else if (e.key === " " && !app.locked) {
193
+ e.preventDefault();
194
+ e.currentTarget.click();
195
+ }
196
+ else if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey)
197
+ jumpToLetter(e.key.toLowerCase());
198
+ }, [step, focusItem, visible.length, closeAndRefocus, jumpToLetter]);
199
+ const handleSelect = (app, e) => {
200
+ if (app.locked) {
201
+ e.preventDefault();
202
+ return;
203
+ }
204
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(app, e);
205
+ if (!e.defaultPrevented)
206
+ setOpen(false);
207
+ };
208
+ const nothingToSwitchTo = apps.length === 0 || (hideWhenAlone && apps.every((a) => a.id === currentAppId));
209
+ if (nothingToSwitchTo)
210
+ return null;
211
+ const rowHoverBg = resolveHoverTint(theme.colors.gray[50], theme.colors.backgroundColor.light);
212
+ const footerRule = ruleInk(theme.colors.boxborder[200], [theme.colors.backgroundColor.subtle], CHROME_RULE);
213
+ // Neutral, not `backgroundColor.tertiary`: that rung is brand-tinted in the hand-authored light
214
+ // palettes, so lifting it painted the row a saturated block. The rail identifies the row, so the
215
+ // fill only has to be perceptible — pushed further it is a mid-grey slab that reads disabled.
216
+ const currentBg = neutralStep(theme.colors.backgroundColor.light, MIN_PERCEPTIBLE_STEP);
217
+ // The brand rail is the whole marker — no ring, no badge. `accentText` already clears AA as ink
218
+ // on the canvas; lifting it to AA_NON_TEXT here guarantees it on the row's own fill too, for a
219
+ // brand whose accent is pale.
220
+ const currentBar = liftToContrast(theme.colors.accentText, currentBg, AA_NON_TEXT);
221
+ // A derived surface is outside what `withSurfaceRungs` settled, so its ink is resolved here.
222
+ const currentName = liftToContrast(theme.colors.text[900], currentBg, AA_NORMAL_TEXT);
223
+ const currentSub = liftToContrast(theme.colors.textMuted, currentBg, AA_NORMAL_TEXT);
224
+ const width = panelWidth !== null && panelWidth !== void 0 ? panelWidth : "28rem";
225
+ const hint = shortcut && showShortcutHint ? formatShortcut(shortcut) : null;
226
+ return (_jsxs(Popover, { isOpen: isOpen, onOpen: () => setOpen(true), onClose: () => { setOpen(false); setQuery(""); }, placement: "bottom-end", initialFocusRef: showSearch ? searchRef : undefined, isLazy: true, children: [_jsx(PopoverTrigger, { children: _jsx(Flex, { ref: triggerRef, as: "button", type: "button", "aria-label": ariaLabel, align: "center", justify: "center", boxSize: s.trigger, borderRadius: "lg", border: "1px solid", borderColor: isOpen ? theme.colors.boxborder[500] : theme.colors.boxborder[200], bg: isOpen ? theme.colors.backgroundColor.tertiary : "transparent", color: isOpen ? theme.colors.text[900] : theme.colors.textMuted, cursor: "pointer", transition: "background 0.15s ease, border-color 0.15s ease", _hover: { bg: rowHoverBg, color: theme.colors.text[900] }, onMouseEnter: handleHoverStart, onMouseLeave: handleHoverEnd, onClick: () => { openedByHover.current = false; cancelHoverTimer(); }, onKeyDown: (e) => {
227
+ if (!isOpen) {
228
+ if (e.key === "ArrowDown") {
229
+ e.preventDefault();
230
+ setOpen(true);
231
+ }
232
+ return;
233
+ }
234
+ handlePanelKeyDown(e);
235
+ }, children: _jsx(Grip, { size: s.icon + 1 }) }) }), _jsx(PopoverContent, { ref: panelRef, onMouseEnter: cancelHoverTimer, onMouseLeave: handleHoverEnd, w: width, maxW: "calc(100vw - 1.5rem)", textAlign: "start", onKeyDown: handlePanelKeyDown, bg: theme.colors.backgroundColor.light, borderRadius: "xl", overflow: "hidden", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}`, _focus: { outline: "none", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}` }, children: _jsxs(PopoverBody, { p: 0, children: [_jsxs(Flex, { align: "center", gap: 2, px: 3, pt: 3, pb: showSearch ? 2 : 1, children: [_jsx(Text, { fontSize: "0.6875rem", fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: theme.colors.textMuted, children: HEADING }), _jsx(Box, { flex: "1" }), hint && (_jsx(Text, { as: "kbd", fontSize: "0.6875rem", fontFamily: "inherit", color: theme.colors.textMuted, border: "1px solid", borderRadius: "base", px: 1.5, py: 0.5, children: hint }))] }), showSearch && (_jsx(Box, { px: 3, pb: 2, children: _jsxs(InputGroup, { size: "sm", children: [_jsx(InputLeftElement, { pointerEvents: "none", children: _jsx(Search, { size: 15, color: theme.colors.textMuted }) }), _jsx(Input, { ref: searchRef, value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search apps", borderRadius: "lg", "aria-label": "Search apps", onKeyDown: (e) => {
236
+ if (e.key === "ArrowDown" && visible.length > 0) {
237
+ e.preventDefault();
238
+ focusItem(0);
239
+ }
240
+ else if (e.key === "Escape") {
241
+ e.preventDefault();
242
+ if (query)
243
+ setQuery("");
244
+ else
245
+ closeAndRefocus();
246
+ }
247
+ } })] }) })), _jsxs(Box, { role: "menu", "aria-label": HEADING, maxH: maxHeight, overflowY: "auto", px: 2, pb: 2, display: "grid", gridTemplateColumns: `repeat(${COLUMNS}, minmax(0, 1fr))`, gap: "2px", children: [isLoading
248
+ ? [0, 1, 2, 3].map((i) => (_jsxs(Flex, { align: "center", gap: 3, px: 2, py: 2, minH: s.row, children: [_jsx(Skeleton, { boxSize: s.mark, borderRadius: "lg" }), _jsx(Skeleton, { h: "0.75rem", flex: "1", borderRadius: "base" })] }, i)))
249
+ : visible.map((app, idx) => {
250
+ var _a;
251
+ const isCurrent = app.id === currentAppId;
252
+ const interactive = !isCurrent && !app.locked;
253
+ return (_jsxs(Flex, { as: interactive ? "a" : "div", role: "menuitem", ref: (el) => { itemRefs.current[idx] = el; }, tabIndex: idx === activeIndex ? 0 : -1, "aria-current": isCurrent ? "true" : undefined, "aria-disabled": app.locked ? true : undefined, title: app.locked ? ((_a = app.lockedReason) !== null && _a !== void 0 ? _a : `${app.name} is not available on your plan`) : undefined, ...(interactive
254
+ ? {
255
+ href: app.url,
256
+ target: "_blank",
257
+ rel: "noopener noreferrer",
258
+ }
259
+ : {}), align: "center", gap: 3, minH: s.row, px: 2, py: 2, borderRadius: "lg", textDecoration: "none", cursor: interactive ? "pointer" : "default", bg: isCurrent ? currentBg : "transparent", position: "relative",
260
+ // Full height, taking the row's own `lg` radius on its outer corners so the
261
+ // rail follows the row's curve instead of cutting across it.
262
+ _before: isCurrent
263
+ ? {
264
+ content: '""',
265
+ position: "absolute",
266
+ insetInlineStart: 0,
267
+ insetBlock: 0,
268
+ width: "3px",
269
+ borderStartStartRadius: "lg",
270
+ borderEndStartRadius: "lg",
271
+ bg: currentBar,
272
+ }
273
+ : undefined, transition: "background 0.12s ease", _hover: interactive ? { bg: rowHoverBg } : undefined, onClick: (e) => handleSelect(app, e), onKeyDown: (e) => handleItemKeyDown(e, app), children: [_jsx(AppMark, { app: app, hue: hues[apps.indexOf(app)], boxSize: s.mark, iconSize: s.icon, muted: app.locked }), _jsxs(Box, { flex: "1", minW: 0, children: [_jsxs(Flex, { align: "center", gap: 1.5, children: [_jsx(Text, { fontSize: s.name, fontWeight: 600, color: app.locked
274
+ ? theme.colors.textMuted
275
+ : isCurrent
276
+ ? currentName
277
+ : theme.colors.text[900], overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: app.name }), app.badge !== undefined && !isCurrent && (_jsx(Text, { fontSize: "0.625rem", fontWeight: 600, color: theme.colors.semanticText.info, flexShrink: 0, children: app.badge }))] }), app.description && (_jsx(Text, { fontSize: s.sub, color: isCurrent ? currentSub : theme.colors.textMuted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: app.description }))] }), app.locked && (_jsx(Box, { as: Lock, size: 14, color: theme.colors.textMuted, flexShrink: 0 }))] }, app.id || app.name));
278
+ }), !isLoading && visible.length === 0 && (_jsx(Box, { gridColumn: `span ${COLUMNS}`, px: 2, py: 6, textAlign: "center", children: _jsx(Text, { fontSize: s.name, color: theme.colors.textMuted, children: emptyMessage }) }))] }), footer && (_jsx(Flex, { align: "center", gap: 1.5, px: 3, py: 2.5, borderTop: "1px solid", borderTopColor: footerRule, bg: theme.colors.backgroundColor.subtle, children: footer }))] }) })] }));
279
+ };
280
+ export default AppSwitcher;