@eocrm/design-system 0.0.0-stage → 0.6.17

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 (897) hide show
  1. package/AI-PRIMER.md +341 -0
  2. package/README.md +262 -2
  3. package/docs/components/Accordion.md +114 -0
  4. package/docs/components/AgentAvatar.md +58 -0
  5. package/docs/components/Alert.md +81 -0
  6. package/docs/components/AppLayout.md +70 -0
  7. package/docs/components/Avatar.md +49 -0
  8. package/docs/components/AvatarGroup.md +47 -0
  9. package/docs/components/Badge.md +60 -0
  10. package/docs/components/Banner.md +89 -0
  11. package/docs/components/BrandIcon.md +32 -0
  12. package/docs/components/Breadcrumb.md +72 -0
  13. package/docs/components/Button.md +107 -0
  14. package/docs/components/ButtonGroup.md +78 -0
  15. package/docs/components/Calendar.md +158 -0
  16. package/docs/components/Card.md +165 -0
  17. package/docs/components/CatalogPicker.md +83 -0
  18. package/docs/components/Chart.md +114 -0
  19. package/docs/components/ChatBubble.md +55 -0
  20. package/docs/components/ChatLauncher.md +97 -0
  21. package/docs/components/Checkbox.md +55 -0
  22. package/docs/components/CircularProgress.md +53 -0
  23. package/docs/components/Cluster.md +96 -0
  24. package/docs/components/Code.md +35 -0
  25. package/docs/components/CodeBlock.md +67 -0
  26. package/docs/components/ColorPicker.md +115 -0
  27. package/docs/components/ConfirmationPopover.md +70 -0
  28. package/docs/components/Constrain.md +39 -0
  29. package/docs/components/CurrencySelect.md +57 -0
  30. package/docs/components/CursorPagination.md +43 -0
  31. package/docs/components/DashboardCanvas.md +79 -0
  32. package/docs/components/DashboardWidget.md +131 -0
  33. package/docs/components/DataTable.md +88 -0
  34. package/docs/components/DatePicker.md +68 -0
  35. package/docs/components/DateRangePicker.md +71 -0
  36. package/docs/components/DateStrip.md +56 -0
  37. package/docs/components/DefinitionList.md +82 -0
  38. package/docs/components/Divider.md +57 -0
  39. package/docs/components/Dot.md +26 -0
  40. package/docs/components/Drawer.md +94 -0
  41. package/docs/components/DropdownMenu.md +299 -0
  42. package/docs/components/EmojiPicker.md +84 -0
  43. package/docs/components/EmptyState.md +55 -0
  44. package/docs/components/EntityChip.md +102 -0
  45. package/docs/components/ErrorState.md +72 -0
  46. package/docs/components/Field.md +91 -0
  47. package/docs/components/FileUpload.md +109 -0
  48. package/docs/components/FilterChip.md +88 -0
  49. package/docs/components/FlowCanvas.md +108 -0
  50. package/docs/components/FormRow.md +31 -0
  51. package/docs/components/FormSection.md +37 -0
  52. package/docs/components/Grid.md +103 -0
  53. package/docs/components/Highlight.md +75 -0
  54. package/docs/components/HighlightedText.md +50 -0
  55. package/docs/components/IconPicker.md +134 -0
  56. package/docs/components/IconTile.md +30 -0
  57. package/docs/components/Image.md +84 -0
  58. package/docs/components/ImageCrop.md +163 -0
  59. package/docs/components/Indent.md +31 -0
  60. package/docs/components/InlineDatePicker.md +58 -0
  61. package/docs/components/InlineDateRangePicker.md +57 -0
  62. package/docs/components/Input.md +54 -0
  63. package/docs/components/Kanban.md +122 -0
  64. package/docs/components/Kbd.md +41 -0
  65. package/docs/components/Lightbox.md +105 -0
  66. package/docs/components/Link.md +65 -0
  67. package/docs/components/LinkCard.md +41 -0
  68. package/docs/components/LiquidEditor.md +84 -0
  69. package/docs/components/List.md +68 -0
  70. package/docs/components/LiveRegion.md +29 -0
  71. package/docs/components/LocaleProvider.md +25 -0
  72. package/docs/components/Logo.md +41 -0
  73. package/docs/components/Masonry.md +48 -0
  74. package/docs/components/MediaTile.md +95 -0
  75. package/docs/components/Modal.md +107 -0
  76. package/docs/components/MoneyInput.md +62 -0
  77. package/docs/components/OptionsPicker.md +123 -0
  78. package/docs/components/OtpInput.md +65 -0
  79. package/docs/components/Page.md +35 -0
  80. package/docs/components/PageHeader.md +133 -0
  81. package/docs/components/Pagination.md +44 -0
  82. package/docs/components/Palette.md +32 -0
  83. package/docs/components/PasswordInput.md +45 -0
  84. package/docs/components/PasswordStrengthMeter.md +27 -0
  85. package/docs/components/PersonDisplay.md +153 -0
  86. package/docs/components/PhoneInput.md +54 -0
  87. package/docs/components/PillMenu.md +91 -0
  88. package/docs/components/Popover.md +127 -0
  89. package/docs/components/Progress.md +56 -0
  90. package/docs/components/QrCode.md +34 -0
  91. package/docs/components/Radio.md +37 -0
  92. package/docs/components/RadioGroup.md +56 -0
  93. package/docs/components/Rail.md +191 -0
  94. package/docs/components/RelativeDateInput.md +114 -0
  95. package/docs/components/RichText.md +44 -0
  96. package/docs/components/RichTextEditor.md +112 -0
  97. package/docs/components/Screen.md +37 -0
  98. package/docs/components/ScrollArea.md +39 -0
  99. package/docs/components/Select.md +131 -0
  100. package/docs/components/SettingRow.md +98 -0
  101. package/docs/components/Skeleton.md +68 -0
  102. package/docs/components/Slider.md +92 -0
  103. package/docs/components/SlotGrid.md +55 -0
  104. package/docs/components/SocialButton.md +31 -0
  105. package/docs/components/Sortable.md +127 -0
  106. package/docs/components/SortableGroup.md +52 -0
  107. package/docs/components/Sparkline.md +51 -0
  108. package/docs/components/Split.md +76 -0
  109. package/docs/components/Stack.md +59 -0
  110. package/docs/components/StagePath.md +66 -0
  111. package/docs/components/StatGroup.md +61 -0
  112. package/docs/components/StatTile.md +58 -0
  113. package/docs/components/Sticky.md +69 -0
  114. package/docs/components/Switch.md +89 -0
  115. package/docs/components/Table.md +184 -0
  116. package/docs/components/TableOfContents.md +76 -0
  117. package/docs/components/Tabs.md +94 -0
  118. package/docs/components/Text.md +63 -0
  119. package/docs/components/Textarea.md +59 -0
  120. package/docs/components/Thread.md +52 -0
  121. package/docs/components/TimeField.md +70 -0
  122. package/docs/components/Timeline.md +57 -0
  123. package/docs/components/Title.md +54 -0
  124. package/docs/components/ToastViewport.md +61 -0
  125. package/docs/components/Tooltip.md +63 -0
  126. package/docs/components/TopBar.md +214 -0
  127. package/docs/components/Tour.md +85 -0
  128. package/docs/components/TreeView.md +113 -0
  129. package/docs/components/VisuallyHidden.md +43 -0
  130. package/docs/components/WidgetPreview.md +54 -0
  131. package/docs/components/useBelowBreakpoint.md +11 -0
  132. package/docs/components/useMonth.md +18 -0
  133. package/docs/setup.md +51 -0
  134. package/docs/theming.md +95 -0
  135. package/docs/tokens.md +123 -0
  136. package/docs/transient-state.md +56 -0
  137. package/package.json +82 -4
  138. package/src/_meta/manifest.ts +293 -0
  139. package/src/app/AppProvider.tsx +78 -0
  140. package/src/app/index.ts +3 -0
  141. package/src/app/themeTokens.ts +95 -0
  142. package/src/calendar/dateMath.ts +114 -0
  143. package/src/calendar/formatters.ts +75 -0
  144. package/src/calendar/index.ts +39 -0
  145. package/src/calendar/types.ts +36 -0
  146. package/src/calendar/useAgenda.ts +70 -0
  147. package/src/calendar/useDay.ts +51 -0
  148. package/src/calendar/useMonth.ts +82 -0
  149. package/src/calendar/useWeek.ts +62 -0
  150. package/src/calendar/weekInfo.ts +83 -0
  151. package/src/components/Accordion/Accordion.module.scss +304 -0
  152. package/src/components/Accordion/Accordion.tokens.scss +64 -0
  153. package/src/components/Accordion/Accordion.tsx +272 -0
  154. package/src/components/Accordion/AccordionContent.tsx +36 -0
  155. package/src/components/Accordion/AccordionItem.tsx +66 -0
  156. package/src/components/Accordion/AccordionTrigger.tsx +121 -0
  157. package/src/components/Accordion/context.ts +47 -0
  158. package/src/components/Accordion/index.ts +14 -0
  159. package/src/components/Alert/Alert.module.scss +92 -0
  160. package/src/components/Alert/Alert.tokens.scss +60 -0
  161. package/src/components/Alert/Alert.tsx +136 -0
  162. package/src/components/Alert/index.ts +2 -0
  163. package/src/components/AppLayout/AppLayout.module.scss +130 -0
  164. package/src/components/AppLayout/AppLayout.tokens.scss +27 -0
  165. package/src/components/AppLayout/AppLayout.tsx +211 -0
  166. package/src/components/AppLayout/index.ts +2 -0
  167. package/src/components/Avatar/AgentAvatar.module.scss +117 -0
  168. package/src/components/Avatar/AgentAvatar.tsx +122 -0
  169. package/src/components/Avatar/Avatar.module.scss +181 -0
  170. package/src/components/Avatar/Avatar.tokens.scss +107 -0
  171. package/src/components/Avatar/Avatar.tsx +210 -0
  172. package/src/components/Avatar/AvatarGroup.module.scss +128 -0
  173. package/src/components/Avatar/AvatarGroup.tsx +119 -0
  174. package/src/components/Avatar/AvatarGroupContext.tsx +20 -0
  175. package/src/components/Avatar/index.ts +6 -0
  176. package/src/components/Badge/Badge.module.scss +124 -0
  177. package/src/components/Badge/Badge.tokens.scss +74 -0
  178. package/src/components/Badge/Badge.tsx +164 -0
  179. package/src/components/Badge/index.ts +2 -0
  180. package/src/components/Banner/Banner.module.scss +73 -0
  181. package/src/components/Banner/Banner.tokens.scss +51 -0
  182. package/src/components/Banner/Banner.tsx +138 -0
  183. package/src/components/Banner/index.ts +2 -0
  184. package/src/components/BrandIcon/BrandIcon.module.scss +5 -0
  185. package/src/components/BrandIcon/BrandIcon.tsx +99 -0
  186. package/src/components/BrandIcon/index.ts +2 -0
  187. package/src/components/Breadcrumb/Breadcrumb.module.scss +32 -0
  188. package/src/components/Breadcrumb/Breadcrumb.tokens.scss +9 -0
  189. package/src/components/Breadcrumb/Breadcrumb.tsx +146 -0
  190. package/src/components/Breadcrumb/index.ts +2 -0
  191. package/src/components/Button/Button.module.scss +167 -0
  192. package/src/components/Button/Button.tokens.scss +90 -0
  193. package/src/components/Button/Button.tsx +205 -0
  194. package/src/components/Button/index.ts +2 -0
  195. package/src/components/ButtonGroup/ButtonGroup.module.scss +134 -0
  196. package/src/components/ButtonGroup/ButtonGroup.tokens.scss +42 -0
  197. package/src/components/ButtonGroup/ButtonGroup.tsx +278 -0
  198. package/src/components/ButtonGroup/ButtonGroupItem.tsx +74 -0
  199. package/src/components/ButtonGroup/context.ts +45 -0
  200. package/src/components/ButtonGroup/index.ts +2 -0
  201. package/src/components/Calendar/AgendaView.module.scss +231 -0
  202. package/src/components/Calendar/AgendaView.tsx +214 -0
  203. package/src/components/Calendar/AllDayBand.module.scss +18 -0
  204. package/src/components/Calendar/AllDayBand.tsx +75 -0
  205. package/src/components/Calendar/Calendar.module.scss +23 -0
  206. package/src/components/Calendar/Calendar.tokens.scss +262 -0
  207. package/src/components/Calendar/Calendar.tsx +370 -0
  208. package/src/components/Calendar/DayCell.module.scss +30 -0
  209. package/src/components/Calendar/DayCell.tsx +50 -0
  210. package/src/components/Calendar/DayView.module.scss +10 -0
  211. package/src/components/Calendar/DayView.tsx +163 -0
  212. package/src/components/Calendar/EventChip.module.scss +171 -0
  213. package/src/components/Calendar/EventChip.tsx +115 -0
  214. package/src/components/Calendar/HourGrid.module.scss +123 -0
  215. package/src/components/Calendar/HourGrid.tsx +381 -0
  216. package/src/components/Calendar/MonthView.module.scss +99 -0
  217. package/src/components/Calendar/MonthView.tsx +296 -0
  218. package/src/components/Calendar/TimedEvent.module.scss +205 -0
  219. package/src/components/Calendar/TimedEvent.tsx +247 -0
  220. package/src/components/Calendar/ViewSwitcher.module.scss +3 -0
  221. package/src/components/Calendar/ViewSwitcher.tsx +47 -0
  222. package/src/components/Calendar/WeekView.module.scss +10 -0
  223. package/src/components/Calendar/WeekView.tsx +137 -0
  224. package/src/components/Calendar/eventColor.ts +60 -0
  225. package/src/components/Calendar/index.ts +18 -0
  226. package/src/components/Calendar/types.ts +451 -0
  227. package/src/components/Calendar/useEventDrag.ts +783 -0
  228. package/src/components/Calendar/utils.ts +622 -0
  229. package/src/components/Card/Card.module.scss +264 -0
  230. package/src/components/Card/Card.tokens.scss +55 -0
  231. package/src/components/Card/Card.tsx +170 -0
  232. package/src/components/Card/CardBody.tsx +45 -0
  233. package/src/components/Card/CardHeader.tsx +113 -0
  234. package/src/components/Card/CardList.tsx +24 -0
  235. package/src/components/Card/CardListRow.tsx +24 -0
  236. package/src/components/Card/index.ts +7 -0
  237. package/src/components/CatalogPicker/CatalogPicker.module.scss +193 -0
  238. package/src/components/CatalogPicker/CatalogPicker.tokens.scss +26 -0
  239. package/src/components/CatalogPicker/CatalogPicker.tsx +339 -0
  240. package/src/components/CatalogPicker/index.ts +2 -0
  241. package/src/components/Chart/Chart.module.scss +287 -0
  242. package/src/components/Chart/Chart.tokens.scss +63 -0
  243. package/src/components/Chart/Chart.tsx +607 -0
  244. package/src/components/Chart/ChartLegend.tsx +99 -0
  245. package/src/components/Chart/ChartTable.tsx +62 -0
  246. package/src/components/Chart/ChartTooltip.tsx +91 -0
  247. package/src/components/Chart/chartModel.ts +689 -0
  248. package/src/components/Chart/index.ts +2 -0
  249. package/src/components/ChatBubble/ChatBubble.module.scss +93 -0
  250. package/src/components/ChatBubble/ChatBubble.tokens.scss +17 -0
  251. package/src/components/ChatBubble/ChatBubble.tsx +61 -0
  252. package/src/components/ChatBubble/ChatBubbleList.tsx +27 -0
  253. package/src/components/ChatBubble/index.ts +3 -0
  254. package/src/components/ChatLauncher/ChatLauncher.module.scss +68 -0
  255. package/src/components/ChatLauncher/ChatLauncher.tokens.scss +27 -0
  256. package/src/components/ChatLauncher/ChatLauncher.tsx +202 -0
  257. package/src/components/ChatLauncher/index.ts +2 -0
  258. package/src/components/ChatLauncher/isAtBottom.ts +12 -0
  259. package/src/components/Checkbox/Checkbox.module.scss +147 -0
  260. package/src/components/Checkbox/Checkbox.tokens.scss +44 -0
  261. package/src/components/Checkbox/Checkbox.tsx +188 -0
  262. package/src/components/Checkbox/index.ts +2 -0
  263. package/src/components/CircularProgress/CircularProgress.module.scss +133 -0
  264. package/src/components/CircularProgress/CircularProgress.tokens.scss +40 -0
  265. package/src/components/CircularProgress/CircularProgress.tsx +139 -0
  266. package/src/components/CircularProgress/index.ts +7 -0
  267. package/src/components/Cluster/Cluster.module.scss +145 -0
  268. package/src/components/Cluster/Cluster.tokens.scss +11 -0
  269. package/src/components/Cluster/Cluster.tsx +215 -0
  270. package/src/components/Cluster/index.ts +2 -0
  271. package/src/components/Code/Code.module.scss +28 -0
  272. package/src/components/Code/Code.tokens.scss +15 -0
  273. package/src/components/Code/Code.tsx +44 -0
  274. package/src/components/Code/index.ts +2 -0
  275. package/src/components/CodeBlock/CodeBlock.module.scss +146 -0
  276. package/src/components/CodeBlock/CodeBlock.tokens.scss +27 -0
  277. package/src/components/CodeBlock/CodeBlock.tsx +274 -0
  278. package/src/components/CodeBlock/highlight.ts +179 -0
  279. package/src/components/CodeBlock/index.ts +8 -0
  280. package/src/components/CodeBlock/sampleSync.ts +42 -0
  281. package/src/components/ColorPicker/ColorPicker.module.scss +286 -0
  282. package/src/components/ColorPicker/ColorPicker.tokens.scss +60 -0
  283. package/src/components/ColorPicker/ColorPicker.tsx +367 -0
  284. package/src/components/ColorPicker/ColorPickerPanel.tsx +265 -0
  285. package/src/components/ColorPicker/SVSquare.tsx +189 -0
  286. package/src/components/ColorPicker/colorMath.ts +141 -0
  287. package/src/components/ColorPicker/index.ts +6 -0
  288. package/src/components/ConfirmationPopover/ConfirmationPopover.module.scss +39 -0
  289. package/src/components/ConfirmationPopover/ConfirmationPopover.tokens.scss +13 -0
  290. package/src/components/ConfirmationPopover/ConfirmationPopover.tsx +395 -0
  291. package/src/components/ConfirmationPopover/index.ts +2 -0
  292. package/src/components/Constrain/Constrain.module.scss +99 -0
  293. package/src/components/Constrain/Constrain.tsx +79 -0
  294. package/src/components/Constrain/index.ts +2 -0
  295. package/src/components/CurrencySelect/CurrencySelect.module.scss +7 -0
  296. package/src/components/CurrencySelect/CurrencySelect.tsx +167 -0
  297. package/src/components/CurrencySelect/index.ts +3 -0
  298. package/src/components/CursorPagination/CursorPagination.module.scss +69 -0
  299. package/src/components/CursorPagination/CursorPagination.tokens.scss +32 -0
  300. package/src/components/CursorPagination/CursorPagination.tsx +121 -0
  301. package/src/components/CursorPagination/index.ts +2 -0
  302. package/src/components/DashboardCanvas/DashboardCanvas.module.scss +360 -0
  303. package/src/components/DashboardCanvas/DashboardCanvas.tokens.scss +48 -0
  304. package/src/components/DashboardCanvas/DashboardCanvas.tsx +1270 -0
  305. package/src/components/DashboardCanvas/DashboardCanvasItem.tsx +140 -0
  306. package/src/components/DashboardCanvas/DashboardCanvasSection.tsx +174 -0
  307. package/src/components/DashboardCanvas/engine.ts +363 -0
  308. package/src/components/DashboardCanvas/index.ts +9 -0
  309. package/src/components/DashboardWidget/DashboardWidget.module.scss +100 -0
  310. package/src/components/DashboardWidget/DashboardWidget.tokens.scss +16 -0
  311. package/src/components/DashboardWidget/DashboardWidget.tsx +188 -0
  312. package/src/components/DashboardWidget/index.ts +7 -0
  313. package/src/components/DataTable/BodyRow.tsx +197 -0
  314. package/src/components/DataTable/ColumnVisibilityTrigger.module.scss +7 -0
  315. package/src/components/DataTable/ColumnVisibilityTrigger.tsx +105 -0
  316. package/src/components/DataTable/DataTable.module.scss +511 -0
  317. package/src/components/DataTable/DataTable.tokens.scss +80 -0
  318. package/src/components/DataTable/DataTable.tsx +734 -0
  319. package/src/components/DataTable/HeaderCell.module.scss +215 -0
  320. package/src/components/DataTable/HeaderCell.tsx +440 -0
  321. package/src/components/DataTable/columnShift.ts +117 -0
  322. package/src/components/DataTable/index.ts +23 -0
  323. package/src/components/DataTable/pinStyle.ts +47 -0
  324. package/src/components/DataTable/reorderColumns.ts +66 -0
  325. package/src/components/DataTable/types.ts +289 -0
  326. package/src/components/DataTable/useColumnDragShift.ts +216 -0
  327. package/src/components/DataTable/useDataTable.ts +372 -0
  328. package/src/components/DataTable/useResizeHandle.ts +97 -0
  329. package/src/components/DatePicker/DatePicker.module.scss +180 -0
  330. package/src/components/DatePicker/DatePicker.tokens.scss +152 -0
  331. package/src/components/DatePicker/DatePicker.tsx +664 -0
  332. package/src/components/DatePicker/DatePickerGrid.module.scss +172 -0
  333. package/src/components/DatePicker/DatePickerGrid.tsx +341 -0
  334. package/src/components/DatePicker/InlineDatePicker.module.scss +32 -0
  335. package/src/components/DatePicker/InlineDatePicker.tsx +201 -0
  336. package/src/components/DatePicker/index.ts +4 -0
  337. package/src/components/DatePicker/utils.ts +364 -0
  338. package/src/components/DateRangePicker/DateRangePicker.module.scss +237 -0
  339. package/src/components/DateRangePicker/DateRangePicker.tokens.scss +78 -0
  340. package/src/components/DateRangePicker/DateRangePicker.tsx +720 -0
  341. package/src/components/DateRangePicker/InlineDateRangePicker.module.scss +94 -0
  342. package/src/components/DateRangePicker/InlineDateRangePicker.tsx +376 -0
  343. package/src/components/DateRangePicker/index.ts +5 -0
  344. package/src/components/DateRangePicker/utils.ts +151 -0
  345. package/src/components/DateStrip/DateStrip.module.scss +78 -0
  346. package/src/components/DateStrip/DateStrip.tokens.scss +23 -0
  347. package/src/components/DateStrip/DateStrip.tsx +205 -0
  348. package/src/components/DateStrip/index.ts +2 -0
  349. package/src/components/DefinitionList/DefinitionList.module.scss +102 -0
  350. package/src/components/DefinitionList/DefinitionList.tokens.scss +29 -0
  351. package/src/components/DefinitionList/DefinitionList.tsx +198 -0
  352. package/src/components/DefinitionList/index.ts +9 -0
  353. package/src/components/Divider/Divider.module.scss +144 -0
  354. package/src/components/Divider/Divider.tokens.scss +21 -0
  355. package/src/components/Divider/Divider.tsx +103 -0
  356. package/src/components/Divider/index.ts +2 -0
  357. package/src/components/Dot/Dot.module.scss +40 -0
  358. package/src/components/Dot/Dot.tokens.scss +4 -0
  359. package/src/components/Dot/Dot.tsx +45 -0
  360. package/src/components/Dot/index.ts +2 -0
  361. package/src/components/Drawer/Body.tsx +23 -0
  362. package/src/components/Drawer/Close.tsx +35 -0
  363. package/src/components/Drawer/Content.tsx +93 -0
  364. package/src/components/Drawer/Drawer.module.scss +232 -0
  365. package/src/components/Drawer/Drawer.tokens.scss +38 -0
  366. package/src/components/Drawer/Drawer.tsx +23 -0
  367. package/src/components/Drawer/DrawerRoot.tsx +190 -0
  368. package/src/components/Drawer/Footer.tsx +24 -0
  369. package/src/components/Drawer/Header.tsx +64 -0
  370. package/src/components/Drawer/Overlay.tsx +82 -0
  371. package/src/components/Drawer/context.ts +44 -0
  372. package/src/components/Drawer/index.ts +12 -0
  373. package/src/components/Drawer/useDragToClose.ts +149 -0
  374. package/src/components/DropdownMenu/CheckboxItem.tsx +144 -0
  375. package/src/components/DropdownMenu/Content.tsx +419 -0
  376. package/src/components/DropdownMenu/DropdownMenu.module.scss +280 -0
  377. package/src/components/DropdownMenu/DropdownMenu.tokens.scss +88 -0
  378. package/src/components/DropdownMenu/DropdownMenu.tsx +43 -0
  379. package/src/components/DropdownMenu/Group.tsx +76 -0
  380. package/src/components/DropdownMenu/Item.tsx +148 -0
  381. package/src/components/DropdownMenu/ItemIndicator.tsx +30 -0
  382. package/src/components/DropdownMenu/Radio.tsx +178 -0
  383. package/src/components/DropdownMenu/Root.tsx +100 -0
  384. package/src/components/DropdownMenu/Sub.tsx +332 -0
  385. package/src/components/DropdownMenu/Trigger.tsx +76 -0
  386. package/src/components/DropdownMenu/context.ts +65 -0
  387. package/src/components/DropdownMenu/index.ts +20 -0
  388. package/src/components/EmojiPicker/EmojiPicker.module.scss +84 -0
  389. package/src/components/EmojiPicker/EmojiPicker.tokens.scss +24 -0
  390. package/src/components/EmojiPicker/EmojiPicker.tsx +341 -0
  391. package/src/components/EmojiPicker/emojiData.ts +245 -0
  392. package/src/components/EmojiPicker/index.ts +3 -0
  393. package/src/components/EmptyState/EmptyState.module.scss +102 -0
  394. package/src/components/EmptyState/EmptyState.tokens.scss +28 -0
  395. package/src/components/EmptyState/EmptyState.tsx +112 -0
  396. package/src/components/EmptyState/index.ts +7 -0
  397. package/src/components/EntityChip/EntityChip.module.scss +275 -0
  398. package/src/components/EntityChip/EntityChip.tokens.scss +66 -0
  399. package/src/components/EntityChip/EntityChip.tsx +649 -0
  400. package/src/components/EntityChip/index.ts +10 -0
  401. package/src/components/ErrorState/ErrorState.module.scss +105 -0
  402. package/src/components/ErrorState/ErrorState.tokens.scss +32 -0
  403. package/src/components/ErrorState/ErrorState.tsx +125 -0
  404. package/src/components/ErrorState/index.ts +8 -0
  405. package/src/components/Field/Field.module.scss +46 -0
  406. package/src/components/Field/Field.tsx +185 -0
  407. package/src/components/Field/index.ts +2 -0
  408. package/src/components/FileUpload/FileUpload.module.scss +187 -0
  409. package/src/components/FileUpload/FileUpload.tokens.scss +81 -0
  410. package/src/components/FileUpload/FileUpload.tsx +569 -0
  411. package/src/components/FileUpload/formatBytes.ts +34 -0
  412. package/src/components/FileUpload/iconForFile.ts +40 -0
  413. package/src/components/FileUpload/index.ts +2 -0
  414. package/src/components/FilterChip/FilterChip.module.scss +61 -0
  415. package/src/components/FilterChip/FilterChip.tokens.scss +25 -0
  416. package/src/components/FilterChip/FilterChip.tsx +208 -0
  417. package/src/components/FilterChip/index.ts +5 -0
  418. package/src/components/FlowCanvas/FlowCanvas.module.scss +329 -0
  419. package/src/components/FlowCanvas/FlowCanvas.tokens.scss +45 -0
  420. package/src/components/FlowCanvas/FlowCanvas.tsx +1867 -0
  421. package/src/components/FlowCanvas/FlowControls.tsx +46 -0
  422. package/src/components/FlowCanvas/FlowEdge.tsx +58 -0
  423. package/src/components/FlowCanvas/FlowNode.tsx +76 -0
  424. package/src/components/FlowCanvas/edgePath.ts +85 -0
  425. package/src/components/FlowCanvas/index.ts +4 -0
  426. package/src/components/FlowCanvas/layout.ts +148 -0
  427. package/src/components/FlowCanvas/spatialNav.ts +76 -0
  428. package/src/components/FlowCanvas/types.ts +59 -0
  429. package/src/components/FlowCanvas/useViewport.ts +122 -0
  430. package/src/components/FormRow/FormRow.module.scss +2 -0
  431. package/src/components/FormRow/FormRow.tsx +37 -0
  432. package/src/components/FormRow/index.ts +2 -0
  433. package/src/components/FormSection/FormSection.module.scss +24 -0
  434. package/src/components/FormSection/FormSection.tsx +46 -0
  435. package/src/components/FormSection/index.ts +2 -0
  436. package/src/components/Grid/Grid.module.scss +162 -0
  437. package/src/components/Grid/Grid.tokens.scss +13 -0
  438. package/src/components/Grid/Grid.tsx +226 -0
  439. package/src/components/Grid/GridItem.tsx +77 -0
  440. package/src/components/Grid/index.ts +11 -0
  441. package/src/components/Highlight/Highlight.module.scss +43 -0
  442. package/src/components/Highlight/Highlight.tokens.scss +35 -0
  443. package/src/components/Highlight/Highlight.tsx +164 -0
  444. package/src/components/Highlight/HighlightOverlay.tsx +100 -0
  445. package/src/components/Highlight/index.ts +2 -0
  446. package/src/components/HighlightedText/HighlightedText.module.scss +11 -0
  447. package/src/components/HighlightedText/HighlightedText.tokens.scss +9 -0
  448. package/src/components/HighlightedText/HighlightedText.tsx +50 -0
  449. package/src/components/HighlightedText/highlightRanges.ts +44 -0
  450. package/src/components/HighlightedText/index.ts +2 -0
  451. package/src/components/IconPicker/IconPicker.module.scss +175 -0
  452. package/src/components/IconPicker/IconPicker.tokens.scss +40 -0
  453. package/src/components/IconPicker/IconPicker.tsx +629 -0
  454. package/src/components/IconPicker/index.ts +9 -0
  455. package/src/components/IconTile/IconTile.module.scss +57 -0
  456. package/src/components/IconTile/IconTile.tokens.scss +16 -0
  457. package/src/components/IconTile/IconTile.tsx +91 -0
  458. package/src/components/IconTile/index.ts +2 -0
  459. package/src/components/Image/Image.module.scss +266 -0
  460. package/src/components/Image/Image.tokens.scss +55 -0
  461. package/src/components/Image/Image.tsx +342 -0
  462. package/src/components/Image/index.ts +2 -0
  463. package/src/components/ImageCrop/ImageCrop.module.scss +274 -0
  464. package/src/components/ImageCrop/ImageCrop.tokens.scss +33 -0
  465. package/src/components/ImageCrop/ImageCrop.tsx +542 -0
  466. package/src/components/ImageCrop/cropGeometry.ts +120 -0
  467. package/src/components/ImageCrop/extractCropBlob.ts +140 -0
  468. package/src/components/ImageCrop/index.ts +11 -0
  469. package/src/components/ImageCrop/useCropPreview.ts +161 -0
  470. package/src/components/Indent/Indent.module.scss +33 -0
  471. package/src/components/Indent/Indent.tokens.scss +10 -0
  472. package/src/components/Indent/Indent.tsx +46 -0
  473. package/src/components/Indent/index.ts +2 -0
  474. package/src/components/Input/Input.module.scss +99 -0
  475. package/src/components/Input/Input.tokens.scss +54 -0
  476. package/src/components/Input/Input.tsx +105 -0
  477. package/src/components/Input/Suggestions.tsx +260 -0
  478. package/src/components/Input/index.ts +2 -0
  479. package/src/components/Kanban/Kanban.module.scss +39 -0
  480. package/src/components/Kanban/Kanban.tokens.scss +15 -0
  481. package/src/components/Kanban/Kanban.tsx +951 -0
  482. package/src/components/Kanban/index.ts +7 -0
  483. package/src/components/Kbd/Kbd.module.scss +47 -0
  484. package/src/components/Kbd/Kbd.tokens.scss +26 -0
  485. package/src/components/Kbd/Kbd.tsx +73 -0
  486. package/src/components/Kbd/index.ts +2 -0
  487. package/src/components/Lightbox/Lightbox.module.scss +283 -0
  488. package/src/components/Lightbox/Lightbox.tokens.scss +36 -0
  489. package/src/components/Lightbox/Lightbox.tsx +506 -0
  490. package/src/components/Lightbox/index.ts +2 -0
  491. package/src/components/Link/Link.module.scss +78 -0
  492. package/src/components/Link/Link.tokens.scss +28 -0
  493. package/src/components/Link/Link.tsx +114 -0
  494. package/src/components/Link/index.ts +2 -0
  495. package/src/components/LinkCard/LinkCard.module.scss +92 -0
  496. package/src/components/LinkCard/LinkCard.tokens.scss +9 -0
  497. package/src/components/LinkCard/LinkCard.tsx +72 -0
  498. package/src/components/LinkCard/index.ts +2 -0
  499. package/src/components/LiquidEditor/AutocompleteMenu.tsx +133 -0
  500. package/src/components/LiquidEditor/HighlightLayer.tsx +53 -0
  501. package/src/components/LiquidEditor/InsertVariableMenu.tsx +81 -0
  502. package/src/components/LiquidEditor/LiquidEditor.module.scss +339 -0
  503. package/src/components/LiquidEditor/LiquidEditor.tokens.scss +81 -0
  504. package/src/components/LiquidEditor/LiquidEditor.tsx +412 -0
  505. package/src/components/LiquidEditor/PreviewPane.tsx +29 -0
  506. package/src/components/LiquidEditor/caretCoordinates.ts +97 -0
  507. package/src/components/LiquidEditor/index.ts +7 -0
  508. package/src/components/LiquidEditor/liquidFilters.ts +23 -0
  509. package/src/components/LiquidEditor/liquidTokenizer.ts +235 -0
  510. package/src/components/LiquidEditor/types.ts +101 -0
  511. package/src/components/LiquidEditor/useLiquidAutocomplete.ts +178 -0
  512. package/src/components/List/List.module.scss +105 -0
  513. package/src/components/List/List.tokens.scss +42 -0
  514. package/src/components/List/List.tsx +124 -0
  515. package/src/components/List/index.ts +9 -0
  516. package/src/components/LiveRegion/LiveRegion.module.scss +3 -0
  517. package/src/components/LiveRegion/LiveRegion.tsx +116 -0
  518. package/src/components/LiveRegion/index.ts +2 -0
  519. package/src/components/Logo/Logo.module.scss +139 -0
  520. package/src/components/Logo/Logo.tokens.scss +14 -0
  521. package/src/components/Logo/Logo.tsx +126 -0
  522. package/src/components/Logo/index.ts +2 -0
  523. package/src/components/Masonry/Masonry.module.scss +42 -0
  524. package/src/components/Masonry/Masonry.tokens.scss +8 -0
  525. package/src/components/Masonry/Masonry.tsx +136 -0
  526. package/src/components/Masonry/index.ts +2 -0
  527. package/src/components/Masonry/masonryUtils.ts +42 -0
  528. package/src/components/MediaTile/MediaTile.module.scss +183 -0
  529. package/src/components/MediaTile/MediaTile.tokens.scss +60 -0
  530. package/src/components/MediaTile/MediaTile.tsx +208 -0
  531. package/src/components/MediaTile/index.ts +8 -0
  532. package/src/components/Modal/Body.tsx +27 -0
  533. package/src/components/Modal/Close.tsx +45 -0
  534. package/src/components/Modal/Content.tsx +98 -0
  535. package/src/components/Modal/Footer.tsx +28 -0
  536. package/src/components/Modal/Header.tsx +51 -0
  537. package/src/components/Modal/Modal.module.scss +262 -0
  538. package/src/components/Modal/Modal.tokens.scss +50 -0
  539. package/src/components/Modal/Modal.tsx +23 -0
  540. package/src/components/Modal/ModalRoot.tsx +299 -0
  541. package/src/components/Modal/Overlay.tsx +101 -0
  542. package/src/components/Modal/context.ts +60 -0
  543. package/src/components/Modal/index.ts +11 -0
  544. package/src/components/MoneyInput/MoneyInput.module.scss +57 -0
  545. package/src/components/MoneyInput/MoneyInput.tsx +433 -0
  546. package/src/components/MoneyInput/index.ts +2 -0
  547. package/src/components/OptionsPicker/OptionsPicker.module.scss +216 -0
  548. package/src/components/OptionsPicker/OptionsPicker.tokens.scss +68 -0
  549. package/src/components/OptionsPicker/OptionsPicker.tsx +936 -0
  550. package/src/components/OptionsPicker/index.ts +9 -0
  551. package/src/components/OtpInput/OtpInput.module.scss +69 -0
  552. package/src/components/OtpInput/OtpInput.tokens.scss +45 -0
  553. package/src/components/OtpInput/OtpInput.tsx +368 -0
  554. package/src/components/OtpInput/index.ts +2 -0
  555. package/src/components/Page/Page.module.scss +30 -0
  556. package/src/components/Page/Page.tokens.scss +11 -0
  557. package/src/components/Page/Page.tsx +53 -0
  558. package/src/components/Page/index.ts +2 -0
  559. package/src/components/PageHeader/PageHeader.module.scss +197 -0
  560. package/src/components/PageHeader/PageHeader.tokens.scss +41 -0
  561. package/src/components/PageHeader/PageHeader.tsx +310 -0
  562. package/src/components/PageHeader/index.ts +20 -0
  563. package/src/components/Pagination/Pagination.module.scss +117 -0
  564. package/src/components/Pagination/Pagination.tokens.scss +54 -0
  565. package/src/components/Pagination/Pagination.tsx +165 -0
  566. package/src/components/Pagination/index.ts +4 -0
  567. package/src/components/Pagination/paginationRange.ts +72 -0
  568. package/src/components/PasswordInput/PasswordInput.module.scss +120 -0
  569. package/src/components/PasswordInput/PasswordInput.tokens.scss +44 -0
  570. package/src/components/PasswordInput/PasswordInput.tsx +210 -0
  571. package/src/components/PasswordInput/index.ts +2 -0
  572. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.module.scss +80 -0
  573. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tokens.scss +40 -0
  574. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tsx +96 -0
  575. package/src/components/PasswordStrengthMeter/index.ts +2 -0
  576. package/src/components/PersonDisplay/PersonDisplay.module.scss +180 -0
  577. package/src/components/PersonDisplay/PersonDisplay.tokens.scss +35 -0
  578. package/src/components/PersonDisplay/PersonDisplay.tsx +420 -0
  579. package/src/components/PersonDisplay/index.ts +10 -0
  580. package/src/components/PhoneInput/PhoneInput.module.scss +23 -0
  581. package/src/components/PhoneInput/PhoneInput.tsx +271 -0
  582. package/src/components/PhoneInput/index.ts +4 -0
  583. package/src/components/PhoneInput/phone.ts +128 -0
  584. package/src/components/PillMenu/PillMenu.module.scss +117 -0
  585. package/src/components/PillMenu/PillMenu.tokens.scss +22 -0
  586. package/src/components/PillMenu/PillMenu.tsx +324 -0
  587. package/src/components/PillMenu/index.ts +2 -0
  588. package/src/components/Popover/Anchor.tsx +37 -0
  589. package/src/components/Popover/Close.tsx +45 -0
  590. package/src/components/Popover/Content.tsx +213 -0
  591. package/src/components/Popover/Heading.tsx +33 -0
  592. package/src/components/Popover/Popover.module.scss +177 -0
  593. package/src/components/Popover/Popover.tokens.scss +22 -0
  594. package/src/components/Popover/Popover.tsx +23 -0
  595. package/src/components/Popover/PopoverRoot.tsx +75 -0
  596. package/src/components/Popover/Trigger.tsx +86 -0
  597. package/src/components/Popover/context.ts +22 -0
  598. package/src/components/Popover/index.ts +7 -0
  599. package/src/components/Progress/Progress.module.scss +110 -0
  600. package/src/components/Progress/Progress.tokens.scss +44 -0
  601. package/src/components/Progress/Progress.tsx +129 -0
  602. package/src/components/Progress/index.ts +2 -0
  603. package/src/components/QrCode/QrCode.module.scss +97 -0
  604. package/src/components/QrCode/QrCode.tokens.scss +17 -0
  605. package/src/components/QrCode/QrCode.tsx +233 -0
  606. package/src/components/QrCode/index.ts +2 -0
  607. package/src/components/QrCode/qr.ts +161 -0
  608. package/src/components/Radio/Radio.module.scss +175 -0
  609. package/src/components/Radio/Radio.tokens.scss +59 -0
  610. package/src/components/Radio/Radio.tsx +127 -0
  611. package/src/components/Radio/RadioGroup.module.scss +42 -0
  612. package/src/components/Radio/RadioGroup.tsx +120 -0
  613. package/src/components/Radio/RadioGroupContext.tsx +30 -0
  614. package/src/components/Radio/index.ts +4 -0
  615. package/src/components/Rail/Rail.module.scss +510 -0
  616. package/src/components/Rail/Rail.tokens.scss +92 -0
  617. package/src/components/Rail/Rail.tsx +287 -0
  618. package/src/components/Rail/RailCollapseToggle.tsx +57 -0
  619. package/src/components/Rail/RailFooter.tsx +17 -0
  620. package/src/components/Rail/RailGroup.tsx +728 -0
  621. package/src/components/Rail/RailGroupContext.ts +9 -0
  622. package/src/components/Rail/RailHeader.tsx +23 -0
  623. package/src/components/Rail/RailItem.tsx +119 -0
  624. package/src/components/Rail/RailSection.tsx +39 -0
  625. package/src/components/Rail/RailSpacer.tsx +19 -0
  626. package/src/components/Rail/index.ts +9 -0
  627. package/src/components/RelativeDateInput/RelativeDateInput.module.scss +15 -0
  628. package/src/components/RelativeDateInput/RelativeDateInput.tokens.scss +6 -0
  629. package/src/components/RelativeDateInput/RelativeDateInput.tsx +474 -0
  630. package/src/components/RelativeDateInput/index.ts +10 -0
  631. package/src/components/RelativeDateInput/relativeDate.ts +127 -0
  632. package/src/components/RichText/RichText.module.scss +5 -0
  633. package/src/components/RichText/RichText.tsx +70 -0
  634. package/src/components/RichText/RichTextAttachment.module.scss +82 -0
  635. package/src/components/RichText/RichTextAttachment.tsx +122 -0
  636. package/src/components/RichText/_prose.scss +79 -0
  637. package/src/components/RichText/attachmentIcon.tsx +24 -0
  638. package/src/components/RichText/engine/attachment.ts +154 -0
  639. package/src/components/RichText/engine/autolink.ts +64 -0
  640. package/src/components/RichText/engine/blockUnit.ts +174 -0
  641. package/src/components/RichText/engine/colorMarks.ts +85 -0
  642. package/src/components/RichText/engine/escape.ts +11 -0
  643. package/src/components/RichText/engine/fromHtml.ts +353 -0
  644. package/src/components/RichText/engine/fromMarkdown.ts +20 -0
  645. package/src/components/RichText/engine/inlines.ts +82 -0
  646. package/src/components/RichText/engine/listDepths.ts +32 -0
  647. package/src/components/RichText/engine/marks.ts +80 -0
  648. package/src/components/RichText/engine/mdToHtml.ts +178 -0
  649. package/src/components/RichText/engine/model.ts +157 -0
  650. package/src/components/RichText/engine/position.ts +118 -0
  651. package/src/components/RichText/engine/renderDoc.tsx +395 -0
  652. package/src/components/RichText/engine/renderLink.ts +15 -0
  653. package/src/components/RichText/engine/renderMention.ts +16 -0
  654. package/src/components/RichText/engine/safeHref.ts +22 -0
  655. package/src/components/RichText/engine/toHtml.ts +154 -0
  656. package/src/components/RichText/engine/toMarkdown.ts +97 -0
  657. package/src/components/RichText/engine/transforms.ts +434 -0
  658. package/src/components/RichText/imageMaxHeightStyle.ts +10 -0
  659. package/src/components/RichText/index.ts +31 -0
  660. package/src/components/RichTextEditor/RichTextAttachmentConfig.tsx +264 -0
  661. package/src/components/RichTextEditor/RichTextBlockControls.tsx +365 -0
  662. package/src/components/RichTextEditor/RichTextBlockMenu.tsx +162 -0
  663. package/src/components/RichTextEditor/RichTextColorMenu.tsx +115 -0
  664. package/src/components/RichTextEditor/RichTextEditor.module.scss +298 -0
  665. package/src/components/RichTextEditor/RichTextEditor.tsx +1910 -0
  666. package/src/components/RichTextEditor/RichTextImageResizer.tsx +131 -0
  667. package/src/components/RichTextEditor/RichTextLinkEditor.tsx +151 -0
  668. package/src/components/RichTextEditor/RichTextMentionMenu.tsx +99 -0
  669. package/src/components/RichTextEditor/RichTextToolbar.tsx +379 -0
  670. package/src/components/RichTextEditor/autolinkInput.ts +65 -0
  671. package/src/components/RichTextEditor/blockDrop.ts +9 -0
  672. package/src/components/RichTextEditor/blockReflow.ts +62 -0
  673. package/src/components/RichTextEditor/commands.ts +300 -0
  674. package/src/components/RichTextEditor/history.ts +93 -0
  675. package/src/components/RichTextEditor/icons.tsx +152 -0
  676. package/src/components/RichTextEditor/imageSize.ts +70 -0
  677. package/src/components/RichTextEditor/index.ts +5 -0
  678. package/src/components/RichTextEditor/input.ts +209 -0
  679. package/src/components/RichTextEditor/inputRules.ts +71 -0
  680. package/src/components/RichTextEditor/links.ts +103 -0
  681. package/src/components/RichTextEditor/mentionContext.ts +37 -0
  682. package/src/components/RichTextEditor/mentions.ts +28 -0
  683. package/src/components/RichTextEditor/preventSelectionLoss.ts +15 -0
  684. package/src/components/RichTextEditor/selection.ts +415 -0
  685. package/src/components/RichTextEditor/shortcuts.ts +44 -0
  686. package/src/components/RichTextEditor/useAnchoredFloating.ts +50 -0
  687. package/src/components/RichTextEditor/useAttachmentConfig.ts +134 -0
  688. package/src/components/RichTextEditor/useDismissOnOutsidePointerDown.ts +26 -0
  689. package/src/components/RichTextEditor/useLinkEditor.ts +107 -0
  690. package/src/components/RichTextEditor/useMention.ts +226 -0
  691. package/src/components/RichTextEditor/useOverlayRect.ts +73 -0
  692. package/src/components/RichTextEditor/useUpload.ts +246 -0
  693. package/src/components/Screen/Screen.module.scss +46 -0
  694. package/src/components/Screen/Screen.tokens.scss +20 -0
  695. package/src/components/Screen/Screen.tsx +97 -0
  696. package/src/components/Screen/index.ts +2 -0
  697. package/src/components/ScrollArea/ScrollArea.module.scss +39 -0
  698. package/src/components/ScrollArea/ScrollArea.tokens.scss +15 -0
  699. package/src/components/ScrollArea/ScrollArea.tsx +154 -0
  700. package/src/components/ScrollArea/index.ts +2 -0
  701. package/src/components/Select/Chip.tsx +45 -0
  702. package/src/components/Select/Empty.tsx +39 -0
  703. package/src/components/Select/Error.tsx +51 -0
  704. package/src/components/Select/HiddenInputs.tsx +44 -0
  705. package/src/components/Select/Listbox.tsx +557 -0
  706. package/src/components/Select/Loading.tsx +30 -0
  707. package/src/components/Select/Select.module.scss +512 -0
  708. package/src/components/Select/Select.tokens.scss +140 -0
  709. package/src/components/Select/Select.tsx +685 -0
  710. package/src/components/Select/Trigger.tsx +952 -0
  711. package/src/components/Select/context.ts +101 -0
  712. package/src/components/Select/emptyStateText.ts +19 -0
  713. package/src/components/Select/index.ts +9 -0
  714. package/src/components/Select/useSelectState.ts +157 -0
  715. package/src/components/Select/utils-types.ts +7 -0
  716. package/src/components/Select/utils.ts +122 -0
  717. package/src/components/SettingRow/SettingRow.module.scss +166 -0
  718. package/src/components/SettingRow/SettingRow.tokens.scss +37 -0
  719. package/src/components/SettingRow/SettingRow.tsx +242 -0
  720. package/src/components/SettingRow/index.ts +7 -0
  721. package/src/components/Skeleton/Skeleton.module.scss +55 -0
  722. package/src/components/Skeleton/Skeleton.tokens.scss +17 -0
  723. package/src/components/Skeleton/Skeleton.tsx +131 -0
  724. package/src/components/Skeleton/index.ts +4 -0
  725. package/src/components/Skeleton/useSkeletonVisibility.ts +95 -0
  726. package/src/components/Slider/Slider.module.scss +293 -0
  727. package/src/components/Slider/Slider.tokens.scss +98 -0
  728. package/src/components/Slider/Slider.tsx +624 -0
  729. package/src/components/Slider/index.ts +9 -0
  730. package/src/components/SlotGrid/SlotGrid.module.scss +61 -0
  731. package/src/components/SlotGrid/SlotGrid.tokens.scss +21 -0
  732. package/src/components/SlotGrid/SlotGrid.tsx +111 -0
  733. package/src/components/SlotGrid/index.ts +2 -0
  734. package/src/components/SocialButton/SocialButton.module.scss +4 -0
  735. package/src/components/SocialButton/SocialButton.tsx +40 -0
  736. package/src/components/SocialButton/index.ts +2 -0
  737. package/src/components/Sortable/Sortable.module.scss +177 -0
  738. package/src/components/Sortable/Sortable.tokens.scss +27 -0
  739. package/src/components/Sortable/Sortable.tsx +572 -0
  740. package/src/components/Sortable/containerAwareCollision.ts +125 -0
  741. package/src/components/Sortable/index.ts +13 -0
  742. package/src/components/SortableGroup/SortableGroup.module.scss +23 -0
  743. package/src/components/SortableGroup/SortableGroup.tsx +315 -0
  744. package/src/components/SortableGroup/index.ts +4 -0
  745. package/src/components/SortableGroup/moveSortableItem.ts +39 -0
  746. package/src/components/Sparkline/Sparkline.module.scss +28 -0
  747. package/src/components/Sparkline/Sparkline.tokens.scss +7 -0
  748. package/src/components/Sparkline/Sparkline.tsx +133 -0
  749. package/src/components/Sparkline/index.ts +2 -0
  750. package/src/components/Sparkline/sparklineModel.ts +119 -0
  751. package/src/components/Split/Split.module.scss +158 -0
  752. package/src/components/Split/Split.tokens.scss +10 -0
  753. package/src/components/Split/Split.tsx +231 -0
  754. package/src/components/Split/index.ts +2 -0
  755. package/src/components/Stack/Stack.module.scss +67 -0
  756. package/src/components/Stack/Stack.tokens.scss +11 -0
  757. package/src/components/Stack/Stack.tsx +100 -0
  758. package/src/components/Stack/index.ts +2 -0
  759. package/src/components/StagePath/StagePath.module.scss +255 -0
  760. package/src/components/StagePath/StagePath.tokens.scss +43 -0
  761. package/src/components/StagePath/StagePath.tsx +176 -0
  762. package/src/components/StagePath/index.ts +2 -0
  763. package/src/components/StatGroup/StatGroup.module.scss +6 -0
  764. package/src/components/StatGroup/StatGroup.tsx +70 -0
  765. package/src/components/StatGroup/StatGroupContext.ts +10 -0
  766. package/src/components/StatGroup/StatTile.module.scss +99 -0
  767. package/src/components/StatGroup/StatTile.tokens.scss +35 -0
  768. package/src/components/StatGroup/StatTile.tsx +143 -0
  769. package/src/components/StatGroup/index.ts +4 -0
  770. package/src/components/Sticky/Sticky.module.scss +98 -0
  771. package/src/components/Sticky/Sticky.tokens.scss +17 -0
  772. package/src/components/Sticky/Sticky.tsx +103 -0
  773. package/src/components/Sticky/index.ts +2 -0
  774. package/src/components/Switch/Switch.module.scss +182 -0
  775. package/src/components/Switch/Switch.tokens.scss +59 -0
  776. package/src/components/Switch/Switch.tsx +214 -0
  777. package/src/components/Switch/index.ts +2 -0
  778. package/src/components/Table/Table.module.scss +308 -0
  779. package/src/components/Table/Table.tokens.scss +79 -0
  780. package/src/components/Table/Table.tsx +381 -0
  781. package/src/components/Table/index.ts +15 -0
  782. package/src/components/TableOfContents/TableOfContents.module.scss +83 -0
  783. package/src/components/TableOfContents/TableOfContents.tokens.scss +26 -0
  784. package/src/components/TableOfContents/TableOfContents.tsx +192 -0
  785. package/src/components/TableOfContents/activeSection.ts +18 -0
  786. package/src/components/TableOfContents/index.ts +2 -0
  787. package/src/components/Tabs/Tabs.module.scss +359 -0
  788. package/src/components/Tabs/Tabs.tokens.scss +50 -0
  789. package/src/components/Tabs/Tabs.tsx +538 -0
  790. package/src/components/Tabs/index.ts +2 -0
  791. package/src/components/Text/Text.module.scss +115 -0
  792. package/src/components/Text/Text.tokens.scss +55 -0
  793. package/src/components/Text/Text.tsx +197 -0
  794. package/src/components/Text/index.ts +2 -0
  795. package/src/components/Textarea/Textarea.module.scss +94 -0
  796. package/src/components/Textarea/Textarea.tokens.scss +48 -0
  797. package/src/components/Textarea/Textarea.tsx +246 -0
  798. package/src/components/Textarea/index.ts +2 -0
  799. package/src/components/Thread/Thread.module.scss +161 -0
  800. package/src/components/Thread/Thread.tokens.scss +32 -0
  801. package/src/components/Thread/Thread.tsx +176 -0
  802. package/src/components/Thread/index.ts +2 -0
  803. package/src/components/TimeField/TimeField.module.scss +232 -0
  804. package/src/components/TimeField/TimeField.tsx +793 -0
  805. package/src/components/TimeField/index.ts +2 -0
  806. package/src/components/Timeline/Timeline.module.scss +58 -0
  807. package/src/components/Timeline/Timeline.tokens.scss +13 -0
  808. package/src/components/Timeline/Timeline.tsx +53 -0
  809. package/src/components/Timeline/index.ts +2 -0
  810. package/src/components/Title/Title.module.scss +84 -0
  811. package/src/components/Title/Title.tokens.scss +37 -0
  812. package/src/components/Title/Title.tsx +150 -0
  813. package/src/components/Title/index.ts +2 -0
  814. package/src/components/Toast/Toast.module.scss +299 -0
  815. package/src/components/Toast/Toast.tokens.scss +65 -0
  816. package/src/components/Toast/Toast.tsx +122 -0
  817. package/src/components/Toast/ToastViewport.tsx +166 -0
  818. package/src/components/Toast/api.ts +175 -0
  819. package/src/components/Toast/index.ts +5 -0
  820. package/src/components/Toast/store.ts +133 -0
  821. package/src/components/Toast/useToastTimer.ts +94 -0
  822. package/src/components/Tooltip/Tooltip.module.scss +99 -0
  823. package/src/components/Tooltip/Tooltip.tokens.scss +23 -0
  824. package/src/components/Tooltip/Tooltip.tsx +341 -0
  825. package/src/components/Tooltip/index.ts +2 -0
  826. package/src/components/TopBar/TopBar.module.scss +235 -0
  827. package/src/components/TopBar/TopBar.tokens.scss +79 -0
  828. package/src/components/TopBar/TopBar.tsx +87 -0
  829. package/src/components/TopBar/TopBarEnd.tsx +20 -0
  830. package/src/components/TopBar/TopBarIconButton.tsx +86 -0
  831. package/src/components/TopBar/TopBarSearch.tsx +319 -0
  832. package/src/components/TopBar/TopBarSearchPanel.tsx +268 -0
  833. package/src/components/TopBar/TopBarStart.tsx +20 -0
  834. package/src/components/TopBar/index.ts +11 -0
  835. package/src/components/Tour/Spotlight.tsx +121 -0
  836. package/src/components/Tour/Tour.module.scss +210 -0
  837. package/src/components/Tour/Tour.tokens.scss +58 -0
  838. package/src/components/Tour/Tour.tsx +617 -0
  839. package/src/components/Tour/index.ts +2 -0
  840. package/src/components/Tour/useTourTarget.ts +115 -0
  841. package/src/components/TreeView/TreeView.module.scss +268 -0
  842. package/src/components/TreeView/TreeView.tokens.scss +36 -0
  843. package/src/components/TreeView/TreeView.tsx +536 -0
  844. package/src/components/TreeView/index.ts +3 -0
  845. package/src/components/TreeView/treeModel.ts +224 -0
  846. package/src/components/VisuallyHidden/VisuallyHidden.module.scss +7 -0
  847. package/src/components/VisuallyHidden/VisuallyHidden.tsx +29 -0
  848. package/src/components/VisuallyHidden/index.ts +2 -0
  849. package/src/components/WidgetPreview/WidgetPreview.module.scss +11 -0
  850. package/src/components/WidgetPreview/WidgetPreview.tokens.scss +26 -0
  851. package/src/components/WidgetPreview/WidgetPreview.tsx +36 -0
  852. package/src/components/WidgetPreview/WidgetShape.module.scss +140 -0
  853. package/src/components/WidgetPreview/WidgetShape.tsx +159 -0
  854. package/src/components/WidgetPreview/index.ts +2 -0
  855. package/src/components/_internal/Trend.tsx +27 -0
  856. package/src/components/_internal/choiceTile.scss +74 -0
  857. package/src/components/_internal/collapse.scss +10 -0
  858. package/src/components/_internal/collapse.ts +58 -0
  859. package/src/components/_internal/currency.ts +257 -0
  860. package/src/components/_internal/dragAnnouncements.ts +200 -0
  861. package/src/components/_internal/fieldWiring.ts +225 -0
  862. package/src/components/_internal/gridSpan.ts +54 -0
  863. package/src/components/_internal/isoDay.ts +35 -0
  864. package/src/components/_internal/letterKey.ts +26 -0
  865. package/src/components/_internal/overlay/focusRestore.ts +21 -0
  866. package/src/components/_internal/overlay/index.ts +12 -0
  867. package/src/components/_internal/overlay/tourHold.ts +23 -0
  868. package/src/components/_internal/overlay/useFocusTrap.ts +120 -0
  869. package/src/components/_internal/overlay/useInOverlay.ts +81 -0
  870. package/src/components/_internal/overlay/useOverlayStack.ts +232 -0
  871. package/src/components/_internal/overlay/useScrollLock.ts +112 -0
  872. package/src/components/_internal/refs.ts +48 -0
  873. package/src/components/_internal/statusColor.ts +26 -0
  874. package/src/components/_internal/timeUtils.ts +23 -0
  875. package/src/components/_internal/useAsyncOptions.ts +145 -0
  876. package/src/components/_internal/useClippedTooltip.ts +50 -0
  877. package/src/components/_internal/useControllableState.ts +50 -0
  878. package/src/components.manifest.json +1141 -0
  879. package/src/hooks/useBelowBreakpoint.ts +58 -0
  880. package/src/i18n/I18nProvider.tsx +52 -0
  881. package/src/i18n/LocaleProvider.tsx +17 -0
  882. package/src/i18n/en.ts +648 -0
  883. package/src/i18n/format.ts +71 -0
  884. package/src/i18n/index.ts +13 -0
  885. package/src/i18n/messages.ts +1275 -0
  886. package/src/i18n/ru.ts +677 -0
  887. package/src/i18n/useLocale.ts +19 -0
  888. package/src/i18n/useTranslation.ts +58 -0
  889. package/src/index.ts +913 -0
  890. package/src/palette/index.ts +2 -0
  891. package/src/palette/palette.ts +108 -0
  892. package/src/styles/dark.scss +4 -0
  893. package/src/styles/global.scss +7 -0
  894. package/src/styles/mixins.scss +143 -0
  895. package/src/styles/reset.scss +78 -0
  896. package/src/styles/tokens.scss +18 -0
  897. package/src/styles/typography.scss +44 -0
