pixelize-design-library 2.4.2-beta.66 → 2.4.2-beta.68

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 (309) hide show
  1. package/CHANGELOG.md +71 -4
  2. package/COMPONENT-GOTCHAS.md +105 -66
  3. package/DESIGN-GUIDE.md +18 -6
  4. package/README.md +14 -11
  5. package/dist/Components/Accordion/AccordionProps.d.ts +10 -0
  6. package/dist/Components/AddressCard/AddressCardProps.d.ts +15 -0
  7. package/dist/Components/Apexcharts/ApexAreaChart/ApexAreaChartProps.d.ts +11 -0
  8. package/dist/Components/Apexcharts/ApexBarChart/ApexBarChartProps.d.ts +17 -1
  9. package/dist/Components/Apexcharts/ApexLineChart/ApexLineChartProps.d.ts +10 -0
  10. package/dist/Components/Apexcharts/ApexPieChart/ApexPieChartProps.d.ts +14 -0
  11. package/dist/Components/Apexcharts/ApexPolarCharts/ApexPolarChartsProps.d.ts +9 -0
  12. package/dist/Components/Apexcharts/ApexRadialChart/ApexRadialChartProps.d.ts +10 -0
  13. package/dist/Components/AppSwitcher/AppSwitcher.d.ts +1 -0
  14. package/dist/Components/AppSwitcher/AppSwitcher.js +1 -0
  15. package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +23 -0
  16. package/dist/Components/AppSwitcher/pixelizeSuite.d.ts +1 -0
  17. package/dist/Components/AppearanceSettings/AppearanceSettings.d.ts +1 -0
  18. package/dist/Components/AppearanceSettings/AppearanceSettings.js +1 -0
  19. package/dist/Components/AppearanceSettings/AppearanceSettingsProps.d.ts +11 -0
  20. package/dist/Components/AudioRecorder/AudioRecorder.d.ts +1 -0
  21. package/dist/Components/AudioRecorder/AudioRecorder.js +1 -0
  22. package/dist/Components/AudioRecorder/AudioRecorderProps.d.ts +6 -0
  23. package/dist/Components/Avatar/AvatarGroup.d.ts +1 -0
  24. package/dist/Components/Avatar/AvatarGroup.js +1 -0
  25. package/dist/Components/Avatar/AvatarGroupProps.d.ts +2 -0
  26. package/dist/Components/Avatar/AvatarProps.d.ts +2 -2
  27. package/dist/Components/Avatar/avatarColorway.d.ts +1 -0
  28. package/dist/Components/BagBar/BagBarProps.d.ts +8 -0
  29. package/dist/Components/BulkSelectionBar/BulkSelectionBar.d.ts +1 -0
  30. package/dist/Components/BulkSelectionBar/BulkSelectionBar.js +1 -0
  31. package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.d.ts +6 -0
  32. package/dist/Components/Button/Button.js +1 -0
  33. package/dist/Components/Button/ButtonProps.d.ts +3 -0
  34. package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.d.ts +1 -0
  35. package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -0
  36. package/dist/Components/ButtonGroupIcon/ButtonGroupIconProps.d.ts +19 -0
  37. package/dist/Components/CalendarGrid/CalendarGrid.d.ts +1 -0
  38. package/dist/Components/CalendarGrid/CalendarGrid.js +1 -0
  39. package/dist/Components/CalendarGrid/CalendarGridProps.d.ts +11 -0
  40. package/dist/Components/CalendarGrid/calendarMath.d.ts +13 -0
  41. package/dist/Components/Card/CardProps.d.ts +4 -0
  42. package/dist/Components/Card/PaymentCard/PaymentCard.d.ts +1 -0
  43. package/dist/Components/Card/PaymentCard/PaymentCard.js +1 -0
  44. package/dist/Components/Card/PaymentCard/PaymentCardProps.d.ts +19 -0
  45. package/dist/Components/CartItem/CartItemProps.d.ts +10 -0
  46. package/dist/Components/Checkbox/CheckboxProps.d.ts +1 -0
  47. package/dist/Components/ChoiceCard/ChoiceCard.d.ts +1 -0
  48. package/dist/Components/ChoiceCard/ChoiceCard.js +1 -0
  49. package/dist/Components/ChoiceCard/ChoiceCardGroup.d.ts +1 -0
  50. package/dist/Components/ChoiceCard/ChoiceCardGroup.js +1 -0
  51. package/dist/Components/ChoiceCard/ChoiceCardProps.d.ts +3 -0
  52. package/dist/Components/ConfirmDialog/ConfirmDialog.d.ts +1 -0
  53. package/dist/Components/ConfirmDialog/ConfirmDialog.js +1 -0
  54. package/dist/Components/ConfirmDialog/ConfirmDialogProps.d.ts +5 -0
  55. package/dist/Components/ContactForm/ContactFormProps.d.ts +13 -0
  56. package/dist/Components/CouponField/CouponFieldProps.d.ts +16 -0
  57. package/dist/Components/CustomModulesTable/CustomModulesTable.d.ts +1 -0
  58. package/dist/Components/CustomModulesTable/CustomModulesTable.js +1 -0
  59. package/dist/Components/CustomModulesTable/CustomModulesTableProps.d.ts +17 -0
  60. package/dist/Components/CustomModulesTable/DeleteModuleModal.d.ts +1 -0
  61. package/dist/Components/CustomModulesTable/DeleteModuleModal.js +1 -0
  62. package/dist/Components/CustomModulesTable/EditModuleModal.d.ts +1 -0
  63. package/dist/Components/CustomModulesTable/EditModuleModal.js +1 -0
  64. package/dist/Components/DatePicker/DatePickerProps.d.ts +7 -1
  65. package/dist/Components/DetailSections/DetailSectionsProps.d.ts +22 -0
  66. package/dist/Components/Drawer/DrawerProps.d.ts +1 -1
  67. package/dist/Components/Dropdown/DropdownProps.d.ts +1 -0
  68. package/dist/Components/Editor/EditorProps.d.ts +33 -0
  69. package/dist/Components/Editor/editorTokens.d.ts +3 -0
  70. package/dist/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtpProps.d.ts +8 -0
  71. package/dist/Components/FilePreview/FilePreviewProps.d.ts +4 -0
  72. package/dist/Components/FileUploader/FileUploaderProps.d.ts +1 -0
  73. package/dist/Components/FilterBar/FilterBarProps.d.ts +2 -0
  74. package/dist/Components/FormDrawer/FormDrawer.d.ts +1 -0
  75. package/dist/Components/FormDrawer/FormDrawer.js +1 -0
  76. package/dist/Components/FormDrawer/FormDrawerProps.d.ts +3 -0
  77. package/dist/Components/FormField/FormField.d.ts +1 -0
  78. package/dist/Components/FormField/FormField.js +1 -0
  79. package/dist/Components/FormField/FormFieldProps.d.ts +4 -0
  80. package/dist/Components/Header/Header.d.ts +1 -0
  81. package/dist/Components/Header/Header.js +1 -0
  82. package/dist/Components/Header/HeaderActions.d.ts +16 -0
  83. package/dist/Components/Header/HeaderActions.js +1 -0
  84. package/dist/Components/Header/HeaderProps.d.ts +22 -0
  85. package/dist/Components/Header/components/desktopMenu.d.ts +1 -0
  86. package/dist/Components/Header/components/desktopMenu.js +1 -0
  87. package/dist/Components/Header/components/mobileMenu.d.ts +1 -0
  88. package/dist/Components/Header/components/mobileMenu.js +1 -0
  89. package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.d.ts +1 -1
  90. package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
  91. package/dist/Components/KanbanBoard/KanbanBoardProps.d.ts +11 -0
  92. package/dist/Components/Kbd/Kbd.d.ts +1 -0
  93. package/dist/Components/Kbd/Kbd.js +1 -0
  94. package/dist/Components/Kbd/KbdProps.d.ts +4 -0
  95. package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +1 -0
  96. package/dist/Components/LauncherTrigger/LauncherTrigger.js +1 -0
  97. package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +1 -0
  98. package/dist/Components/LimitReachedModal/LimitReachedModal.d.ts +1 -0
  99. package/dist/Components/LimitReachedModal/LimitReachedModal.js +1 -0
  100. package/dist/Components/LimitReachedModal/LimitReachedModalProps.d.ts +7 -0
  101. package/dist/Components/Loading/LoadingProps.d.ts +2 -0
  102. package/dist/Components/MediaGallery/MediaGalleryProps.d.ts +7 -1
  103. package/dist/Components/MediaGallery/MediaLightboxProps.d.ts +5 -0
  104. package/dist/Components/MediaViewer/components/MediaAudioPlayer.d.ts +1 -0
  105. package/dist/Components/MediaViewer/components/MediaAudioPlayer.js +1 -0
  106. package/dist/Components/MediaViewer/components/MediaDocumentStage.d.ts +1 -0
  107. package/dist/Components/MediaViewer/components/MediaDocumentStage.js +1 -0
  108. package/dist/Components/MediaViewer/components/MediaImageViewer.d.ts +1 -0
  109. package/dist/Components/MediaViewer/components/MediaImageViewer.js +1 -0
  110. package/dist/Components/MediaViewer/components/MediaVideoPlayer.d.ts +1 -0
  111. package/dist/Components/MediaViewer/components/MediaVideoPlayer.js +1 -0
  112. package/dist/Components/MediaViewer/components/MediaVideoPlayerProps.d.ts +1 -0
  113. package/dist/Components/MediaViewer/hooks/useFileViewer.d.ts +1 -0
  114. package/dist/Components/MediaViewer/mediaItems.d.ts +25 -0
  115. package/dist/Components/MediaViewer/mediaItems.js +5 -0
  116. package/dist/Components/Modal/Modal.d.ts +3 -0
  117. package/dist/Components/Modal/Modal.js +3 -0
  118. package/dist/Components/Modal/ModalProps.d.ts +6 -1
  119. package/dist/Components/MoreItems/MoreItemsProps.d.ts +2 -0
  120. package/dist/Components/MultiSelect/MultiSelect.d.ts +1 -0
  121. package/dist/Components/MultiSelect/MultiSelect.js +1 -0
  122. package/dist/Components/MultiSelect/MultiSelectProps.d.ts +13 -0
  123. package/dist/Components/NavLauncher/NavLauncher.js +3 -0
  124. package/dist/Components/NavLauncher/NavLauncherProps.d.ts +58 -0
  125. package/dist/Components/NavLauncher/engine/engine.d.ts +19 -0
  126. package/dist/Components/NavLauncher/launcherMotion.d.ts +19 -0
  127. package/dist/Components/NavLauncher/launcherMotion.js +15 -0
  128. package/dist/Components/NavLauncher/match.d.ts +34 -0
  129. package/dist/Components/NavigationBar/NavigationBar.d.ts +1 -0
  130. package/dist/Components/NavigationBar/NavigationBar.js +1 -0
  131. package/dist/Components/NavigationBar/NavigationBarProps.d.ts +15 -0
  132. package/dist/Components/NoteTextArea/NoteTextAreaProps.d.ts +2 -1
  133. package/dist/Components/NotificationBell/NotificationBellProps.d.ts +27 -0
  134. package/dist/Components/NotificationBell/notificationHelpers.d.ts +13 -0
  135. package/dist/Components/NumberInput/NumberInputProps.d.ts +1 -1
  136. package/dist/Components/OffersList/OffersListProps.d.ts +12 -1
  137. package/dist/Components/OptionChip/OptionChipProps.d.ts +5 -0
  138. package/dist/Components/OrderStatusTimeline/OrderStatusTimelineProps.d.ts +10 -0
  139. package/dist/Components/OrderSummary/OrderSummaryProps.d.ts +10 -0
  140. package/dist/Components/OrgSwitcher/OrgSwitcher.d.ts +1 -0
  141. package/dist/Components/OrgSwitcher/OrgSwitcher.js +11 -3
  142. package/dist/Components/OrgSwitcher/OrgSwitcherProps.d.ts +1 -0
  143. package/dist/Components/OrganizationDetails/OrganizationDetailsProps.d.ts +52 -0
  144. package/dist/Components/PageHeader/PageHeaderProps.d.ts +1 -0
  145. package/dist/Components/Pagination/PaginationProps.d.ts +4 -0
  146. package/dist/Components/PaymentOption/PaymentOptionProps.d.ts +9 -0
  147. package/dist/Components/PincodeChecker/PincodeCheckerProps.d.ts +13 -0
  148. package/dist/Components/PlanPill/PlanPill.d.ts +1 -0
  149. package/dist/Components/PlanPill/PlanPill.js +1 -0
  150. package/dist/Components/PlanPill/PlanPillProps.d.ts +1 -0
  151. package/dist/Components/PriceBlock/PriceBlockProps.d.ts +6 -0
  152. package/dist/Components/ProductBadges/ProductBadgesProps.d.ts +3 -0
  153. package/dist/Components/ProductCard/ProductCardProps.d.ts +10 -0
  154. package/dist/Components/ProductCard/QuickAddSheetProps.d.ts +8 -0
  155. package/dist/Components/ProductDetails/ProductDetailsProps.d.ts +25 -1
  156. package/dist/Components/ProfileCard/ProfileCardProps.d.ts +1 -1
  157. package/dist/Components/QuantityStepper/QuantityStepperProps.d.ts +9 -0
  158. package/dist/Components/QueryState/QueryStateProps.d.ts +2 -0
  159. package/dist/Components/RadioButton/RadioButtonProps.d.ts +1 -1
  160. package/dist/Components/Rating/RatingProps.d.ts +2 -0
  161. package/dist/Components/Reorder/ReorderProps.d.ts +6 -0
  162. package/dist/Components/ReviewList/ReviewListProps.d.ts +21 -0
  163. package/dist/Components/ReviewSummary/ReviewSummaryProps.d.ts +8 -1
  164. package/dist/Components/RolesPermission/RolesPermissionProps.d.ts +59 -1
  165. package/dist/Components/SearchSelect/SearchSelect.js +17 -5
  166. package/dist/Components/SearchSelect/SearchSelectProps.d.ts +12 -0
  167. package/dist/Components/SectionRail/SectionRail.d.ts +1 -0
  168. package/dist/Components/SectionRail/SectionRail.js +1 -0
  169. package/dist/Components/SectionRail/SectionRailProps.d.ts +4 -0
  170. package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +4 -0
  171. package/dist/Components/Select/Select.styles.js +25 -0
  172. package/dist/Components/Select/SelectNative.d.ts +10 -0
  173. package/dist/Components/Select/SelectNative.js +11 -4
  174. package/dist/Components/Select/SelectPopup.js +45 -14
  175. package/dist/Components/Select/SelectProps.d.ts +46 -2
  176. package/dist/Components/Select/selectKeyboardRing.d.ts +2 -0
  177. package/dist/Components/Select/selectKeyboardRing.js +7 -0
  178. package/dist/Components/Select/selectRowInk.d.ts +14 -0
  179. package/dist/Components/Select/selectRowInk.js +41 -0
  180. package/dist/Components/SelectSearch/SelectSearch.js +8 -2
  181. package/dist/Components/SelectSearch/SelectSearchProps.d.ts +9 -0
  182. package/dist/Components/SelectV2/SelectV2Props.d.ts +7 -0
  183. package/dist/Components/SideBar/SecondaryBar.d.ts +1 -0
  184. package/dist/Components/SideBar/SecondaryBar.js +1 -0
  185. package/dist/Components/SideBar/SideBar.d.ts +1 -0
  186. package/dist/Components/SideBar/SideBar.js +1 -0
  187. package/dist/Components/SideBar/SideBarProps.d.ts +32 -0
  188. package/dist/Components/SideBar/sidebarAccentColors.d.ts +2 -0
  189. package/dist/Components/SignInActivityTable/SignInActivityTable.d.ts +1 -0
  190. package/dist/Components/SignInActivityTable/SignInActivityTable.js +1 -0
  191. package/dist/Components/SignInActivityTable/SignInActivityTableProps.d.ts +2 -0
  192. package/dist/Components/SpecTable/SpecTableProps.d.ts +9 -0
  193. package/dist/Components/StageProgress/StageProgressProps.d.ts +2 -0
  194. package/dist/Components/StageProgress/StepperStage.d.ts +1 -0
  195. package/dist/Components/StageProgress/StepperStage.js +1 -0
  196. package/dist/Components/StatCard/StatCardProps.d.ts +1 -0
  197. package/dist/Components/StatusTag/StatusTagProps.d.ts +4 -0
  198. package/dist/Components/StickyBuyBar/StickyBuyBar.d.ts +1 -0
  199. package/dist/Components/StickyBuyBar/StickyBuyBar.js +1 -0
  200. package/dist/Components/StickyBuyBar/StickyBuyBarProps.d.ts +6 -0
  201. package/dist/Components/StockStatus/StockStatusProps.d.ts +2 -0
  202. package/dist/Components/Switch/SwitchProps.d.ts +4 -0
  203. package/dist/Components/Table/TableProps.d.ts +20 -1
  204. package/dist/Components/Table/components/TableBody.js +10 -1
  205. package/dist/Components/Table/components/rowFocusRing.d.ts +21 -0
  206. package/dist/Components/Table/components/rowFocusRing.js +20 -0
  207. package/dist/Components/Table/variants/tokens.d.ts +4 -0
  208. package/dist/Components/Tabs/TabsContext.d.ts +14 -0
  209. package/dist/Components/Tabs/TabsProps.d.ts +18 -0
  210. package/dist/Components/Tabs/components/CountBadge.d.ts +4 -0
  211. package/dist/Components/Tabs/components/TabPanels.d.ts +1 -0
  212. package/dist/Components/Tabs/components/TabPanels.js +1 -0
  213. package/dist/Components/Tabs/tabStyles.d.ts +4 -0
  214. package/dist/Components/Tag/TagProps.d.ts +3 -0
  215. package/dist/Components/TagInput/TagInput.d.ts +1 -0
  216. package/dist/Components/TagInput/TagInput.js +1 -0
  217. package/dist/Components/TagInput/TagInputProps.d.ts +2 -0
  218. package/dist/Components/TimePicker/TimePicker.d.ts +1 -0
  219. package/dist/Components/TimePicker/TimePicker.js +1 -0
  220. package/dist/Components/TimePicker/TimePickerProps.d.ts +2 -0
  221. package/dist/Components/Timeline/TimelineProps.d.ts +7 -1
  222. package/dist/Components/Toggle/TableToggleProps.d.ts +2 -0
  223. package/dist/Components/ToggleSwitch/ToggleSwitchProps.d.ts +2 -0
  224. package/dist/Components/ToolTip/OverflowToolTip.d.ts +1 -0
  225. package/dist/Components/ToolTip/OverflowToolTip.js +1 -1
  226. package/dist/Components/ToolTip/ToolTipProps.d.ts +1 -0
  227. package/dist/Components/TrustBadges/TrustBadgesProps.d.ts +4 -0
  228. package/dist/Components/UpgradeButton/UpgradeButton.d.ts +1 -0
  229. package/dist/Components/UpgradeButton/UpgradeButton.js +1 -0
  230. package/dist/Components/UpgradeButton/UpgradeButtonProps.d.ts +4 -1
  231. package/dist/Components/UserDetails/UserDetailsProps.d.ts +58 -0
  232. package/dist/Components/VariantSelector/VariantSelectorProps.d.ts +4 -0
  233. package/dist/Components/WishlistToggle/WishlistToggleProps.d.ts +4 -0
  234. package/dist/Components/WizardDialog/WizardDialogProps.d.ts +15 -0
  235. package/dist/Components/WizardDialog/wizardSteps.d.ts +17 -5
  236. package/dist/Components/WorkspaceWindow/WorkspaceTrayPreview.d.ts +1 -0
  237. package/dist/Components/WorkspaceWindow/WorkspaceTrayPreview.js +1 -1
  238. package/dist/Components/WorkspaceWindow/WorkspaceWindow.d.ts +2 -0
  239. package/dist/Components/WorkspaceWindow/WorkspaceWindow.js +2 -1
  240. package/dist/Components/WorkspaceWindow/WorkspaceWindowProps.d.ts +13 -0
  241. package/dist/Theme/tokens/brands/index.d.ts +1 -1
  242. package/dist/Theme/tokens/brands/index.js +1 -1
  243. package/dist/Theme/tokens/builders/rowStates.d.ts +6 -0
  244. package/dist/Theme/tokens/builders/rowStates.js +19 -1
  245. package/dist/Theme/tokens/builders/scrollShadow.d.ts +1 -0
  246. package/dist/Theme/tokens/builders/scrollShadow.js +16 -0
  247. package/dist/esm/Components/AppSwitcher/AppSwitcher.js +1 -0
  248. package/dist/esm/Components/AppearanceSettings/AppearanceSettings.js +1 -0
  249. package/dist/esm/Components/AudioRecorder/AudioRecorder.js +1 -0
  250. package/dist/esm/Components/Avatar/AvatarGroup.js +1 -0
  251. package/dist/esm/Components/BulkSelectionBar/BulkSelectionBar.js +1 -0
  252. package/dist/esm/Components/Button/Button.js +1 -0
  253. package/dist/esm/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -0
  254. package/dist/esm/Components/CalendarGrid/CalendarGrid.js +1 -0
  255. package/dist/esm/Components/Card/PaymentCard/PaymentCard.js +1 -0
  256. package/dist/esm/Components/ChoiceCard/ChoiceCard.js +1 -0
  257. package/dist/esm/Components/ChoiceCard/ChoiceCardGroup.js +1 -0
  258. package/dist/esm/Components/ConfirmDialog/ConfirmDialog.js +1 -0
  259. package/dist/esm/Components/CustomModulesTable/CustomModulesTable.js +1 -0
  260. package/dist/esm/Components/CustomModulesTable/DeleteModuleModal.js +1 -0
  261. package/dist/esm/Components/CustomModulesTable/EditModuleModal.js +1 -0
  262. package/dist/esm/Components/FormDrawer/FormDrawer.js +1 -0
  263. package/dist/esm/Components/FormField/FormField.js +1 -0
  264. package/dist/esm/Components/Header/Header.js +1 -0
  265. package/dist/esm/Components/Header/HeaderActions.js +1 -0
  266. package/dist/esm/Components/Header/components/desktopMenu.js +1 -0
  267. package/dist/esm/Components/Header/components/mobileMenu.js +1 -0
  268. package/dist/esm/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
  269. package/dist/esm/Components/Kbd/Kbd.js +1 -0
  270. package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +1 -0
  271. package/dist/esm/Components/LimitReachedModal/LimitReachedModal.js +1 -0
  272. package/dist/esm/Components/MediaViewer/components/MediaAudioPlayer.js +1 -0
  273. package/dist/esm/Components/MediaViewer/components/MediaDocumentStage.js +1 -0
  274. package/dist/esm/Components/MediaViewer/components/MediaImageViewer.js +1 -0
  275. package/dist/esm/Components/MediaViewer/components/MediaVideoPlayer.js +1 -0
  276. package/dist/esm/Components/MediaViewer/mediaItems.js +5 -0
  277. package/dist/esm/Components/Modal/Modal.js +3 -0
  278. package/dist/esm/Components/MultiSelect/MultiSelect.js +1 -0
  279. package/dist/esm/Components/NavLauncher/NavLauncher.js +4 -1
  280. package/dist/esm/Components/NavLauncher/launcherMotion.js +11 -0
  281. package/dist/esm/Components/NavigationBar/NavigationBar.js +1 -0
  282. package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +13 -5
  283. package/dist/esm/Components/PlanPill/PlanPill.js +1 -0
  284. package/dist/esm/Components/SearchSelect/SearchSelect.js +19 -7
  285. package/dist/esm/Components/SectionRail/SectionRail.js +1 -0
  286. package/dist/esm/Components/Select/Select.styles.js +26 -1
  287. package/dist/esm/Components/Select/SelectNative.js +12 -5
  288. package/dist/esm/Components/Select/SelectPopup.js +47 -16
  289. package/dist/esm/Components/Select/selectKeyboardRing.js +3 -0
  290. package/dist/esm/Components/Select/selectRowInk.js +33 -0
  291. package/dist/esm/Components/SelectSearch/SelectSearch.js +10 -4
  292. package/dist/esm/Components/SideBar/SecondaryBar.js +1 -0
  293. package/dist/esm/Components/SideBar/SideBar.js +1 -0
  294. package/dist/esm/Components/SignInActivityTable/SignInActivityTable.js +1 -0
  295. package/dist/esm/Components/StageProgress/StepperStage.js +1 -0
  296. package/dist/esm/Components/StickyBuyBar/StickyBuyBar.js +1 -0
  297. package/dist/esm/Components/Table/components/TableBody.js +10 -1
  298. package/dist/esm/Components/Table/components/rowFocusRing.js +16 -0
  299. package/dist/esm/Components/Tabs/components/TabPanels.js +1 -0
  300. package/dist/esm/Components/TagInput/TagInput.js +1 -0
  301. package/dist/esm/Components/TimePicker/TimePicker.js +1 -0
  302. package/dist/esm/Components/ToolTip/OverflowToolTip.js +1 -1
  303. package/dist/esm/Components/UpgradeButton/UpgradeButton.js +1 -0
  304. package/dist/esm/Components/WorkspaceWindow/WorkspaceTrayPreview.js +1 -1
  305. package/dist/esm/Components/WorkspaceWindow/WorkspaceWindow.js +2 -1
  306. package/dist/esm/Theme/tokens/brands/index.js +1 -1
  307. package/dist/esm/Theme/tokens/builders/rowStates.js +18 -1
  308. package/dist/esm/Theme/tokens/builders/scrollShadow.js +15 -0
  309. package/package.json +1 -1
