@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,952 @@
1
+ import {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ type KeyboardEvent,
7
+ type MutableRefObject,
8
+ type PointerEvent,
9
+ type Ref,
10
+ } from 'react';
11
+ import clsx from 'clsx';
12
+ import { useSelectContext } from './context';
13
+ import { Chip } from './Chip';
14
+ import type { SelectOption } from './Select';
15
+ import type { FlatRow } from './utils';
16
+ import { useTranslation } from '../../i18n/useTranslation';
17
+ import styles from './Select.module.scss';
18
+
19
+ export interface TriggerProps {
20
+ placeholder?: string;
21
+ disabled?: boolean;
22
+ readOnly?: boolean;
23
+ invalid?: boolean;
24
+ clearable?: boolean;
25
+ /** Select the combobox text on open (single searchable trigger only). */
26
+ selectOnOpen?: boolean;
27
+ 'aria-label'?: string;
28
+ 'aria-labelledby'?: string;
29
+ 'aria-describedby'?: string;
30
+ 'aria-required'?: boolean | 'true' | 'false';
31
+ }
32
+
33
+ // ────────────────────────────────────────────────────────────────────────────
34
+ // ClearButton — the trailing ✕ rendered inside the trigger when
35
+ // `effectiveClearable` is true AND a value is set. Wired by every variant.
36
+ //
37
+ // Wired as a real `<button>` so screen readers announce a focusable
38
+ // "Clear selection" action. `stopPropagation` on both pointerdown + click
39
+ // keeps the parent trigger's own toggle handler from firing (clicking ✕
40
+ // must NOT also open or close the listbox), and the explicit
41
+ // `setValue('')` / `setValue([])` path bypasses `toggleValue` so a
42
+ // multi-select wipe is one event, not N.
43
+ //
44
+ // `tabIndex={0}` (not `-1`) — the clear button MUST be reachable by
45
+ // keyboard for accessibility. The default Tab order takes the user from
46
+ // the trigger to ✕ and then on to the next focusable element on the page;
47
+ // Enter/Space on the focused ✕ activates the native button's `onClick` so
48
+ // no extra keyboard handler is needed.
49
+ // ────────────────────────────────────────────────────────────────────────────
50
+ function ClearButton({ variant }: { variant: 'overlay' | 'inline' }) {
51
+ const ctx = useSelectContext('Trigger.ClearButton');
52
+ const t = useTranslation();
53
+ return (
54
+ <button
55
+ type="button"
56
+ tabIndex={0}
57
+ className={clsx(styles.clearButton, variant === 'inline' && styles.clearButtonInline)}
58
+ aria-label={t('select.clear')}
59
+ onPointerDown={(e) => e.stopPropagation()}
60
+ onMouseDown={(e) => e.stopPropagation()}
61
+ onClick={(e) => {
62
+ e.stopPropagation();
63
+ if (ctx.multiple) {
64
+ ctx.setValue([]);
65
+ } else {
66
+ ctx.setValue('');
67
+ }
68
+ }}
69
+ >
70
+ <svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true">
71
+ <path d="M1 1 L9 9 M9 1 L1 9" stroke="currentColor" strokeWidth="1.5" fill="none" />
72
+ </svg>
73
+ </button>
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Helper: would pressing "clear" change anything?
79
+ *
80
+ * Deliberately value-shaped, not lookup-shaped: clearing resets the Select
81
+ * to the empty value (`''` / `[]`), so the ✕ is only useful when the current
82
+ * value differs from that. A consumer who models "no explicit choice" as a
83
+ * real `value: ''` option (issue #470) is already sitting on the cleared
84
+ * value — there is nothing for ✕ to do, and it stays hidden.
85
+ */
86
+ function selectHasValue(value: string | string[], multiple: boolean): boolean {
87
+ return multiple
88
+ ? Array.isArray(value) && value.length > 0
89
+ : typeof value === 'string' && value !== '';
90
+ }
91
+
92
+ /**
93
+ * Resolve the selected option in single mode by looking `value` up in the
94
+ * supplied rows — never by treating `''` as "nothing selected" (issue #470).
95
+ *
96
+ * `''` is a legitimate option value: a common shape is one sentinel row
97
+ * ("Use the default scheme", `value: ''`) plus N real catalog ids. Such a
98
+ * row must render in the closed trigger exactly like any other option.
99
+ * "Nothing selected" is then simply "no row carries this value" — which is
100
+ * what an untouched Select (whose value is `''` with no `''` option in the
101
+ * list) resolves to anyway.
102
+ *
103
+ * Always searches `allRows`, never the query-filtered `rows`: the closed
104
+ * trigger must keep showing the selected label while the user filters the
105
+ * open searchable listbox down to something else.
106
+ *
107
+ * Returns `null` in multi mode, for a non-string value, or when no row
108
+ * matches (e.g. a stale value no longer present in `options`).
109
+ */
110
+ function findSelectedOption(
111
+ allRows: readonly FlatRow<unknown>[],
112
+ value: string | string[],
113
+ multiple: boolean,
114
+ ): SelectOption<unknown> | null {
115
+ if (multiple || typeof value !== 'string') return null;
116
+ const row = allRows.find((r) => r.kind === 'option' && r.option.value === value);
117
+ return row && row.kind === 'option' ? row.option : null;
118
+ }
119
+
120
+ // ────────────────────────────────────────────────────────────────────────────
121
+ // Shared helpers
122
+ // ────────────────────────────────────────────────────────────────────────────
123
+
124
+ /**
125
+ * Computes the `aria-activedescendant` value for whichever trigger is
126
+ * currently mounted. Returns `undefined` when the listbox is closed,
127
+ * when the active row is a group header, or when no row is active —
128
+ * those states must omit the attribute entirely rather than dangling
129
+ * a stale id (screen readers will announce a non-existent element).
130
+ */
131
+ function useActiveOptionId(): string | undefined {
132
+ const ctx = useSelectContext('Trigger');
133
+ if (!ctx.open || ctx.activeIndex < 0) return undefined;
134
+ const row = ctx.rows[ctx.activeIndex];
135
+ if (!row || row.kind !== 'option') return undefined;
136
+ return ctx.getOptionId(row.option.value);
137
+ }
138
+
139
+ /**
140
+ * Shared keyboard navigation hook for both trigger variants.
141
+ *
142
+ * Returns `handleNavKey(e)` which:
143
+ * - Returns `true` if the key was a navigation key the trigger handled
144
+ * (caller should NOT continue to its own variant-specific branch).
145
+ * - Returns `false` for keys this hook doesn't own — Tab, printable
146
+ * chars, anything not in the WAI-ARIA combobox navigation contract —
147
+ * so the caller can layer its own behaviour (typeahead for the button
148
+ * variant, query updates for the combobox-input variant).
149
+ *
150
+ * Tab returns `false` (and does NOT preventDefault) so the browser
151
+ * continues normal tab traversal after the side-effect of closing
152
+ * the popover.
153
+ */
154
+ function useTriggerKeyboard(opts: { disabled?: boolean; readOnly?: boolean }) {
155
+ const ctx = useSelectContext('Trigger');
156
+
157
+ // Recompute on every render — these are cheap O(n) walks and depending
158
+ // on stable identity here means memoising over `ctx.rows`, which already
159
+ // re-creates per render of `<Select>`.
160
+ const firstSelectableIdx = ctx.rows.findIndex((r) => r.kind === 'option' && !r.option.disabled);
161
+ const lastSelectableIdx = (() => {
162
+ for (let i = ctx.rows.length - 1; i >= 0; i--) {
163
+ const r = ctx.rows[i];
164
+ if (r.kind === 'option' && !r.option.disabled) return i;
165
+ }
166
+ return -1;
167
+ })();
168
+
169
+ // Cyclic step through rows skipping headers + disabled options.
170
+ // `delta` of ±1 maps to ArrowDown/Up; PageDown/Up call this 5 times.
171
+ const moveActive = useCallback(
172
+ (delta: number) => {
173
+ const { rows, activeIndex } = ctx;
174
+ if (rows.length === 0) return;
175
+ const len = rows.length;
176
+ let i = activeIndex;
177
+ for (let step = 0; step < len; step++) {
178
+ i = (i + delta + len) % len;
179
+ const r = rows[i];
180
+ if (r.kind === 'option' && !r.option.disabled) {
181
+ ctx.setActiveIndex(i);
182
+ return;
183
+ }
184
+ }
185
+ },
186
+ [ctx],
187
+ );
188
+
189
+ // Typeahead state (only used by ButtonTrigger but lives here so the
190
+ // hook owns its own timer cleanup).
191
+ const typeaheadBufferRef = useRef('');
192
+ const typeaheadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
193
+
194
+ useEffect(() => {
195
+ return () => {
196
+ if (typeaheadTimerRef.current) clearTimeout(typeaheadTimerRef.current);
197
+ };
198
+ }, []);
199
+
200
+ const stepTypeahead = useCallback(
201
+ (char: string) => {
202
+ if (typeaheadTimerRef.current) clearTimeout(typeaheadTimerRef.current);
203
+ typeaheadBufferRef.current += char.toLowerCase();
204
+ const buffer = typeaheadBufferRef.current;
205
+ // When activeIndex is -1 (no current cursor — typeahead opening the menu
206
+ // from the closed state), use -1 as the start so the `+1` first-char
207
+ // offset lands on index 0 instead of skipping past it.
208
+ const start = ctx.activeIndex >= 0 ? ctx.activeIndex : -1;
209
+ const len = ctx.rows.length;
210
+ for (let i = 0; i < len; i++) {
211
+ const idx = (start + i + (buffer.length === 1 ? 1 : 0)) % len;
212
+ const r = ctx.rows[idx];
213
+ if (r.kind !== 'option' || r.option.disabled) continue;
214
+ if (r.option.label.toLowerCase().startsWith(buffer)) {
215
+ ctx.setActiveIndex(idx);
216
+ break;
217
+ }
218
+ }
219
+ typeaheadTimerRef.current = setTimeout(() => {
220
+ typeaheadBufferRef.current = '';
221
+ }, 500);
222
+ },
223
+ [ctx],
224
+ );
225
+
226
+ const handleNavKey = (e: KeyboardEvent<HTMLElement>): boolean => {
227
+ if (opts.disabled || opts.readOnly) return false;
228
+
229
+ if (!ctx.open) {
230
+ if (e.key === 'ArrowDown' || e.key === 'Enter') {
231
+ e.preventDefault();
232
+ ctx.setOpen(true);
233
+ return true;
234
+ }
235
+ if (e.key === 'ArrowUp') {
236
+ e.preventDefault();
237
+ ctx.setOpen(true);
238
+ // Override the Listbox open-effect's "first selectable" pick in
239
+ // a microtask so this runs after Listbox mounts and sets it.
240
+ queueMicrotask(() => ctx.setActiveIndex(lastSelectableIdx));
241
+ return true;
242
+ }
243
+ return false;
244
+ }
245
+
246
+ switch (e.key) {
247
+ case 'ArrowDown':
248
+ e.preventDefault();
249
+ moveActive(+1);
250
+ return true;
251
+ case 'ArrowUp':
252
+ e.preventDefault();
253
+ moveActive(-1);
254
+ return true;
255
+ case 'Home':
256
+ e.preventDefault();
257
+ if (firstSelectableIdx >= 0) ctx.setActiveIndex(firstSelectableIdx);
258
+ return true;
259
+ case 'End':
260
+ e.preventDefault();
261
+ if (lastSelectableIdx >= 0) ctx.setActiveIndex(lastSelectableIdx);
262
+ return true;
263
+ case 'PageDown':
264
+ e.preventDefault();
265
+ for (let step = 0; step < 5; step++) moveActive(+1);
266
+ return true;
267
+ case 'PageUp':
268
+ e.preventDefault();
269
+ for (let step = 0; step < 5; step++) moveActive(-1);
270
+ return true;
271
+ case 'Enter': {
272
+ e.preventDefault();
273
+ const row = ctx.rows[ctx.activeIndex];
274
+ if (row && row.kind === 'option' && !row.option.disabled) {
275
+ // Creatable: when the active row is the synthetic "+ Create"
276
+ // row, fire `onCreate` and commit the new value. The sentinel
277
+ // is `data.__create === true`; inline-detect it here rather
278
+ // than importing `isCreateRow` to keep the keyboard handler
279
+ // self-contained.
280
+ if (
281
+ typeof row.option.data === 'object' &&
282
+ row.option.data !== null &&
283
+ (row.option.data as { __create?: boolean }).__create
284
+ ) {
285
+ ctx.onCreate?.(row.option.label);
286
+ if (ctx.multiple) {
287
+ const next = [...((ctx.value as string[]) ?? []), row.option.value];
288
+ ctx.setValue(next);
289
+ if (ctx.searchable) ctx.setQuery('');
290
+ } else {
291
+ ctx.setValue(row.option.value);
292
+ ctx.closeAndFocusTrigger();
293
+ }
294
+ } else if (ctx.multiple) {
295
+ ctx.toggleValue(row.option.value);
296
+ if (ctx.searchable) ctx.setQuery('');
297
+ } else {
298
+ ctx.setValue(row.option.value);
299
+ ctx.closeAndFocusTrigger();
300
+ }
301
+ }
302
+ return true;
303
+ }
304
+ case 'Escape':
305
+ e.preventDefault();
306
+ ctx.closeAndFocusTrigger();
307
+ return true;
308
+ case 'Tab':
309
+ // No preventDefault — browser continues Tab traversal from the
310
+ // (now-closed) trigger to the next focusable element. Return
311
+ // false so the caller doesn't think it owns the keystroke.
312
+ ctx.setOpen(false);
313
+ return false;
314
+ default:
315
+ return false;
316
+ }
317
+ };
318
+
319
+ return { handleNavKey, stepTypeahead, moveActive };
320
+ }
321
+
322
+ // ────────────────────────────────────────────────────────────────────────────
323
+ // ButtonTrigger — single, non-searchable; also the fallback for multi-mode
324
+ // before Phase 5/6 lands their dedicated triggers.
325
+ // ────────────────────────────────────────────────────────────────────────────
326
+
327
+ /**
328
+ * Button-styled trigger for the single, non-searchable Select mode.
329
+ *
330
+ * Owns the keyboard interaction for the closed-and-open lifecycle:
331
+ * - Closed: ArrowDown / Enter / Space open with the Listbox's default
332
+ * "first selectable" active row. ArrowUp opens and overrides the
333
+ * active row to the last selectable (via a microtask so it runs
334
+ * AFTER Listbox's open-effect picks the first one).
335
+ * - Open: ArrowUp/Down step through rows (skipping headers + disabled),
336
+ * Home/End jump to ends, PageUp/Down move ±5, Enter/Space commit the
337
+ * active option (single mode → close; multi → toggle), Escape closes
338
+ * without changing selection, Tab closes (no preventDefault — normal
339
+ * browser traversal continues).
340
+ *
341
+ * Printable chars (no modifier keys) drive the 500ms typeahead buffer —
342
+ * mirrors the DropdownMenu pattern.
343
+ */
344
+ function ButtonTrigger(props: TriggerProps) {
345
+ const ctx = useSelectContext('Trigger');
346
+ const t = useTranslation();
347
+ const { handleNavKey, stepTypeahead } = useTriggerKeyboard({
348
+ disabled: props.disabled,
349
+ readOnly: props.readOnly,
350
+ });
351
+
352
+ // Resolve the visible label string for both single and multi modes.
353
+ //
354
+ // Multi: walk `ctx.allRows` (NOT `ctx.rows` which is filtered when
355
+ // searchable) and comma-join the labels of every selected value so chip
356
+ // labels don't drop out of the summary when the user types a filter
357
+ // query that excludes them.
358
+ //
359
+ // Single: `allRows` lookup — keeps the closed-state label visible while
360
+ // the user is filtering in the open searchable variant (which actually
361
+ // renders the combobox-input trigger, but symmetry here is harmless and
362
+ // keeps the resolution logic in one place).
363
+ const selectedOption = findSelectedOption(ctx.allRows, ctx.value, ctx.multiple);
364
+ const label = (() => {
365
+ if (ctx.multiple) {
366
+ const selectedValues = Array.isArray(ctx.value) ? ctx.value : [];
367
+ if (selectedValues.length === 0) return '';
368
+ const labels: string[] = [];
369
+ for (const row of ctx.allRows) {
370
+ if (row.kind === 'option' && selectedValues.includes(row.option.value)) {
371
+ labels.push(row.option.label);
372
+ }
373
+ }
374
+ return labels.join(', ');
375
+ }
376
+ return selectedOption?.label ?? '';
377
+ })();
378
+ // Derived from whether an option MATCHED, not from whether the resolved
379
+ // label happens to be non-empty — a selected `value: ''` row (issue #470)
380
+ // has a real label and must not render placeholder-styled.
381
+ const hasValue = ctx.multiple ? label !== '' : selectedOption !== null;
382
+
383
+ const activeOptionId = useActiveOptionId();
384
+
385
+ // When multi-summary text overflows it's truncated with CSS ellipsis,
386
+ // so screen-reader users would miss the tail of the selection. Surface
387
+ // the full comma-joined list via aria-label, but ONLY when the consumer
388
+ // didn't pass their own aria-label — their string is authoritative.
389
+ const computedAriaLabel = (() => {
390
+ if (props['aria-label']) return props['aria-label'];
391
+ if (ctx.multiple && label) return t('select.selectedPrefix', { labels: label });
392
+ // An option may carry an empty label. It is still a selection — the
393
+ // rendered gate below uses `hasValue` so it doesn't show placeholder text
394
+ // over a checked row — but the trigger must not end up with NO accessible
395
+ // name at all, so the placeholder remains the naming fallback.
396
+ return label || props.placeholder || undefined;
397
+ })();
398
+
399
+ const handleKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {
400
+ if (props.disabled || props.readOnly) return;
401
+
402
+ // Nav keys (Arrow/Home/End/Page/Enter/Escape/Tab) — let the shared
403
+ // hook handle. Returns true if it claimed the keystroke.
404
+ if (handleNavKey(e)) return;
405
+
406
+ // Space: same semantics as Enter (open-or-commit). The shared hook
407
+ // doesn't claim Space because the combobox-input variant needs it
408
+ // as a literal character in the query string.
409
+ if (e.key === ' ') {
410
+ e.preventDefault();
411
+ if (!ctx.open) {
412
+ ctx.setOpen(true);
413
+ return;
414
+ }
415
+ const row = ctx.rows[ctx.activeIndex];
416
+ if (row && row.kind === 'option' && !row.option.disabled) {
417
+ if (ctx.multiple) {
418
+ ctx.toggleValue(row.option.value);
419
+ } else {
420
+ ctx.setValue(row.option.value);
421
+ ctx.closeAndFocusTrigger();
422
+ }
423
+ }
424
+ return;
425
+ }
426
+
427
+ // Printable char (no modifiers) → typeahead. Opens the menu first if
428
+ // closed so the user sees the highlighted option.
429
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
430
+ e.preventDefault();
431
+ if (!ctx.open) ctx.setOpen(true);
432
+ stepTypeahead(e.key);
433
+ }
434
+ };
435
+
436
+ // Not `hasValue`: ✕ resets to the empty value, so it's shown when the
437
+ // current value differs from that — including for a value with no
438
+ // matching option (a stale id), which the user must still be able to
439
+ // clear, and excluding a selected `value: ''` option, which already IS
440
+ // the cleared state.
441
+ const showClear = props.clearable && selectHasValue(ctx.value, ctx.multiple);
442
+
443
+ // Resolve the visible label node. In single mode, `renderValue` (if
444
+ // supplied) replaces the bare string with whatever the consumer returns.
445
+ // Multi-summary deliberately stays as the comma-joined string — applying
446
+ // `renderValue` to it would mean passing N options, which doesn't fit the
447
+ // single-option signature; for richer multi labels use `renderTag` on
448
+ // chips mode instead.
449
+ //
450
+ // The accessible-name computation falls back to the option label string,
451
+ // not the rendered node, so screen readers still hear "Pending" even if
452
+ // the consumer wraps it in decoration.
453
+ const labelNode: React.ReactNode = (() => {
454
+ // Gated on `hasValue`, not on `label` being truthy: an option may
455
+ // legitimately carry an empty label, and gating the two on different
456
+ // predicates would drop the placeholder styling while still rendering
457
+ // placeholder text.
458
+ if (!hasValue) return null;
459
+ if (!ctx.multiple && ctx.renderValue && selectedOption) {
460
+ return ctx.renderValue(selectedOption as SelectOption);
461
+ }
462
+ return label;
463
+ })();
464
+
465
+ return (
466
+ <div ref={ctx.triggerRootRef as Ref<HTMLDivElement>} className={styles.triggerWrap}>
467
+ <button
468
+ type="button"
469
+ role="combobox"
470
+ id={ctx.triggerId}
471
+ ref={ctx.triggerRef as Ref<HTMLButtonElement>}
472
+ className={clsx(styles.trigger, styles.triggerButton, !hasValue && styles.placeholder)}
473
+ aria-haspopup="listbox"
474
+ aria-expanded={ctx.open}
475
+ aria-controls={ctx.open ? ctx.listboxId : undefined}
476
+ aria-activedescendant={activeOptionId}
477
+ aria-label={computedAriaLabel}
478
+ aria-labelledby={props['aria-labelledby']}
479
+ aria-describedby={props['aria-describedby']}
480
+ aria-required={props['aria-required']}
481
+ aria-invalid={props.invalid || undefined}
482
+ aria-readonly={props.readOnly || undefined}
483
+ disabled={props.disabled}
484
+ onClick={() => {
485
+ if (props.disabled || props.readOnly) return;
486
+ ctx.setOpen(!ctx.open);
487
+ }}
488
+ onKeyDown={handleKeyDown}
489
+ >
490
+ {/* Gated on `hasValue`, not on the label being truthy: an option may
491
+ legitimately carry an empty label, and it is still a selection —
492
+ rendering the placeholder there would say "nothing is selected"
493
+ about a row the listbox shows as checked. */}
494
+ {hasValue ? (
495
+ <span>{labelNode}</span>
496
+ ) : (
497
+ <span className={styles.placeholder}>{props.placeholder ?? ''}</span>
498
+ )}
499
+ </button>
500
+ {showClear && <ClearButton variant="overlay" />}
501
+ <span className={styles.chevron} aria-hidden="true">
502
+ ▾
503
+ </span>
504
+ </div>
505
+ );
506
+ }
507
+
508
+ // `renderValue` deliberately does NOT apply to the ComboboxInputTrigger
509
+ // variant: the input's `value` prop must be a string, but `renderValue`
510
+ // returns a ReactNode. Consumers using `searchable` who want decorated
511
+ // labels should look at non-searchable mode or compose at the option level.
512
+
513
+ // ────────────────────────────────────────────────────────────────────────────
514
+ // ComboboxInputTrigger — single + searchable (WAI-ARIA combobox 1.2
515
+ // "input as trigger" pattern). Headless UI Combobox shape: focusing the
516
+ // input does NOT auto-open; clicking the input opens; typing opens and
517
+ // filters; Escape reverts the query to the selected label.
518
+ // ────────────────────────────────────────────────────────────────────────────
519
+
520
+ function ComboboxInputTrigger(props: TriggerProps) {
521
+ const ctx = useSelectContext('Trigger');
522
+ const { handleNavKey } = useTriggerKeyboard({
523
+ disabled: props.disabled,
524
+ readOnly: props.readOnly,
525
+ });
526
+
527
+ // Resolve the selected label from `allRows` (NOT `rows`). When the user
528
+ // types "arc" and the selected option "Pending" filters out of `rows`,
529
+ // we still need its label as the closed-state display + Escape fallback.
530
+ //
531
+ // Lookup-driven, never `value === ''`-driven: a consumer's sentinel
532
+ // `value: ''` option is a real selection and must display its label
533
+ // (issue #470).
534
+ const selectedOption = useMemo(
535
+ () => findSelectedOption(ctx.allRows, ctx.value, ctx.multiple),
536
+ [ctx.allRows, ctx.value, ctx.multiple],
537
+ );
538
+ const selectedLabel = selectedOption?.label ?? '';
539
+ const hasSelection = selectedOption !== null;
540
+
541
+ // Reset the query to '' whenever the listbox closes, so the next open
542
+ // shows the full option list (instead of the stale filter) and the
543
+ // input falls back to displaying the selected label.
544
+ //
545
+ // Depends only on `ctx.open` (not query / setQuery) — the goal is to
546
+ // run exactly once per close transition; including `ctx.query` in the
547
+ // deps would re-fire on every keystroke and wipe the user's input.
548
+ useEffect(() => {
549
+ if (!ctx.open) {
550
+ ctx.setQuery('');
551
+ }
552
+ // eslint-disable-next-line react-hooks/exhaustive-deps
553
+ }, [ctx.open]);
554
+
555
+ // Display rules:
556
+ // - closed → always show the selected label (query is reset to '' by
557
+ // the effect below when `ctx.open` flips false).
558
+ // - open & query is empty → show the selected label as a "ghost"
559
+ // starting value. The first onChange will detect the user typing
560
+ // "over" this label and strip the prefix.
561
+ // - open & query is non-empty → show the live query.
562
+ //
563
+ // This mirrors Headless UI's Combobox where the selected label sits
564
+ // in the input until the user starts editing, then is replaced.
565
+ const displayValue = ctx.open && ctx.query !== '' ? ctx.query : selectedLabel;
566
+
567
+ const activeOptionId = useActiveOptionId();
568
+
569
+ const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
570
+ if (props.disabled || props.readOnly) return;
571
+ // Nav keys (Arrow/Home/End/Page/Enter/Escape/Tab). Crucially we do
572
+ // NOT layer typeahead on top — typing IS the filter mechanism here,
573
+ // handled by `onChange` below. Space is a literal char in the query
574
+ // and must reach the input's default `change` event, so the shared
575
+ // hook deliberately doesn't claim it.
576
+ handleNavKey(e);
577
+ };
578
+
579
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
580
+ if (props.disabled || props.readOnly) return;
581
+ let next = e.target.value;
582
+ // When the input was sitting on the selected label (closed, or open
583
+ // with an empty query), the browser's native key handling appends
584
+ // the typed char — producing `"<selectedLabel><typed>"`. Strip the
585
+ // prefix so the user's *intent* (replace, not append) wins.
586
+ if (ctx.query === '' && selectedLabel !== '' && next.startsWith(selectedLabel)) {
587
+ next = next.slice(selectedLabel.length);
588
+ }
589
+ if (!ctx.open) ctx.setOpen(true);
590
+ ctx.setQuery(next);
591
+ };
592
+
593
+ // `ctx.triggerRef` is typed as `RefObject<HTMLElement | null>`. Cast at
594
+ // the assignment site rather than widening the context shape; both
595
+ // <button> and <input> are HTMLElement and the callers that read this
596
+ // ref only need `.focus()` / `.contains()` which are on HTMLElement.
597
+ const inputRefHolder = ctx.triggerRef as MutableRefObject<HTMLInputElement | null>;
598
+
599
+ // On open, optionally select the visible text so the user can immediately
600
+ // type to replace the selected label (type-to-search). Gated on an empty
601
+ // query so opening BY TYPING (handleChange sets the query then opens) doesn't
602
+ // re-select + clobber that first character — only the label-showing open
603
+ // (click / keyboard) selects. Runs once per open transition.
604
+ useEffect(() => {
605
+ if (ctx.open && props.selectOnOpen && ctx.query === '') inputRefHolder.current?.select();
606
+ // eslint-disable-next-line react-hooks/exhaustive-deps
607
+ }, [ctx.open]);
608
+
609
+ const showClear = props.clearable && selectHasValue(ctx.value, ctx.multiple);
610
+
611
+ return (
612
+ <div ref={ctx.triggerRootRef as Ref<HTMLDivElement>} className={styles.triggerWrap}>
613
+ <input
614
+ type="text"
615
+ id={ctx.triggerId}
616
+ ref={(node) => {
617
+ inputRefHolder.current = node;
618
+ }}
619
+ className={clsx(styles.trigger, styles.triggerInput, !hasSelection && styles.placeholder)}
620
+ role="combobox"
621
+ aria-autocomplete="list"
622
+ aria-haspopup="listbox"
623
+ aria-expanded={ctx.open}
624
+ aria-controls={ctx.open ? ctx.listboxId : undefined}
625
+ aria-activedescendant={activeOptionId}
626
+ aria-label={props['aria-label']}
627
+ aria-labelledby={props['aria-labelledby']}
628
+ aria-describedby={props['aria-describedby']}
629
+ aria-required={props['aria-required']}
630
+ aria-invalid={props.invalid || undefined}
631
+ aria-readonly={props.readOnly || undefined}
632
+ autoComplete="off"
633
+ spellCheck={false}
634
+ disabled={props.disabled}
635
+ readOnly={props.readOnly}
636
+ value={displayValue}
637
+ placeholder={hasSelection ? undefined : props.placeholder}
638
+ onClick={() => {
639
+ if (props.disabled || props.readOnly) return;
640
+ // Click opens but does NOT toggle — matches Headless UI Combobox
641
+ // and avoids the surprising "click the focused input to close
642
+ // it" behaviour.
643
+ if (!ctx.open) ctx.setOpen(true);
644
+ }}
645
+ onChange={handleChange}
646
+ onKeyDown={handleKeyDown}
647
+ />
648
+ {showClear && <ClearButton variant="overlay" />}
649
+ <span className={styles.chevron} aria-hidden="true">
650
+ ▾
651
+ </span>
652
+ </div>
653
+ );
654
+ }
655
+
656
+ // ────────────────────────────────────────────────────────────────────────────
657
+ // ChipsInputTrigger — multi + chips + searchable. Selected options render as
658
+ // removable chips alongside an inline `<input role="combobox">` that owns
659
+ // the live filter query. Backspace on an empty input removes the trailing
660
+ // chip (matches GitHub label picker, Material UI Autocomplete multiple,
661
+ // Headless UI tag inputs). Pattern reference: WAI-ARIA APG combobox + the
662
+ // de-facto tag-input convention.
663
+ // ────────────────────────────────────────────────────────────────────────────
664
+
665
+ /**
666
+ * Chips trigger with an inline `role="combobox"` input. Wrapper is a plain
667
+ * `<div>` (no role) because the input IS the WAI-ARIA combobox; assigning
668
+ * `role="button"` to the wrapper would create two competing focus targets.
669
+ * The visual wrapper owns positioning and outside-click containment while
670
+ * the input remains the semantic combobox and focus-restoration target.
671
+ *
672
+ * Backspace behaviour: removes the trailing chip ONLY when `ctx.query === ''`.
673
+ * On non-empty input it falls through to the browser's native text-deletion.
674
+ *
675
+ * Enter behaviour: `useTriggerKeyboard.handleNavKey` toggles the active
676
+ * option in multi mode but does NOT clear the query — we clear it here in
677
+ * a microtask so the input is empty for the next pick. Listbox's option
678
+ * click handler also clears the query for multi+searchable so mouse
679
+ * selection follows the same UX.
680
+ */
681
+ function ChipsInputTrigger(props: TriggerProps) {
682
+ const ctx = useSelectContext('Trigger');
683
+ const { handleNavKey } = useTriggerKeyboard({
684
+ disabled: props.disabled,
685
+ readOnly: props.readOnly,
686
+ });
687
+ const activeOptionId = useActiveOptionId();
688
+ const inputRef = useRef<HTMLInputElement>(null);
689
+
690
+ const selectedValues = Array.isArray(ctx.value) ? (ctx.value as string[]) : [];
691
+ // Walk `selectedValues` (selection order — `ctx.value` appends new picks
692
+ // to the end via `useSelectState.toggleValue`) so the latest selected chip
693
+ // renders last. Look options up from `allRows` so labels persist while
694
+ // typing filters `rows` down. Synthetic `{value, label: value}` fallback
695
+ // covers creatable values not yet in `allRows`.
696
+ const optionByValue = new Map<string, SelectOption>();
697
+ for (const row of ctx.allRows) {
698
+ if (row.kind === 'option') optionByValue.set(row.option.value, row.option as SelectOption);
699
+ }
700
+ const selectedOptions: SelectOption[] = selectedValues.map(
701
+ (v) => optionByValue.get(v) ?? { value: v, label: v },
702
+ );
703
+
704
+ const handleWrapperPointerDown = (e: PointerEvent<HTMLDivElement>) => {
705
+ if (props.disabled || props.readOnly) return;
706
+ // Redirect any pointer landing on the wrapper itself (gap between
707
+ // chips, padding area) into the input so the caret position stays
708
+ // predictable. Don't steal events that already target the input —
709
+ // the browser owns selection/caret placement there.
710
+ if (e.target !== inputRef.current) {
711
+ e.preventDefault();
712
+ inputRef.current?.focus();
713
+ }
714
+ };
715
+
716
+ const handleWrapperClick = () => {
717
+ if (props.disabled || props.readOnly) return;
718
+ inputRef.current?.focus();
719
+ if (!ctx.open) ctx.setOpen(true);
720
+ };
721
+
722
+ const handleInputKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
723
+ if (props.disabled || props.readOnly) return;
724
+ // Enter handling is layered: handleNavKey toggles in multi mode but
725
+ // doesn't clear the query. Schedule the clear AFTER the toggle so the
726
+ // input is empty for the next selection. Microtask + open-guard so we
727
+ // don't blow away a query if the user pressed Enter while closed.
728
+ if (e.key === 'Enter' && ctx.open) {
729
+ queueMicrotask(() => ctx.setQuery(''));
730
+ }
731
+ if (handleNavKey(e)) return;
732
+ // Backspace on empty input removes the trailing chip. On non-empty
733
+ // input we DO NOT preventDefault — the browser handles char deletion.
734
+ if (e.key === 'Backspace' && ctx.query === '' && selectedOptions.length > 0) {
735
+ e.preventDefault();
736
+ const last = selectedOptions[selectedOptions.length - 1];
737
+ ctx.toggleValue(last.value);
738
+ }
739
+ };
740
+
741
+ const inputRefHolder = ctx.triggerRef as MutableRefObject<HTMLInputElement | null>;
742
+
743
+ return (
744
+ <div
745
+ ref={ctx.triggerRootRef as Ref<HTMLDivElement>}
746
+ className={clsx(styles.trigger, styles.triggerChips, styles.triggerChipsInput)}
747
+ onClick={handleWrapperClick}
748
+ onPointerDown={handleWrapperPointerDown}
749
+ >
750
+ {selectedOptions.map((o) => {
751
+ const remove = () => {
752
+ if (props.disabled || props.readOnly) return;
753
+ ctx.toggleValue(o.value);
754
+ inputRef.current?.focus();
755
+ };
756
+ if (ctx.renderTag) {
757
+ // Wrap in a keyed Fragment so React can track ordering. The
758
+ // consumer owns the inner markup (click handlers, styling, etc.).
759
+ return <span key={o.value}>{ctx.renderTag(o, remove)}</span>;
760
+ }
761
+ return <Chip key={o.value} label={o.label} disabled={props.disabled} onRemove={remove} />;
762
+ })}
763
+ <input
764
+ ref={(node) => {
765
+ inputRef.current = node;
766
+ inputRefHolder.current = node;
767
+ }}
768
+ type="text"
769
+ id={ctx.triggerId}
770
+ role="combobox"
771
+ aria-autocomplete="list"
772
+ aria-haspopup="listbox"
773
+ aria-expanded={ctx.open}
774
+ aria-controls={ctx.open ? ctx.listboxId : undefined}
775
+ aria-activedescendant={activeOptionId}
776
+ aria-label={props['aria-label']}
777
+ aria-labelledby={props['aria-labelledby']}
778
+ aria-describedby={props['aria-describedby']}
779
+ aria-required={props['aria-required']}
780
+ aria-invalid={props.invalid || undefined}
781
+ disabled={props.disabled}
782
+ readOnly={props.readOnly}
783
+ autoComplete="off"
784
+ spellCheck={false}
785
+ className={styles.chipsInput}
786
+ placeholder={selectedOptions.length === 0 ? props.placeholder : undefined}
787
+ value={ctx.query}
788
+ onChange={(e) => {
789
+ if (!ctx.open) ctx.setOpen(true);
790
+ ctx.setQuery(e.target.value);
791
+ }}
792
+ onKeyDown={handleInputKeyDown}
793
+ />
794
+ {props.clearable && selectedOptions.length > 0 && <ClearButton variant="inline" />}
795
+ </div>
796
+ );
797
+ }
798
+
799
+ // ────────────────────────────────────────────────────────────────────────────
800
+ // ChipsButtonTrigger — multi + chips + !searchable. Renders selected options
801
+ // as removable inline chips around a read-only combobox input. The visual
802
+ // wrapper is also the floating anchor and outside-click boundary.
803
+ // ────────────────────────────────────────────────────────────────────────────
804
+
805
+ /**
806
+ * Chips trigger for multi-select without inline search. A dedicated read-only
807
+ * input owns combobox semantics and keyboard focus so chip remove buttons are
808
+ * siblings rather than descendants of the combobox. Chip resolution walks
809
+ * `ctx.allRows`, not `ctx.rows`, so the
810
+ * chip list stays stable even if a future hover-search filters the
811
+ * popover — unselected options never show as chips, but selected ones
812
+ * never disappear.
813
+ *
814
+ * Keyboard:
815
+ * - Closed: ArrowDown/Up/Enter open via the shared hook; printable chars
816
+ * open + start typeahead; Space opens.
817
+ * - Open: shared hook owns Arrow/Home/End/PageUp/Down/Enter/Escape/Tab.
818
+ * Enter toggles the active option without closing (multi semantics).
819
+ * - Backspace is NOT bound here (no input to detect emptiness against);
820
+ * the searchable variant handles it.
821
+ */
822
+ function ChipsButtonTrigger(props: TriggerProps) {
823
+ const ctx = useSelectContext('Trigger');
824
+ const { handleNavKey, stepTypeahead } = useTriggerKeyboard({
825
+ disabled: props.disabled,
826
+ readOnly: props.readOnly,
827
+ });
828
+ const t = useTranslation();
829
+ const activeOptionId = useActiveOptionId();
830
+
831
+ const selectedValues = Array.isArray(ctx.value) ? (ctx.value as string[]) : [];
832
+ // Walk `selectedValues` (selection order — `ctx.value` appends new picks
833
+ // to the end via `useSelectState.toggleValue`) so the latest selected chip
834
+ // renders last. Look options up from `allRows` so labels persist while
835
+ // searching. Synthetic `{value, label: value}` fallback covers creatable
836
+ // values not yet in `allRows`.
837
+ const optionByValue = new Map<string, SelectOption>();
838
+ for (const row of ctx.allRows) {
839
+ if (row.kind === 'option') optionByValue.set(row.option.value, row.option as SelectOption);
840
+ }
841
+ const selectedOptions: SelectOption[] = selectedValues.map(
842
+ (v) => optionByValue.get(v) ?? { value: v, label: v },
843
+ );
844
+
845
+ const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
846
+ if (props.disabled || props.readOnly) return;
847
+ if (handleNavKey(e)) return;
848
+ if (e.key === ' ') {
849
+ e.preventDefault();
850
+ if (!ctx.open) ctx.setOpen(true);
851
+ return;
852
+ }
853
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
854
+ e.preventDefault();
855
+ if (!ctx.open) ctx.setOpen(true);
856
+ stepTypeahead(e.key);
857
+ }
858
+ };
859
+
860
+ // Mirrors ButtonTrigger's computedAriaLabel pattern: consumer's
861
+ // aria-label wins; otherwise synthesize from selections so screen
862
+ // readers hear the full list even when chips overflow visually.
863
+ const labelForAria = selectedOptions.map((o) => o.label).join(', ');
864
+ const computedAriaLabel =
865
+ props['aria-label'] ||
866
+ (selectedOptions.length > 0
867
+ ? t('select.selectedPrefix', { labels: labelForAria })
868
+ : t('select.openSelect'));
869
+
870
+ const handleWrapperClick = () => {
871
+ if (props.disabled || props.readOnly) return;
872
+ ctx.triggerRef.current?.focus();
873
+ ctx.setOpen(!ctx.open);
874
+ };
875
+
876
+ return (
877
+ <div
878
+ ref={ctx.triggerRootRef as Ref<HTMLDivElement>}
879
+ className={clsx(styles.trigger, styles.triggerChips)}
880
+ onClick={handleWrapperClick}
881
+ >
882
+ {selectedOptions.map((o) => {
883
+ const remove = () => {
884
+ if (props.disabled || props.readOnly) return;
885
+ ctx.toggleValue(o.value);
886
+ };
887
+ if (ctx.renderTag) {
888
+ return <span key={o.value}>{ctx.renderTag(o, remove)}</span>;
889
+ }
890
+ return <Chip key={o.value} label={o.label} disabled={props.disabled} onRemove={remove} />;
891
+ })}
892
+ {selectedOptions.length === 0 && (
893
+ <span aria-hidden="true" className={styles.placeholder}>
894
+ {props.placeholder ?? ''}
895
+ </span>
896
+ )}
897
+ <input
898
+ ref={ctx.triggerRef as Ref<HTMLInputElement>}
899
+ id={ctx.triggerId}
900
+ type="text"
901
+ role="combobox"
902
+ readOnly
903
+ tabIndex={props.disabled ? -1 : 0}
904
+ className={styles.chipsInput}
905
+ aria-haspopup="listbox"
906
+ aria-expanded={ctx.open}
907
+ aria-controls={ctx.open ? ctx.listboxId : undefined}
908
+ aria-activedescendant={activeOptionId}
909
+ aria-label={computedAriaLabel}
910
+ aria-labelledby={props['aria-labelledby']}
911
+ aria-describedby={props['aria-describedby']}
912
+ aria-required={props['aria-required']}
913
+ aria-invalid={props.invalid || undefined}
914
+ aria-readonly={props.readOnly || undefined}
915
+ aria-disabled={props.disabled || undefined}
916
+ value=""
917
+ onChange={() => undefined}
918
+ onKeyDown={handleKeyDown}
919
+ />
920
+ {props.clearable && selectedOptions.length > 0 && <ClearButton variant="inline" />}
921
+ </div>
922
+ );
923
+ }
924
+
925
+ // ────────────────────────────────────────────────────────────────────────────
926
+ // Dispatcher — picks the variant based on context flags. The public
927
+ // `Trigger` export is what `<Select>` renders; the variants stay module-
928
+ // local so the future Phase 5/6 multi-mode variants slot in here too.
929
+ // ────────────────────────────────────────────────────────────────────────────
930
+
931
+ /**
932
+ * Internal dispatcher rendered by `<Select>`. Routes to the appropriate
933
+ * trigger variant based on mode flags from context.
934
+ *
935
+ * - `single + !searchable` → `ButtonTrigger`
936
+ * - `single + searchable` → `ComboboxInputTrigger`
937
+ * - `multi + chips + !searchable` → `ChipsButtonTrigger`
938
+ * - `multi + chips + searchable` → `ChipsInputTrigger` (Phase 6 task 15)
939
+ * - `multi + summary` (both forms) → `ButtonTrigger` w/ comma-joined labels
940
+ */
941
+ export function Trigger(props: TriggerProps) {
942
+ const ctx = useSelectContext('Trigger');
943
+ if (!ctx.multiple && ctx.searchable) {
944
+ return <ComboboxInputTrigger {...props} />;
945
+ }
946
+ if (ctx.multiple && ctx.triggerDisplay === 'chips') {
947
+ return ctx.searchable ? <ChipsInputTrigger {...props} /> : <ChipsButtonTrigger {...props} />;
948
+ }
949
+ // Multi-summary (both searchable + non-searchable) renders as the
950
+ // comma-joined select-only combobox trigger.
951
+ return <ButtonTrigger {...props} />;
952
+ }