@@ -0,0 +1,936 @@
1
+ import {
2
+ Children,
3
+ cloneElement,
4
+ createContext,
5
+ forwardRef,
6
+ isValidElement,
7
+ useCallback,
8
+ useContext,
9
+ useEffect,
10
+ useId,
11
+ useMemo,
12
+ useRef,
13
+ useState,
14
+ type KeyboardEvent as ReactKeyboardEvent,
15
+ type ReactElement,
16
+ type ReactNode,
17
+ type Ref,
18
+ } from 'react';
19
+ import clsx from 'clsx';
20
+ import { Search } from 'lucide-react';
21
+ import { mergeRefs } from '../_internal/refs';
22
+ import { useAsyncOptions } from '../_internal/useAsyncOptions';
23
+ import { Popover } from '../Popover';
24
+ import { Button } from '../Button';
25
+ import { Checkbox } from '../Checkbox';
26
+ import { Radio } from '../Radio';
27
+ import { Cluster } from '../Cluster';
28
+ import { Input } from '../Input';
29
+ import { Badge, type BadgeTone } from '../Badge';
30
+ import { type PaletteColor } from '../../palette';
31
+ import { Text } from '../Text';
32
+ import { useTranslation } from '../../i18n/useTranslation';
33
+ import styles from './OptionsPicker.module.scss';
34
+
35
+ // ----------------------------------------------------------------------------
36
+ // Public types
37
+ // ----------------------------------------------------------------------------
38
+
39
+ export type OptionsPickerMode = 'multi' | 'single';
40
+
41
+ export interface OptionsPickerOption {
42
+ /** Unique identifier; persisted as the selected value. */
43
+ value: string;
44
+ /** Displayed text. */
45
+ label: string;
46
+ /** Override what the search filter matches against. Defaults to `label`. */
47
+ searchText?: string;
48
+ }
49
+
50
+ export interface OptionsPickerGroup {
51
+ /** Unique per group; used as React key and ARIA target. */
52
+ id: string;
53
+ /** Header text shown in the group label slot. */
54
+ label: string;
55
+ options: OptionsPickerOption[];
56
+ /** Colored dot tone for the group header. Default `'neutral'`. */
57
+ tone?: BadgeTone;
58
+ /**
59
+ * Categorical palette color. When set, takes precedence over `tone`:
60
+ * the group header dot uses the palette color, AND every option's
61
+ * Checkbox in this group adopts the same palette color for its
62
+ * checked / indeterminate fill. Use to give each namespace its own
63
+ * visual identity (e.g., auth.* = blue, role.* = amber).
64
+ */
65
+ color?: PaletteColor;
66
+ /** Right-side hint label (e.g., `"auth.*"`). Omitted → no hint renders. */
67
+ hint?: string;
68
+ }
69
+
70
+ interface MultiProps {
71
+ mode?: 'multi';
72
+ /**
73
+ * Committed selection: `string[]` (multi) or `string | null` (single).
74
+ * Multi: pass a stable array (state, or memoized) — a new array identity on
75
+ * every render resets the open panel's draft.
76
+ */
77
+ selected: string[];
78
+ /** Called on Apply with the committed values and their option objects, in the same order. */
79
+ onApply: (next: string[], options: OptionsPickerOption[]) => void;
80
+ onCancel?: () => void;
81
+ open?: boolean;
82
+ onOpenChange?: (open: boolean) => void;
83
+ children: ReactNode;
84
+ }
85
+
86
+ interface SingleProps {
87
+ mode: 'single';
88
+ /** Committed value, or `null` when nothing is picked. */
89
+ selected: string | null;
90
+ /** Called on pick with the value and its option object (`null` / `null` when nothing is picked). */
91
+ onApply: (next: string | null, option: OptionsPickerOption | null) => void;
92
+ onCancel?: () => void;
93
+ open?: boolean;
94
+ onOpenChange?: (open: boolean) => void;
95
+ children: ReactNode;
96
+ }
97
+
98
+ export type OptionsPickerProps = MultiProps | SingleProps;
99
+
100
+ type FlatContentProps = {
101
+ options: OptionsPickerOption[];
102
+ groups?: never;
103
+ loadOptions?: never;
104
+ };
105
+ type GroupedContentProps = {
106
+ groups: OptionsPickerGroup[];
107
+ options?: never;
108
+ loadOptions?: never;
109
+ };
110
+ type AsyncContentProps = {
111
+ /**
112
+ * Server search: called with the search query (`''` on every open) after
113
+ * `searchDebounceMs`, and its answer replaces the list — no client-side
114
+ * filtering on top. Flat options only. The previous request is aborted via
115
+ * `signal` when a newer one starts. Takes precedence over `options` /
116
+ * `groups` (dev warning if both are passed). The latest function is always
117
+ * the one called; a new identity (e.g. an inline arrow) does not refetch.
118
+ */
119
+ loadOptions: (query: string, signal: AbortSignal) => Promise<OptionsPickerOption[]>;
120
+ /**
121
+ * Option objects for currently selected values that may not be in any
122
+ * server answer (e.g. values restored from a URL), so they can be labelled.
123
+ */
124
+ selectedOptions?: OptionsPickerOption[];
125
+ /** Delay between the last keystroke (or opening) and the `loadOptions` call. Default `250`. */
126
+ searchDebounceMs?: number;
127
+ options?: never;
128
+ groups?: never;
129
+ };
130
+ type SharedContentProps = {
131
+ /** Accessible label on the panel (the dialog's `aria-label`). */
132
+ label: string;
133
+ /**
134
+ * Rendered when search produces zero matches. Defaults to the
135
+ * `optionsPicker.noMatches` i18n string (en: `'No matches'`). With
136
+ * `loadOptions` the visible row is hidden from assistive tech and a live
137
+ * region announces it instead — this text when it is a string, otherwise the
138
+ * default.
139
+ *
140
+ * An EMPTY string counts as unset, matching every other label override in
141
+ * the library. There is no way to suppress the message entirely — a blank
142
+ * listbox reads as a broken filter rather than as a deliberate silence.
143
+ */
144
+ emptyState?: ReactNode;
145
+ /**
146
+ * Footer count formatter (multi only). Default `'${selected} of ${total}'`.
147
+ * Not called with `loadOptions` — there is no known total, so the footer
148
+ * reads `optionsPicker.selectedCount` (en: `'N selected'`).
149
+ */
150
+ footerCount?: (selected: number, total: number) => ReactNode;
151
+ /**
152
+ * Whether to render the search bar at the top of the panel. Defaults to
153
+ * `true`. Hide it (`false`) for small/curated option lists where typing
154
+ * filters would just be noise — the selection-count header is hidden
155
+ * alongside the search input (the footer's `N of TOTAL` text still
156
+ * shows in multi mode). Ignored with `loadOptions`: server search needs
157
+ * the input, so it always shows.
158
+ */
159
+ searchable?: boolean;
160
+ className?: string;
161
+ };
162
+ export type OptionsPickerContentProps = (
163
+ | FlatContentProps
164
+ | GroupedContentProps
165
+ | AsyncContentProps
166
+ ) &
167
+ SharedContentProps;
168
+
169
+ // ----------------------------------------------------------------------------
170
+ // Context
171
+ // ----------------------------------------------------------------------------
172
+
173
+ interface PickerContextValue {
174
+ mode: OptionsPickerMode;
175
+ /** Always normalized to string[] internally; single mode uses [] or [value]. */
176
+ selected: string[];
177
+ open: boolean;
178
+ setOpen: (next: boolean) => void;
179
+ /** Called when Apply (multi) or click (single) commits draft to consumer. */
180
+ commit: (next: string[], options: OptionsPickerOption[]) => void;
181
+ /** Called on Cancel/Esc/click-outside. */
182
+ cancel: () => void;
183
+ /** Stable id for the Content panel; shared with Trigger for aria-controls. */
184
+ contentId: string;
185
+ }
186
+
187
+ const PickerContext = createContext<PickerContextValue | null>(null);
188
+
189
+ function usePickerContext(label: string): PickerContextValue {
190
+ const ctx = useContext(PickerContext);
191
+ if (!ctx) {
192
+ throw new Error(`<OptionsPicker.${label}> must be used inside <OptionsPicker>.`);
193
+ }
194
+ return ctx;
195
+ }
196
+
197
+ // ----------------------------------------------------------------------------
198
+ // Root
199
+ // ----------------------------------------------------------------------------
200
+
201
+ /**
202
+ * Compound multi/single-select filter picker with search, optional grouping and draft-then-Apply commit; not a form field.
203
+ * @see docs/components/OptionsPicker.md
204
+ */
205
+ function OptionsPickerRoot(props: OptionsPickerProps) {
206
+ const mode: OptionsPickerMode = props.mode ?? 'multi';
207
+
208
+ // Normalize selected to string[] internally. Keyed on the value itself, not
209
+ // `props` (a new object every parent render): Content resets its draft /
210
+ // filter / pinned snapshot when this identity changes.
211
+ const rawSelected = props.selected;
212
+ const normalizedSelected = useMemo<string[]>(() => {
213
+ if (mode === 'multi') return rawSelected as string[];
214
+ return rawSelected == null ? [] : [rawSelected as string];
215
+ }, [mode, rawSelected]);
216
+
217
+ // Open state — controlled or uncontrolled.
218
+ const [internalOpen, setInternalOpen] = useState(false);
219
+ const isControlled = props.open !== undefined;
220
+ const open = isControlled ? (props.open as boolean) : internalOpen;
221
+
222
+ // justCommittedRef: true when the most-recent open→close was triggered by
223
+ // a commit (Apply button / radio-click). When false on close, we fire onCancel.
224
+ const justCommittedRef = useRef(false);
225
+ // justFiredCancelRef: guards against double-fire when both Popover's
226
+ // document-level Escape listener AND Content's synthetic onKeyDown both
227
+ // call setOpen(false) in the same React event batch.
228
+ const justFiredCancelRef = useRef(false);
229
+
230
+ const setOpenRaw = useCallback(
231
+ (next: boolean) => {
232
+ if (!isControlled) setInternalOpen(next);
233
+ props.onOpenChange?.(next);
234
+ },
235
+ [isControlled, props],
236
+ );
237
+
238
+ // Intercepts every open→close transition and fires onCancel exactly once
239
+ // when the close was NOT preceded by a commit (i.e., Esc or click-outside).
240
+ const setOpen = useCallback(
241
+ (next: boolean) => {
242
+ if (!next && open && !justCommittedRef.current && !justFiredCancelRef.current) {
243
+ justFiredCancelRef.current = true;
244
+ (props as { onCancel?: () => void }).onCancel?.();
245
+ }
246
+ if (next) {
247
+ // Reset both guards when reopening.
248
+ justCommittedRef.current = false;
249
+ justFiredCancelRef.current = false;
250
+ }
251
+ setOpenRaw(next);
252
+ },
253
+ [open, props, setOpenRaw],
254
+ );
255
+
256
+ const commit = useCallback(
257
+ (next: string[], options: OptionsPickerOption[]) => {
258
+ justCommittedRef.current = true;
259
+ if (mode === 'multi') {
260
+ (props as MultiProps).onApply(next, options);
261
+ } else {
262
+ (props as SingleProps).onApply(next[0] ?? null, options[0] ?? null);
263
+ }
264
+ setOpen(false);
265
+ },
266
+ [mode, props, setOpen],
267
+ );
268
+
269
+ const cancel = useCallback(() => {
270
+ // Explicit cancel (Cancel button, Esc keydown on content) — mark as NOT a
271
+ // commit so setOpen fires onCancel via the interceptor above.
272
+ setOpen(false);
273
+ }, [setOpen]);
274
+
275
+ const contentId = useId();
276
+
277
+ const ctxValue = useMemo<PickerContextValue>(
278
+ () => ({ mode, selected: normalizedSelected, open, setOpen, commit, cancel, contentId }),
279
+ [mode, normalizedSelected, open, setOpen, commit, cancel, contentId],
280
+ );
281
+
282
+ return (
283
+ <PickerContext.Provider value={ctxValue}>
284
+ <Popover open={open} onOpenChange={setOpen}>
285
+ {props.children}
286
+ </Popover>
287
+ </PickerContext.Provider>
288
+ );
289
+ }
290
+
291
+ // ----------------------------------------------------------------------------
292
+ // Trigger
293
+ // ----------------------------------------------------------------------------
294
+
295
+ export interface OptionsPickerTriggerProps {
296
+ /** Must be a single React element that accepts a ref (e.g. `<Button>`). */
297
+ children: ReactElement;
298
+ }
299
+
300
+ /**
301
+ * The element that opens the picker panel: a single child (typically a `<Button>`) that gets the open wiring and ARIA injected.
302
+ * @see docs/components/OptionsPicker.md
303
+ */
304
+ const OptionsPickerTrigger = forwardRef<HTMLButtonElement, OptionsPickerTriggerProps>(
305
+ function OptionsPickerTrigger({ children }, ref) {
306
+ // usePickerContext validates that this component is inside an OptionsPicker.
307
+ usePickerContext('Trigger');
308
+
309
+ if (!isValidElement(children)) {
310
+ throw new Error('<OptionsPicker.Trigger> requires exactly one React element child.');
311
+ }
312
+
313
+ // Merge the consumer's ref with the existing child ref so both receive the
314
+ // same DOM node. mergeRefs handles callback refs and object refs alike.
315
+ const child = Children.only(children) as ReactElement<{ ref?: Ref<HTMLButtonElement> }>;
316
+ const childRef = (child as ReactElement & { ref?: Ref<HTMLButtonElement> }).ref;
317
+ const mergedRef = ref ? mergeRefs<HTMLButtonElement>(ref, childRef ?? null) : childRef;
318
+ const childWithRef = cloneElement(child, { ref: mergedRef } as Partial<{
319
+ ref: Ref<HTMLButtonElement>;
320
+ }>);
321
+
322
+ return (
323
+ // Popover.Trigger handles aria-expanded, aria-controls, and click/keydown
324
+ // internally. We only override aria-haspopup to 'listbox' because the
325
+ // content panel surfaces a listbox role, not a generic dialog.
326
+ <Popover.Trigger aria-haspopup="listbox">{childWithRef}</Popover.Trigger>
327
+ );
328
+ },
329
+ );
330
+
331
+ // ----------------------------------------------------------------------------
332
+ // Content
333
+ // ----------------------------------------------------------------------------
334
+
335
+ function isGrouped(
336
+ props: OptionsPickerContentProps,
337
+ ): props is GroupedContentProps & SharedContentProps {
338
+ // `loadOptions` wins over `groups` when a consumer passes both.
339
+ return !props.loadOptions && 'groups' in props && props.groups !== undefined;
340
+ }
341
+
342
+ function isAsync(
343
+ props: OptionsPickerContentProps,
344
+ ): props is AsyncContentProps & SharedContentProps {
345
+ return props.loadOptions !== undefined;
346
+ }
347
+
348
+ function getAllOptions(props: OptionsPickerContentProps): OptionsPickerOption[] {
349
+ if (isGrouped(props)) return props.groups.flatMap((g) => g.options);
350
+ return props.options ?? [];
351
+ }
352
+
353
+ function defaultFooterCount(selected: number, total: number): string {
354
+ return `${selected} of ${total}`;
355
+ }
356
+
357
+ type TriState = 'false' | 'mixed' | 'true';
358
+
359
+ function tristate(groupOptionValues: string[], draft: string[]): TriState {
360
+ const selectedInGroup = groupOptionValues.filter((v) => draft.includes(v)).length;
361
+ if (selectedInGroup === 0) return 'false';
362
+ if (selectedInGroup === groupOptionValues.length) return 'true';
363
+ return 'mixed';
364
+ }
365
+
366
+ /**
367
+ * The picker panel: search bar, options list and (multi mode) Apply/Cancel footer; takes `options`, `groups` or `loadOptions`.
368
+ * @see docs/components/OptionsPicker.md
369
+ */
370
+ const OptionsPickerContent = forwardRef<HTMLDivElement, OptionsPickerContentProps>(
371
+ function OptionsPickerContent(props, ref) {
372
+ const { label, className, searchable = true } = props;
373
+ const t = useTranslation();
374
+ const searchPlaceholder = t('optionsPicker.filter');
375
+ const ctx = usePickerContext('Content');
376
+
377
+ const contentId = ctx.contentId;
378
+
379
+ const [draft, setDraft] = useState<string[]>(ctx.selected);
380
+ const [filter, setFilter] = useState('');
381
+ // Async only: the selected values pinned above the server rows. A snapshot
382
+ // — taken on open and on every query change, never on tick/untick — so
383
+ // rows don't move under the pointer.
384
+ const [pinned, setPinned] = useState<string[]>(ctx.selected);
385
+
386
+ const async = isAsync(props);
387
+ // Latest consumer loadOptions, read at call time: the hook re-arms (and
388
+ // aborts the in-flight request) on a new function identity, so an inline
389
+ // arrow would refetch on every parent render. trackedLoad stays stable.
390
+ const loadRef = useRef(async ? props.loadOptions : undefined);
391
+ loadRef.current = async ? props.loadOptions : undefined;
392
+ // The hook's state outlives a close, so until a request of THIS open has
393
+ // settled its rows / error / loadedQuery belong to the previous open.
394
+ // Tracked here (not in the shared hook) by flagging when loadOptions is
395
+ // actually invoked; reset on close.
396
+ const [fetchedSinceOpen, setFetchedSinceOpen] = useState(false);
397
+ const trackedLoad = useCallback((q: string, signal: AbortSignal) => {
398
+ setFetchedSinceOpen(true);
399
+ return loadRef.current!(q, signal);
400
+ }, []);
401
+ const [settledSinceOpen, setSettledSinceOpen] = useState(false);
402
+ useEffect(() => {
403
+ if (!ctx.open) {
404
+ setFetchedSinceOpen(false);
405
+ setSettledSinceOpen(false);
406
+ }
407
+ }, [ctx.open]);
408
+ // `enabled` follows the open state, so every open fetches '' afresh.
409
+ const asyncResult = useAsyncOptions({
410
+ loadOptions: async ? trackedLoad : undefined,
411
+ query: filter,
412
+ enabled: ctx.open,
413
+ debounceMs: (async ? props.searchDebounceMs : undefined) ?? 250,
414
+ });
415
+ // Latches on the first settle of this open (later searches keep their
416
+ // rows during the refetch). Set during render — React's "adjust state on
417
+ // change" pattern — so no frame paints a stale 'loading'. runFetch flips
418
+ // `loading` in the same batch as the fetched flag, so this cannot latch
419
+ // on the previous open's state.
420
+ if (fetchedSinceOpen && !asyncResult.loading && !settledSinceOpen) {
421
+ setSettledSinceOpen(true);
422
+ }
423
+
424
+ // Mirrors Select: `loadOptions` wins over static options; warn once per
425
+ // mount in dev. Stripped in prod builds.
426
+ const conflictingSources =
427
+ async &&
428
+ ((props as { options?: unknown[] }).options?.length ||
429
+ (props as { groups?: unknown[] }).groups?.length);
430
+ useEffect(() => {
431
+ if (process.env.NODE_ENV !== 'production' && conflictingSources) {
432
+ console.warn(
433
+ '<OptionsPicker.Content> received both `options`/`groups` and `loadOptions`. `loadOptions` wins; `options`/`groups` are ignored.',
434
+ );
435
+ }
436
+ }, [conflictingSources]);
437
+
438
+ // Ref for the search input — used to focus it on open (see effect below).
439
+ const searchInputRef = useRef<HTMLInputElement>(null);
440
+
441
+ useEffect(() => {
442
+ if (ctx.open) {
443
+ setDraft(ctx.selected);
444
+ setPinned(ctx.selected);
445
+ setFilter('');
446
+ }
447
+ }, [ctx.open, ctx.selected]);
448
+
449
+ // Focus the search input on open. Deferred past Popover.Content's own
450
+ // queueMicrotask that focuses the panel div, so our microtask runs after.
451
+ useEffect(() => {
452
+ if (ctx.open) {
453
+ queueMicrotask(() => searchInputRef.current?.focus());
454
+ }
455
+ }, [ctx.open]);
456
+
457
+ const matchesFilter = useCallback(
458
+ (opt: OptionsPickerOption): boolean => {
459
+ if (filter === '') return true;
460
+ const haystack = (opt.searchText ?? opt.label).toLowerCase();
461
+ return haystack.includes(filter.toLowerCase());
462
+ },
463
+ [filter],
464
+ );
465
+
466
+ const optionByValue = useMemo(
467
+ () => new Map((async ? asyncResult.options : getAllOptions(props)).map((o) => [o.value, o])),
468
+ [async, asyncResult.options, props],
469
+ );
470
+ // Async label cache: every server answer and `selectedOptions` seen while
471
+ // mounted, so a pinned value keeps its label after a search omits it.
472
+ // Never cleared. The current answer still wins via `optionByValue`.
473
+ const labelCache = useRef(new Map<string, OptionsPickerOption>());
474
+ const selectedOptions = async ? props.selectedOptions : undefined;
475
+ // Unknown values (e.g. a preselected value not in `options`) fall back to value-as-label.
476
+ const resolveOption = useMemo(() => {
477
+ const cache = labelCache.current;
478
+ for (const o of asyncResult.options) cache.set(o.value, o);
479
+ for (const o of selectedOptions ?? []) cache.set(o.value, o);
480
+ return (value: string): OptionsPickerOption =>
481
+ optionByValue.get(value) ?? cache.get(value) ?? { value, label: value };
482
+ }, [optionByValue, asyncResult.options, selectedOptions]);
483
+
484
+ const toggle = useCallback(
485
+ (value: string) => {
486
+ if (ctx.mode === 'multi') {
487
+ setDraft((prev) =>
488
+ prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value],
489
+ );
490
+ } else {
491
+ ctx.commit([value], [resolveOption(value)]);
492
+ }
493
+ },
494
+ [ctx, resolveOption],
495
+ );
496
+
497
+ const toggleGroup = useCallback(
498
+ (groupOptions: OptionsPickerOption[]) => {
499
+ if (ctx.mode !== 'multi') return;
500
+ setDraft((prev) => {
501
+ const allValues = groupOptions.map((o) => o.value);
502
+ const allSelected = allValues.every((v) => prev.includes(v));
503
+ if (allSelected) return prev.filter((v) => !allValues.includes(v));
504
+ const next = [...prev];
505
+ for (const v of allValues) if (!next.includes(v)) next.push(v);
506
+ return next;
507
+ });
508
+ },
509
+ [ctx.mode],
510
+ );
511
+
512
+ // Async: the server's answer IS the list (no client filter), minus the
513
+ // pinned values that are rendered above it. An error replaces it with the
514
+ // error row, as in Select; pinned rows stay in every state.
515
+ const serverRows = useMemo(() => {
516
+ if (!async || asyncResult.error || !settledSinceOpen) return [];
517
+ return asyncResult.options.filter((o) => !pinned.includes(o.value));
518
+ }, [async, asyncResult.error, asyncResult.options, settledSinceOpen, pinned]);
519
+
520
+ const visibleFlat = useMemo(() => {
521
+ if (async) return [...pinned.map(resolveOption), ...serverRows];
522
+ if (isGrouped(props)) return [];
523
+ return (props.options ?? []).filter(matchesFilter);
524
+ }, [async, pinned, resolveOption, serverRows, props, matchesFilter]);
525
+
526
+ const visibleGroups = useMemo(() => {
527
+ if (!isGrouped(props)) return [];
528
+ return props.groups
529
+ .map((g) => ({ ...g, visibleOptions: g.options.filter(matchesFilter) }))
530
+ .filter((g) => g.visibleOptions.length > 0);
531
+ }, [props, matchesFilter]);
532
+
533
+ const hasAnyVisible = isGrouped(props) ? visibleGroups.length > 0 : visibleFlat.length > 0;
534
+
535
+ // Async state row, rendered below the pinned rows. Driven by the server
536
+ // answer alone: pinned rows never suppress it. Empty means the server
537
+ // returned nothing for the settled query on screen (during the debounce
538
+ // window the rows are the previous query's) — it describes the search, so
539
+ // it shows even with pinned rows above it. An answer holding only pinned
540
+ // values is not empty: those values matched.
541
+ const asyncState = !async
542
+ ? null
543
+ : !settledSinceOpen
544
+ ? 'loading'
545
+ : asyncResult.error
546
+ ? 'error'
547
+ : asyncResult.loading && serverRows.length === 0
548
+ ? 'loading'
549
+ : !asyncResult.loading &&
550
+ asyncResult.options.length === 0 &&
551
+ asyncResult.loadedQuery === filter
552
+ ? 'empty'
553
+ : null;
554
+ const emptyText =
555
+ (typeof props.emptyState === 'string' && props.emptyState) || t('optionsPicker.noMatches');
556
+ // Hard rule 10: one live region (async only), mounted with the panel —
557
+ // Popover.Content renders nothing while closed. The text is '' while
558
+ // closed and set in an effect, so the region mounts empty on every open
559
+ // and the open-time state arrives after it (a region that mounts with
560
+ // its text announces nothing). The visible state rows are aria-hidden so
561
+ // readers meet each once.
562
+ const [statusText, setStatusText] = useState('');
563
+ useEffect(() => {
564
+ setStatusText(
565
+ !ctx.open
566
+ ? ''
567
+ : asyncState === 'loading'
568
+ ? t('optionsPicker.statusLoading')
569
+ : asyncState === 'error'
570
+ ? t('optionsPicker.loadFailed')
571
+ : asyncState === 'empty'
572
+ ? emptyText
573
+ : '',
574
+ );
575
+ }, [ctx.open, asyncState, emptyText, t]);
576
+
577
+ const allOptionsForCount = useMemo(() => getAllOptions(props), [props]);
578
+
579
+ const visibleOptionsInOrder = useMemo<OptionsPickerOption[]>(() => {
580
+ if (isGrouped(props)) return visibleGroups.flatMap((g) => g.visibleOptions);
581
+ return visibleFlat;
582
+ }, [props, visibleGroups, visibleFlat]);
583
+
584
+ const [focusedValue, setFocusedValue] = useState<string | null>(null);
585
+
586
+ useEffect(() => {
587
+ if (!ctx.open) {
588
+ setFocusedValue(null);
589
+ return;
590
+ }
591
+ if (focusedValue && !visibleOptionsInOrder.some((o) => o.value === focusedValue)) {
592
+ setFocusedValue(null);
593
+ }
594
+ }, [ctx.open, visibleOptionsInOrder, focusedValue]);
595
+
596
+ const handleKeyDown = useCallback(
597
+ (e: ReactKeyboardEvent<HTMLDivElement>) => {
598
+ if (visibleOptionsInOrder.length === 0) return;
599
+ const idx = focusedValue
600
+ ? visibleOptionsInOrder.findIndex((o) => o.value === focusedValue)
601
+ : -1;
602
+ if (e.key === 'ArrowDown') {
603
+ e.preventDefault();
604
+ const next = idx + 1 >= visibleOptionsInOrder.length ? 0 : idx + 1;
605
+ setFocusedValue(visibleOptionsInOrder[next]!.value);
606
+ } else if (e.key === 'ArrowUp') {
607
+ e.preventDefault();
608
+ const next = idx <= 0 ? visibleOptionsInOrder.length - 1 : idx - 1;
609
+ setFocusedValue(visibleOptionsInOrder[next]!.value);
610
+ } else if (e.key === 'Home') {
611
+ e.preventDefault();
612
+ setFocusedValue(visibleOptionsInOrder[0]!.value);
613
+ } else if (e.key === 'End') {
614
+ e.preventDefault();
615
+ setFocusedValue(visibleOptionsInOrder[visibleOptionsInOrder.length - 1]!.value);
616
+ } else if ((e.metaKey || e.ctrlKey) && e.key === 'Enter' && ctx.mode === 'multi') {
617
+ e.preventDefault();
618
+ ctx.commit(draft, draft.map(resolveOption));
619
+ } else if (e.key === 'Enter' || e.key === ' ') {
620
+ // Enter acts on the row from the search input or the panel; Space
621
+ // only from the panel itself — in the search it types a space, and
622
+ // every native control (row checkbox/radio, Retry/Apply/Cancel, a
623
+ // consumer emptyState link) keeps its own Enter/Space.
624
+ const handles =
625
+ e.target === e.currentTarget ||
626
+ (e.key === 'Enter' && e.target === searchInputRef.current);
627
+ if (focusedValue && handles) {
628
+ e.preventDefault();
629
+ toggle(focusedValue);
630
+ }
631
+ } else if (e.key === 'Escape') {
632
+ e.preventDefault();
633
+ setDraft(ctx.selected);
634
+ ctx.cancel();
635
+ }
636
+ },
637
+ [visibleOptionsInOrder, focusedValue, toggle, ctx, draft, resolveOption],
638
+ );
639
+
640
+ return (
641
+ <Popover.Content
642
+ ref={ref}
643
+ className={clsx(styles.panel, className)}
644
+ aria-label={label}
645
+ onKeyDown={handleKeyDown}
646
+ >
647
+ {/*
648
+ Plain <div> instead of <Stack> — each region (searchBar, list,
649
+ footer) has its own padding + borders that separate them
650
+ visually. A Stack gap here would add unwanted whitespace between
651
+ the borders.
652
+ */}
653
+ <div>
654
+ {(searchable || async) && (
655
+ <div className={styles.searchBar}>
656
+ <Search size={14} aria-hidden className={styles.searchIcon} />
657
+ <Input
658
+ ref={searchInputRef}
659
+ type="text"
660
+ value={filter}
661
+ onChange={(e) => {
662
+ setFilter(e.currentTarget.value);
663
+ setPinned(draft);
664
+ }}
665
+ placeholder={searchPlaceholder}
666
+ aria-label={searchPlaceholder}
667
+ className={styles.searchInput}
668
+ />
669
+ </div>
670
+ )}
671
+
672
+ {async && (
673
+ <span role="status" aria-live="polite" className={styles.srOnly}>
674
+ {statusText}
675
+ </span>
676
+ )}
677
+
678
+ <div
679
+ className={styles.list}
680
+ role="listbox"
681
+ aria-multiselectable={ctx.mode === 'multi'}
682
+ aria-activedescendant={focusedValue ? `${contentId}-opt-${focusedValue}` : undefined}
683
+ >
684
+ {!async && !hasAnyVisible && (
685
+ <Text size="sm" tone="muted" className={styles.empty}>
686
+ {props.emptyState || t('optionsPicker.noMatches')}
687
+ </Text>
688
+ )}
689
+ {hasAnyVisible &&
690
+ isGrouped(props) &&
691
+ visibleGroups.map((g) => {
692
+ // When the group has a palette color, draw a 2px bottom
693
+ // border on the header in that color — visually carries
694
+ // the namespace identity from the dot across the full
695
+ // header width, separating each group's options.
696
+ //
697
+ // The width is load-bearing, not decorative: #510 gave the
698
+ // header an INSET focus ring (the list is a padding-less
699
+ // scroller, so an outset ring lost both vertical bands), and
700
+ // the inset band is --ring-width, also 2px. In multi mode,
701
+ // where the header is a focusable <button>, a focused header
702
+ // therefore covers this border completely — single mode's
703
+ // header is a <div role="presentation">, which never takes a
704
+ // ring and never occludes anything. That trade is only
705
+ // acceptable because the GroupDot beside the label carries the
706
+ // same --color-palette-<name>-fg and the ring does not touch
707
+ // it. Reasoning from the old "1px" gave a partial occlusion
708
+ // and a different verdict on whether the trade is sound.
709
+ const headerClassName = clsx(
710
+ styles.groupHeader,
711
+ g.color && styles.groupHeaderWithBorder,
712
+ );
713
+ const headerStyle = g.color
714
+ ? { borderBottomColor: `var(--color-palette-${g.color}-fg)` }
715
+ : undefined;
716
+ return (
717
+ <div key={g.id} className={styles.group}>
718
+ {ctx.mode === 'multi' ? (
719
+ <button
720
+ type="button"
721
+ className={headerClassName}
722
+ style={headerStyle}
723
+ aria-pressed={tristate(
724
+ g.options.map((o) => o.value),
725
+ draft,
726
+ )}
727
+ aria-label={t('optionsPicker.toggleGroup', { label: g.label })}
728
+ aria-controls={g.options
729
+ .map((o) => `${contentId}-opt-${o.value}`)
730
+ .join(' ')}
731
+ onClick={() => toggleGroup(g.options)}
732
+ >
733
+ <GroupDot color={g.color} tone={g.tone} />
734
+ <Text size="xs" weight="semibold" className={styles.groupLabel}>
735
+ {g.label}
736
+ </Text>
737
+ {g.hint && (
738
+ <Text size="xs" tone="muted" className={styles.groupHint}>
739
+ {g.hint}
740
+ </Text>
741
+ )}
742
+ </button>
743
+ ) : (
744
+ <div className={headerClassName} style={headerStyle} role="presentation">
745
+ <GroupDot color={g.color} tone={g.tone} />
746
+ <Text size="xs" weight="semibold" className={styles.groupLabel}>
747
+ {g.label}
748
+ </Text>
749
+ {g.hint && (
750
+ <Text size="xs" tone="muted" className={styles.groupHint}>
751
+ {g.hint}
752
+ </Text>
753
+ )}
754
+ </div>
755
+ )}
756
+ {g.visibleOptions.map((opt) => (
757
+ <OptionRow
758
+ key={opt.value}
759
+ option={opt}
760
+ checked={draft.includes(opt.value)}
761
+ mode={ctx.mode}
762
+ rowId={`${contentId}-opt-${opt.value}`}
763
+ focused={focusedValue === opt.value}
764
+ onToggle={toggle}
765
+ color={g.color}
766
+ />
767
+ ))}
768
+ </div>
769
+ );
770
+ })}
771
+ {hasAnyVisible &&
772
+ !isGrouped(props) &&
773
+ visibleFlat.map((opt) => (
774
+ <OptionRow
775
+ key={opt.value}
776
+ option={opt}
777
+ checked={draft.includes(opt.value)}
778
+ mode={ctx.mode}
779
+ rowId={`${contentId}-opt-${opt.value}`}
780
+ focused={focusedValue === opt.value}
781
+ onToggle={toggle}
782
+ />
783
+ ))}
784
+ {/* Async state rows sit below the pinned rows (server rows are empty then). */}
785
+ {asyncState === 'loading' && (
786
+ <Text size="sm" tone="muted" className={styles.empty} aria-hidden="true">
787
+ {t('optionsPicker.statusLoading')}
788
+ </Text>
789
+ )}
790
+ {asyncState === 'error' && (
791
+ <Cluster gap="sm" justify="center" className={styles.empty}>
792
+ <Text size="sm" tone="muted" aria-hidden="true">
793
+ {t('optionsPicker.loadFailed')}
794
+ </Text>
795
+ {/* Name stays plain: the status region keeps the failure text, so folding it in double-reads (as in Select/Error.tsx). */}
796
+ <Button
797
+ variant="secondary"
798
+ size="sm"
799
+ onClick={() => {
800
+ // Back to "nothing settled this open": the hook still holds
801
+ // the last good answer (maybe a previous open's), so show
802
+ // loading until the retry settles, not those rows.
803
+ setFetchedSinceOpen(false);
804
+ setSettledSinceOpen(false);
805
+ asyncResult.retry();
806
+ // Retry unmounts with this click; keep focus in the panel.
807
+ searchInputRef.current?.focus();
808
+ }}
809
+ >
810
+ {t('optionsPicker.retry')}
811
+ </Button>
812
+ </Cluster>
813
+ )}
814
+ {asyncState === 'empty' && (
815
+ <Text size="sm" tone="muted" className={styles.empty} aria-hidden="true">
816
+ {props.emptyState || t('optionsPicker.noMatches')}
817
+ </Text>
818
+ )}
819
+ </div>
820
+
821
+ {ctx.mode === 'multi' && (
822
+ <div className={styles.footer}>
823
+ <Text size="xs" tone="muted">
824
+ {async
825
+ ? t('optionsPicker.selectedCount', { count: draft.length })
826
+ : (props.footerCount ?? defaultFooterCount)(
827
+ draft.length,
828
+ allOptionsForCount.length,
829
+ )}
830
+ </Text>
831
+ <Cluster gap="sm">
832
+ <Button
833
+ variant="secondary"
834
+ size="sm"
835
+ onClick={() => {
836
+ setDraft(ctx.selected);
837
+ ctx.cancel();
838
+ }}
839
+ >
840
+ {t('optionsPicker.cancel')}
841
+ </Button>
842
+ <Button
843
+ variant="primary"
844
+ size="sm"
845
+ onClick={() => ctx.commit(draft, draft.map(resolveOption))}
846
+ >
847
+ {t('optionsPicker.apply')}
848
+ </Button>
849
+ </Cluster>
850
+ </div>
851
+ )}
852
+ </div>
853
+ </Popover.Content>
854
+ );
855
+ },
856
+ );
857
+
858
+ /**
859
+ * Group-header dot. Default path uses `<Badge tone>` (the legacy
860
+ * semantic-tone behavior). When a palette `color` is set on the group,
861
+ * render a custom palette-colored dot instead — the same saturated
862
+ * `--color-palette-<name>-fg` token used by the group's Checkboxes,
863
+ * so the header and the row fills match exactly.
864
+ */
865
+ function GroupDot({ color, tone }: { color?: PaletteColor; tone?: BadgeTone }) {
866
+ if (color) {
867
+ return (
868
+ <span
869
+ aria-hidden="true"
870
+ className={styles.groupDot}
871
+ style={{ background: `var(--color-palette-${color}-fg)` }}
872
+ data-palette={color}
873
+ />
874
+ );
875
+ }
876
+ return <Badge tone={tone ?? 'neutral'} dot="start" size="sm" className={styles.groupDot} />;
877
+ }
878
+
879
+ interface OptionRowProps {
880
+ option: OptionsPickerOption;
881
+ checked: boolean;
882
+ mode: OptionsPickerMode;
883
+ rowId: string;
884
+ focused: boolean;
885
+ onToggle: (value: string) => void;
886
+ color?: PaletteColor;
887
+ }
888
+
889
+ function OptionRow({ option, checked, mode, rowId, focused, onToggle, color }: OptionRowProps) {
890
+ const checkboxOnChange = useCallback(() => onToggle(option.value), [onToggle, option.value]);
891
+ const radioOnChange = useCallback(() => onToggle(option.value), [onToggle, option.value]);
892
+ // Click anywhere inside the row toggles — the inner Checkbox/Radio handles
893
+ // clicks on the input + label itself (via native <label> semantics + onChange),
894
+ // and this onClick covers the surrounding padding. The `target === currentTarget`
895
+ // guard prevents double-fire: the row's onClick only runs when the click landed
896
+ // directly on the row div (i.e., on the padding), not when it bubbled up from
897
+ // the inner input or label.
898
+ const handleRowClick = useCallback(
899
+ (e: React.MouseEvent<HTMLDivElement>) => {
900
+ if (e.target === e.currentTarget) onToggle(option.value);
901
+ },
902
+ [onToggle, option.value],
903
+ );
904
+ return (
905
+ <div
906
+ id={rowId}
907
+ className={clsx(styles.row, checked && styles.rowSelected, focused && styles.rowFocused)}
908
+ onClick={handleRowClick}
909
+ >
910
+ {mode === 'multi' ? (
911
+ <Checkbox
912
+ checked={checked}
913
+ onChange={checkboxOnChange}
914
+ label={<Text size="sm">{option.label}</Text>}
915
+ color={color}
916
+ />
917
+ ) : (
918
+ <Radio
919
+ value={option.value}
920
+ checked={checked}
921
+ onChange={radioOnChange}
922
+ label={<Text size="sm">{option.label}</Text>}
923
+ />
924
+ )}
925
+ </div>
926
+ );
927
+ }
928
+
929
+ // ----------------------------------------------------------------------------
930
+ // Compound export
931
+ // ----------------------------------------------------------------------------
932
+
933
+ export const OptionsPicker = Object.assign(OptionsPickerRoot, {
934
+ Trigger: OptionsPickerTrigger,
935
+ Content: OptionsPickerContent,
936
+ });