@eocrm/design-system 0.0.0-stage → 0.6.18

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 (898) hide show
  1. package/AI-PRIMER.md +341 -0
  2. package/LICENSE +202 -0
  3. package/README.md +266 -2
  4. package/docs/components/Accordion.md +114 -0
  5. package/docs/components/AgentAvatar.md +58 -0
  6. package/docs/components/Alert.md +81 -0
  7. package/docs/components/AppLayout.md +70 -0
  8. package/docs/components/Avatar.md +49 -0
  9. package/docs/components/AvatarGroup.md +47 -0
  10. package/docs/components/Badge.md +60 -0
  11. package/docs/components/Banner.md +89 -0
  12. package/docs/components/BrandIcon.md +32 -0
  13. package/docs/components/Breadcrumb.md +72 -0
  14. package/docs/components/Button.md +107 -0
  15. package/docs/components/ButtonGroup.md +78 -0
  16. package/docs/components/Calendar.md +158 -0
  17. package/docs/components/Card.md +165 -0
  18. package/docs/components/CatalogPicker.md +83 -0
  19. package/docs/components/Chart.md +114 -0
  20. package/docs/components/ChatBubble.md +55 -0
  21. package/docs/components/ChatLauncher.md +97 -0
  22. package/docs/components/Checkbox.md +55 -0
  23. package/docs/components/CircularProgress.md +53 -0
  24. package/docs/components/Cluster.md +96 -0
  25. package/docs/components/Code.md +35 -0
  26. package/docs/components/CodeBlock.md +67 -0
  27. package/docs/components/ColorPicker.md +115 -0
  28. package/docs/components/ConfirmationPopover.md +70 -0
  29. package/docs/components/Constrain.md +39 -0
  30. package/docs/components/CurrencySelect.md +57 -0
  31. package/docs/components/CursorPagination.md +43 -0
  32. package/docs/components/DashboardCanvas.md +79 -0
  33. package/docs/components/DashboardWidget.md +131 -0
  34. package/docs/components/DataTable.md +88 -0
  35. package/docs/components/DatePicker.md +68 -0
  36. package/docs/components/DateRangePicker.md +71 -0
  37. package/docs/components/DateStrip.md +56 -0
  38. package/docs/components/DefinitionList.md +82 -0
  39. package/docs/components/Divider.md +57 -0
  40. package/docs/components/Dot.md +26 -0
  41. package/docs/components/Drawer.md +94 -0
  42. package/docs/components/DropdownMenu.md +299 -0
  43. package/docs/components/EmojiPicker.md +84 -0
  44. package/docs/components/EmptyState.md +55 -0
  45. package/docs/components/EntityChip.md +102 -0
  46. package/docs/components/ErrorState.md +72 -0
  47. package/docs/components/Field.md +91 -0
  48. package/docs/components/FileUpload.md +109 -0
  49. package/docs/components/FilterChip.md +88 -0
  50. package/docs/components/FlowCanvas.md +108 -0
  51. package/docs/components/FormRow.md +31 -0
  52. package/docs/components/FormSection.md +37 -0
  53. package/docs/components/Grid.md +103 -0
  54. package/docs/components/Highlight.md +75 -0
  55. package/docs/components/HighlightedText.md +50 -0
  56. package/docs/components/IconPicker.md +134 -0
  57. package/docs/components/IconTile.md +30 -0
  58. package/docs/components/Image.md +84 -0
  59. package/docs/components/ImageCrop.md +163 -0
  60. package/docs/components/Indent.md +31 -0
  61. package/docs/components/InlineDatePicker.md +58 -0
  62. package/docs/components/InlineDateRangePicker.md +57 -0
  63. package/docs/components/Input.md +54 -0
  64. package/docs/components/Kanban.md +122 -0
  65. package/docs/components/Kbd.md +41 -0
  66. package/docs/components/Lightbox.md +105 -0
  67. package/docs/components/Link.md +65 -0
  68. package/docs/components/LinkCard.md +41 -0
  69. package/docs/components/LiquidEditor.md +84 -0
  70. package/docs/components/List.md +68 -0
  71. package/docs/components/LiveRegion.md +29 -0
  72. package/docs/components/LocaleProvider.md +25 -0
  73. package/docs/components/Logo.md +41 -0
  74. package/docs/components/Masonry.md +48 -0
  75. package/docs/components/MediaTile.md +95 -0
  76. package/docs/components/Modal.md +107 -0
  77. package/docs/components/MoneyInput.md +62 -0
  78. package/docs/components/OptionsPicker.md +123 -0
  79. package/docs/components/OtpInput.md +65 -0
  80. package/docs/components/Page.md +35 -0
  81. package/docs/components/PageHeader.md +133 -0
  82. package/docs/components/Pagination.md +44 -0
  83. package/docs/components/Palette.md +32 -0
  84. package/docs/components/PasswordInput.md +45 -0
  85. package/docs/components/PasswordStrengthMeter.md +27 -0
  86. package/docs/components/PersonDisplay.md +153 -0
  87. package/docs/components/PhoneInput.md +54 -0
  88. package/docs/components/PillMenu.md +91 -0
  89. package/docs/components/Popover.md +127 -0
  90. package/docs/components/Progress.md +56 -0
  91. package/docs/components/QrCode.md +34 -0
  92. package/docs/components/Radio.md +37 -0
  93. package/docs/components/RadioGroup.md +56 -0
  94. package/docs/components/Rail.md +191 -0
  95. package/docs/components/RelativeDateInput.md +114 -0
  96. package/docs/components/RichText.md +44 -0
  97. package/docs/components/RichTextEditor.md +112 -0
  98. package/docs/components/Screen.md +37 -0
  99. package/docs/components/ScrollArea.md +39 -0
  100. package/docs/components/Select.md +131 -0
  101. package/docs/components/SettingRow.md +98 -0
  102. package/docs/components/Skeleton.md +68 -0
  103. package/docs/components/Slider.md +92 -0
  104. package/docs/components/SlotGrid.md +55 -0
  105. package/docs/components/SocialButton.md +31 -0
  106. package/docs/components/Sortable.md +127 -0
  107. package/docs/components/SortableGroup.md +52 -0
  108. package/docs/components/Sparkline.md +51 -0
  109. package/docs/components/Split.md +76 -0
  110. package/docs/components/Stack.md +59 -0
  111. package/docs/components/StagePath.md +66 -0
  112. package/docs/components/StatGroup.md +61 -0
  113. package/docs/components/StatTile.md +58 -0
  114. package/docs/components/Sticky.md +69 -0
  115. package/docs/components/Switch.md +89 -0
  116. package/docs/components/Table.md +184 -0
  117. package/docs/components/TableOfContents.md +76 -0
  118. package/docs/components/Tabs.md +94 -0
  119. package/docs/components/Text.md +63 -0
  120. package/docs/components/Textarea.md +59 -0
  121. package/docs/components/Thread.md +52 -0
  122. package/docs/components/TimeField.md +70 -0
  123. package/docs/components/Timeline.md +57 -0
  124. package/docs/components/Title.md +54 -0
  125. package/docs/components/ToastViewport.md +61 -0
  126. package/docs/components/Tooltip.md +63 -0
  127. package/docs/components/TopBar.md +214 -0
  128. package/docs/components/Tour.md +85 -0
  129. package/docs/components/TreeView.md +113 -0
  130. package/docs/components/VisuallyHidden.md +43 -0
  131. package/docs/components/WidgetPreview.md +54 -0
  132. package/docs/components/useBelowBreakpoint.md +11 -0
  133. package/docs/components/useMonth.md +18 -0
  134. package/docs/setup.md +51 -0
  135. package/docs/theming.md +95 -0
  136. package/docs/tokens.md +123 -0
  137. package/docs/transient-state.md +56 -0
  138. package/package.json +86 -4
  139. package/src/_meta/manifest.ts +293 -0
  140. package/src/app/AppProvider.tsx +78 -0
  141. package/src/app/index.ts +3 -0
  142. package/src/app/themeTokens.ts +95 -0
  143. package/src/calendar/dateMath.ts +114 -0
  144. package/src/calendar/formatters.ts +75 -0
  145. package/src/calendar/index.ts +39 -0
  146. package/src/calendar/types.ts +36 -0
  147. package/src/calendar/useAgenda.ts +70 -0
  148. package/src/calendar/useDay.ts +51 -0
  149. package/src/calendar/useMonth.ts +82 -0
  150. package/src/calendar/useWeek.ts +62 -0
  151. package/src/calendar/weekInfo.ts +83 -0
  152. package/src/components/Accordion/Accordion.module.scss +304 -0
  153. package/src/components/Accordion/Accordion.tokens.scss +64 -0
  154. package/src/components/Accordion/Accordion.tsx +272 -0
  155. package/src/components/Accordion/AccordionContent.tsx +36 -0
  156. package/src/components/Accordion/AccordionItem.tsx +66 -0
  157. package/src/components/Accordion/AccordionTrigger.tsx +121 -0
  158. package/src/components/Accordion/context.ts +47 -0
  159. package/src/components/Accordion/index.ts +14 -0
  160. package/src/components/Alert/Alert.module.scss +92 -0
  161. package/src/components/Alert/Alert.tokens.scss +60 -0
  162. package/src/components/Alert/Alert.tsx +136 -0
  163. package/src/components/Alert/index.ts +2 -0
  164. package/src/components/AppLayout/AppLayout.module.scss +130 -0
  165. package/src/components/AppLayout/AppLayout.tokens.scss +27 -0
  166. package/src/components/AppLayout/AppLayout.tsx +211 -0
  167. package/src/components/AppLayout/index.ts +2 -0
  168. package/src/components/Avatar/AgentAvatar.module.scss +117 -0
  169. package/src/components/Avatar/AgentAvatar.tsx +122 -0
  170. package/src/components/Avatar/Avatar.module.scss +181 -0
  171. package/src/components/Avatar/Avatar.tokens.scss +107 -0
  172. package/src/components/Avatar/Avatar.tsx +210 -0
  173. package/src/components/Avatar/AvatarGroup.module.scss +128 -0
  174. package/src/components/Avatar/AvatarGroup.tsx +119 -0
  175. package/src/components/Avatar/AvatarGroupContext.tsx +20 -0
  176. package/src/components/Avatar/index.ts +6 -0
  177. package/src/components/Badge/Badge.module.scss +124 -0
  178. package/src/components/Badge/Badge.tokens.scss +74 -0
  179. package/src/components/Badge/Badge.tsx +164 -0
  180. package/src/components/Badge/index.ts +2 -0
  181. package/src/components/Banner/Banner.module.scss +73 -0
  182. package/src/components/Banner/Banner.tokens.scss +51 -0
  183. package/src/components/Banner/Banner.tsx +138 -0
  184. package/src/components/Banner/index.ts +2 -0
  185. package/src/components/BrandIcon/BrandIcon.module.scss +5 -0
  186. package/src/components/BrandIcon/BrandIcon.tsx +99 -0
  187. package/src/components/BrandIcon/index.ts +2 -0
  188. package/src/components/Breadcrumb/Breadcrumb.module.scss +32 -0
  189. package/src/components/Breadcrumb/Breadcrumb.tokens.scss +9 -0
  190. package/src/components/Breadcrumb/Breadcrumb.tsx +146 -0
  191. package/src/components/Breadcrumb/index.ts +2 -0
  192. package/src/components/Button/Button.module.scss +167 -0
  193. package/src/components/Button/Button.tokens.scss +90 -0
  194. package/src/components/Button/Button.tsx +205 -0
  195. package/src/components/Button/index.ts +2 -0
  196. package/src/components/ButtonGroup/ButtonGroup.module.scss +134 -0
  197. package/src/components/ButtonGroup/ButtonGroup.tokens.scss +42 -0
  198. package/src/components/ButtonGroup/ButtonGroup.tsx +278 -0
  199. package/src/components/ButtonGroup/ButtonGroupItem.tsx +74 -0
  200. package/src/components/ButtonGroup/context.ts +45 -0
  201. package/src/components/ButtonGroup/index.ts +2 -0
  202. package/src/components/Calendar/AgendaView.module.scss +231 -0
  203. package/src/components/Calendar/AgendaView.tsx +214 -0
  204. package/src/components/Calendar/AllDayBand.module.scss +18 -0
  205. package/src/components/Calendar/AllDayBand.tsx +75 -0
  206. package/src/components/Calendar/Calendar.module.scss +23 -0
  207. package/src/components/Calendar/Calendar.tokens.scss +262 -0
  208. package/src/components/Calendar/Calendar.tsx +370 -0
  209. package/src/components/Calendar/DayCell.module.scss +30 -0
  210. package/src/components/Calendar/DayCell.tsx +50 -0
  211. package/src/components/Calendar/DayView.module.scss +10 -0
  212. package/src/components/Calendar/DayView.tsx +163 -0
  213. package/src/components/Calendar/EventChip.module.scss +171 -0
  214. package/src/components/Calendar/EventChip.tsx +115 -0
  215. package/src/components/Calendar/HourGrid.module.scss +123 -0
  216. package/src/components/Calendar/HourGrid.tsx +381 -0
  217. package/src/components/Calendar/MonthView.module.scss +99 -0
  218. package/src/components/Calendar/MonthView.tsx +296 -0
  219. package/src/components/Calendar/TimedEvent.module.scss +205 -0
  220. package/src/components/Calendar/TimedEvent.tsx +247 -0
  221. package/src/components/Calendar/ViewSwitcher.module.scss +3 -0
  222. package/src/components/Calendar/ViewSwitcher.tsx +47 -0
  223. package/src/components/Calendar/WeekView.module.scss +10 -0
  224. package/src/components/Calendar/WeekView.tsx +137 -0
  225. package/src/components/Calendar/eventColor.ts +60 -0
  226. package/src/components/Calendar/index.ts +18 -0
  227. package/src/components/Calendar/types.ts +451 -0
  228. package/src/components/Calendar/useEventDrag.ts +783 -0
  229. package/src/components/Calendar/utils.ts +622 -0
  230. package/src/components/Card/Card.module.scss +264 -0
  231. package/src/components/Card/Card.tokens.scss +55 -0
  232. package/src/components/Card/Card.tsx +170 -0
  233. package/src/components/Card/CardBody.tsx +45 -0
  234. package/src/components/Card/CardHeader.tsx +113 -0
  235. package/src/components/Card/CardList.tsx +24 -0
  236. package/src/components/Card/CardListRow.tsx +24 -0
  237. package/src/components/Card/index.ts +7 -0
  238. package/src/components/CatalogPicker/CatalogPicker.module.scss +193 -0
  239. package/src/components/CatalogPicker/CatalogPicker.tokens.scss +26 -0
  240. package/src/components/CatalogPicker/CatalogPicker.tsx +339 -0
  241. package/src/components/CatalogPicker/index.ts +2 -0
  242. package/src/components/Chart/Chart.module.scss +287 -0
  243. package/src/components/Chart/Chart.tokens.scss +63 -0
  244. package/src/components/Chart/Chart.tsx +607 -0
  245. package/src/components/Chart/ChartLegend.tsx +99 -0
  246. package/src/components/Chart/ChartTable.tsx +62 -0
  247. package/src/components/Chart/ChartTooltip.tsx +91 -0
  248. package/src/components/Chart/chartModel.ts +689 -0
  249. package/src/components/Chart/index.ts +2 -0
  250. package/src/components/ChatBubble/ChatBubble.module.scss +93 -0
  251. package/src/components/ChatBubble/ChatBubble.tokens.scss +17 -0
  252. package/src/components/ChatBubble/ChatBubble.tsx +61 -0
  253. package/src/components/ChatBubble/ChatBubbleList.tsx +27 -0
  254. package/src/components/ChatBubble/index.ts +3 -0
  255. package/src/components/ChatLauncher/ChatLauncher.module.scss +68 -0
  256. package/src/components/ChatLauncher/ChatLauncher.tokens.scss +27 -0
  257. package/src/components/ChatLauncher/ChatLauncher.tsx +202 -0
  258. package/src/components/ChatLauncher/index.ts +2 -0
  259. package/src/components/ChatLauncher/isAtBottom.ts +12 -0
  260. package/src/components/Checkbox/Checkbox.module.scss +147 -0
  261. package/src/components/Checkbox/Checkbox.tokens.scss +44 -0
  262. package/src/components/Checkbox/Checkbox.tsx +188 -0
  263. package/src/components/Checkbox/index.ts +2 -0
  264. package/src/components/CircularProgress/CircularProgress.module.scss +133 -0
  265. package/src/components/CircularProgress/CircularProgress.tokens.scss +40 -0
  266. package/src/components/CircularProgress/CircularProgress.tsx +139 -0
  267. package/src/components/CircularProgress/index.ts +7 -0
  268. package/src/components/Cluster/Cluster.module.scss +145 -0
  269. package/src/components/Cluster/Cluster.tokens.scss +11 -0
  270. package/src/components/Cluster/Cluster.tsx +215 -0
  271. package/src/components/Cluster/index.ts +2 -0
  272. package/src/components/Code/Code.module.scss +28 -0
  273. package/src/components/Code/Code.tokens.scss +15 -0
  274. package/src/components/Code/Code.tsx +44 -0
  275. package/src/components/Code/index.ts +2 -0
  276. package/src/components/CodeBlock/CodeBlock.module.scss +146 -0
  277. package/src/components/CodeBlock/CodeBlock.tokens.scss +27 -0
  278. package/src/components/CodeBlock/CodeBlock.tsx +274 -0
  279. package/src/components/CodeBlock/highlight.ts +179 -0
  280. package/src/components/CodeBlock/index.ts +8 -0
  281. package/src/components/CodeBlock/sampleSync.ts +42 -0
  282. package/src/components/ColorPicker/ColorPicker.module.scss +286 -0
  283. package/src/components/ColorPicker/ColorPicker.tokens.scss +60 -0
  284. package/src/components/ColorPicker/ColorPicker.tsx +367 -0
  285. package/src/components/ColorPicker/ColorPickerPanel.tsx +265 -0
  286. package/src/components/ColorPicker/SVSquare.tsx +189 -0
  287. package/src/components/ColorPicker/colorMath.ts +141 -0
  288. package/src/components/ColorPicker/index.ts +6 -0
  289. package/src/components/ConfirmationPopover/ConfirmationPopover.module.scss +39 -0
  290. package/src/components/ConfirmationPopover/ConfirmationPopover.tokens.scss +13 -0
  291. package/src/components/ConfirmationPopover/ConfirmationPopover.tsx +395 -0
  292. package/src/components/ConfirmationPopover/index.ts +2 -0
  293. package/src/components/Constrain/Constrain.module.scss +99 -0
  294. package/src/components/Constrain/Constrain.tsx +79 -0
  295. package/src/components/Constrain/index.ts +2 -0
  296. package/src/components/CurrencySelect/CurrencySelect.module.scss +7 -0
  297. package/src/components/CurrencySelect/CurrencySelect.tsx +167 -0
  298. package/src/components/CurrencySelect/index.ts +3 -0
  299. package/src/components/CursorPagination/CursorPagination.module.scss +69 -0
  300. package/src/components/CursorPagination/CursorPagination.tokens.scss +32 -0
  301. package/src/components/CursorPagination/CursorPagination.tsx +121 -0
  302. package/src/components/CursorPagination/index.ts +2 -0
  303. package/src/components/DashboardCanvas/DashboardCanvas.module.scss +360 -0
  304. package/src/components/DashboardCanvas/DashboardCanvas.tokens.scss +48 -0
  305. package/src/components/DashboardCanvas/DashboardCanvas.tsx +1270 -0
  306. package/src/components/DashboardCanvas/DashboardCanvasItem.tsx +140 -0
  307. package/src/components/DashboardCanvas/DashboardCanvasSection.tsx +174 -0
  308. package/src/components/DashboardCanvas/engine.ts +363 -0
  309. package/src/components/DashboardCanvas/index.ts +9 -0
  310. package/src/components/DashboardWidget/DashboardWidget.module.scss +100 -0
  311. package/src/components/DashboardWidget/DashboardWidget.tokens.scss +16 -0
  312. package/src/components/DashboardWidget/DashboardWidget.tsx +188 -0
  313. package/src/components/DashboardWidget/index.ts +7 -0
  314. package/src/components/DataTable/BodyRow.tsx +197 -0
  315. package/src/components/DataTable/ColumnVisibilityTrigger.module.scss +7 -0
  316. package/src/components/DataTable/ColumnVisibilityTrigger.tsx +105 -0
  317. package/src/components/DataTable/DataTable.module.scss +511 -0
  318. package/src/components/DataTable/DataTable.tokens.scss +80 -0
  319. package/src/components/DataTable/DataTable.tsx +734 -0
  320. package/src/components/DataTable/HeaderCell.module.scss +215 -0
  321. package/src/components/DataTable/HeaderCell.tsx +440 -0
  322. package/src/components/DataTable/columnShift.ts +117 -0
  323. package/src/components/DataTable/index.ts +23 -0
  324. package/src/components/DataTable/pinStyle.ts +47 -0
  325. package/src/components/DataTable/reorderColumns.ts +66 -0
  326. package/src/components/DataTable/types.ts +289 -0
  327. package/src/components/DataTable/useColumnDragShift.ts +216 -0
  328. package/src/components/DataTable/useDataTable.ts +372 -0
  329. package/src/components/DataTable/useResizeHandle.ts +97 -0
  330. package/src/components/DatePicker/DatePicker.module.scss +180 -0
  331. package/src/components/DatePicker/DatePicker.tokens.scss +152 -0
  332. package/src/components/DatePicker/DatePicker.tsx +664 -0
  333. package/src/components/DatePicker/DatePickerGrid.module.scss +172 -0
  334. package/src/components/DatePicker/DatePickerGrid.tsx +341 -0
  335. package/src/components/DatePicker/InlineDatePicker.module.scss +32 -0
  336. package/src/components/DatePicker/InlineDatePicker.tsx +201 -0
  337. package/src/components/DatePicker/index.ts +4 -0
  338. package/src/components/DatePicker/utils.ts +364 -0
  339. package/src/components/DateRangePicker/DateRangePicker.module.scss +237 -0
  340. package/src/components/DateRangePicker/DateRangePicker.tokens.scss +78 -0
  341. package/src/components/DateRangePicker/DateRangePicker.tsx +720 -0
  342. package/src/components/DateRangePicker/InlineDateRangePicker.module.scss +94 -0
  343. package/src/components/DateRangePicker/InlineDateRangePicker.tsx +376 -0
  344. package/src/components/DateRangePicker/index.ts +5 -0
  345. package/src/components/DateRangePicker/utils.ts +151 -0
  346. package/src/components/DateStrip/DateStrip.module.scss +78 -0
  347. package/src/components/DateStrip/DateStrip.tokens.scss +23 -0
  348. package/src/components/DateStrip/DateStrip.tsx +205 -0
  349. package/src/components/DateStrip/index.ts +2 -0
  350. package/src/components/DefinitionList/DefinitionList.module.scss +102 -0
  351. package/src/components/DefinitionList/DefinitionList.tokens.scss +29 -0
  352. package/src/components/DefinitionList/DefinitionList.tsx +198 -0
  353. package/src/components/DefinitionList/index.ts +9 -0
  354. package/src/components/Divider/Divider.module.scss +144 -0
  355. package/src/components/Divider/Divider.tokens.scss +21 -0
  356. package/src/components/Divider/Divider.tsx +103 -0
  357. package/src/components/Divider/index.ts +2 -0
  358. package/src/components/Dot/Dot.module.scss +40 -0
  359. package/src/components/Dot/Dot.tokens.scss +4 -0
  360. package/src/components/Dot/Dot.tsx +45 -0
  361. package/src/components/Dot/index.ts +2 -0
  362. package/src/components/Drawer/Body.tsx +23 -0
  363. package/src/components/Drawer/Close.tsx +35 -0
  364. package/src/components/Drawer/Content.tsx +93 -0
  365. package/src/components/Drawer/Drawer.module.scss +232 -0
  366. package/src/components/Drawer/Drawer.tokens.scss +38 -0
  367. package/src/components/Drawer/Drawer.tsx +23 -0
  368. package/src/components/Drawer/DrawerRoot.tsx +190 -0
  369. package/src/components/Drawer/Footer.tsx +24 -0
  370. package/src/components/Drawer/Header.tsx +64 -0
  371. package/src/components/Drawer/Overlay.tsx +82 -0
  372. package/src/components/Drawer/context.ts +44 -0
  373. package/src/components/Drawer/index.ts +12 -0
  374. package/src/components/Drawer/useDragToClose.ts +149 -0
  375. package/src/components/DropdownMenu/CheckboxItem.tsx +144 -0
  376. package/src/components/DropdownMenu/Content.tsx +419 -0
  377. package/src/components/DropdownMenu/DropdownMenu.module.scss +280 -0
  378. package/src/components/DropdownMenu/DropdownMenu.tokens.scss +88 -0
  379. package/src/components/DropdownMenu/DropdownMenu.tsx +43 -0
  380. package/src/components/DropdownMenu/Group.tsx +76 -0
  381. package/src/components/DropdownMenu/Item.tsx +148 -0
  382. package/src/components/DropdownMenu/ItemIndicator.tsx +30 -0
  383. package/src/components/DropdownMenu/Radio.tsx +178 -0
  384. package/src/components/DropdownMenu/Root.tsx +100 -0
  385. package/src/components/DropdownMenu/Sub.tsx +332 -0
  386. package/src/components/DropdownMenu/Trigger.tsx +76 -0
  387. package/src/components/DropdownMenu/context.ts +65 -0
  388. package/src/components/DropdownMenu/index.ts +20 -0
  389. package/src/components/EmojiPicker/EmojiPicker.module.scss +84 -0
  390. package/src/components/EmojiPicker/EmojiPicker.tokens.scss +24 -0
  391. package/src/components/EmojiPicker/EmojiPicker.tsx +341 -0
  392. package/src/components/EmojiPicker/emojiData.ts +245 -0
  393. package/src/components/EmojiPicker/index.ts +3 -0
  394. package/src/components/EmptyState/EmptyState.module.scss +102 -0
  395. package/src/components/EmptyState/EmptyState.tokens.scss +28 -0
  396. package/src/components/EmptyState/EmptyState.tsx +112 -0
  397. package/src/components/EmptyState/index.ts +7 -0
  398. package/src/components/EntityChip/EntityChip.module.scss +275 -0
  399. package/src/components/EntityChip/EntityChip.tokens.scss +66 -0
  400. package/src/components/EntityChip/EntityChip.tsx +649 -0
  401. package/src/components/EntityChip/index.ts +10 -0
  402. package/src/components/ErrorState/ErrorState.module.scss +105 -0
  403. package/src/components/ErrorState/ErrorState.tokens.scss +32 -0
  404. package/src/components/ErrorState/ErrorState.tsx +125 -0
  405. package/src/components/ErrorState/index.ts +8 -0
  406. package/src/components/Field/Field.module.scss +46 -0
  407. package/src/components/Field/Field.tsx +185 -0
  408. package/src/components/Field/index.ts +2 -0
  409. package/src/components/FileUpload/FileUpload.module.scss +187 -0
  410. package/src/components/FileUpload/FileUpload.tokens.scss +81 -0
  411. package/src/components/FileUpload/FileUpload.tsx +569 -0
  412. package/src/components/FileUpload/formatBytes.ts +34 -0
  413. package/src/components/FileUpload/iconForFile.ts +40 -0
  414. package/src/components/FileUpload/index.ts +2 -0
  415. package/src/components/FilterChip/FilterChip.module.scss +61 -0
  416. package/src/components/FilterChip/FilterChip.tokens.scss +25 -0
  417. package/src/components/FilterChip/FilterChip.tsx +208 -0
  418. package/src/components/FilterChip/index.ts +5 -0
  419. package/src/components/FlowCanvas/FlowCanvas.module.scss +329 -0
  420. package/src/components/FlowCanvas/FlowCanvas.tokens.scss +45 -0
  421. package/src/components/FlowCanvas/FlowCanvas.tsx +1867 -0
  422. package/src/components/FlowCanvas/FlowControls.tsx +46 -0
  423. package/src/components/FlowCanvas/FlowEdge.tsx +58 -0
  424. package/src/components/FlowCanvas/FlowNode.tsx +76 -0
  425. package/src/components/FlowCanvas/edgePath.ts +85 -0
  426. package/src/components/FlowCanvas/index.ts +4 -0
  427. package/src/components/FlowCanvas/layout.ts +148 -0
  428. package/src/components/FlowCanvas/spatialNav.ts +76 -0
  429. package/src/components/FlowCanvas/types.ts +59 -0
  430. package/src/components/FlowCanvas/useViewport.ts +122 -0
  431. package/src/components/FormRow/FormRow.module.scss +2 -0
  432. package/src/components/FormRow/FormRow.tsx +37 -0
  433. package/src/components/FormRow/index.ts +2 -0
  434. package/src/components/FormSection/FormSection.module.scss +24 -0
  435. package/src/components/FormSection/FormSection.tsx +46 -0
  436. package/src/components/FormSection/index.ts +2 -0
  437. package/src/components/Grid/Grid.module.scss +162 -0
  438. package/src/components/Grid/Grid.tokens.scss +13 -0
  439. package/src/components/Grid/Grid.tsx +226 -0
  440. package/src/components/Grid/GridItem.tsx +77 -0
  441. package/src/components/Grid/index.ts +11 -0
  442. package/src/components/Highlight/Highlight.module.scss +43 -0
  443. package/src/components/Highlight/Highlight.tokens.scss +35 -0
  444. package/src/components/Highlight/Highlight.tsx +164 -0
  445. package/src/components/Highlight/HighlightOverlay.tsx +100 -0
  446. package/src/components/Highlight/index.ts +2 -0
  447. package/src/components/HighlightedText/HighlightedText.module.scss +11 -0
  448. package/src/components/HighlightedText/HighlightedText.tokens.scss +9 -0
  449. package/src/components/HighlightedText/HighlightedText.tsx +50 -0
  450. package/src/components/HighlightedText/highlightRanges.ts +44 -0
  451. package/src/components/HighlightedText/index.ts +2 -0
  452. package/src/components/IconPicker/IconPicker.module.scss +175 -0
  453. package/src/components/IconPicker/IconPicker.tokens.scss +40 -0
  454. package/src/components/IconPicker/IconPicker.tsx +629 -0
  455. package/src/components/IconPicker/index.ts +9 -0
  456. package/src/components/IconTile/IconTile.module.scss +57 -0
  457. package/src/components/IconTile/IconTile.tokens.scss +16 -0
  458. package/src/components/IconTile/IconTile.tsx +91 -0
  459. package/src/components/IconTile/index.ts +2 -0
  460. package/src/components/Image/Image.module.scss +266 -0
  461. package/src/components/Image/Image.tokens.scss +55 -0
  462. package/src/components/Image/Image.tsx +342 -0
  463. package/src/components/Image/index.ts +2 -0
  464. package/src/components/ImageCrop/ImageCrop.module.scss +274 -0
  465. package/src/components/ImageCrop/ImageCrop.tokens.scss +33 -0
  466. package/src/components/ImageCrop/ImageCrop.tsx +542 -0
  467. package/src/components/ImageCrop/cropGeometry.ts +120 -0
  468. package/src/components/ImageCrop/extractCropBlob.ts +140 -0
  469. package/src/components/ImageCrop/index.ts +11 -0
  470. package/src/components/ImageCrop/useCropPreview.ts +161 -0
  471. package/src/components/Indent/Indent.module.scss +33 -0
  472. package/src/components/Indent/Indent.tokens.scss +10 -0
  473. package/src/components/Indent/Indent.tsx +46 -0
  474. package/src/components/Indent/index.ts +2 -0
  475. package/src/components/Input/Input.module.scss +99 -0
  476. package/src/components/Input/Input.tokens.scss +54 -0
  477. package/src/components/Input/Input.tsx +105 -0
  478. package/src/components/Input/Suggestions.tsx +260 -0
  479. package/src/components/Input/index.ts +2 -0
  480. package/src/components/Kanban/Kanban.module.scss +39 -0
  481. package/src/components/Kanban/Kanban.tokens.scss +15 -0
  482. package/src/components/Kanban/Kanban.tsx +951 -0
  483. package/src/components/Kanban/index.ts +7 -0
  484. package/src/components/Kbd/Kbd.module.scss +47 -0
  485. package/src/components/Kbd/Kbd.tokens.scss +26 -0
  486. package/src/components/Kbd/Kbd.tsx +73 -0
  487. package/src/components/Kbd/index.ts +2 -0
  488. package/src/components/Lightbox/Lightbox.module.scss +283 -0
  489. package/src/components/Lightbox/Lightbox.tokens.scss +36 -0
  490. package/src/components/Lightbox/Lightbox.tsx +506 -0
  491. package/src/components/Lightbox/index.ts +2 -0
  492. package/src/components/Link/Link.module.scss +78 -0
  493. package/src/components/Link/Link.tokens.scss +28 -0
  494. package/src/components/Link/Link.tsx +114 -0
  495. package/src/components/Link/index.ts +2 -0
  496. package/src/components/LinkCard/LinkCard.module.scss +92 -0
  497. package/src/components/LinkCard/LinkCard.tokens.scss +9 -0
  498. package/src/components/LinkCard/LinkCard.tsx +72 -0
  499. package/src/components/LinkCard/index.ts +2 -0
  500. package/src/components/LiquidEditor/AutocompleteMenu.tsx +133 -0
  501. package/src/components/LiquidEditor/HighlightLayer.tsx +53 -0
  502. package/src/components/LiquidEditor/InsertVariableMenu.tsx +81 -0
  503. package/src/components/LiquidEditor/LiquidEditor.module.scss +339 -0
  504. package/src/components/LiquidEditor/LiquidEditor.tokens.scss +81 -0
  505. package/src/components/LiquidEditor/LiquidEditor.tsx +412 -0
  506. package/src/components/LiquidEditor/PreviewPane.tsx +29 -0
  507. package/src/components/LiquidEditor/caretCoordinates.ts +97 -0
  508. package/src/components/LiquidEditor/index.ts +7 -0
  509. package/src/components/LiquidEditor/liquidFilters.ts +23 -0
  510. package/src/components/LiquidEditor/liquidTokenizer.ts +235 -0
  511. package/src/components/LiquidEditor/types.ts +101 -0
  512. package/src/components/LiquidEditor/useLiquidAutocomplete.ts +178 -0
  513. package/src/components/List/List.module.scss +105 -0
  514. package/src/components/List/List.tokens.scss +42 -0
  515. package/src/components/List/List.tsx +124 -0
  516. package/src/components/List/index.ts +9 -0
  517. package/src/components/LiveRegion/LiveRegion.module.scss +3 -0
  518. package/src/components/LiveRegion/LiveRegion.tsx +116 -0
  519. package/src/components/LiveRegion/index.ts +2 -0
  520. package/src/components/Logo/Logo.module.scss +139 -0
  521. package/src/components/Logo/Logo.tokens.scss +14 -0
  522. package/src/components/Logo/Logo.tsx +126 -0
  523. package/src/components/Logo/index.ts +2 -0
  524. package/src/components/Masonry/Masonry.module.scss +42 -0
  525. package/src/components/Masonry/Masonry.tokens.scss +8 -0
  526. package/src/components/Masonry/Masonry.tsx +136 -0
  527. package/src/components/Masonry/index.ts +2 -0
  528. package/src/components/Masonry/masonryUtils.ts +42 -0
  529. package/src/components/MediaTile/MediaTile.module.scss +183 -0
  530. package/src/components/MediaTile/MediaTile.tokens.scss +60 -0
  531. package/src/components/MediaTile/MediaTile.tsx +208 -0
  532. package/src/components/MediaTile/index.ts +8 -0
  533. package/src/components/Modal/Body.tsx +27 -0
  534. package/src/components/Modal/Close.tsx +45 -0
  535. package/src/components/Modal/Content.tsx +98 -0
  536. package/src/components/Modal/Footer.tsx +28 -0
  537. package/src/components/Modal/Header.tsx +51 -0
  538. package/src/components/Modal/Modal.module.scss +262 -0
  539. package/src/components/Modal/Modal.tokens.scss +50 -0
  540. package/src/components/Modal/Modal.tsx +23 -0
  541. package/src/components/Modal/ModalRoot.tsx +299 -0
  542. package/src/components/Modal/Overlay.tsx +101 -0
  543. package/src/components/Modal/context.ts +60 -0
  544. package/src/components/Modal/index.ts +11 -0
  545. package/src/components/MoneyInput/MoneyInput.module.scss +57 -0
  546. package/src/components/MoneyInput/MoneyInput.tsx +433 -0
  547. package/src/components/MoneyInput/index.ts +2 -0
  548. package/src/components/OptionsPicker/OptionsPicker.module.scss +216 -0
  549. package/src/components/OptionsPicker/OptionsPicker.tokens.scss +68 -0
  550. package/src/components/OptionsPicker/OptionsPicker.tsx +936 -0
  551. package/src/components/OptionsPicker/index.ts +9 -0
  552. package/src/components/OtpInput/OtpInput.module.scss +69 -0
  553. package/src/components/OtpInput/OtpInput.tokens.scss +45 -0
  554. package/src/components/OtpInput/OtpInput.tsx +368 -0
  555. package/src/components/OtpInput/index.ts +2 -0
  556. package/src/components/Page/Page.module.scss +30 -0
  557. package/src/components/Page/Page.tokens.scss +11 -0
  558. package/src/components/Page/Page.tsx +53 -0
  559. package/src/components/Page/index.ts +2 -0
  560. package/src/components/PageHeader/PageHeader.module.scss +197 -0
  561. package/src/components/PageHeader/PageHeader.tokens.scss +41 -0
  562. package/src/components/PageHeader/PageHeader.tsx +310 -0
  563. package/src/components/PageHeader/index.ts +20 -0
  564. package/src/components/Pagination/Pagination.module.scss +117 -0
  565. package/src/components/Pagination/Pagination.tokens.scss +54 -0
  566. package/src/components/Pagination/Pagination.tsx +165 -0
  567. package/src/components/Pagination/index.ts +4 -0
  568. package/src/components/Pagination/paginationRange.ts +72 -0
  569. package/src/components/PasswordInput/PasswordInput.module.scss +120 -0
  570. package/src/components/PasswordInput/PasswordInput.tokens.scss +44 -0
  571. package/src/components/PasswordInput/PasswordInput.tsx +210 -0
  572. package/src/components/PasswordInput/index.ts +2 -0
  573. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.module.scss +80 -0
  574. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tokens.scss +40 -0
  575. package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tsx +96 -0
  576. package/src/components/PasswordStrengthMeter/index.ts +2 -0
  577. package/src/components/PersonDisplay/PersonDisplay.module.scss +180 -0
  578. package/src/components/PersonDisplay/PersonDisplay.tokens.scss +35 -0
  579. package/src/components/PersonDisplay/PersonDisplay.tsx +420 -0
  580. package/src/components/PersonDisplay/index.ts +10 -0
  581. package/src/components/PhoneInput/PhoneInput.module.scss +23 -0
  582. package/src/components/PhoneInput/PhoneInput.tsx +271 -0
  583. package/src/components/PhoneInput/index.ts +4 -0
  584. package/src/components/PhoneInput/phone.ts +128 -0
  585. package/src/components/PillMenu/PillMenu.module.scss +117 -0
  586. package/src/components/PillMenu/PillMenu.tokens.scss +22 -0
  587. package/src/components/PillMenu/PillMenu.tsx +324 -0
  588. package/src/components/PillMenu/index.ts +2 -0
  589. package/src/components/Popover/Anchor.tsx +37 -0
  590. package/src/components/Popover/Close.tsx +45 -0
  591. package/src/components/Popover/Content.tsx +213 -0
  592. package/src/components/Popover/Heading.tsx +33 -0
  593. package/src/components/Popover/Popover.module.scss +177 -0
  594. package/src/components/Popover/Popover.tokens.scss +22 -0
  595. package/src/components/Popover/Popover.tsx +23 -0
  596. package/src/components/Popover/PopoverRoot.tsx +75 -0
  597. package/src/components/Popover/Trigger.tsx +86 -0
  598. package/src/components/Popover/context.ts +22 -0
  599. package/src/components/Popover/index.ts +7 -0
  600. package/src/components/Progress/Progress.module.scss +110 -0
  601. package/src/components/Progress/Progress.tokens.scss +44 -0
  602. package/src/components/Progress/Progress.tsx +129 -0
  603. package/src/components/Progress/index.ts +2 -0
  604. package/src/components/QrCode/QrCode.module.scss +97 -0
  605. package/src/components/QrCode/QrCode.tokens.scss +17 -0
  606. package/src/components/QrCode/QrCode.tsx +233 -0
  607. package/src/components/QrCode/index.ts +2 -0
  608. package/src/components/QrCode/qr.ts +161 -0
  609. package/src/components/Radio/Radio.module.scss +175 -0
  610. package/src/components/Radio/Radio.tokens.scss +59 -0
  611. package/src/components/Radio/Radio.tsx +127 -0
  612. package/src/components/Radio/RadioGroup.module.scss +42 -0
  613. package/src/components/Radio/RadioGroup.tsx +120 -0
  614. package/src/components/Radio/RadioGroupContext.tsx +30 -0
  615. package/src/components/Radio/index.ts +4 -0
  616. package/src/components/Rail/Rail.module.scss +510 -0
  617. package/src/components/Rail/Rail.tokens.scss +92 -0
  618. package/src/components/Rail/Rail.tsx +287 -0
  619. package/src/components/Rail/RailCollapseToggle.tsx +57 -0
  620. package/src/components/Rail/RailFooter.tsx +17 -0
  621. package/src/components/Rail/RailGroup.tsx +728 -0
  622. package/src/components/Rail/RailGroupContext.ts +9 -0
  623. package/src/components/Rail/RailHeader.tsx +23 -0
  624. package/src/components/Rail/RailItem.tsx +119 -0
  625. package/src/components/Rail/RailSection.tsx +39 -0
  626. package/src/components/Rail/RailSpacer.tsx +19 -0
  627. package/src/components/Rail/index.ts +9 -0
  628. package/src/components/RelativeDateInput/RelativeDateInput.module.scss +15 -0
  629. package/src/components/RelativeDateInput/RelativeDateInput.tokens.scss +6 -0
  630. package/src/components/RelativeDateInput/RelativeDateInput.tsx +474 -0
  631. package/src/components/RelativeDateInput/index.ts +10 -0
  632. package/src/components/RelativeDateInput/relativeDate.ts +127 -0
  633. package/src/components/RichText/RichText.module.scss +5 -0
  634. package/src/components/RichText/RichText.tsx +70 -0
  635. package/src/components/RichText/RichTextAttachment.module.scss +82 -0
  636. package/src/components/RichText/RichTextAttachment.tsx +122 -0
  637. package/src/components/RichText/_prose.scss +79 -0
  638. package/src/components/RichText/attachmentIcon.tsx +24 -0
  639. package/src/components/RichText/engine/attachment.ts +154 -0
  640. package/src/components/RichText/engine/autolink.ts +64 -0
  641. package/src/components/RichText/engine/blockUnit.ts +174 -0
  642. package/src/components/RichText/engine/colorMarks.ts +85 -0
  643. package/src/components/RichText/engine/escape.ts +11 -0
  644. package/src/components/RichText/engine/fromHtml.ts +353 -0
  645. package/src/components/RichText/engine/fromMarkdown.ts +20 -0
  646. package/src/components/RichText/engine/inlines.ts +82 -0
  647. package/src/components/RichText/engine/listDepths.ts +32 -0
  648. package/src/components/RichText/engine/marks.ts +80 -0
  649. package/src/components/RichText/engine/mdToHtml.ts +178 -0
  650. package/src/components/RichText/engine/model.ts +157 -0
  651. package/src/components/RichText/engine/position.ts +118 -0
  652. package/src/components/RichText/engine/renderDoc.tsx +395 -0
  653. package/src/components/RichText/engine/renderLink.ts +15 -0
  654. package/src/components/RichText/engine/renderMention.ts +16 -0
  655. package/src/components/RichText/engine/safeHref.ts +22 -0
  656. package/src/components/RichText/engine/toHtml.ts +154 -0
  657. package/src/components/RichText/engine/toMarkdown.ts +97 -0
  658. package/src/components/RichText/engine/transforms.ts +434 -0
  659. package/src/components/RichText/imageMaxHeightStyle.ts +10 -0
  660. package/src/components/RichText/index.ts +31 -0
  661. package/src/components/RichTextEditor/RichTextAttachmentConfig.tsx +264 -0
  662. package/src/components/RichTextEditor/RichTextBlockControls.tsx +365 -0
  663. package/src/components/RichTextEditor/RichTextBlockMenu.tsx +162 -0
  664. package/src/components/RichTextEditor/RichTextColorMenu.tsx +115 -0
  665. package/src/components/RichTextEditor/RichTextEditor.module.scss +298 -0
  666. package/src/components/RichTextEditor/RichTextEditor.tsx +1910 -0
  667. package/src/components/RichTextEditor/RichTextImageResizer.tsx +131 -0
  668. package/src/components/RichTextEditor/RichTextLinkEditor.tsx +151 -0
  669. package/src/components/RichTextEditor/RichTextMentionMenu.tsx +99 -0
  670. package/src/components/RichTextEditor/RichTextToolbar.tsx +379 -0
  671. package/src/components/RichTextEditor/autolinkInput.ts +65 -0
  672. package/src/components/RichTextEditor/blockDrop.ts +9 -0
  673. package/src/components/RichTextEditor/blockReflow.ts +62 -0
  674. package/src/components/RichTextEditor/commands.ts +300 -0
  675. package/src/components/RichTextEditor/history.ts +93 -0
  676. package/src/components/RichTextEditor/icons.tsx +152 -0
  677. package/src/components/RichTextEditor/imageSize.ts +70 -0
  678. package/src/components/RichTextEditor/index.ts +5 -0
  679. package/src/components/RichTextEditor/input.ts +209 -0
  680. package/src/components/RichTextEditor/inputRules.ts +71 -0
  681. package/src/components/RichTextEditor/links.ts +103 -0
  682. package/src/components/RichTextEditor/mentionContext.ts +37 -0
  683. package/src/components/RichTextEditor/mentions.ts +28 -0
  684. package/src/components/RichTextEditor/preventSelectionLoss.ts +15 -0
  685. package/src/components/RichTextEditor/selection.ts +415 -0
  686. package/src/components/RichTextEditor/shortcuts.ts +44 -0
  687. package/src/components/RichTextEditor/useAnchoredFloating.ts +50 -0
  688. package/src/components/RichTextEditor/useAttachmentConfig.ts +134 -0
  689. package/src/components/RichTextEditor/useDismissOnOutsidePointerDown.ts +26 -0
  690. package/src/components/RichTextEditor/useLinkEditor.ts +107 -0
  691. package/src/components/RichTextEditor/useMention.ts +226 -0
  692. package/src/components/RichTextEditor/useOverlayRect.ts +73 -0
  693. package/src/components/RichTextEditor/useUpload.ts +246 -0
  694. package/src/components/Screen/Screen.module.scss +46 -0
  695. package/src/components/Screen/Screen.tokens.scss +20 -0
  696. package/src/components/Screen/Screen.tsx +97 -0
  697. package/src/components/Screen/index.ts +2 -0
  698. package/src/components/ScrollArea/ScrollArea.module.scss +39 -0
  699. package/src/components/ScrollArea/ScrollArea.tokens.scss +15 -0
  700. package/src/components/ScrollArea/ScrollArea.tsx +154 -0
  701. package/src/components/ScrollArea/index.ts +2 -0
  702. package/src/components/Select/Chip.tsx +45 -0
  703. package/src/components/Select/Empty.tsx +39 -0
  704. package/src/components/Select/Error.tsx +51 -0
  705. package/src/components/Select/HiddenInputs.tsx +44 -0
  706. package/src/components/Select/Listbox.tsx +557 -0
  707. package/src/components/Select/Loading.tsx +30 -0
  708. package/src/components/Select/Select.module.scss +512 -0
  709. package/src/components/Select/Select.tokens.scss +140 -0
  710. package/src/components/Select/Select.tsx +685 -0
  711. package/src/components/Select/Trigger.tsx +952 -0
  712. package/src/components/Select/context.ts +101 -0
  713. package/src/components/Select/emptyStateText.ts +19 -0
  714. package/src/components/Select/index.ts +9 -0
  715. package/src/components/Select/useSelectState.ts +157 -0
  716. package/src/components/Select/utils-types.ts +7 -0
  717. package/src/components/Select/utils.ts +122 -0
  718. package/src/components/SettingRow/SettingRow.module.scss +166 -0
  719. package/src/components/SettingRow/SettingRow.tokens.scss +37 -0
  720. package/src/components/SettingRow/SettingRow.tsx +242 -0
  721. package/src/components/SettingRow/index.ts +7 -0
  722. package/src/components/Skeleton/Skeleton.module.scss +55 -0
  723. package/src/components/Skeleton/Skeleton.tokens.scss +17 -0
  724. package/src/components/Skeleton/Skeleton.tsx +131 -0
  725. package/src/components/Skeleton/index.ts +4 -0
  726. package/src/components/Skeleton/useSkeletonVisibility.ts +95 -0
  727. package/src/components/Slider/Slider.module.scss +293 -0
  728. package/src/components/Slider/Slider.tokens.scss +98 -0
  729. package/src/components/Slider/Slider.tsx +624 -0
  730. package/src/components/Slider/index.ts +9 -0
  731. package/src/components/SlotGrid/SlotGrid.module.scss +61 -0
  732. package/src/components/SlotGrid/SlotGrid.tokens.scss +21 -0
  733. package/src/components/SlotGrid/SlotGrid.tsx +111 -0
  734. package/src/components/SlotGrid/index.ts +2 -0
  735. package/src/components/SocialButton/SocialButton.module.scss +4 -0
  736. package/src/components/SocialButton/SocialButton.tsx +40 -0
  737. package/src/components/SocialButton/index.ts +2 -0
  738. package/src/components/Sortable/Sortable.module.scss +177 -0
  739. package/src/components/Sortable/Sortable.tokens.scss +27 -0
  740. package/src/components/Sortable/Sortable.tsx +572 -0
  741. package/src/components/Sortable/containerAwareCollision.ts +125 -0
  742. package/src/components/Sortable/index.ts +13 -0
  743. package/src/components/SortableGroup/SortableGroup.module.scss +23 -0
  744. package/src/components/SortableGroup/SortableGroup.tsx +315 -0
  745. package/src/components/SortableGroup/index.ts +4 -0
  746. package/src/components/SortableGroup/moveSortableItem.ts +39 -0
  747. package/src/components/Sparkline/Sparkline.module.scss +28 -0
  748. package/src/components/Sparkline/Sparkline.tokens.scss +7 -0
  749. package/src/components/Sparkline/Sparkline.tsx +133 -0
  750. package/src/components/Sparkline/index.ts +2 -0
  751. package/src/components/Sparkline/sparklineModel.ts +119 -0
  752. package/src/components/Split/Split.module.scss +158 -0
  753. package/src/components/Split/Split.tokens.scss +10 -0
  754. package/src/components/Split/Split.tsx +231 -0
  755. package/src/components/Split/index.ts +2 -0
  756. package/src/components/Stack/Stack.module.scss +67 -0
  757. package/src/components/Stack/Stack.tokens.scss +11 -0
  758. package/src/components/Stack/Stack.tsx +100 -0
  759. package/src/components/Stack/index.ts +2 -0
  760. package/src/components/StagePath/StagePath.module.scss +255 -0
  761. package/src/components/StagePath/StagePath.tokens.scss +43 -0
  762. package/src/components/StagePath/StagePath.tsx +176 -0
  763. package/src/components/StagePath/index.ts +2 -0
  764. package/src/components/StatGroup/StatGroup.module.scss +6 -0
  765. package/src/components/StatGroup/StatGroup.tsx +70 -0
  766. package/src/components/StatGroup/StatGroupContext.ts +10 -0
  767. package/src/components/StatGroup/StatTile.module.scss +99 -0
  768. package/src/components/StatGroup/StatTile.tokens.scss +35 -0
  769. package/src/components/StatGroup/StatTile.tsx +143 -0
  770. package/src/components/StatGroup/index.ts +4 -0
  771. package/src/components/Sticky/Sticky.module.scss +98 -0
  772. package/src/components/Sticky/Sticky.tokens.scss +17 -0
  773. package/src/components/Sticky/Sticky.tsx +103 -0
  774. package/src/components/Sticky/index.ts +2 -0
  775. package/src/components/Switch/Switch.module.scss +182 -0
  776. package/src/components/Switch/Switch.tokens.scss +59 -0
  777. package/src/components/Switch/Switch.tsx +214 -0
  778. package/src/components/Switch/index.ts +2 -0
  779. package/src/components/Table/Table.module.scss +308 -0
  780. package/src/components/Table/Table.tokens.scss +79 -0
  781. package/src/components/Table/Table.tsx +381 -0
  782. package/src/components/Table/index.ts +15 -0
  783. package/src/components/TableOfContents/TableOfContents.module.scss +83 -0
  784. package/src/components/TableOfContents/TableOfContents.tokens.scss +26 -0
  785. package/src/components/TableOfContents/TableOfContents.tsx +192 -0
  786. package/src/components/TableOfContents/activeSection.ts +18 -0
  787. package/src/components/TableOfContents/index.ts +2 -0
  788. package/src/components/Tabs/Tabs.module.scss +359 -0
  789. package/src/components/Tabs/Tabs.tokens.scss +50 -0
  790. package/src/components/Tabs/Tabs.tsx +538 -0
  791. package/src/components/Tabs/index.ts +2 -0
  792. package/src/components/Text/Text.module.scss +115 -0
  793. package/src/components/Text/Text.tokens.scss +55 -0
  794. package/src/components/Text/Text.tsx +197 -0
  795. package/src/components/Text/index.ts +2 -0
  796. package/src/components/Textarea/Textarea.module.scss +94 -0
  797. package/src/components/Textarea/Textarea.tokens.scss +48 -0
  798. package/src/components/Textarea/Textarea.tsx +246 -0
  799. package/src/components/Textarea/index.ts +2 -0
  800. package/src/components/Thread/Thread.module.scss +161 -0
  801. package/src/components/Thread/Thread.tokens.scss +32 -0
  802. package/src/components/Thread/Thread.tsx +176 -0
  803. package/src/components/Thread/index.ts +2 -0
  804. package/src/components/TimeField/TimeField.module.scss +232 -0
  805. package/src/components/TimeField/TimeField.tsx +793 -0
  806. package/src/components/TimeField/index.ts +2 -0
  807. package/src/components/Timeline/Timeline.module.scss +58 -0
  808. package/src/components/Timeline/Timeline.tokens.scss +13 -0
  809. package/src/components/Timeline/Timeline.tsx +53 -0
  810. package/src/components/Timeline/index.ts +2 -0
  811. package/src/components/Title/Title.module.scss +84 -0
  812. package/src/components/Title/Title.tokens.scss +37 -0
  813. package/src/components/Title/Title.tsx +150 -0
  814. package/src/components/Title/index.ts +2 -0
  815. package/src/components/Toast/Toast.module.scss +299 -0
  816. package/src/components/Toast/Toast.tokens.scss +65 -0
  817. package/src/components/Toast/Toast.tsx +122 -0
  818. package/src/components/Toast/ToastViewport.tsx +166 -0
  819. package/src/components/Toast/api.ts +175 -0
  820. package/src/components/Toast/index.ts +5 -0
  821. package/src/components/Toast/store.ts +133 -0
  822. package/src/components/Toast/useToastTimer.ts +94 -0
  823. package/src/components/Tooltip/Tooltip.module.scss +99 -0
  824. package/src/components/Tooltip/Tooltip.tokens.scss +23 -0
  825. package/src/components/Tooltip/Tooltip.tsx +341 -0
  826. package/src/components/Tooltip/index.ts +2 -0
  827. package/src/components/TopBar/TopBar.module.scss +235 -0
  828. package/src/components/TopBar/TopBar.tokens.scss +79 -0
  829. package/src/components/TopBar/TopBar.tsx +87 -0
  830. package/src/components/TopBar/TopBarEnd.tsx +20 -0
  831. package/src/components/TopBar/TopBarIconButton.tsx +86 -0
  832. package/src/components/TopBar/TopBarSearch.tsx +319 -0
  833. package/src/components/TopBar/TopBarSearchPanel.tsx +268 -0
  834. package/src/components/TopBar/TopBarStart.tsx +20 -0
  835. package/src/components/TopBar/index.ts +11 -0
  836. package/src/components/Tour/Spotlight.tsx +121 -0
  837. package/src/components/Tour/Tour.module.scss +210 -0
  838. package/src/components/Tour/Tour.tokens.scss +58 -0
  839. package/src/components/Tour/Tour.tsx +617 -0
  840. package/src/components/Tour/index.ts +2 -0
  841. package/src/components/Tour/useTourTarget.ts +115 -0
  842. package/src/components/TreeView/TreeView.module.scss +268 -0
  843. package/src/components/TreeView/TreeView.tokens.scss +36 -0
  844. package/src/components/TreeView/TreeView.tsx +536 -0
  845. package/src/components/TreeView/index.ts +3 -0
  846. package/src/components/TreeView/treeModel.ts +224 -0
  847. package/src/components/VisuallyHidden/VisuallyHidden.module.scss +7 -0
  848. package/src/components/VisuallyHidden/VisuallyHidden.tsx +29 -0
  849. package/src/components/VisuallyHidden/index.ts +2 -0
  850. package/src/components/WidgetPreview/WidgetPreview.module.scss +11 -0
  851. package/src/components/WidgetPreview/WidgetPreview.tokens.scss +26 -0
  852. package/src/components/WidgetPreview/WidgetPreview.tsx +36 -0
  853. package/src/components/WidgetPreview/WidgetShape.module.scss +140 -0
  854. package/src/components/WidgetPreview/WidgetShape.tsx +159 -0
  855. package/src/components/WidgetPreview/index.ts +2 -0
  856. package/src/components/_internal/Trend.tsx +27 -0
  857. package/src/components/_internal/choiceTile.scss +74 -0
  858. package/src/components/_internal/collapse.scss +10 -0
  859. package/src/components/_internal/collapse.ts +58 -0
  860. package/src/components/_internal/currency.ts +257 -0
  861. package/src/components/_internal/dragAnnouncements.ts +200 -0
  862. package/src/components/_internal/fieldWiring.ts +225 -0
  863. package/src/components/_internal/gridSpan.ts +54 -0
  864. package/src/components/_internal/isoDay.ts +35 -0
  865. package/src/components/_internal/letterKey.ts +26 -0
  866. package/src/components/_internal/overlay/focusRestore.ts +21 -0
  867. package/src/components/_internal/overlay/index.ts +12 -0
  868. package/src/components/_internal/overlay/tourHold.ts +23 -0
  869. package/src/components/_internal/overlay/useFocusTrap.ts +120 -0
  870. package/src/components/_internal/overlay/useInOverlay.ts +81 -0
  871. package/src/components/_internal/overlay/useOverlayStack.ts +232 -0
  872. package/src/components/_internal/overlay/useScrollLock.ts +112 -0
  873. package/src/components/_internal/refs.ts +48 -0
  874. package/src/components/_internal/statusColor.ts +26 -0
  875. package/src/components/_internal/timeUtils.ts +23 -0
  876. package/src/components/_internal/useAsyncOptions.ts +145 -0
  877. package/src/components/_internal/useClippedTooltip.ts +50 -0
  878. package/src/components/_internal/useControllableState.ts +50 -0
  879. package/src/components.manifest.json +1141 -0
  880. package/src/hooks/useBelowBreakpoint.ts +58 -0
  881. package/src/i18n/I18nProvider.tsx +52 -0
  882. package/src/i18n/LocaleProvider.tsx +17 -0
  883. package/src/i18n/en.ts +648 -0
  884. package/src/i18n/format.ts +71 -0
  885. package/src/i18n/index.ts +13 -0
  886. package/src/i18n/messages.ts +1275 -0
  887. package/src/i18n/ru.ts +677 -0
  888. package/src/i18n/useLocale.ts +19 -0
  889. package/src/i18n/useTranslation.ts +58 -0
  890. package/src/index.ts +913 -0
  891. package/src/palette/index.ts +2 -0
  892. package/src/palette/palette.ts +108 -0
  893. package/src/styles/dark.scss +4 -0
  894. package/src/styles/global.scss +7 -0
  895. package/src/styles/mixins.scss +143 -0
  896. package/src/styles/reset.scss +78 -0
  897. package/src/styles/tokens.scss +18 -0
  898. package/src/styles/typography.scss +44 -0