@@ -4,7 +4,7 @@ import { Search as SearchIcon, X } from "lucide-react";
4
4
  import { memo, useCallback, useEffect, useId, useMemo, useRef, useState, } from "react";
5
5
  import { useMatchMedia } from "../../Hooks/useMatchMedia.js";
6
6
  import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
7
- import { reducedMotionSx } from "../../Theme/chakra/motion.js";
7
+ import { reducedMotionSx, usePrefersReducedMotion } from "../../Theme/chakra/motion.js";
8
8
  import { chartMode } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
9
9
  import { placeholderInk } from "../../Theme/tokens/builders/placeholderInk.js";
10
10
  import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
@@ -24,6 +24,7 @@ import { LauncherTabs, panelId, tabId, } from "./LauncherTabs.js";
24
24
  import { filterItems, searchLauncherSections, } from "./match.js";
25
25
  import NewTabLink from "./NewTabLink.js";
26
26
  import PinToggle from "./PinToggle.js";
27
+ import { launcherSlide } from "./launcherMotion.js";
27
28
  import { moveResult } from "./resultNavigation.js";
28
29
  import { ScopeBar } from "./ScopeBar.js";
29
30
  import { copiesOnEnter, countByScope, inScope, layoutSearch, scopeGroups, takeTopHit, } from "./searchLayout.js";
@@ -204,6 +205,7 @@ export default function NavLauncher({ isOpen, onClose, sections: rawSections, pi
204
205
  const isDesktop = useMatchMedia(MD_QUERY, true);
205
206
  const isLg = useMatchMedia(LG_QUERY, true);
206
207
  const isWide = useMatchMedia(XL_QUERY, false);
208
+ const isReducedMotion = usePrefersReducedMotion();
207
209
  const columns = isWide ? 3 : isDesktop ? 2 : 1;
208
210
  const [innerQuery, setInnerQuery] = useState("");
209
211
  const [activeIndex, setActiveIndex] = useState(0);
@@ -789,6 +791,7 @@ export default function NavLauncher({ isOpen, onClose, sections: rawSections, pi
789
791
  borderBottomRightRadius: { base: 0, lg: "16px" },
790
792
  boxShadow: colors.boxShadow.default,
791
793
  overflow: "hidden",
794
+ motionProps: launcherSlide(isReducedMotion),
792
795
  onKeyDown: onContentKeyDown,
793
796
  ...{ "data-testid": testId },
794
797
  }, children: [_jsxs(DrawerHeader, { p: 0, flexShrink: 0, children: [_jsxs(Flex, { "data-testid": "launcher-header-row", align: "center", gap: { base: "8px", md: "12px" }, minH: { base: "60px", md: "68px" }, pl: { base: "12px", md: "18px" }, pr: { base: "8px", md: "12px" }, children: [_jsx(VisuallyHidden, { as: "h2", id: titleId, children: labels.title }), _jsx(SearchField, { inputRef: inputRef, value: query, onChange: setQuery, onKeyDown: onInputKeyDown, labels: labels, placeholder: (_c = currentScope === null || currentScope === void 0 ? void 0 : currentScope.placeholder) !== null && _c !== void 0 ? _c : labels.searchPlaceholder, hint: searchHint, listboxId: listboxId, activeId: isListView && hasResults ? optionId(active) : undefined, isExpanded: isListView, hasResults: hasResults, minW: inlineScopes ? FIELD_MIN_WIDTH : 0 }), inlineScopes, _jsx(Box, { "aria-hidden": true, "data-testid": "launcher-count", display: { base: "none", sm: "block" }, flexShrink: 0, overflow: "hidden", w: countText ? "88px" : "0", opacity: countText ? 1 : 0, textAlign: "right", fontSize: "13px", whiteSpace: "nowrap", color: colors.textMuted, transition: transitionFor(["width", "opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...reducedMotionSx() }, children: countText }), headerActions ? (_jsx(Flex, { align: "center", gap: "4px", flexShrink: 0, children: headerActions })) : null, _jsx(IconButton, { "aria-label": labels.close, icon: _jsx(X, { size: 18, "aria-hidden": true }), variant: "ghost", boxSize: { base: "40px", md: "44px" }, minW: { base: "40px", md: "44px" }, borderRadius: "10px", flexShrink: 0, color: colors.textMuted, onClick: onClose })] }), scopeRow ? (_jsx(Box, { "data-testid": "launcher-scope-row", pl: "7px", pr: "3px", mt: "-6px", pb: "4px", children: scopeRow })) : null] }), _jsx(DrawerBody, { p: 0, display: "flex", flexDirection: "column", overflowY: "hidden", minH: 0, children: _jsxs(Flex, { ref: bodyRef, direction: "column", flex: 1, minH: 0, minW: 0, children: [_jsx(VisuallyHidden, { "data-testid": "launcher-live", "aria-live": "polite", "aria-atomic": "true", children: countText }), _jsx(VisuallyHidden, { "data-testid": "launcher-announce", "aria-live": "polite", "aria-atomic": "true", children: _jsx("span", { children: announcement.text }, announcement.n) }), isTabbed ? (_jsx(LauncherTabs, { tabs: tabs, selected: activeTab, onSelect: setTab, idBase: baseId, label: labels.tabsLabel, onLeaveUp: () => focusField(), onLeaveDown: () => moveFocus("first") })) : null, isLg ? (_jsx(Flex, { direction: "row", flex: 1, minH: 0, minW: 0, children: body })) : (compactBody), dock] }) }), _jsxs(DrawerFooter, { p: 0, minH: "32px", flexShrink: 0, px: "18px", py: "6px", gap: "6px 14px", flexWrap: "wrap", bg: colors.backgroundColor.subtle, fontSize: "xs-minus", color: colors.textMuted, whiteSpace: "nowrap", justifyContent: "flex-start", children: [_jsxs(Flex, { display: { base: "none", md: "flex" }, gap: "6px 14px", align: "center", wrap: "wrap", "aria-hidden": true, sx: { "@media (hover: none)": { display: "none" } }, children: [_jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(Flex, { as: "span", gap: "2px", children: ARROW_KEYS.map((arrow) => (_jsx(Kbd, { size: "xs", children: arrow }, arrow))) }), labels.hintMove] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(KbdShortcut, { shortcut: "enter", size: "xs" }), _jsx(HintLabel, { text: enterLabel, reserve: enterReserve })] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", opacity: canOpenInTab ? 1 : 0.45, transition: transitionFor(["opacity"]), sx: reducedMotionSx(), children: [modKeyLabel ? (_jsxs(Flex, { as: "span", gap: "4px", children: [_jsx(Kbd, { size: "xs", children: modKeyLabel }), _jsx(Kbd, { size: "xs", children: ENTER_KEY })] })) : (_jsx(KbdShortcut, { shortcut: "mod+enter", size: "xs" })), labels.hintNewTab] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", display: { base: "none", lg: "flex" }, children: [_jsx(Kbd, { size: "xs", children: modKeyLabel !== null && modKeyLabel !== void 0 ? modKeyLabel : MOD_KEY }), labels.hintClickNewTab] }), onTogglePin ? (_jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(Kbd, { size: "xs", children: "P" }), labels.hintPin] })) : null, _jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(KbdShortcut, { shortcut: "esc", size: "xs" }), labels.hintClose] })] }), _jsx(Text, { as: "span", ml: "auto", children: labels.footerNote })] })] }) }));
@@ -0,0 +1,11 @@
1
+ import { EASINGS } from "@chakra-ui/react";
2
+ import { motionTransition } from "../../Theme/tokens/primitives/motion.js";
3
+ const ENTER = motionTransition("enter", "out");
4
+ const INSTANT = { duration: 0 };
5
+ const EXIT = { duration: 0.15, ease: [...EASINGS.easeInOut] };
6
+ export const launcherSlide = (isReducedMotion) => ({
7
+ variants: {
8
+ enter: { x: 0, y: 0, transition: isReducedMotion ? INSTANT : ENTER },
9
+ exit: { x: "-100%", y: 0, transition: EXIT },
10
+ },
11
+ });
@@ -14,6 +14,7 @@ import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
14
14
  const BACKGROUND_TRANSITION = transitionFor(["background"]);
15
15
  // Roomy enough to hover without aiming: the glyph is only 1.25rem.
16
16
  const SIDEBAR_TOGGLE_HIT = "2.5rem";
17
+ /** Top bar with sidebar toggle, optional back button and title, a right-hand slot, and an avatar menu of navigation entries plus logout. */
17
18
  export default function NavBar({ userAvathar, userName, userRole, navMenu, moreIcon, handleNavOnClick, handleLogout, logoutText, navigationBarText, sideBarToggole, onSideBarToggole, forceSideBarToggle = false, onSideBarPeek, leading, }) {
18
19
  const theme = useCustomTheme();
19
20
  const rowBackgrounds = listRowBackgrounds(theme.colors);
@@ -7,13 +7,16 @@ import { accentTextOnCanvas, isDarkCanvas, onFilled } from "../../Theme/tokens/b
7
7
  import { chartMode } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
8
8
  import { resolveHoverTint } from "../../Theme/tokens/builders/outlineRung.js";
9
9
  import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
10
- import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
11
- import { optionRowCursorSx } from "../Select/optionRowInk.js";
10
+ import { selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
11
+ import { optionRowWeight } from "../Select/optionRowInk.js";
12
+ import { keyboardRingShadow } from "../Select/selectKeyboardRing.js";
13
+ import { selectRowDescriptionInk, selectRowInk } from "../Select/selectRowInk.js";
12
14
  import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
13
15
  import { useMatchMedia } from "../../Hooks/useMatchMedia.js";
14
16
  import ToolTip from "../ToolTip/ToolTip.js";
15
17
  import { avatarColor, orgInitial, } from "./OrgSwitcherProps.js";
16
18
  import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
19
+ import { OPTION_ROW_RADIUS } from "../../Theme/tokens/primitives/optionRowSizes.js";
17
20
  const BACKGROUND_TRANSITION = transitionFor(["background"]);
18
21
  const sizeMap = {
19
22
  sm: { h: "32px", avatarH: "32px", avatar: "22px", font: "0.8rem", sub: "0.65rem", px: 2 },
@@ -29,12 +32,17 @@ const Avatar = ({ org, boxSize, fontSize, }) => {
29
32
  const fill = avatarColor(org.name, chartMode(colors));
30
33
  return (_jsx(Flex, { "aria-hidden": "true", "data-initials": orgInitial(org.name), _before: { content: "attr(data-initials)" }, align: "center", justify: "center", boxSize: boxSize, borderRadius: "lg", flexShrink: 0, bg: fill, color: onFilled(fill, colors.white, colors.black), fontSize: fontSize, fontWeight: 700 }));
31
34
  };
35
+ /** Session-scoped organization switcher: a trigger plus searchable popover that reports the chosen org via `onChange`. */
32
36
  const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSearchable, searchThreshold = 6, triggerVariant = "ghost", size = "md", maxWidth = "200px", menuMinWidth = "280px", placeholder = "Select organization", ariaLabel = "Switch organization", showRole = true, display = "full", }) => {
33
37
  var _a, _b, _c;
34
38
  const theme = useCustomTheme();
35
39
  const isMd = useMatchMedia(MD_QUERY, true);
36
40
  const avatarOnly = display === "avatar" || (display === "auto" && !isMd);
37
- const rowBackgrounds = useMemo(() => listRowBackgrounds(theme.colors), [theme.colors]);
41
+ const rowBackgrounds = useMemo(() => selectRowBackgrounds(theme.colors), [theme.colors]);
42
+ const keyboardRing = {
43
+ outline: "2px solid transparent",
44
+ boxShadow: keyboardRingShadow(rowBackgrounds.keyboardRing),
45
+ };
38
46
  const s = sizeMap[size];
39
47
  const { isOpen, onOpen, onClose } = useDisclosure();
40
48
  const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
@@ -105,7 +113,7 @@ const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSear
105
113
  return (_jsxs(Popover, { isOpen: isOpen, onOpen: handlePopoverOpen, onClose: () => {
106
114
  onClose();
107
115
  setQuery("");
108
- }, placement: "bottom-start", initialFocusRef: showSearch ? searchRef : firstOptionRef, isLazy: true, children: [avatarOnly ? (_jsx(ToolTip, { label: (_c = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _c !== void 0 ? _c : placeholder, placement: "bottom", isDisabled: isOpen, children: _jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: _jsx(PopoverTrigger, { children: trigger }) }) })) : (_jsx(PopoverTrigger, { children: trigger })), _jsx(PopoverContent, { minW: menuMinWidth, maxW: "calc(100vw - 32px)", w: "auto",
116
+ }, placement: "bottom-start", initialFocusRef: showSearch ? searchRef : firstOptionRef, isLazy: true, children: [avatarOnly ? (_jsx(ToolTip, { label: (_c = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _c !== void 0 ? _c : placeholder, placement: "bottom", isDisabled: isOpen, children: _jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: _jsx(PopoverTrigger, { children: trigger }) }) })) : (_jsx(PopoverTrigger, { children: trigger })), _jsx(PopoverContent, { "aria-label": ariaLabel, minW: menuMinWidth, maxW: "calc(100vw - 32px)", w: "auto",
109
117
  // NOTE: Chakra's --popper-bg is hardcoded white via dead _dark — without an explicit bg the panel stayed white.
110
118
  bg: theme.colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", boxShadow: "float", _focus: { outline: "none", boxShadow: "float" }, children: _jsxs(PopoverBody, { p: 0, children: [showSearch && (_jsx(Box, { p: 2, borderBottom: "1px solid", borderColor: searchRule, children: _jsxs(InputGroup, { size: "sm", children: [_jsx(InputLeftElement, { pointerEvents: "none", children: _jsx(Search, { size: 15, color: theme.colors.textMuted }) }), _jsx(Input, { ref: searchRef, placeholder: "Search organizations", value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: (e) => {
111
119
  if (e.key === "ArrowDown" && filtered.length > 0) {
@@ -118,7 +126,7 @@ const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSear
118
126
  optionRefs.current[idx] = el;
119
127
  if (idx === 0)
120
128
  firstOptionRef.current = el;
121
- }, tabIndex: idx === activeIndex ? 0 : -1, align: "center", gap: "10px", w: "100%", textAlign: "left", border: "none", fontFamily: "inherit", px: 3, py: 2, cursor: "pointer", bg: "transparent", transition: BACKGROUND_TRANSITION, ...optionRowCursorSx(rowBackgrounds), onClick: () => handleSelect(org), children: [_jsx(Avatar, { org: org, boxSize: s.avatar, fontSize: s.sub }), _jsxs(Box, { flex: "1", minW: 0, children: [_jsx(Text, { fontSize: s.font, fontWeight: isActive ? 600 : 500, color: theme.colors.text[900], overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.name }), showRole && org.role && (_jsx(Text, { fontSize: s.sub, color: theme.colors.text[500], overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.role }))] }), isActive && (_jsx(Box, { as: Check, size: 16, color: theme.colors.accentText, flexShrink: 0 }))] }, org.id));
129
+ }, tabIndex: idx === activeIndex ? 0 : -1, align: "center", gap: "10px", w: "calc(100% - 0.5rem)", mx: 1, textAlign: "left", border: "none", fontFamily: "inherit", px: 3, py: 2, borderRadius: OPTION_ROW_RADIUS, cursor: "pointer", bg: isActive ? rowBackgrounds.selected : "transparent", transition: BACKGROUND_TRANSITION, _hover: { bg: isActive ? rowBackgrounds.selectedActive : rowBackgrounds.active }, _focus: { bg: isActive ? rowBackgrounds.selectedActive : rowBackgrounds.active }, _focusVisible: keyboardRing, onClick: () => handleSelect(org), children: [_jsx(Avatar, { org: org, boxSize: s.avatar, fontSize: s.sub }), _jsxs(Box, { flex: "1", minW: 0, children: [_jsx(Text, { fontSize: s.font, fontWeight: optionRowWeight(isActive), color: selectRowInk(theme.colors, isActive), overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.name }), showRole && org.role && (_jsx(Text, { fontSize: s.sub, color: selectRowDescriptionInk(theme.colors), overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.role }))] }), isActive && (_jsx(Box, { as: Check, size: 16, color: theme.colors.accentText, flexShrink: 0 }))] }, org.id));
122
130
  }), filtered.length === 0 && (_jsx(Text, { fontSize: "sm", color: theme.colors.textMuted, textAlign: "center", py: 6, children: "No organizations found" }))] })] }) })] }));
123
131
  };