@@ -0,0 +1,97 @@
1
+ # `<ChatLauncher>` — floating launcher + non-modal chat panel
2
+
3
+ A fixed bottom-end icon button that toggles a chat panel above it. The panel is a `role="dialog"` **without** `aria-modal`: no focus trap, backdrop or scroll lock, so the page stays usable while chatting. It is portaled to `document.body` and layered on `--z-chat-launcher` (above app chrome, below dropdowns, popovers and modals). Controlled only: `open` + `onOpenChange`.
4
+
5
+ ```tsx
6
+ import { useRef, useState } from 'react';
7
+ import { Sparkles, Send } from 'lucide-react';
8
+ import {
9
+ Button,
10
+ ChatBubble,
11
+ ChatLauncher,
12
+ Cluster,
13
+ Constrain,
14
+ Textarea,
15
+ } from '@eocrm/design-system';
16
+
17
+ const [open, setOpen] = useState(false);
18
+ const composerRef = useRef<HTMLTextAreaElement>(null);
19
+
20
+ <ChatLauncher
21
+ label="Ask Scout"
22
+ title="Scout"
23
+ icon={<Sparkles size={20} aria-hidden="true" />}
24
+ open={open}
25
+ onOpenChange={setOpen}
26
+ initialFocusRef={composerRef}
27
+ footer={
28
+ // One row: the Textarea grows (autoGrow, up to 4 rows), the send button keeps its size.
29
+ <Cluster wrap={false} gap="sm" align="end">
30
+ <Constrain flex="grow">
31
+ <Textarea
32
+ ref={composerRef}
33
+ aria-label="Message"
34
+ minRows={1}
35
+ maxRows={4}
36
+ onKeyDown={(e) => {
37
+ // Enter sends, Shift+Enter is a newline; never send mid-IME-composition.
38
+ if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
39
+ e.preventDefault(); // no stray newline after the draft is cleared
40
+ send(e);
41
+ }
42
+ }}
43
+ />
44
+ </Constrain>
45
+ <Button variant="primary" iconOnly aria-label="Send" onClick={send}>
46
+ <Send size={16} aria-hidden="true" />
47
+ </Button>
48
+ </Cluster>
49
+ }
50
+ >
51
+ <ChatBubble.List aria-label="Conversation with Scout">
52
+ {messages.map((m) => (
53
+ <ChatBubble key={m.id} from={m.from}>
54
+ {m.text}
55
+ </ChatBubble>
56
+ ))}
57
+ {waiting && <ChatBubble from="assistant" pending />}
58
+ </ChatBubble.List>
59
+ </ChatLauncher>;
60
+ ```
61
+
62
+ <!-- props:start -->
63
+
64
+ ## Props
65
+
66
+ <!-- prettier-ignore -->
67
+ | Prop | Type | Required | Description |
68
+ |---|---|---|---|
69
+ | `label` | `string` | yes | Accessible name of the launcher button — names the assistant ("Ask Scout"). |
70
+ | `open` | `boolean` | yes | Whether the panel is open. Controlled only: pair with `onOpenChange`. |
71
+ | `onOpenChange` | `(open: boolean) => void` | yes | Called with the requested state: launcher toggle, close button, Escape. |
72
+ | `icon` | `ReactNode` | yes | Launcher glyph (e.g. a lucide icon with `aria-hidden`). |
73
+ | `title` | `ReactNode` | yes | Panel header text; also the dialog's accessible name. |
74
+ | `children` | `ReactNode` | yes | Scrolling body — usually a `<ChatBubble.List>`. Stays mounted while closed. |
75
+ | `footer` | `ReactNode` | no | Pinned area below the body — the composer (`Textarea` + send `Button`). |
76
+ | `initialFocusRef` | `RefObject<HTMLElement \| null>` | no | Element focused when the panel opens (point it at the composer). Default: the panel itself. Focus already inside the panel is not moved. |
77
+ | …native | | | plus native `<div>` attributes |
78
+
79
+ <!-- props:end -->
80
+
81
+ - **Stays mounted when closed.** The panel gets `hidden`; children are not unmounted, so an uncontrolled draft and the scroll position survive close/reopen.
82
+ - **Sticks to the bottom.** While open, the body follows new content if the user was already at (within 24px of) the bottom; a user scrolled up to read is not moved. Reopening scrolls to the bottom if it was at the bottom when closed (replies that arrived meanwhile are in view), otherwise restores the saved position.
83
+ - **Focus.** Opening moves focus to `initialFocusRef` (point it at the composer), else to the panel; focus already inside is left alone. Escape (inside the panel or on the launcher) and the close button close it and return focus to the launcher. A programmatic close returns focus only if it was inside the chat. An Escape a child already handled (`event.preventDefault()`, e.g. a menu closing itself) is ignored.
84
+ - The close button's name is the `chatLauncher.close` i18n key (no label prop). `label` names your assistant and is the launcher's accessible name.
85
+ - `ref`, `className`, `id` and other native attributes go to the panel; `role`, `hidden`, `aria-labelledby` and `tabIndex` always win.
86
+ - **Closed means silent.** While closed, the `role="log"` sits in a `hidden` subtree, so replies that arrive then are not announced (and there is no unread badge). On reopen they are in view at the bottom if the chat was left scrolled to the bottom; otherwise the saved scroll position is restored. If a reply must reach the user while the chat is closed, announce it yourself (e.g. a `toast` — with the viewport at a top position, see below).
87
+ - **Toasts share the corner.** `ToastViewport` defaults to `bottom-right`, above the launcher's layer, so toasts would cover the launcher and the open panel's composer. On a page with a ChatLauncher, use `<ToastViewport position="top-right" />` (or another top position).
88
+ - Size: `--chat-launcher-panel-width` (380px) × `--chat-launcher-panel-height` (560px), capped to the viewport minus `--chat-launcher-inset`.
89
+
90
+ **When NOT to use:** page-level dialogs or forms that must block the page (use `Modal` / `Drawer`); content anchored to a trigger in the page (use `Popover`); notifications (use `toast`); more than one launcher per page.
91
+
92
+ **Anti-patterns**
93
+
94
+ - Unmounting `<ChatLauncher>` when closed: you lose the draft and scroll position the component keeps for you.
95
+ - Positioning it yourself (wrapping it in a fixed container, adding `className` with `position`/`bottom`): it already owns the viewport corner.
96
+ - Putting the composer in `children`: it would scroll away. Use `footer`.
97
+ - Bubbles without `ChatBubble.List`: new replies won't be announced.
@@ -0,0 +1,55 @@
1
+ # `<Checkbox>` — checkbox with native input + custom paint
2
+
3
+ ```tsx
4
+ <Checkbox label="I agree" />
5
+ <Checkbox checked={agreed} onChange={setAgreed} label="Subscribe" />
6
+ <Checkbox indeterminate checked={allSelected} onChange={selectAll} aria-label="Select all" />
7
+
8
+ // "Select all" pattern
9
+ <Checkbox
10
+ checked={allSelected}
11
+ indeterminate={someSelected && !allSelected}
12
+ onChange={(next) => (next ? selectAll() : selectNone())}
13
+ aria-label="Select all rows"
14
+ />
15
+
16
+ // Icon-only (no visible label)
17
+ <Checkbox aria-label="Select row" checked={isSelected} onChange={setIsSelected} />
18
+ ```
19
+
20
+ <!-- props:start -->
21
+
22
+ ## Props
23
+
24
+ <!-- prettier-ignore -->
25
+ | Prop | Type | Required | Description |
26
+ |---|---|---|---|
27
+ | `size` | `'sm' \| 'md' \| 'lg'` | no | Box diameter + label type scale. Defaults to `'md'`. - `'sm'` — 14px box, font-size-sm label. Dense tables, inline filters. - `'md'` — 16px box, font-size-md label. Default. - `'lg'` — 20px box, font-size-lg label. Hero forms, mobile-friendly. Note: shadows the native HTML `<input size>` attribute (which on checkboxes is meaningless anyway). |
28
+ | `checked` | `boolean` | no | Controlled checked state. Pair with `onChange`. Omit (with optional `defaultChecked`) for uncontrolled use. |
29
+ | `defaultChecked` | `boolean` | no | Initial checked state for uncontrolled use. Defaults to `false`. |
30
+ | `indeterminate` | `boolean` | no | Indeterminate (mixed) visual + a11y state. Independent of `checked` — the box paints with a dash icon and `input.indeterminate = true` so AT announces "mixed". Consumer drives this based on partial selection (e.g., a "select all" header where some-but-not-all rows are selected). When the user clicks an indeterminate checkbox, the native change event fires with the next `checked` value (`true` if it was `false`). The consumer typically responds by clearing `indeterminate`. |
31
+ | `label` | `ReactNode` | no | Optional label rendered next to the box. The whole `<label>` is the click target. Omit for icon-only checkboxes (e.g., a DataTable row selector) — pass `aria-label` instead. |
32
+ | `invalid` | `boolean` | no | Toggles the error visual + sets `aria-invalid="true"`. Pair with a visible error message and `aria-describedby`. |
33
+ | `color` | `PaletteColor` | no | Optional palette color for the checked / indeterminate fill. When set, the filled state uses the palette color's fg token instead of `--color-accent`. Use to color-tag checkbox groups (per-team, per-status, per-category). Default unchanged (accent blue). The focus ring, hover border, and unchecked state remain accent-colored regardless of `color` — only the checked / indeterminate fill is affected. |
34
+ | `onChange` | `((checked: boolean, event: ChangeEvent<HTMLInputElement, Element>) => void)` | no | Fires on every change. Receives the next checked state AND the native event so consumers can do `event.preventDefault()`, read modifier keys, etc. |
35
+ | …native | | | plus native `<input>` attributes |
36
+
37
+ <!-- props:end -->
38
+
39
+ - Native `<input type='checkbox'>` is visually hidden but stays in tab order + AT tree — keyboard, screen reader, form submission, RHF/Zod, autofill all work for free.
40
+ - Native HTML attrs flow through (`name`, `value`, `required`, `form`, `autoFocus`, etc.). `FormData.getAll(name)` returns the array of checked values for same-`name` checkboxes.
41
+ - forwardRef points at the native `<input>` so consumers can `.focus()` or programmatically set `.indeterminate`.
42
+
43
+ - The native input owns all a11y (keyboard, screen reader, form submission); the custom paint owns the look. States: checked / unchecked / indeterminate / disabled / invalid. `aria-disabled="true"` gets the disabled look and ignores clicks/Space but stays focusable — use it for a busy state (a write in flight) where real `disabled` would drop keyboard focus.
44
+
45
+ #### When NOT to use
46
+
47
+ - ❌ A single binary on/off setting that applies immediately → `Switch`.
48
+ - ❌ One-of-many choice from a fixed set → `Radio`.
49
+ - ❌ Multi-select from a long list → `<Select multi>`.
50
+
51
+ #### Anti-patterns
52
+
53
+ - ❌ Treating `indeterminate` as a third value. It is a display flag; `checked` is still the underlying boolean.
54
+ - ❌ Wrapping the checkbox in your own `<label>`. It is already wrapped; an outer `<label>` nests two and breaks the click contract.
55
+ - ❌ Omitting `label` AND `aria-label`. Screen readers announce just "checkbox" with no context.
@@ -0,0 +1,53 @@
1
+ # `<CircularProgress>` — circular progress / spinner
2
+
3
+ ```tsx
4
+ <CircularProgress value={45} /> // donut, 32px default
5
+ <CircularProgress value={75} label /> // centered "75%"
6
+ <CircularProgress /> // indeterminate spinner (the "Loader" use case)
7
+ <CircularProgress size="sm" /> // 16px inline spinner next to a button
8
+ <CircularProgress tone="success" value={100} /> // green full circle
9
+ <CircularProgress size="lg" value={80} tone="success" label /> // stat-card-style donut
10
+ <CircularProgress value={3} max={10} label={`3 / 10`} /> // custom label ("n of N", a status word)
11
+
12
+ // Canonical inline loader next to a button
13
+ <Cluster gap="sm">
14
+ <Button>Save</Button>
15
+ <CircularProgress size="sm" aria-label="Saving" />
16
+ </Cluster>
17
+ ```
18
+
19
+ <!-- props:start -->
20
+
21
+ ## Props
22
+
23
+ <!-- prettier-ignore -->
24
+ | Prop | Type | Required | Description |
25
+ |---|---|---|---|
26
+ | `value` | `number` | no | Current progress value. Omit (or pass `undefined`) to render the indeterminate spinning animation. Two-channel behavior for out-of-range and degenerate values: - **Visual arc** is clamped to [0%, 100%]. Values outside [0, max] render at the nearest valid bound. - **ARIA `aria-valuenow`** reports the raw number so consumer bugs (a value drifting past max) surface in audits and SR announcements. Degenerate inputs fall back to the indeterminate spinner: `NaN`, `Infinity`, `-Infinity`, and the case where `max <= 0`. This is the common file-upload race condition (`bytes_uploaded / total_bytes` before `total_bytes` is known produces NaN). |
27
+ | `max` | `number` | no | Upper bound. Defaults to `100`. Consumers using fraction values (0.0–1.0) pass `max={1}`. Consumers tracking a count ("3 of 10") pass `max={10}`. |
28
+ | `size` | `'sm' \| 'md' \| 'lg'` | no | Diameter + stroke pairing. - `sm` — 16px diameter, 2px stroke (inline next to a button — also the shape to use for "Saving…" loading affordances; pass no `value` for the indeterminate spinner.) - `md` — 32px diameter, 3px stroke (default — near a heading) - `lg` — 56px diameter, 4px stroke (page-level loader) |
29
+ | `tone` | `'default' \| 'success' \| 'warning' \| 'danger'` | no | Stroke color tone. Defaults to `'default'` (accent blue). Same vocab as `<Progress>`. Indeterminate ignores tone — always accent. |
30
+ | `label` | `CircularProgressLabel` | no | Optional centered label. - `false` (default) — no label - `true` — render `{Math.round(percent)}%` centered. Auto-suppressed at `size='sm'` (16px circle has no room for text) AND when indeterminate. - `ReactNode` — render the node centered in BOTH modes. Still auto-suppressed at `size='sm'` regardless — the geometry doesn't change. |
31
+ | …native | | | plus native `<div>` attributes |
32
+
33
+ <!-- props:end -->
34
+
35
+ - Same prop vocabulary as `<Progress>` — `value?`, `max?`, `size`, `tone`, `label`.
36
+ - Same NaN/Infinity/max<=0 indeterminate fallback as `<Progress>`.
37
+ - Built as inline `<svg viewBox="0 0 36 36">` with two `<circle>` elements (track + fill). Determinate arc is driven by `stroke-dashoffset`; indeterminate is a CSS `rotate` animation on a partial arc.
38
+ - Centered `label` auto-suppressed at `size="sm"` (no room for text) AND when `label=true` on indeterminate. ReactNode labels still suppress at `size="sm"` regardless.
39
+ - `prefers-reduced-motion` disables the spin animation and shows a static accent ring.
40
+
41
+ - `role="progressbar"` is locked (can't be overridden via the `role` prop) and is announced to screen readers. Tracks known progress like `<Progress>` in a circular geometry, better for inline loading indicators and tight spaces; indeterminate mode is the canonical spinner.
42
+
43
+ #### When NOT to use
44
+
45
+ - ❌ Horizontal progress next to row content → `<Progress>` linear.
46
+ - ❌ Replacing `<Skeleton>` for loading placeholders. Skeleton implies "structure on its way"; CircularProgress implies "I'm working on it."
47
+ - ❌ A decorative icon: the `progressbar` role is announced.
48
+
49
+ #### Anti-patterns
50
+
51
+ - ❌ Hand-rolled spinning `<svg>` per page. `<CircularProgress />` indeterminate is the same visual, accessible and reduced-motion-aware.
52
+ - ❌ `<CircularProgress value={0}>` to render an empty circle. `value={0}` is determinate (0% done); the intent is usually indeterminate, so omit `value`.
53
+ - ❌ `<CircularProgress size="sm" label>` expecting centered text in a 16px circle. The label is auto-suppressed at `sm` by design; use `md` or `lg`.
@@ -0,0 +1,96 @@
1
+ # `<Cluster>` — horizontal layout that wraps
2
+
3
+ ```tsx
4
+ // Form footer:
5
+ <Cluster justify="end" gap="sm">
6
+ <Button variant="secondary">Cancel</Button>
7
+ <Button type="submit">Save</Button>
8
+ </Cluster>
9
+
10
+ // Toolbar (title left, actions right):
11
+ <Cluster justify="between" gap="md">
12
+ <h1>Users</h1>
13
+ <Cluster gap="sm">
14
+ <Button variant="secondary">Filter</Button>
15
+ <Button>Add user</Button>
16
+ </Cluster>
17
+ </Cluster>
18
+
19
+ // Tag list: wraps to multiple lines when narrow
20
+ <Cluster gap="xs">
21
+ {tags.map((t) => <Badge key={t.id} tone={t.tone}>{t.label}</Badge>)}
22
+ </Cluster>
23
+
24
+ // Pill left + two buttons right while they fit; buttons drop under the pill
25
+ // at full width (equal columns) when they don't — no breakpoint to guess:
26
+ <Cluster stretchWrapped gap="sm">
27
+ <Badge tone="info">In review</Badge>
28
+ <Grid columns={2} gap="sm">
29
+ <Button variant="secondary">Reject</Button>
30
+ <Button>Approve</Button>
31
+ </Grid>
32
+ </Cluster>
33
+
34
+ // Inline, inside a <button>/<a>/<label> where a <div> is invalid HTML —
35
+ // as="span" renders inline-flex (e.g. icon + label in a ButtonGroup.Item):
36
+ <ButtonGroup.Item value="list">
37
+ <Cluster as="span" gap="xs" align="center" wrap={false}>
38
+ <List size={14} aria-hidden />
39
+ List
40
+ </Cluster>
41
+ </ButtonGroup.Item>
42
+ ```
43
+
44
+ <!-- props:start -->
45
+
46
+ ## Props
47
+
48
+ <!-- prettier-ignore -->
49
+ | Prop | Type | Required | Description |
50
+ |---|---|---|---|
51
+ | `as` | `'div' \| 'span' \| 'section' \| 'aside'` | no | Element to render. Defaults to `'div'`. - `div` (default) — block-level flex container; right for nearly all uses. - `span` — renders `display: inline-flex`, for phrasing-content contexts where a block element is invalid HTML: inside `<button>` (e.g. a `ButtonGroup.Item` icon + label), `<a>`, or `<label>`. - `section` — only for a genuinely standalone, nameable region; pair with `aria-label`/`aria-labelledby` (an unnamed section is just a div to AT). - `aside` — exposes a `complementary` landmark to screen readers; label it, and never use it for mere visual grouping. `span` is the ONLY value valid inside `<button>`/`<a>`/`<label>` — `section` and `aside` are flow content and remain invalid HTML there. |
52
+ | `gap` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | no | Gap between children, in pixels: `xs` (4) / `sm` (8) / `md` (12, default) / `lg` (16) / `xl` (24) / `2xl` (32). |
53
+ | `justify` | `'start' \| 'center' \| 'end' \| 'between'` | no | Horizontal distribution. - `start` (default) — items at the start. - `center` — items centered. - `end` — items at the end. Canonical form-footer pattern. - `between` — first item at start, last at end, gap between. Canonical toolbar pattern (title left, actions right). |
54
+ | `align` | `'start' \| 'center' \| 'end' \| 'baseline'` | no | Vertical alignment. - `start` / `center` (default) / `end` / `baseline`. |
55
+ | `minWidth0` | `boolean` | no | Lets the container shrink below its content's intrinsic width (`min-width: 0`), so a `<Text truncate>` inside can ellipsize instead of being hard-cut by a clipping ancestor. Reach for it when this Cluster is an item of a **row** flex container (or a grid item) that clips — a `Calendar` `renderEvent` chip, a `Card.Header` row. Without it the flex default (`min-width: auto`) floors the container at its content's min-content width and the ellipsis never appears. That floor applies because a Cluster sets no `overflow` — per CSS Flexbox §4.5 a flex item keeps its automatic minimum size while its computed `overflow` is non-scrollable (`visible` or `clip`); the scrollable values (`hidden`/`auto`/`scroll`) drop it to `0`. That is why a `<Text truncate>` (overflow hidden) never needs this and a Cluster around it does. It is a **no-op** inside a column `Stack` (the automatic minimum size applies only on the flex MAIN axis, so there is no horizontal floor), and inside a plain block or a table cell (the automatic minimum size applies to flex and grid ITEMS only, so `min-width: auto` is just `0` there). A table cell is a no-op for a different reason than it looks: auto table layout floors the cell at its content's min-content width regardless, so what makes text truncate there is `table-layout: fixed` or a `max-width` on the cell — not this prop. Opt-in rather than the default on purpose: a container that CAN shrink also VOLUNTEERS for shrink, so turning it on where the content is NOT truncatable (buttons, badges, icons) lets that content be clipped instead. Set it on the container whose text should give way, not on one holding controls. Related: `<Constrain flex="grow">` applies the same `min-width: 0` but also forces `flex: 1 1 0`, and renders a `<div>` — reach for this prop when you want only the shrink permission, or when you are inside a `<button>`/`<a>`/`<label>` where a `<div>` is invalid HTML. Default: `false`. |
56
+ | `maxWidthFull` | `boolean` | no | Caps the cluster at its container's width (`max-width: 100%`). Meant for `as="span"` inside running text: an inline cluster otherwise sizes to its content, so a `<Text as="span" truncate>` child with a long title makes the cluster overflow the paragraph instead of ellipsizing at the line edge. Combine with `wrap={false}` + a truncating child. The chip is one inline box: if the text before it leaves too little room on the line, the whole chip wraps to the next line, then ellipsizes at the full paragraph width. Phrasing-safe (still a span) — unlike `<Constrain maxWidth>`, which renders a `<div>` that is invalid inside `<p>`. For an entity link row (icon, KEY, title, status, adornments) prefer `<EntityChip truncate trailing={…}>`, which does exactly this. Default: `false`. |
57
+ | `stretchWrapped` | `boolean` | no | Content-driven switch between one row and stacked. While the children fit on one line they stay content-sized and the free space collects between the first child and the rest (the rest sit at the end). When they no longer fit, every child that wraps onto its own line stretches to fill that line. Canonical case: a status pill plus a `<Grid columns={2}>` of two buttons — pill left and buttons right while there is room, buttons dropping under the pill at full width (equal columns) when there is not. No breakpoint to guess, unlike `Grid collapseBelow`. How: an invisible `::before` spacer soaks up a shared line's slack, so the children there get a negligible share (at most 1px per 1000px of slack); a non-first child alone on a wrapped line takes the whole line. Children wrapped together share it. The spacer cancels its own gap, so it adds no width: the switch happens exactly when the content stops fitting, also with a single child or `as="span"`. Limits: `justify` is inert while this is on (the spacer owns the slack); `wrap={false}` makes it moot (nothing wraps); a consumer `order` on children breaks it — don't. A child's own `flex` (via its `className`) overrides the stretch for that child. "First child" means the first ELEMENT: a leading bare text node is not it — wrap text in an element (e.g. `<Text>`). Default: `false`. |
58
+ | `wrap` | `boolean` | no | Whether children wrap to additional lines when the container is narrow. - `true` (default) — natural for toolbars and tag lists. - `false` — use sparingly, when overflow is preferable to wrapping (e.g. inside a narrow table cell with fixed-width action buttons). |
59
+ | …native | | | plus native HTML attributes |
60
+
61
+ <!-- props:end -->
62
+
63
+ ```tsx
64
+ // Custom Calendar chip content that ellipsizes instead of hard-clipping.
65
+ // The chip is a <button> with overflow: hidden, so the Cluster is a flex item:
66
+ // as="span" for valid HTML, minWidth0 so it can actually shrink.
67
+ <Calendar
68
+ events={events}
69
+ renderEvent={(event) => (
70
+ <Cluster as="span" gap="xs" align="center" wrap={false} minWidth0>
71
+ <Dot color="violet" />
72
+ <Text as="span" size="inherit" truncate>
73
+ {event.title}
74
+ </Text>
75
+ </Cluster>
76
+ )}
77
+ />
78
+ ```
79
+
80
+ Use it for button rows, toolbars, tag lists and breadcrumbs: anywhere you would otherwise write `display: flex; gap: ...; flex-wrap: wrap`.
81
+
82
+ #### When NOT to use
83
+
84
+ - ❌ Aligned columns of equal width → `<Grid>`. Cluster wraps unpredictably at narrow widths and isn't a column system.
85
+ - ❌ Content that must never wrap: `wrap={false}` if you must, but reconsider whether that's truly required on narrow viewports.
86
+
87
+ #### Anti-patterns
88
+
89
+ - ❌ Cluster as a 2-column layout. Use `<Grid columns={2}>` for "two columns, always".
90
+ - ❌ `style={{ marginLeft: 'auto' }}` on a child to push it right. Use `justify="between"` (with a sibling on the left) or split into two Clusters in the parent.
91
+ - ❌ `minWidth0` on a Cluster holding buttons or badges: it lets the container shrink, so non-truncatable content gets clipped. Use it on the container whose TEXT should give way.
92
+ - ❌ `Grid collapseBelow` with a guessed pixel threshold to stack a pill + buttons row. The right width depends on content and locale; use `stretchWrapped` and let the content decide.
93
+ - ❌ `stretchWrapped` with `justify`, `wrap={false}` or a child `order`: `justify` is inert, `wrap={false}` never wraps so nothing switches, `order` breaks the spacer.
94
+ - ❌ `stretchWrapped` with a leading bare text node: the first child is the first ELEMENT, so the text is not treated as it. Wrap the text in an element (e.g. `<Text>`).
95
+ - A child's own `flex` (via its `className`) overrides `stretchWrapped` for that child; that child no longer stretches when it wraps.
96
+ - ❌ `as="span"` as a general styling hook. Reach for it only when block content is invalid HTML (inside `<button>`, `<a>`, `<label>`); in normal flow the default block `div` is what you want.
@@ -0,0 +1,35 @@
1
+ # `<Code>` — inline `<code>` chip
2
+
3
+ ```tsx
4
+ <Text>Use <Code>npm install</Code> to add deps.</Text>
5
+ <Code tone="danger">--no-verify</Code>
6
+ ```
7
+
8
+ <!-- props:start -->
9
+
10
+ ## Props
11
+
12
+ <!-- prettier-ignore -->
13
+ | Prop | Type | Required | Description |
14
+ |---|---|---|---|
15
+ | `tone` | `'default' \| 'muted' \| 'accent' \| 'danger'` | no | Color tone for the code text. The chip background stays the same; only the text color changes. - `default` — `--color-fg` - `muted` — `--color-fg-muted` - `accent` — `--color-accent` - `danger` — `--color-danger` |
16
+ | `children` | `ReactNode` | yes | Code content. |
17
+ | …native | | | plus native HTML attributes |
18
+
19
+ <!-- props:end -->
20
+
21
+ - **Inline only.** Multi-line or syntax-highlighted code belongs in [`CodeBlock`](CodeBlock.md).
22
+
23
+ - The chip background is intentionally `--color-bg-muted` so it subordinates to body text.
24
+
25
+ #### When NOT to use
26
+
27
+ - ❌ Block-level code with multiple lines or syntax highlighting.
28
+ - ❌ Action triggers that LOOK like code (`<Button variant="ghost">`).
29
+ - ❌ A substitute for `<kbd>` (keyboard input rendering; not yet shipped).
30
+
31
+ #### Anti-patterns
32
+
33
+ - ❌ `<Code>multi-line\nblock</Code>`: Code is inline only; the chip background doesn't extend across newlines. Use `CodeBlock`, or `<pre><Code>...</Code></pre>` for a static block inside the library.
34
+ - ❌ `<Code style={{ background: '#xxx' }}>`: a different background is a token-vocabulary conversation.
35
+ - ❌ Wrapping a `<Button>` or `<Link>` in `<Code>` to style it code-like. Code is for content semantics (it IS code), not visual styling.
@@ -0,0 +1,67 @@
1
+ # `<CodeBlock>` — highlighted, copyable code sample
2
+
3
+ ```tsx
4
+ <CodeBlock language="json" code={'{ "id": 42 }'} />;
5
+
6
+ // Persist the choice: ONE shared state for every block of the group (see below).
7
+ const [lang, setLang] = usePersistedSample('api-lang');
8
+
9
+ <CodeBlock samples={samples} activeSampleId={lang} onSampleChange={setLang} />;
10
+ ```
11
+
12
+ ```tsx
13
+ // Call once at page/layout level and pass the same pair to every block.
14
+ function usePersistedSample(storageKey: string) {
15
+ const [id, setId] = useState<string>();
16
+ // Client-only read in an effect so server and client render the same first frame.
17
+ useEffect(() => {
18
+ try {
19
+ setId(localStorage.getItem(storageKey) ?? undefined);
20
+ } catch {} // storage blocked
21
+ }, [storageKey]);
22
+ const set = (next: string) => {
23
+ setId(next);
24
+ try {
25
+ localStorage.setItem(storageKey, next);
26
+ } catch {} // storage blocked
27
+ };
28
+ return [id, set] as const;
29
+ }
30
+ ```
31
+
32
+ <!-- props:start -->
33
+
34
+ ## Props
35
+
36
+ <!-- prettier-ignore -->
37
+ | Prop | Type | Required | Description |
38
+ |---|---|---|---|
39
+ | `code` | `string` | no | Single-sample mode: the source. Ignored when `samples` is set. |
40
+ | `language` | `(string & {}) \| CodeBlockLanguage` | no | Single-sample mode: grammar for highlighting — `'json'`, `'bash'`, `'javascript'`, `'typescript'`, `'python'`, `'go'`, `'kotlin'`, `'java'`, `'csharp'` or `'rust'` (aliases `sh`, `shell`, `js`, `ts`, `py`, `golang`, `kt`, `kts`, `cs`, `c#`, `rs`). Anything else renders as plain text. |
41
+ | `title` | `string` | no | Header label in single-sample mode. Defaults to a one-item `samples` entry's `label`, else the language name (`JSON`, `Bash`…), else "Plain text". Also names the body (single-sample) or the sample tabs (multi-sample). |
42
+ | `samples` | `readonly CodeBlockSample[]` | no | Multi-sample mode: 2+ samples render as tabs. Wins over `code`/`language`. |
43
+ | `activeSampleId` | `string` | no | Controlled active sample id. Wins over `syncKey` and the user's own pick. |
44
+ | `defaultSampleId` | `string` | no | Initial sample id when uncontrolled and nothing is synced. Defaults to the first sample. |
45
+ | `onSampleChange` | `((id: string) => void)` | no | Called with the new id when the user picks a tab in THIS block (not when a synced block changes it). |
46
+ | `syncKey` | `string` | no | Blocks sharing a `syncKey` switch together: picking `py` in one selects `py` in every block with the same key that has a `py` sample. In-memory, per page load; for uncontrolled blocks (controlled blocks sharing one state don't need it). |
47
+ | `highlightLines` | `CodeBlockHighlightLines` | no | Lines to emphasise (visual only — explain the emphasis in prose). |
48
+ | `maxHeight` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | no | Caps the body height; the body scrolls inside it (it is always a focusable, named region). `xs` 200px · `sm` 320px · `md` 448px · `lg` 640px · `xl` 800px. Default: no cap. |
49
+ | `defaultWrap` | `boolean` | no | Initial state of the wrap-lines toggle. Default `false` (long lines scroll horizontally). |
50
+ | …native | | | plus native `<div>` attributes |
51
+
52
+ <!-- props:end -->
53
+
54
+ - **Languages:** `json`, `bash`, `javascript`, `typescript`, `python`, `go`, `kotlin`, `java`, `csharp`, `rust` (aliases `sh`, `shell`, `js`, `ts`, `py`, `golang`, `kt`, `kts`, `cs`, `c#`, `rs`). Unknown languages render as plain text.
55
+ - **Header label** is `title`, then the label of a one-item `samples` array, then the language name, then "Plain text".
56
+ - **Accessibility:** the code body is always keyboard-focusable. Single mode is a `role="group"` named by the header label (`title`, else the one-item sample label, else the language name, else "Plain text"); multi mode is a tabpanel labelled by its tab, and `title` names the tablist.
57
+ - **`highlightLines` is visual only.** Explain the emphasis in the surrounding prose.
58
+ - **`syncKey` is in-memory** (per page load) and is for UNCONTROLLED blocks. The library never touches storage. To persist, own the choice as ONE piece of state shared by every block of the group (the `usePersistedSample` hook above, called once at page/layout level, or React context) and give every block `activeSampleId={lang}` + `onSampleChange={setLang}`. Controlled blocks don't need `syncKey`: the shared state keeps them in step. Do NOT give each block its own `useState` plus `syncKey`: the controlled `activeSampleId` wins over `syncKey`, so return visits would leave blocks out of sync. `defaultSampleId` is only the static initial choice.
59
+ - **Sample ids** are used in DOM ids: don't put spaces in them.
60
+ - **Active sample precedence:** controlled `activeSampleId`, then `syncKey`, then the user's own pick, then `defaultSampleId`, then the first sample. An `activeSampleId` that matches no sample id falls through to the next rule.
61
+ - Renders identically on the server (SSR-safe).
62
+
63
+ #### When NOT to use
64
+
65
+ - ❌ Inline code inside a sentence: use `Code`.
66
+ - ❌ Editable code: use `LiquidEditor` / `RichTextEditor`.
67
+ - ❌ A language outside the list above and you expect colours: it renders as plain text.
@@ -0,0 +1,115 @@
1
+ # `<ColorPicker>` — controlled HEX color picker (popover + inline)
2
+
3
+ ```tsx
4
+ const [hex, setHex] = useState('#4F46E5');
5
+
6
+ // Default popover with the built-in trigger swatch:
7
+ <Field label="Brand color">
8
+ <ColorPicker value={hex} onChange={setHex} />
9
+ </Field>
10
+
11
+ // Custom trigger:
12
+ <Field label="Brand color" description="Used for campaign accents">
13
+ <ColorPicker value={hex} onChange={setHex}>
14
+ <ColorPicker.Trigger asChild>
15
+ <Button variant="secondary">Pick a color</Button>
16
+ </ColorPicker.Trigger>
17
+ </ColorPicker>
18
+ </Field>
19
+
20
+ // Inline (always-visible panel — for theme builders, settings rows):
21
+ <ColorPicker.Panel value={hex} onChange={setHex} />
22
+
23
+ // With consumer-supplied preset swatches:
24
+ <ColorPicker.Panel value={hex} onChange={setHex} presets={['#4F46E5', '#10B981', '#F59E0B', '#EF4444']} />
25
+ ```
26
+
27
+ <!-- props:start -->
28
+
29
+ ## Props
30
+
31
+ ### `ColorPickerProps`
32
+
33
+ <!-- prettier-ignore -->
34
+ | Prop | Type | Required | Description |
35
+ |---|---|---|---|
36
+ | `value` | `string` | yes | Current color as `#RRGGBB`. Controlled — required. |
37
+ | `onChange` | `(hex: string) => void` | yes | Fires per drag tick + on input change + on preset click. High frequency during drags. |
38
+ | `onChangeEnd` | `((hex: string) => void)` | no | Fires on the trailing edge of an interaction. Use for commit-style logic. |
39
+ | `presets` | `string[]` | no | Optional preset color swatches. If provided, rendered as a grid below the hue slider in the panel. |
40
+ | `disabled` | `boolean` | no | Disable interaction. Trigger doesn't open; panel is non-interactive. |
41
+ | `invalid` | `boolean` | no | Marks the focusable trigger invalid for Field composition. Default: `false`. |
42
+ | `required` | `boolean` | no | Consumed for Field composition so Field can render its visible required marker. Native buttons do not support `aria-required`, so this does not add required semantics to the trigger. Default: `false`. |
43
+ | `aria-label` | `string` | no | Accessible name for the focusable trigger. Forwarded to the default or custom trigger, not the root wrapper. On the default trigger the current value is appended (`aria-label="Accent colour"` → "Accent colour, current value #3366FF"); the popover dialog is named by the bare `aria-label`. Ignored when `aria-labelledby` is provided. An explicit name on a custom trigger child takes precedence — but an EMPTY one on that child does not, since an empty string names nothing. |
44
+ | `triggerLabel` | `string` | no | Accessible label for the default trigger. Defaults to the i18n value at `colorPicker.triggerLabel` (`'Pick a color'` in English) when omitted OR empty — an empty string is not an explicit name, so it takes the default too. Ignored when a custom trigger is provided via `<ColorPicker.Trigger>`. |
45
+ | `aria-labelledby` | `string` | no | Id(s) of element(s) that label the trigger button. Forwarded onto the focusable trigger (not the root wrapper) and takes precedence over the generated `aria-label`. Set automatically when wrapped in `<Field label>`. On the default trigger a visually hidden span holding the current HEX is appended, so the name reads e.g. "Accent colour #0052CC". The popover dialog is named by these ids alone (purpose, no value). |
46
+ | `aria-describedby` | `string` | no | Id(s) of element(s) that describe the trigger button (e.g. a Field error or helper text). Forwarded onto the focusable trigger, not the root wrapper. |
47
+ | `popoverPlacement` | `'top' \| 'top-start' \| 'top-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end'` | no | Popover placement (split internally into side + align). Default `'bottom-start'`. |
48
+ | `panelFooter` | `ReactNode` | no | Optional content rendered inside the popover, below the panel (and below its presets grid, if any) — for live feedback about the color being picked, e.g. a contrast-ratio readout that updates as the user drags. Not available on the standalone `<ColorPicker.Panel>`, which has no popover to render it inside. Not an `aria-live` region by default — announcing every drag tick would be disruptive. If the footer's text should be announced as it settles, wire that up yourself (e.g. wrap it in your own `role="status"` span); that tradeoff is the consumer's call, not the picker's default. The footer is wired as the popover dialog's `aria-describedby`, so its text is read when the dialog opens. Keep it short — it is read in full. |
49
+ | `children` | `ReactNode` | no | Optional `<ColorPicker.Trigger asChild>` override for custom triggers. |
50
+ | …native | | | plus native `<div>` attributes |
51
+
52
+ ### `ColorPickerPanelProps`
53
+
54
+ <!-- prettier-ignore -->
55
+ | Prop | Type | Required | Description |
56
+ |---|---|---|---|
57
+ | `value` | `string` | yes | Current color as `#RRGGBB`. Controlled — required. |
58
+ | `onChange` | `(hex: string) => void` | yes | Fires per drag tick + on input change + on preset click. High frequency during drags. |
59
+ | `onChangeEnd` | `((hex: string) => void)` | no | Fires on the trailing edge of an interaction — pointer release on the SV pad / hue slider, blur on the HEX input, preset click. Use for commit-style logic (network calls, history snapshots). |
60
+ | `presets` | `string[]` | no | Optional preset color swatches. If provided, rendered as a grid below the hue slider. Clicking a swatch commits the color (fires both onChange and onChangeEnd). Invalid entries are silently dropped. |
61
+ | `disabled` | `boolean` | no | Disable interaction. |
62
+ | `framed` | `boolean` | no | Whether the panel draws its own chrome (border, shadow, padding, background, radius). Default `true` — the standalone `<ColorPicker.Panel>` needs that chrome to read as a discrete surface on a page. Set `false` when nesting the panel inside a container that already draws a frame — a `Popover.Content`, a `Card`, or your own bordered surface — so the two frames don't nest. `<ColorPicker>` renders its popover panel this way internally, for exactly this reason. |
63
+ | …native | | | plus native `<div>` attributes |
64
+
65
+ ### `ColorPickerTriggerProps`
66
+
67
+ <!-- prettier-ignore -->
68
+ | Prop | Type | Required | Description |
69
+ |---|---|---|---|
70
+ | `asChild` | `boolean` | no | Currently always behaves as `true` (the child is merged with trigger semantics via `<Popover.Trigger>`'s cloneElement). Reserved for a future Slot-style variant where `false` would wrap the child instead of merging. Documented for API stability. |
71
+ | `children` | `ReactNode` | yes | The element to render as the trigger. Must accept `ref`, `onClick`, `id`, and ARIA attributes so ColorPicker can connect it to Field. Explicit child ARIA naming, description, and invalid state win over the parent values; ColorPicker's public `id` still owns the label association. |
72
+
73
+ <!-- props:end -->
74
+
75
+ - **Controlled-only.** `value: string` in `#RRGGBB` form. Loose input accepted on the HEX text field (`#FFF`, `FFF`, `#ffffff`); the component always emits the canonical `#RRGGBB` (uppercase, with `#`).
76
+ - **The SV pad** is `role="application"` with an `aria-valuetext` describing the state (a 2D control has no standard ARIA pattern). Keyboard adjusts S/V by 1% per arrow, 10% with Shift. Use `<ColorPicker>` (not the Panel) when you need a compact trigger.
77
+ - **Two distribution shapes via the compound API.** `<ColorPicker>` is the popover-wrapped form-field-ready widget. `<ColorPicker.Panel>` is the same picker without the popover wrapping — drop it directly into a settings page or theme builder.
78
+ - **Default trigger** is an input-field-shaped button with a 16×16 swatch + uppercase HEX text. Override via `<ColorPicker.Trigger asChild>{customNode}</ColorPicker.Trigger>` (the child must `forwardRef` and accept `onClick` because `<Popover.Trigger>` clones it). `<ColorPicker.Trigger>` renders nothing itself; it is a marker child that `<ColorPicker>` reads from its `children`, so it only has meaning inside `<ColorPicker>`.
79
+ - **Field-ready.** `<Field label>` connects `id`, naming, description, and invalid state to either the default or custom trigger rather than the role-less picker wrapper. `required` remains a visible Field marker; native trigger buttons do not expose `aria-required` because that state is unsupported for buttons.
80
+ - **`onChange` fires per drag/zoom tick (high frequency).** Use `onChangeEnd` for commit-style logic (network calls, history snapshots) — it fires on pointer release, slider release, HEX input blur, and preset click.
81
+ - **Presets via `presets?: string[]`.** Invalid entries are dropped silently. The library doesn't ship a default palette — pass your own brand colors. Selected swatch gets an inset ring + check overlay.
82
+ - **Color math is exported.** `hexToHsv(hex)`, `hsvToHex({h,s,v})`, `normalizeHex(loose)` are usable directly for downstream theme builders, contrast calculators, etc.
83
+ - **Keyboard (SV pad)**: arrows ±1% S/V, Shift+arrow ±10%, Home/End for S=0/100, PageUp/Down for V=100/0.
84
+ - **Keyboard (hue slider)**: inherits Slider's keyboard — arrows ±1°, PgUp/Dn ±10°, Home/End for 0°/360°.
85
+ - **Popover placement** via `popoverPlacement?: 'bottom-start' | 'bottom' | 'top-start' | ...`. Default `'bottom-start'`.
86
+ - **Disabled** dims the panel, sets `aria-disabled` on the SV pad, disables the slider + input, makes presets non-interactive. Trigger doesn't open.
87
+ - **`panelFooter?: ReactNode`** — rendered inside the popover below the panel (and below presets). For live feedback about the color being picked, e.g. a contrast-ratio readout that updates as the user drags. Not available on the standalone `<ColorPicker.Panel>` (no popover to render it inside). Not an `aria-live` region by default — if the text should be announced as it changes, wrap it in your own `role="status"` span; announcing every drag tick would be disruptive by default. It IS the popover dialog's `aria-describedby`, so it's read once when the dialog opens.
88
+ - **Accessible names.** Under `<Field label>` / `aria-labelledby` the default trigger's name is the label plus the current value ("Accent colour #0052CC"). An explicit `aria-label` on the default trigger also gets the value appended ("Accent colour, current value #0052CC"). The popover dialog is named by the trigger's purpose (Field label / `aria-label` / `triggerLabel`), no value — don't add your own `Popover.Heading` via `panelFooter` to name it.
89
+ - **`<ColorPickerPanel>` (`<ColorPicker.Panel>`) takes `framed?: boolean`, default `true`.** `framed={false}` drops the panel's own chrome (border, shadow, padding, background, radius) — use it when nesting the panel inside something that already draws a frame (your own `Popover.Content`, a `Card`). `<ColorPicker>` renders its popover panel with `framed={false}` internally so the popover shows exactly one frame; a standalone `<ColorPicker.Panel>` keeps its frame by default.
90
+ - **`invalid` now paints the default trigger's border AND its focus ring**, not just `aria-invalid` — `.trigger[aria-invalid=true]` uses the same danger primitive as `Input invalid` for both: the border (`--color-picker-trigger-border-color-invalid`, same primitive as `--input-border-color-invalid`, and it survives `:hover`) and the `:focus-visible` ring (`--color-picker-trigger-ring-invalid`, same primitive as `--input-ring-invalid`, both `var(--ring-danger)`) — each reached through ColorPicker's own component token, never a direct `--input-*` read.
91
+
92
+ #### Color math API
93
+
94
+ ```ts
95
+ import { hexToHsv, hsvToHex, normalizeHex } from '@eocrm/design-system';
96
+
97
+ normalizeHex('#fff'); // '#FFFFFF'
98
+ normalizeHex('orange'); // null
99
+
100
+ hexToHsv('#FF0000'); // { h: 0, s: 100, v: 100 }
101
+ hexToHsv('not a color'); // null
102
+
103
+ hsvToHex({ h: 240, s: 100, v: 100 }); // '#0000FF'
104
+ ```
105
+
106
+ #### Hard rule
107
+
108
+ - ❌ Passing non-HEX `value` — named colors, `rgb()`, `hsl()`, alpha hex (`#RRGGBBAA`). Convert in the consumer or use the exported `normalizeHex` first. Invalid input falls back to `#000000` with a dev-only warning.
109
+ - ❌ Using `<ColorPicker.Trigger>` outside `<ColorPicker>`.
110
+ - ❌ Reaching into the picker's internal HSV state. Consumer contract is HEX-only.
111
+ - ❌ Hand-rolling a color picker per page. Use this.
112
+ - ❌ Bundling a default palette inside the consumer. Pass via `presets`.
113
+ - ❌ Calling expensive work in `onChange`. Use `onChangeEnd` (one fire per gesture).
114
+ - ❌ Wrapping a non-`forwardRef` component in `<ColorPicker.Trigger asChild>`. `<Popover.Trigger>` clones the child to inject the ref; non-forwardRef silently drops it.
115
+ - ❌ Forgetting to wire `disabled` into the consumer's custom trigger element. The picker dims its wrapper and blocks pointer events (`pointer-events: none`), but the trigger button's own disabled visuals are the consumer's responsibility.
@@ -0,0 +1,70 @@
1
+ # `<ConfirmationPopover>` — opinionated "Are you sure?" preset
2
+
3
+ ```tsx
4
+ <ConfirmationPopover
5
+ title="Delete record?"
6
+ description="This action cannot be undone."
7
+ confirmLabel="Delete"
8
+ variant="danger"
9
+ onConfirm={async () => {
10
+ await api.deleteRecord(id);
11
+ }}
12
+ >
13
+ <Button variant="danger">Delete</Button>
14
+ </ConfirmationPopover>
15
+
16
+ // Default variant: lighter-weight (archive, publish, etc.)
17
+ <ConfirmationPopover
18
+ title="Archive this contact?"
19
+ description="You can unarchive later from the archive view."
20
+ onConfirm={() => archive(id)}
21
+ >
22
+ <Button variant="secondary">Archive</Button>
23
+ </ConfirmationPopover>
24
+ ```
25
+
26
+ <!-- props:start -->
27
+
28
+ ## Props
29
+
30
+ <!-- prettier-ignore -->
31
+ | Prop | Type | Required | Description |
32
+ |---|---|---|---|
33
+ | `children` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | yes | The trigger element. Same forwardRef-required contract as `<Popover.Trigger>` — `<Button>` qualifies; a custom component without `forwardRef` does not. |
34
+ | `title` | `string` | yes | Heading text. Renders inside `<Popover.Heading>` (an `<h3>`) and wires `aria-labelledby` on the dialog automatically. |
35
+ | `description` | `ReactNode` | no | Optional body text below the title. When provided, wires `aria-describedby` so screen readers announce it. |
36
+ | `confirmLabel` | `string` | no | Confirm button label. Defaults to the i18n value at `confirmationPopover.confirm` (`'Confirm'` in English). An EMPTY string counts as unset, not as an explicit blank: this label is the confirm button's only name source (the pending spinner beside it is `aria-hidden`), so an empty one would leave the button anonymous. |
37
+ | `variant` | `'default' \| 'danger'` | no | `'danger'` makes Confirm a danger-variant button. Defaults to `'default'` (primary). |
38
+ | `onConfirm` | `() => void \| Promise<void>` | yes | Async-aware confirm handler. May return a Promise; while pending, both buttons disable, the Confirm button shows a small spinner, and Escape / click-outside dismissal is blocked. - Resolve → popover closes. - Reject → popover stays open, buttons re-enable. `onCancel` is NOT fired (the user neither confirmed nor cancelled — it's an error state owned by the consumer). The consumer is expected to surface error feedback externally (toast, inline message, etc.). ConfirmationPopover does NOT render errors. |
39
+ | `onCancel` | `(() => void)` | no | Optional. Fires on Cancel click, Escape, or click-outside dismissal. NOT fired if `onConfirm` rejects. |
40
+ | `initialFocusRef` | `RefObject<HTMLElement \| null>` | no | Direct initial focus into the panel's content instead of the default Cancel button — e.g. an `<Input>` rendered in `description` for a rename flow. When provided and its `.current` is non-null on open, the component focuses this element after the panel mounts (skipping the Cancel-focus). Tip: add `onFocus={(e) => e.currentTarget.select()}` to a text input to select its contents so the user can type/replace immediately. |
41
+ | `returnFocusRef` | `RefObject<HTMLElement \| null>` | no | Where focus goes when the popover closes. Default: back to the trigger. That default breaks whenever the trigger is gone by close time — the confirmed action deleted the row that owned it — and focus lands on `<body>`, so a keyboard user loses their place. The ref is read **at close time**, not at open time, so it may be pointed at whatever still exists once the work is done — the next row's trigger, the empty state's first button. If it is empty or its element has left the document, focus falls back to the trigger (when it still exists). It applies when the popover closes via Confirm, Cancel or Escape. An outside click never restores focus — focus stays wherever the click put it (on a control, or `<body>` for an inert area). A ref that is set statically (not only inside `onConfirm`) ALSO fires when the user closes by clicking the trigger again, pulling focus off the trigger. To return to the trigger on Cancel / toggle and only move elsewhere after a confirm, aim the ref inside `onConfirm` and clear it in `onOpenChange(true)`. If focus is already on some other live element when the popover closes, it is left alone. The target is scrolled into view with `{ block: 'nearest' }` (a no-op when it is already visible) — it may be far from where the user was. |
42
+ | `side` | `'left' \| 'right' \| 'top' \| 'bottom'` | no | Preferred side. Default `'top'` — confirmations anchor above the trigger by convention. |
43
+ | `align` | `'start' \| 'end' \| 'center'` | no | Edge alignment. Default `'center'`. |
44
+ | `sideOffset` | `number` | no | Gap in px between trigger and panel. Default `10`. |
45
+ | `open` | `boolean` | no | Controlled open state. Pair with `onOpenChange`. |
46
+ | `onOpenChange` | `((open: boolean) => void)` | no | Open-change callback. Required when `open` is provided. |
47
+ | `defaultOpen` | `boolean` | no | Default open state for uncontrolled usage. Defaults to `false`. |
48
+
49
+ <!-- props:end -->
50
+
51
+ - Built on top of `<Popover>`. Declarative: `title` / `description` / `confirmLabel` / `cancelLabel` / `variant` / `onConfirm` / `onCancel`.
52
+ - **Initial focus on Cancel** for both variants — keyboard Enter never accidentally confirms. Tab once to Confirm.
53
+ - **`initialFocusRef`** (`RefObject<HTMLElement | null>`) overrides the Cancel default: directs initial focus into the `description` content instead — e.g. an `<Input>` rendered there for a rename flow. The component focuses `initialFocusRef.current` after the panel mounts (mirrors `<Modal>`'s `initialFocusRef`). Tip: add `onFocus={(e) => e.currentTarget.select()}` to a text input so its contents are selected on open and the user can type a replacement immediately.
54
+ - **Async-aware** `onConfirm`. May return a Promise. While pending, both buttons take `aria-disabled` (NOT the native `disabled`, which would drop them from the tab order and blow away focus mid-operation), their handlers no-op, Confirm shows a spinner, the pending state is announced, and Escape / click-outside are blocked.
55
+ - **Failure mode**: on reject, popover stays open and buttons re-enable. Consumer surfaces the error externally — ConfirmationPopover does NOT render inline errors.
56
+ - **Return focus** (`returnFocusRef`): on Confirm, Cancel and Escape focus returns to `returnFocusRef` when it is connected, otherwise to the trigger. Unlike `<Modal>`, an outside click never restores focus — it stays wherever the user clicked. `returnFocusRef` is read at CLOSE time. A statically-set ref also fires when the user closes by clicking the trigger again; to apply it only after a successful confirm, set it in `onConfirm` and clear it in `onOpenChange(true)`. A named target is scrolled into view with `{ block: 'nearest' }`.
57
+ - Anchors above the trigger by default (`side="top"`).
58
+ - **From a DropdownMenu item (kebab Delete pattern).** Wrap a `<DropdownMenu.Item closeOnSelect={false}>` as the trigger — clicking anywhere on the row opens the confirmation. The menu stays open until the user dismisses it (Escape or click outside). To close the menu after the action resolves, drive `DropdownMenu`'s `open` state externally and call `setMenuOpen(false)` inside `onConfirm` — and pass `returnFocusRef={kebabTriggerRef}` (the menu's trigger), because closing the menu unmounts the item that is the popover's trigger, so focus would otherwise drop to `<body>`.
59
+
60
+ #### When NOT to use
61
+
62
+ - ❌ A multi-step flow ("type the name to confirm") → `Modal`. This is for one-tap confirmations.
63
+ - ❌ A non-blocking heads-up that needs no yes/no answer → a Toast or inline UI.
64
+
65
+ #### Anti-patterns
66
+
67
+ - ❌ A hanging-forever `onConfirm` Promise. There is no timeout; the popover stays pending indefinitely, so add a timeout / abort inside your `onConfirm` if the operation may stall.
68
+ - ❌ Expecting the rejection to render inside the popover body. Surface errors via toast or page-level UI.
69
+ - ❌ Controlled `open` while relying on pending-blocks-close. With `open` / `onOpenChange` you can force-close from outside while pending; coordinate `pending` in your own code if that matters.
70
+ - ❌ Letting a confirmed delete remove the trigger with no `returnFocusRef`: focus drops to `<body>`. Aim the ref at the next row's trigger (or the empty state) inside `onConfirm`.