124
132
  export default OrgSwitcher;
@@ -13,6 +13,7 @@ const TONE_ICON = {
13
13
  warning: Clock,
14
14
  danger: AlertCircle,
15
15
  };
16
+ /** Gradient pill button with plan label, optional time-left chip and a tone-based icon; always renders a `<button>`. */
16
17
  const PlanPill = ({ label, mobileLabel, status, tone = "normal", icon, gradient, maxLabelWidth = "150px", ariaLabel, onClick, }) => {
17
18
  const theme = useCustomTheme();
18
19
  const c = theme.colors;
@@ -7,9 +7,11 @@ import Tag from "../Tag/Tag.js";
7
7
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
8
8
  import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
9
9
  import { ruleOn } from "../../Theme/tokens/builders/planeStep.js";
10
- import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
10
+ import { listRowBackgrounds, selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
11
11
  import { OPTION_ROW_RADIUS, optionRowSpec } from "../../Theme/tokens/primitives/optionRowSizes.js";
12
- import { optionRowCursorSx, optionRowFill, optionRowInk, optionRowWeight } from "../Select/optionRowInk.js";
12
+ import { optionRowCursorSx, optionRowInk, optionRowWeight } from "../Select/optionRowInk.js";
13
+ import { keyboardRingShadow } from "../Select/selectKeyboardRing.js";
14
+ import { selectRowFill, selectRowInk } from "../Select/selectRowInk.js";
13
15
  import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
14
16
  import FieldMessage from "../Common/FieldMessage.js";
15
17
  import { useFieldIds } from "../Common/useFieldIds.js";
@@ -41,9 +43,9 @@ const normalizeMultiValue = (val) => {
41
43
  return [];
42
44
  };
43
45
  const DECORATIVE_CHECKBOX_SX = { pointerEvents: "none", "& input": { visibility: "hidden" } };
44
- const OptionRow = React.memo(function OptionRow({ option, index, optionId, isSelected, isActive, isMultiple, spec, colors, rowBackgrounds, checkboxSize, onSelect, onActivate, }) {
46
+ const OptionRow = React.memo(function OptionRow({ option, index, optionId, isSelected, isActive, showRing, isMultiple, spec, colors, rowBackgrounds, checkboxSize, onSelect, onActivate, }) {
45
47
  const singleSelected = isSelected && !isMultiple;
46
- return (_jsxs(Box, { id: optionId, "data-index": index, role: "option", "aria-selected": isSelected, mx: spec.mx, py: spec.py, px: spec.px, minH: spec.minH, borderRadius: OPTION_ROW_RADIUS, cursor: "pointer", bg: optionRowFill(rowBackgrounds, isActive), onClick: () => onSelect(option), onMouseEnter: () => onActivate(index), display: "flex", alignItems: "center", gap: spec.gap, color: optionRowInk(colors, singleSelected),
48
+ return (_jsxs(Box, { id: optionId, "data-index": index, role: "option", "aria-selected": isSelected, mx: spec.mx, py: spec.py, px: spec.px, minH: spec.minH, borderRadius: OPTION_ROW_RADIUS, cursor: "pointer", bg: selectRowFill(rowBackgrounds, { isSelected, isActive }), boxShadow: showRing ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined, onClick: () => onSelect(option), onMouseEnter: () => onActivate(index), display: "flex", alignItems: "center", gap: spec.gap, color: selectRowInk(colors, singleSelected),
47
49
  // `view` is consumer JSX with no size of its own; it inherited the 16px body font.
48
50
  fontSize: spec.fontSize, fontWeight: optionRowWeight(singleSelected), children: [isMultiple && (_jsx(Checkbox, { isChecked: isSelected, sx: DECORATIVE_CHECKBOX_SX, size: checkboxSize })), option.view ? (_jsx(_Fragment, { children: option.view })) : (_jsx(Text, { flex: 1, minW: 0, isTruncated: true, children: option.label })), singleSelected && (_jsx(Box, { as: Check, size: spec.iconSize, color: colors.accentText, flexShrink: 0 }))] }));
49
51
  });
@@ -79,7 +81,8 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
79
81
  width: 0,
80
82
  });
81
83
  const theme = useCustomTheme();
82
- const rowBackgrounds = useMemo(() => listRowBackgrounds(theme.colors), [theme.colors]);
84
+ const rowBackgrounds = useMemo(() => selectRowBackgrounds(theme.colors), [theme.colors]);
85
+ const insideRowBackgrounds = useMemo(() => listRowBackgrounds(theme.colors), [theme.colors]);
83
86
  // NOTE: inset — the trigger sits flush in the field and the menu clips overflow.
84
87
  const insideFocusRing = {
85
88
  outline: "2px solid transparent",
@@ -93,6 +96,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
93
96
  const selectAllId = `${baseId}-select-all`;
94
97
  const addNewId = `${baseId}-add-new`;
95
98
  const [activeIndex, setActiveIndex] = useState(NO_ROW);
99
+ const [isKeyboardNav, setIsKeyboardNav] = useState(false);
96
100
  const pendingFocusRestoreRef = useRef(false);
97
101
  // NOTE: the ARIA host follows real focus; opening from a button doesn't move it.
98
102
  const [focusedTrigger, setFocusedTrigger] = useState(null);
@@ -246,7 +250,12 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
246
250
  setCustomSelectOpen(false);
247
251
  setIsOpen(true);
248
252
  };
253
+ const handlePointerActivate = useCallback((index) => {
254
+ setIsKeyboardNav(false);
255
+ setActiveIndex(index);
256
+ }, []);
249
257
  const moveActive = (delta) => {
258
+ setIsKeyboardNav(true);
250
259
  setActiveIndex((prev) => stepRow(rowLayout, prev, delta));
251
260
  };
252
261
  const restoreFocus = () => {
@@ -284,12 +293,14 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
284
293
  case "Home":
285
294
  if (isOpen && firstRow(rowLayout) !== NO_ROW && !isTextInputTarget) {
286
295
  e.preventDefault();
296
+ setIsKeyboardNav(true);
287
297
  setActiveIndex(firstRow(rowLayout));
288
298
  }
289
299
  break;
290
300
  case "End":
291
301
  if (isOpen && lastRow(rowLayout) !== NO_ROW && !isTextInputTarget) {
292
302
  e.preventDefault();
303
+ setIsKeyboardNav(true);
293
304
  setActiveIndex(lastRow(rowLayout));
294
305
  }
295
306
  break;
@@ -337,6 +348,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
337
348
  useEffect(() => {
338
349
  if (!isOpen) {
339
350
  setActiveIndex(NO_ROW);
351
+ setIsKeyboardNav(false);
340
352
  return;
341
353
  }
342
354
  const layout = { optionCount: filteredOptions.length, hasSelectAll: hasSelectAllRow, hasAddNew: hasAddNewRow };
@@ -613,7 +625,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
613
625
  }, children: [_jsxs(Box, { minW: "4.5rem", cursor: "pointer", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, width: insideSelect.width, height: insideSelectBoxHeight, pl: "0.5rem", pr: "0.375rem", children: [insideLabel ? (_jsx(SelectTruncatedLabel, { label: insideLabel, maxWidth: insideSelect.width || "6rem", fontSize: s.fieldFontSize })) : (_jsx(Text, { fontSize: s.fieldFontSize, fontWeight: 500, color: theme.colors.textMuted, isTruncated: true, maxW: insideSelect.width || "6rem", children: insidePlaceholder })), _jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, color: customSelectOpen ? theme.colors.accentText : theme.colors.textMuted, transition: CHEVRON_TRANSITION, transform: customSelectOpen ? "rotate(180deg)" : "rotate(0deg)", children: _jsx(ChevronDown, { size: s.chevronSize }) })] }), customSelectOpen && (_jsx(Portal, { children: _jsx(Box, { ref: setInsideSelectMenuRef, id: insideSelectPortalId, role: "listbox", "aria-label": insidePlaceholder, onKeyDown: handleInsideMenuKeyDown, onFocus: stopFocusBubbling, onBlur: stopFocusBubbling, position: "fixed", style: { top: `${customSelectPos.top}px`, left: `${customSelectPos.left}px` }, zIndex: zIndices.popoverNested, bg: theme.colors.surfaceRaised, border: `${BORDER_WIDTHS.hairline} solid`, borderColor: theme.colors.boxborder[200], borderRadius: "xl", boxShadow: theme.shadows.lg, minW: `${customSelectPos.width}px`, maxW: "20rem", py: 1, overflow: "hidden", children: (_c = insideSelect.option) === null || _c === void 0 ? void 0 : _c.map((item) => {
614
626
  var _a;
615
627
  const selected = ((_a = insideSelect.value) === null || _a === void 0 ? void 0 : _a.id) === item.id;
616
- return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: optionRowWeight(selected), color: optionRowInk(theme.colors, selected), bg: "transparent", ...optionRowCursorSx(rowBackgrounds), cursor: "pointer", role: "option", "aria-selected": selected, tabIndex: -1, _focusVisible: insideFocusRing, onClick: (e) => {
628
+ return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: optionRowWeight(selected), color: optionRowInk(theme.colors, selected), bg: "transparent", ...optionRowCursorSx(insideRowBackgrounds), cursor: "pointer", role: "option", "aria-selected": selected, tabIndex: -1, _focusVisible: insideFocusRing, onClick: (e) => {
617
629
  e.stopPropagation();
618
630
  handleCustomSelect(item);
619
631
  }, children: [_jsx(Text, { isTruncated: true, children: item.label }), selected && (_jsx(Check, { size: spec.iconSize, color: theme.colors.accentText }))] }, item.id));
@@ -652,6 +664,6 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
652
664
  top: `${dropdownPos.top}px`,
653
665
  left: `${dropdownPos.left}px`,
654
666
  width: `${dropdownPos.width}px`,
655
- }, zIndex: zIndices.popover, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [hasSelectAllRow && (_jsxs(Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, bg: activeIndex === SELECT_ALL_ROW ? rowBackgrounds.active : undefined, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", onClick: handleSelectAll, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.text[700], borderBottom: `${BORDER_WIDTHS.hairline} solid ${raisedRule(theme.colors)}`, children: [_jsx(Checkbox, { isChecked: allFilteredSelected, sx: DECORATIVE_CHECKBOX_SX, size: s.checkboxSize }), _jsx(Text, { fontSize: spec.fontSize, children: allFilteredSelected ? "Unselect All" : "Select All" })] })), _jsx(Box, { ref: optionsListRef, flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", py: 1, borderBottom: `${BORDER_WIDTHS.hairline} solid`, borderColor: raisedRule(theme.colors), children: isOptionLoading && filteredOptions.length === 0 ? (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] })) : !isOptionLoading && filteredOptions.length === 0 ? (_jsx(Text, { p: 3, fontSize: spec.fontSize, color: theme.colors.textMuted, children: "No results found" })) : (_jsxs(_Fragment, { children: [filteredOptions.map((option, index) => (_jsx(OptionRow, { option: option, index: index, optionId: getOptionId(index), isSelected: isSelected(option.id), isActive: index === activeIndex, isMultiple: isMultiple, spec: spec, colors: theme.colors, rowBackgrounds: rowBackgrounds, checkboxSize: s.checkboxSize, onSelect: selectOption, onActivate: setActiveIndex }, option.id))), ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", justifyContent: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] }))] })) }), (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && (_jsxs(Box, { id: addNewId, role: "option", "aria-selected": false, py: spec.py, px: spec.px, borderTop: `${BORDER_WIDTHS.hairline} solid`, borderColor: raisedRule(theme.colors), bg: activeIndex === ADD_NEW_ROW ? rowBackgrounds.active : theme.colors.surfaceRaised, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.accentText, fontWeight: 500, minH: spec.minH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: rowBackgrounds.active }, children: [addNew.icon || _jsx(Plus, { size: s.addNewIconSize }), _jsx(Text, { fontSize: spec.fontSize, children: addNew.text || "Add New" })] }))] }) })), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
667
+ }, zIndex: zIndices.popover, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [hasSelectAllRow && (_jsxs(Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, bg: selectRowFill(rowBackgrounds, { isSelected: false, isActive: activeIndex === SELECT_ALL_ROW }), boxShadow: isKeyboardNav && activeIndex === SELECT_ALL_ROW ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", onClick: handleSelectAll, onMouseEnter: () => handlePointerActivate(SELECT_ALL_ROW), display: "flex", alignItems: "center", gap: spec.gap, color: selectRowInk(theme.colors, false), borderBottom: `${BORDER_WIDTHS.hairline} solid ${raisedRule(theme.colors)}`, children: [_jsx(Checkbox, { isChecked: allFilteredSelected, sx: DECORATIVE_CHECKBOX_SX, size: s.checkboxSize }), _jsx(Text, { fontSize: spec.fontSize, children: allFilteredSelected ? "Unselect All" : "Select All" })] })), _jsx(Box, { ref: optionsListRef, flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", py: 1, borderBottom: `${BORDER_WIDTHS.hairline} solid`, borderColor: raisedRule(theme.colors), children: isOptionLoading && filteredOptions.length === 0 ? (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] })) : !isOptionLoading && filteredOptions.length === 0 ? (_jsx(Text, { p: 3, fontSize: spec.fontSize, color: theme.colors.textMuted, children: "No results found" })) : (_jsxs(_Fragment, { children: [filteredOptions.map((option, index) => (_jsx(OptionRow, { option: option, index: index, optionId: getOptionId(index), isSelected: isSelected(option.id), isActive: index === activeIndex, showRing: isKeyboardNav && index === activeIndex, isMultiple: isMultiple, spec: spec, colors: theme.colors, rowBackgrounds: rowBackgrounds, checkboxSize: s.checkboxSize, onSelect: selectOption, onActivate: handlePointerActivate }, option.id))), ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", justifyContent: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] }))] })) }), (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && (_jsxs(Box, { id: addNewId, role: "option", "aria-selected": false, py: spec.py, px: spec.px, borderTop: `${BORDER_WIDTHS.hairline} solid`, borderColor: raisedRule(theme.colors), bg: activeIndex === ADD_NEW_ROW ? rowBackgrounds.active : theme.colors.surfaceRaised, boxShadow: isKeyboardNav && activeIndex === ADD_NEW_ROW ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.accentText, fontWeight: 500, minH: spec.minH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: rowBackgrounds.active }, children: [addNew.icon || _jsx(Plus, { size: s.addNewIconSize }), _jsx(Text, { fontSize: spec.fontSize, children: addNew.text || "Add New" })] }))] }) })), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
656
668
  };
657
669
  export default SearchSelect;
@@ -24,6 +24,7 @@ const useMinWidth = (minWidth) => {
24
24
  }, [query]);
25
25
  return matches;
26
26
  };
27
+ /** Section navigation for a long screen: a vertical rail from `collapseBelow` up, a Select below it; dirty sections are flagged. */
27
28
  const SectionRail = ({ sections, activeId, onSelect, "aria-label": ariaLabel, dirtyHint, collapseBelow = "md", }) => {
28
29
  var _a;
29
30
  const { colors } = useCustomTheme();
@@ -1,6 +1,7 @@
1
1
  import { placeholderInk } from "../../Theme/tokens/builders/placeholderInk.js";
2
2
  import { CONTROL_SIZE_NAMES } from "../../Theme/tokens/primitives/controlSizes.js";
3
- import { controlSizeStyles, fieldControlStyles } from "../Common/fieldStyles.js";
3
+ import { fieldFocusVisible, focusVisibleRing } from "../../Theme/chakra/focusRing.styles.js";
4
+ import { FIELD_RADIUS, controlSizeStyles, fieldControlStyles, fieldHoverBorder } from "../Common/fieldStyles.js";
4
5
  const sizeField = (size) => {
5
6
  const { h, minH, fontSize, px } = controlSizeStyles(size);
6
7
  return { field: { h, minH, fontSize, ps: px, pe: "2rem", py: 0 } };
@@ -28,6 +29,30 @@ export const Select = {
28
29
  sizes: Object.fromEntries(CONTROL_SIZE_NAMES.map((size) => [size, sizeField(size)])),
29
30
  variants: {
30
31
  outline: ({ theme }) => ({ field: fieldControlStyles(theme.colors) }),
32
+ filled: ({ theme }) => ({
33
+ field: {
34
+ borderRadius: FIELD_RADIUS,
35
+ _focusVisible: { borderColor: theme.colors.primary[500], ...fieldFocusVisible(theme.colors) },
36
+ },
37
+ }),
38
+ flushed: ({ theme }) => {
39
+ const { colors } = theme;
40
+ return {
41
+ field: {
42
+ borderColor: colors.boxborder[500],
43
+ _hover: { borderColor: fieldHoverBorder(colors) },
44
+ _focusVisible: {
45
+ borderColor: colors.primary[500],
46
+ borderBottomWidth: "2px",
47
+ ...fieldFocusVisible(colors),
48
+ },
49
+ _invalid: { borderColor: colors.semanticText.error, borderBottomWidth: "2px", boxShadow: "none" },
50
+ },
51
+ };
52
+ },
53
+ unstyled: ({ theme }) => ({
54
+ field: { borderRadius: FIELD_RADIUS, _focusVisible: focusVisibleRing(theme.colors) },
55
+ }),
31
56
  },
32
57
  defaultProps: {
33
58
  variant: "outline",
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { forwardRef, useCallback } from "react";
2
+ import { forwardRef, useCallback, useRef, useState } from "react";
3
3
  import { Select as ChakraSelect, FormControl } from "@chakra-ui/react";
4
4
  import { CONTROL_SIZES, DEFAULT_CONTROL_SIZE } from "../../Theme/tokens/primitives/controlSizes.js";
5
5
  import { TextLabel } from "../Common/FormLabel.js";
@@ -19,8 +19,13 @@ const optionGroups = (options) => {
19
19
  return segments;
20
20
  };
21
21
  /** Native `<select>` renderer — today's `Select`, unchanged, plus optgroup/disabled from `option.group`/`isDisabled`. */
22
- const SelectNative = forwardRef(function SelectNative({ placeholder = "Select Option", size = DEFAULT_CONTROL_SIZE, variant = "outline", icon, onChange, onBlur, onFocus, errorBorderColor, isDisabled, isReadOnly, isRequired = false, value = "", colorScheme, label, id, name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, error = false, errorMessage, helperText, options = [], width, height, formControlStyle, isInformation = false, informationMessage, selectStyle, noOptionsMessage, }, ref) {
22
+ const SelectNative = forwardRef(function SelectNative({ placeholder = "Select Option", size = DEFAULT_CONTROL_SIZE, variant = "outline", icon, onChange, onBlur, onFocus, errorBorderColor, isDisabled, isReadOnly, isRequired = false, value, defaultValue, colorScheme, label, id, name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, error = false, errorMessage, helperText, options = [], width, height, formControlStyle, isInformation = false, informationMessage, selectStyle, noOptionsMessage, }, ref) {
23
23
  const ids = useFieldIds(id, { error, errorMessage, helperText });
24
+ const [internalValue, setInternalValue] = useState(defaultValue !== null && defaultValue !== void 0 ? defaultValue : "");
25
+ const wasControlledRef = useRef(false);
26
+ if (value !== undefined)
27
+ wasControlledRef.current = true;
28
+ const shownValue = value !== null && value !== void 0 ? value : (wasControlledRef.current ? "" : internalValue);
24
29
  const getOptionsList = useCallback(() => {
25
30
  if (!options.length) {
26
31
  return _jsx("option", { value: "", children: String(noOptionsMessage !== null && noOptionsMessage !== void 0 ? noOptionsMessage : "No Options") });
@@ -36,9 +41,11 @@ const SelectNative = forwardRef(function SelectNative({ placeholder = "Select Op
36
41
  if (isReadOnly)
37
42
  return;
38
43
  const selectedOption = options.find((option) => option.id.toString() === event.target.value.toString());
39
- if (selectedOption)
40
- onChange(selectedOption);
44
+ if (!selectedOption)
45
+ return;
46
+ setInternalValue(selectedOption.id);
47
+ onChange(selectedOption);
41
48
  };
42
- return (_jsxs(FormControl, { isInvalid: error, style: formControlStyle, width: width, children: [label && (_jsx(TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsx(ChakraSelect, { ref: ref, ...ids.controlProps, name: name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, placeholder: placeholder, size: size, variant: variant, icon: icon, iconSize: `${CONTROL_SIZES[size].iconSize}px`, isInvalid: error, isRequired: isRequired, value: value, onChange: handleOnChange, onBlur: onBlur, onFocus: onFocus, isDisabled: isDisabled, isReadOnly: isReadOnly, colorScheme: colorScheme, errorBorderColor: errorBorderColor, style: selectStyle, ...(height != null && height !== "" ? { minH: height, h: height } : {}), children: getOptionsList() }), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
49
+ return (_jsxs(FormControl, { isInvalid: error, style: formControlStyle, width: width, children: [label && (_jsx(TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsx(ChakraSelect, { ref: ref, ...ids.controlProps, name: name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, placeholder: placeholder, size: size, variant: variant, icon: icon, iconSize: `${CONTROL_SIZES[size].iconSize}px`, isInvalid: error, isRequired: isRequired, value: shownValue, onChange: handleOnChange, onBlur: onBlur, onFocus: onFocus, isDisabled: isDisabled, isReadOnly: isReadOnly, colorScheme: colorScheme, errorBorderColor: errorBorderColor, style: selectStyle, ...(height != null && height !== "" ? { minH: height, h: height } : {}), children: getOptionsList() }), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
43
50
  });
44
51
  export default SelectNative;
@@ -5,10 +5,13 @@ import { Check, ChevronDown, Plus, X } from "lucide-react";
5
5
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
6
  import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
7
7
  import { focusGlowShadow } from "../../Theme/chakra/focusRing.styles.js";
8
- import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
9
- import { optionRowDescriptionInk, optionRowFill, optionRowInk, optionRowWeight } from "./optionRowInk.js";
8
+ import { selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
9
+ import { optionRowWeight } from "./optionRowInk.js";
10
+ import { SELECT_ROW_DISABLED_CONTENT_OPACITY, selectFilterPlaceholderInk, selectRowDescriptionInk, selectRowDisabledInk, selectRowFill, selectRowInk, } from "./selectRowInk.js";
11
+ import { keyboardRingShadow, NAVIGATION_KEYS } from "./selectKeyboardRing.js";
10
12
  import { optionRowSpec } from "../../Theme/tokens/primitives/optionRowSizes.js";
11
13
  import { scrollbarStyles } from "../../Theme/tokens/builders/scrollbar.js";
14
+ import { scrollShadowStylesVertical } from "../../Theme/tokens/builders/scrollShadow.js";
12
15
  import { CONTROL_SIZES, DEFAULT_CONTROL_SIZE } from "../../Theme/tokens/primitives/controlSizes.js";
13
16
  import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
14
17
  import { BORDER_WIDTHS } from "../../Theme/tokens/primitives/borderWidths.js";
@@ -29,11 +32,13 @@ const DEFAULT_MAX_VISIBLE_CHIPS = 6;
29
32
  const VIRTUAL_OVERSCAN = 4;
30
33
  const DECORATIVE_CHECKBOX_SX = { "& input": { visibility: "hidden" } };
31
34
  const GROUP_HEADER_ESTIMATE_PX = 29;
32
- const LIST_PADDING_PX = 4;
33
35
  const NO_ROW = -1;
34
36
  const SELECT_ALL_ROW = -2;
35
37
  const CREATE_ROW = -3;
36
38
  const FOOTER_ROW = -4;
39
+ const CHECK_SLOT_MIN_PX = 16;
40
+ const SELECT_ALL_RULE_MARGIN_PX = 4;
41
+ const DISABLED_CONTENT_SX = { "& > *": { opacity: SELECT_ROW_DISABLED_CONTENT_OPACITY } };
37
42
  const truncateLabel = (label, max) => max !== undefined && label.length > max ? `${label.slice(0, max)}…` : label;
38
43
  const asIdSet = (values) => new Set((values !== null && values !== void 0 ? values : []).map((v) => v.toString()));
39
44
  /** Regression-guarded id scheme — tickets e2e (`e2e/tests/helpers/selectV2.ts`) resolves it verbatim. */
@@ -42,19 +47,26 @@ const parseRemToPx = (value, fallbackPx) => {
42
47
  const match = /^(\d*\.?\d+)rem$/.exec(value);
43
48
  return match ? parseFloat(match[1]) * 16 : fallbackPx;
44
49
  };
45
- const OptionRow = React.memo(function OptionRow({ option, index, listboxId, isSelected, isActive, isMulti, theme, backgrounds, spec, renderOption, onSelect, onActivate, }) {
50
+ const OptionRow = React.memo(function OptionRow({ option, index, listboxId, isSelected, isActive, isMulti, theme, backgrounds, ink, showRing, spec, renderOption, renderOptionEnd, optionAction, checkPlacement, onSelect, onActivate, }) {
46
51
  const isSingleSelected = isSelected && !isMulti;
47
- return (_jsxs(Box, { id: optionDomId(listboxId, option.id), "data-index": index, role: "option", "aria-selected": isSelected, "aria-disabled": option.isDisabled, onMouseEnter: () => onActivate(index), onClick: () => onSelect(option), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: option.isDisabled ? "not-allowed" : "pointer", opacity: option.isDisabled ? 0.4 : 1, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: optionRowWeight(isSingleSelected), color: optionRowInk(theme.colors, isSingleSelected), bg: optionRowFill(backgrounds, isActive), transition: ROW_TRANSITION, sx: reducedMotionSx(), children: [isMulti ? (_jsx(Checkbox, { id: `${optionDomId(listboxId, option.id)}-checkbox`, isChecked: isSelected, isDisabled: option.isDisabled, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX })) : null, renderOption ? (_jsx(Box, { flex: 1, minW: 0, children: renderOption(option, { isSelected, isActive }) })) : (_jsxs(_Fragment, { children: [option.icon ? (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: option.icon })) : null, _jsxs(Box, { flex: 1, minW: 0, isTruncated: true, children: [_jsx(Box, { as: "span", isTruncated: true, children: option.label }), option.description ? (_jsx(Box, { as: "span", display: "block", fontSize: spec.descriptionFontSize, color: optionRowDescriptionInk(theme.colors), isTruncated: true, children: option.description })) : null] })] })), isSingleSelected ? (_jsx(Box, { as: Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 })) : null] }));
52
+ const solidDisabledInk = option.isDisabled ? ink.disabled : null;
53
+ const checkSlotPx = Math.max(CHECK_SLOT_MIN_PX, spec.iconSize);
54
+ const checkSlot = (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", justifyContent: "center", flexShrink: 0, width: `${checkSlotPx}px`, children: isSingleSelected ? _jsx(Box, { as: Check, size: spec.iconSize, color: theme.colors.accentText }) : null }));
55
+ return (_jsxs(Box, { id: optionDomId(listboxId, option.id), "data-index": index, role: "option", "aria-selected": isSelected, "aria-disabled": option.isDisabled, onMouseEnter: () => onActivate(index), onClick: () => onSelect(option), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: option.isDisabled ? "not-allowed" : "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: optionRowWeight(isSingleSelected), color: solidDisabledInk !== null && solidDisabledInk !== void 0 ? solidDisabledInk : (isSingleSelected ? ink.selected : ink.rest), bg: selectRowFill(backgrounds, { isSelected, isActive }), boxShadow: showRing ? keyboardRingShadow(backgrounds.keyboardRing) : undefined, transition: ROW_TRANSITION, sx: option.isDisabled && !ink.disabled ? { ...reducedMotionSx(), ...DISABLED_CONTENT_SX } : reducedMotionSx(), children: [!isMulti && checkPlacement === "start" ? checkSlot : null, isMulti ? (_jsx(Checkbox, { id: `${optionDomId(listboxId, option.id)}-checkbox`, isChecked: isSelected, isDisabled: option.isDisabled, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX })) : null, renderOption ? (_jsx(Box, { flex: 1, minW: 0, children: renderOption(option, { isSelected, isActive }) })) : (_jsxs(_Fragment, { children: [option.icon ? (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: option.icon })) : null, _jsxs(Box, { flex: 1, minW: 0, isTruncated: true, children: [_jsx(Box, { as: "span", isTruncated: true, children: option.label }), option.description ? (_jsx(Box, { as: "span", display: "block", fontSize: spec.descriptionFontSize, color: solidDisabledInk !== null && solidDisabledInk !== void 0 ? solidDisabledInk : ink.description, isTruncated: true, children: option.description })) : null] })] })), renderOptionEnd ? (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: renderOptionEnd(option, { isSelected, isActive }) })) : null, optionAction ? (_jsx(Box, { as: "span", "aria-hidden": true, title: optionAction.label, onMouseDown: (event) => event.preventDefault(), onClick: (event) => {
56
+ event.stopPropagation();
57
+ optionAction.onAction(option);
58
+ }, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, cursor: "pointer", color: ink.description, _hover: { color: ink.selected }, children: optionAction.icon })) : null, !isMulti && checkPlacement === "end" ? checkSlot : null] }));
48
59
  });
49
60
  /** The unified `Select`'s popup mode (`native={false}`) — absorbs SelectV2's behaviour, plus clear/create/groups/virtualization. */
50
61
  const SelectPopup = forwardRef(function SelectPopup(props, ref) {
51
62
  var _a, _b, _c, _d, _e, _f;
52
- const { options = [], id, name, placeholder = "Select Option", label, error = false, errorMessage, helperText, isDisabled = false, isReadOnly = false, isRequired = false, isInformation = false, informationMessage, size = DEFAULT_CONTROL_SIZE, isSearchable = true, width, formControlStyle, onBlur, onFocus, renderOption, renderValue, maxMenuHeight = "16rem", defaultOpen = false, isLoading = false, loadingText = "Loading...", onLoadMore, hasMore, onSearchChange, searchValue, defaultSearchValue, searchDebounceMs = 0, filterOptions, isClearable = false, onClear, noOptionsMessage, onCreate, createLabel, createWhen = "no-options", footerAction, searchFields, filterOption, isFieldLoading = false, rightIcon, virtualizeAfter, "data-testid": dataTestId, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, } = props;
63
+ const { options = [], id, name, placeholder = "Select Option", label, error = false, errorMessage, helperText, isDisabled = false, isReadOnly = false, isRequired = false, isInformation = false, informationMessage, size = DEFAULT_CONTROL_SIZE, isSearchable = true, width, formControlStyle, onBlur, onFocus, renderOption, renderOptionEnd, optionAction, checkPlacement = "end", renderValue, maxMenuHeight = "16rem", defaultOpen = false, isLoading = false, loadingText = "Loading...", onLoadMore, hasMore, onSearchChange, searchValue, defaultSearchValue, searchDebounceMs = 0, filterOptions, isClearable = false, onClear, noOptionsMessage, onCreate, createLabel, createWhen = "no-options", footerAction, searchFields, filterOption, isFieldLoading = false, rightIcon, virtualizeAfter, "data-testid": dataTestId, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, } = props;
53
64
  const theme = useCustomTheme();
54
65
  const { colors } = theme;
55
66
  const { isOpen, onOpen, onClose } = useDisclosure({ defaultIsOpen: defaultOpen });
56
67
  const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
57
68
  const [activeIndex, setActiveIndex] = useState(-1);
69
+ const [isKeyboardNav, setIsKeyboardNav] = useState(false);
58
70
  const triggerRef = useRef(null);
59
71
  const mergedTriggerRef = useMergeRefs(triggerRef, ref);
60
72
  const filterInputRef = useRef(null);
@@ -116,6 +128,7 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
116
128
  if (!isSearchControlled)
117
129
  setUncontrolledSearch("");
118
130
  setActiveIndex(-1);
131
+ setIsKeyboardNav(false);
119
132
  onBlur === null || onBlur === void 0 ? void 0 : onBlur();
120
133
  }, [onClose, onBlur, props.isMulti, isSearchControlled]);
121
134
  const handleOpen = () => {
@@ -218,13 +231,14 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
218
231
  return () => cancelAnimationFrame(frame);
219
232
  }, [isOpen, isSearchable]);
220
233
  const spec = useMemo(() => optionRowSpec(size), [size]);
234
+ const listPaddingPx = parseRemToPx(spec.mx, 6);
221
235
  const maxMenuHeightPx = useMemo(() => parseRemToPx(maxMenuHeight, 256), [maxMenuHeight]);
222
236
  const shouldVirtualize = !!virtualizeAfter && filteredOptions.length > virtualizeAfter;
223
237
  const [scrollTop, setScrollTop] = useState(0);
224
238
  // A description, an avatar or a renderOption row outgrows the spec's single-line height.
225
239
  const [rowHeightPx, setRowHeightPx] = useState(spec.virtualRowHeightPx);
226
240
  const [headerHeightPx, setHeaderHeightPx] = useState(GROUP_HEADER_ESTIMATE_PX);
227
- const selectAllLeadPx = showSelectAllRow ? rowHeightPx : 0;
241
+ const selectAllLeadPx = showSelectAllRow ? rowHeightPx + parseInt(BORDER_WIDTHS.hairline, 10) + 2 * SELECT_ALL_RULE_MARGIN_PX : 0;
228
242
  const windowTop = Math.max(0, scrollTop - selectAllLeadPx);
229
243
  const visibleCount = Math.ceil(maxMenuHeightPx / rowHeightPx) + VIRTUAL_OVERSCAN * 2;
230
244
  const groupedRows = useMemo(() => (hasGroups ? buildGroupedRows(filteredOptions) : []), [hasGroups, filteredOptions]);
@@ -292,8 +306,8 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
292
306
  targetTop = groupedLayout.tops[startsGroup ? rowIndex - 1 : rowIndex];
293
307
  targetBottom = groupedLayout.tops[rowIndex + 1];
294
308
  }
295
- targetTop += selectAllLeadPx + LIST_PADDING_PX;
296
- targetBottom += selectAllLeadPx + LIST_PADDING_PX;
309
+ targetTop += selectAllLeadPx + listPaddingPx;
310
+ targetBottom += selectAllLeadPx + listPaddingPx;
297
311
  if (targetTop < el.scrollTop) {
298
312
  el.scrollTop = targetTop;
299
313
  setScrollTop(targetTop);
@@ -306,7 +320,7 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
306
320
  return;
307
321
  }
308
322
  (_d = (_c = el.querySelector(`[data-index="${activeIndex}"]`)) === null || _c === void 0 ? void 0 : _c.scrollIntoView) === null || _d === void 0 ? void 0 : _d.call(_c, { block: "nearest" });
309
- }, [activeIndex, shouldVirtualize, maxMenuHeightPx, rowHeightPx, groupedLayout, groupedRows, selectAllLeadPx, selectAllId, createRowId, footerId]);
323
+ }, [activeIndex, shouldVirtualize, maxMenuHeightPx, rowHeightPx, groupedLayout, groupedRows, selectAllLeadPx, listPaddingPx, selectAllId, createRowId, footerId]);
310
324
  const loadMoreInFlightRef = useRef(false);
311
325
  const prevOptionsLengthRef = useRef(options.length);
312
326
  const prevIsLoadingRef = useRef(isLoading);
@@ -392,6 +406,8 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
392
406
  const handleListKeyDown = (event) => {
393
407
  var _a, _b;
394
408
  const isTextInput = ((_a = event.target) === null || _a === void 0 ? void 0 : _a.tagName) === "INPUT";
409
+ if (NAVIGATION_KEYS.has(event.key))
410
+ setIsKeyboardNav(true);
395
411
  switch (event.key) {
396
412
  case "ArrowDown":
397
413
  event.preventDefault();
@@ -498,8 +514,23 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
498
514
  : showCreateRow && (isCreateImplicit || activeIndex === CREATE_ROW)
499
515
  ? createRowId
500
516
  : undefined;
501
- const listScrollStyles = useMemo(() => scrollbarStyles(colors, "overlay"), [colors]);
502
- const rowBackgrounds = useMemo(() => listRowBackgrounds(colors), [colors]);
517
+ const listScrollStyles = useMemo(() => ({
518
+ ...scrollbarStyles(colors, "overlay"),
519
+ ...scrollShadowStylesVertical(colors.surfaceRaised),
520
+ }), [colors]);
521
+ const rowBackgrounds = useMemo(() => selectRowBackgrounds(colors), [colors]);
522
+ const rowInks = useMemo(() => ({
523
+ rest: selectRowInk(colors, false),
524
+ selected: selectRowInk(colors, true),
525
+ description: selectRowDescriptionInk(colors),
526
+ disabled: selectRowDisabledInk(colors),
527
+ }), [colors]);
528
+ const filterPlaceholderInk = useMemo(() => selectFilterPlaceholderInk(colors), [colors]);
529
+ const handlePointerActivate = useCallback((index) => {
530
+ setIsKeyboardNav(false);
531
+ setActiveIndex(index);
532
+ }, []);
533
+ const ringShadowFor = (isActive) => isActive && isKeyboardNav ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined;
503
534
  const showChips = !!props.isMulti && !renderValue && selectedOptions.length > 0;
504
535
  const chipSize = size === "xs" ? "xs" : "sm";
505
536
  const maxVisibleChips = props.isMulti ? ((_a = props.maxVisibleChips) !== null && _a !== void 0 ? _a : DEFAULT_MAX_VISIBLE_CHIPS) : 0;
@@ -542,14 +573,14 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
542
573
  return (_jsxs(FormControl, { isInvalid: error, isDisabled: isDisabled, isReadOnly: isReadOnly, style: formControlStyle, width: width, children: [label && (_jsx(TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsxs(Popover, { isOpen: isOpen, onClose: () => resetAndClose(), placement: "bottom-start", autoFocus: false, returnFocusOnClose: false, gutter: 4, children: [_jsx(PopoverAnchor, { children: _jsxs(Box, { as: "div", ref: mergedTriggerRef, ...ids.controlProps, role: "combobox", tabIndex: isDisabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-owns": `${listboxId} ${filterInputId}`, "aria-activedescendant": activeDescendantId, "aria-disabled": isDisabled || undefined, "aria-busy": isFieldLoading || undefined, "data-readonly": isReadOnly || undefined, "aria-required": isRequired || undefined, "aria-label": triggerAriaLabel(), "aria-labelledby": ariaLabelledBy, onClick: handleOpen, onKeyDown: handleTriggerKeyDown, display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", textAlign: "left", cursor: isDisabled ? "not-allowed" : isReadOnly ? "default" : "pointer", "data-testid": dataTestId, sx: triggerSx, children: [_jsx(Box, { as: "span", display: "inline-flex", flexWrap: showChips ? "wrap" : "nowrap", alignItems: "center", gap: showChips ? 1 : 1.5, minW: 0, flex: 1, isTruncated: !showChips, children: showChips ? (_jsxs(_Fragment, { children: [visibleChipOptions.map((option) => (_jsx(Tag, { label: truncateLabel(String(option.label), chipMaxLength), size: chipSize, variant: "subtle", icon: canRemoveChips ? X : undefined, onIconClick: canRemoveChips ? () => handleSelect(option) : undefined }, option.id))), overflowChipCount > 0 && (_jsx(Tag, { label: `+${overflowChipCount}`, size: chipSize, variant: "subtle", colorScheme: "gray" }))] })) : (_jsxs(_Fragment, { children: [triggerIcon ? (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: triggerIcon })) : null, _jsx(Box, { as: "span", isTruncated: true, children: renderValue ? renderValue(selectedOptions) : defaultTriggerContent() })] })) }), showClear && (_jsx(Box, { as: "button", type: "button", "aria-label": "Clear selection", onClick: handleClear, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: _jsx(X, { size: CONTROL_SIZES[size].iconSize }) })), isFieldLoading ? (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", flexShrink: 0, ml: 2, children: _jsx(Spinner, { size: "xs", label: "" }) })) : rightIcon ? ("onClick" in rightIcon && isInteractive ? (_jsx(Box, { as: "button", type: "button", "aria-label": rightIcon.ariaLabel, onClick: (event) => {
543
574
  event.stopPropagation();
544
575
  rightIcon.onClick();
545
- }, onKeyDown: (event) => event.stopPropagation(), display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: rightIcon.icon })) : (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", flexShrink: 0, ml: 2, color: colors.textMuted, children: rightIcon.icon }))) : (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, ml: 2, transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", color: colors.textMuted, sx: reducedMotionSx(), children: _jsx(ChevronDown, { size: CONTROL_SIZES[size].iconSize }) }))] }) }), _jsx(Portal, { children: _jsxs(PopoverContent, { "aria-label": (_d = label !== null && label !== void 0 ? label : ariaLabel) !== null && _d !== void 0 ? _d : placeholder, width: "max-content", minWidth: triggerWidth ? `${triggerWidth}px` : undefined, maxWidth: "min(24rem, calc(100vw - 2rem))", borderRadius: "xl", boxShadow: theme.shadows.lg, overflow: "hidden", onKeyDown: handleListKeyDown, children: [_jsx(Box, { p: spec.mx, borderBottom: `${BORDER_WIDTHS.hairline} solid ${raisedRule(colors)}`, display: isSearchable ? undefined : "none", children: _jsx(Input, { ref: filterInputRef, id: filterInputId, size: size, px: spec.px, placeholder: "Type to filter...", value: currentSearch, onChange: handleFilterChange, "aria-activedescendant": activeDescendantId, "aria-controls": listboxId, "aria-label": "Filter options", tabIndex: isSearchable ? undefined : -1 }) }), _jsxs(Box, { id: listboxId, ref: listRef, role: "listbox", "aria-label": (_e = label !== null && label !== void 0 ? label : ariaLabel) !== null && _e !== void 0 ? _e : placeholder, "aria-multiselectable": props.isMulti || undefined, "aria-busy": isLoading || undefined, maxH: maxMenuHeight, overflowY: "auto", py: 1, sx: listScrollStyles, onScroll: handleListScroll, children: [filteredOptions.length === 0 && !isLoading && !showCreateRow && (_jsx(Box, { mx: spec.mx, px: spec.px, py: spec.py, fontSize: spec.fontSize, color: colors.textMuted, children: noOptionsMessage !== null && noOptionsMessage !== void 0 ? noOptionsMessage : "No Options" })), showSelectAllRow && (_jsxs(Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, onClick: handleSelectAll, onMouseEnter: () => setActiveIndex(SELECT_ALL_ROW), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: optionRowInk(colors, false), bg: optionRowFill(rowBackgrounds, activeIndex === SELECT_ALL_ROW), transition: ROW_TRANSITION, sx: reducedMotionSx(), children: [_jsx(Checkbox, { isChecked: allFilteredSelected, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX }), allFilteredSelected ? "Unselect All" : "Select All"] })), hasGroups
576
+ }, onKeyDown: (event) => event.stopPropagation(), display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: rightIcon.icon })) : (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", flexShrink: 0, ml: 2, color: colors.textMuted, children: rightIcon.icon }))) : (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, ml: 2, transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", color: colors.textMuted, sx: reducedMotionSx(), children: _jsx(ChevronDown, { size: CONTROL_SIZES[size].iconSize }) }))] }) }), _jsx(Portal, { children: _jsxs(PopoverContent, { "aria-label": (_d = label !== null && label !== void 0 ? label : ariaLabel) !== null && _d !== void 0 ? _d : placeholder, width: "max-content", minWidth: triggerWidth ? `${triggerWidth}px` : undefined, maxWidth: "min(24rem, calc(100vw - 2rem))", borderRadius: "xl", boxShadow: theme.shadows.lg, overflow: "hidden", onKeyDown: handleListKeyDown, children: [_jsx(Box, { p: spec.mx, borderBottom: `${BORDER_WIDTHS.hairline} solid ${raisedRule(colors)}`, display: isSearchable ? undefined : "none", children: _jsx(Input, { ref: filterInputRef, id: filterInputId, size: size, px: spec.px, placeholder: "Type to filter...", _placeholder: filterPlaceholderInk ? { color: filterPlaceholderInk } : undefined, value: currentSearch, onChange: handleFilterChange, "aria-activedescendant": activeDescendantId, "aria-controls": listboxId, "aria-label": "Filter options", tabIndex: isSearchable ? undefined : -1 }) }), _jsxs(Box, { id: listboxId, ref: listRef, role: "listbox", "aria-label": (_e = label !== null && label !== void 0 ? label : ariaLabel) !== null && _e !== void 0 ? _e : placeholder, "aria-multiselectable": props.isMulti || undefined, "aria-busy": isLoading || undefined, maxH: maxMenuHeight, overflowY: "auto", py: spec.mx, sx: listScrollStyles, onScroll: handleListScroll, children: [filteredOptions.length === 0 && !isLoading && !showCreateRow && (_jsx(Box, { mx: spec.mx, px: spec.px, py: spec.py, fontSize: spec.fontSize, color: colors.textMuted, children: noOptionsMessage !== null && noOptionsMessage !== void 0 ? noOptionsMessage : "No Options" })), showSelectAllRow && (_jsxs(Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, onClick: handleSelectAll, onMouseEnter: () => handlePointerActivate(SELECT_ALL_ROW), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: rowInks.rest, bg: selectRowFill(rowBackgrounds, { isSelected: false, isActive: activeIndex === SELECT_ALL_ROW }), boxShadow: ringShadowFor(activeIndex === SELECT_ALL_ROW), transition: ROW_TRANSITION, sx: reducedMotionSx(), children: [_jsx(Checkbox, { isChecked: allFilteredSelected, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX }), allFilteredSelected ? "Unselect All" : "Select All"] })), showSelectAllRow && (_jsx(Box, { role: "presentation", mx: spec.mx, my: `${SELECT_ALL_RULE_MARGIN_PX}px`, height: BORDER_WIDTHS.hairline, bg: raisedRule(colors) })), hasGroups
546
577
  ? (_jsxs(_Fragment, { children: [topPadPx > 0 && _jsx(Box, { height: `${topPadPx}px` }), renderedGroupedRows.map((row) => {
547
578
  var _a;
548
- return row.kind === "header" ? (_jsx(Box, { "data-group-header": shouldVirtualize ? "" : undefined, mx: spec.mx, px: spec.px, pt: 2, pb: 1, fontSize: "xs-minus", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.03em", color: colors.textMuted, children: row.group }, row.key)) : (_jsx(OptionRow, { option: row.option, index: row.index, listboxId: listboxId, isSelected: selectedIds.has(row.option.id.toString()), isActive: row.index === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, spec: spec, renderOption: renderOption, onSelect: handleSelect, onActivate: setActiveIndex }, row.key));
579
+ return row.kind === "header" ? (_jsx(Box, { "data-group-header": shouldVirtualize ? "" : undefined, mx: spec.mx, px: spec.px, pt: 2, pb: 1, fontSize: "xs-minus", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.03em", color: colors.textMuted, children: row.group }, row.key)) : (_jsx(OptionRow, { option: row.option, index: row.index, listboxId: listboxId, isSelected: selectedIds.has(row.option.id.toString()), isActive: row.index === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, ink: rowInks, showRing: isKeyboardNav && row.index === activeIndex, spec: spec, renderOption: renderOption, renderOptionEnd: renderOptionEnd, optionAction: optionAction, checkPlacement: checkPlacement, onSelect: handleSelect, onActivate: handlePointerActivate }, row.key));
549
580
  }), bottomPadPx > 0 && _jsx(Box, { height: `${bottomPadPx}px` })] }))
550
581
  : (_jsxs(_Fragment, { children: [topPadPx > 0 && _jsx(Box, { height: `${topPadPx}px` }), renderedOptions.map((option, i) => {
551
582
  var _a;
552
- return (_jsx(OptionRow, { option: option, index: startIndex + i, listboxId: listboxId, isSelected: selectedIds.has(option.id.toString()), isActive: startIndex + i === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, spec: spec, renderOption: renderOption, onSelect: handleSelect, onActivate: setActiveIndex }, option.id));
553
- }), bottomPadPx > 0 && _jsx(Box, { height: `${bottomPadPx}px` })] })), showCreateRow && (_jsx(Box, { id: createRowId, role: "option", "aria-selected": false, "data-index": -1, onClick: handleCreate, mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", fontSize: spec.fontSize, fontWeight: 600, color: colors.primary[600], bg: activeDescendantId === createRowId ? rowBackgrounds.active : "transparent", children: createLabel ? createLabel(currentSearch.trim()) : `Create "${currentSearch.trim()}"` })), isLoading && (_jsxs(Box, { mx: spec.mx, px: spec.px, py: spec.py, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: colors.textMuted, children: [_jsx(Spinner, { size: "xs" }), loadingText] })), footerAction && (_jsx(Box, { id: footerId, role: "option", "aria-selected": false, onClick: handleFooterAction, onMouseEnter: () => setActiveIndex(FOOTER_ROW), position: "sticky", bottom: -1, pt: 1, bg: colors.surfaceRaised, borderTop: `${BORDER_WIDTHS.hairline} solid ${raisedRule(colors)}`, children: _jsxs(Box, { mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: 500, color: colors.accentText, bg: activeIndex === FOOTER_ROW ? rowBackgrounds.active : "transparent", children: [(_f = footerAction.icon) !== null && _f !== void 0 ? _f : _jsx(Plus, { size: spec.iconSize }), footerAction.label] }) }))] })] }) })] }), hiddenValues.map((value) => (_jsx("input", { type: "hidden", name: name, value: value }, value))), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
583
+ return (_jsx(OptionRow, { option: option, index: startIndex + i, listboxId: listboxId, isSelected: selectedIds.has(option.id.toString()), isActive: startIndex + i === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, ink: rowInks, showRing: isKeyboardNav && startIndex + i === activeIndex, spec: spec, renderOption: renderOption, renderOptionEnd: renderOptionEnd, optionAction: optionAction, checkPlacement: checkPlacement, onSelect: handleSelect, onActivate: handlePointerActivate }, option.id));
584
+ }), bottomPadPx > 0 && _jsx(Box, { height: `${bottomPadPx}px` })] })), showCreateRow && (_jsx(Box, { id: createRowId, role: "option", "aria-selected": false, "data-index": -1, onClick: handleCreate, mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", fontSize: spec.fontSize, fontWeight: 600, color: colors.primary[600], bg: activeDescendantId === createRowId ? rowBackgrounds.active : "transparent", boxShadow: ringShadowFor(activeDescendantId === createRowId), children: createLabel ? createLabel(currentSearch.trim()) : `Create "${currentSearch.trim()}"` })), isLoading && (_jsxs(Box, { mx: spec.mx, px: spec.px, py: spec.py, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: colors.textMuted, children: [_jsx(Spinner, { size: "xs" }), loadingText] })), footerAction && (_jsx(Box, { id: footerId, role: "option", "aria-selected": false, onClick: handleFooterAction, onMouseEnter: () => handlePointerActivate(FOOTER_ROW), position: "sticky", bottom: `-${spec.mx}`, pt: 1, bg: colors.surfaceRaised, borderTop: `${BORDER_WIDTHS.hairline} solid ${raisedRule(colors)}`, children: _jsxs(Box, { mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: 500, color: colors.accentText, bg: activeIndex === FOOTER_ROW ? rowBackgrounds.active : "transparent", boxShadow: ringShadowFor(activeIndex === FOOTER_ROW), children: [(_f = footerAction.icon) !== null && _f !== void 0 ? _f : _jsx(Plus, { size: spec.iconSize }), footerAction.label] }) }))] })] }) })] }), hiddenValues.map((value) => (_jsx("input", { type: "hidden", name: name, value: value }, value))), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
554
585
  });
555
586
  export default SelectPopup;
@@ -0,0 +1,3 @@
1
+ import { BORDER_WIDTHS } from "../../Theme/tokens/primitives/borderWidths.js";
2
+ export const NAVIGATION_KEYS = new Set(["ArrowDown", "ArrowUp", "Home", "End", "PageUp", "PageDown"]);
3
+ export const keyboardRingShadow = (ring) => `inset 0 0 0 ${BORDER_WIDTHS.hairline} ${ring}`;
@@ -0,0 +1,33 @@
1
+ import { isDarkCanvas, AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
2
+ import { liftToContrast, neutralStep } from "../../Theme/tokens/builders/outlineRung.js";
3
+ import { selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
4
+ import { optionRowInk } from "./optionRowInk.js";
5
+ export const SELECT_ROW_DISABLED_CONTENT_OPACITY = 0.55;
6
+ const NEUTRAL_INK_STEPS = {
7
+ label: 12.8,
8
+ selected: 15.4,
9
+ description: 7,
10
+ disabled: 3.6,
11
+ placeholder: 4.6,
12
+ };
13
+ // NOTE: light is a brand-free grey ladder off the panel; dark keeps the shared inks.
14
+ const neutralInk = (colors, role) => neutralStep(colors.surfaceRaised, NEUTRAL_INK_STEPS[role]);
15
+ const isDarkPanel = (colors) => isDarkCanvas(colors.surfaceRaised);
16
+ export const selectRowInk = (colors, isSingleSelected) => {
17
+ if (isDarkPanel(colors))
18
+ return optionRowInk(colors, isSingleSelected);
19
+ return neutralInk(colors, isSingleSelected ? "selected" : "label");
20
+ };
21
+ export const selectRowDescriptionInk = (colors) => isDarkPanel(colors)
22
+ ? liftToContrast(colors.textMuted, selectRowBackgrounds(colors).selectedActive, AA_NORMAL_TEXT)
23
+ : neutralInk(colors, "description");
24
+ /** Solid ink for a disabled row's content in light; dark dims content by opacity instead (null). */
25
+ export const selectRowDisabledInk = (colors) => isDarkPanel(colors) ? null : neutralInk(colors, "disabled");
26
+ /** Placeholder of the popup's filter input in light; dark keeps the shared placeholder ink (null). */
27
+ export const selectFilterPlaceholderInk = (colors) => isDarkPanel(colors) ? null : neutralInk(colors, "placeholder");
28
+ /** Cursor and selection each paint a rung; the stacked state paints the third. */
29
+ export const selectRowFill = (rows, state) => {
30
+ if (state.isActive)
31
+ return state.isSelected ? rows.selectedActive : rows.active;
32
+ return state.isSelected ? rows.selected : "transparent";
33
+ };