@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,81 @@
1
+ // FileUpload.tokens.scss — component-scoped tokens for <FileUpload>. Covers the drop-zone, drag-over state, and per-row item chrome (border, status icon colors).
2
+ :root {
3
+ // Root layout
4
+ --file-upload-gap: var(--space-3);
5
+
6
+ // Dropzone
7
+ --file-upload-dropzone-gap: var(--space-2);
8
+ --file-upload-dropzone-padding: var(--space-4);
9
+ --file-upload-dropzone-border-color: var(--color-border);
10
+ --file-upload-dropzone-radius: var(--radius-md);
11
+ --file-upload-dropzone-bg: var(--color-bg);
12
+ --file-upload-dropzone-fg: var(--color-fg-muted);
13
+ --file-upload-dropzone-border-color-hover: var(--color-accent);
14
+ --file-upload-dropzone-fg-hover: var(--color-fg);
15
+
16
+ // Drag-over (active drop target)
17
+ --file-upload-dragover-border-color: var(--color-accent);
18
+ --file-upload-dragover-bg: var(--color-accent-bg-subtle);
19
+ --file-upload-dragover-fg: var(--color-fg);
20
+
21
+ // Dropzone children
22
+ --file-upload-icon-fg: var(--color-fg-subtle);
23
+ --file-upload-label-font-size: var(--font-size-md);
24
+ --file-upload-hint-font-size: var(--font-size-sm);
25
+
26
+ // .dropzoneHint declares its own colour, so it wins over .dragOver's
27
+ // inherited --file-upload-dragover-fg -- the hint stays this colour through
28
+ // drag-over, whose --file-upload-dragover-bg is --color-accent-bg-subtle.
29
+ // The hint is --font-size-sm (12px), so WCAG AA small-text applies at 4.5:1;
30
+ // there is no large-text exemption to fall back on.
31
+ // #511: was --color-fg-subtle, well below AA there (3.97/3.61).
32
+ // --color-fg-muted was a substantial improvement that still missed AA in
33
+ // light, at 4.41, while dark cleared. #522 closed that at the PRIMITIVE
34
+ // rather than here: light --color-fg-muted moved #5e6c84 -> #5b6980, an
35
+ // imperceptible ΔE 0.0109 step that clears every light surface. So this
36
+ // token needed no change for #522 -- the annotations below moved, the
37
+ // declaration did not.
38
+ // --file-upload-icon-fg above is unaffected (icon, not text; separate
39
+ // token; not re-pointed).
40
+ // @contrast --color-fg-subtle on --color-accent-bg-subtle = 3.97:1 light
41
+ // @contrast --color-fg-subtle on --color-accent-bg-subtle = 3.61:1 dark
42
+ // @contrast --color-fg-muted on --color-accent-bg-subtle = 4.62:1 light
43
+ // @contrast --color-fg-muted on --color-accent-bg-subtle = 4.72:1 dark
44
+ --file-upload-hint-fg: var(--color-fg-muted);
45
+
46
+ // File list
47
+ --file-upload-list-gap: var(--space-2);
48
+
49
+ // Row — surface
50
+ --file-upload-row-gap: var(--space-3);
51
+ --file-upload-row-padding-y: var(--space-2);
52
+ --file-upload-row-padding-x: var(--space-3);
53
+ --file-upload-row-border-color: var(--color-border);
54
+ --file-upload-row-radius: var(--radius-md);
55
+ --file-upload-row-bg: var(--color-bg);
56
+
57
+ // Row — error variant
58
+ --file-upload-row-border-color-error: var(--color-danger);
59
+
60
+ // Row — status cell. The 4th grid column (`.rowStatus`) is `auto`-sized,
61
+ // which measures to 0 for an uploading row: <Progress> is `width: 100%`
62
+ // with no intrinsic size, so it has nothing to be 100% of (#588). This
63
+ // gives the uploading status cell a floor so the bar has somewhere to
64
+ // grow into. `--size-xl` is the closest existing shared size token to a
65
+ // sensible progress-bar minimum (80px) — no dedicated 80-120px token
66
+ // exists for this, so this reuses the primitive rather than inventing one.
67
+ --file-upload-progress-min-width: var(--size-xl);
68
+
69
+ // Row — text + icon colors
70
+ --file-upload-row-icon-fg: var(--color-fg-subtle);
71
+ --file-upload-row-name-font-size: var(--font-size-md);
72
+ --file-upload-row-name-fg: var(--color-fg);
73
+ --file-upload-row-meta-font-size: var(--font-size-sm);
74
+ --file-upload-row-meta-fg: var(--color-fg-subtle);
75
+ --file-upload-row-error-font-size: var(--font-size-sm);
76
+ --file-upload-row-error-fg: var(--color-danger);
77
+ --file-upload-row-done-fg: var(--color-success);
78
+
79
+ // Disabled state
80
+ --file-upload-opacity-disabled: var(--opacity-subtle);
81
+ }
@@ -0,0 +1,569 @@
1
+ import {
2
+ forwardRef,
3
+ useCallback,
4
+ useRef,
5
+ useEffect,
6
+ useState,
7
+ type DragEvent,
8
+ type HTMLAttributes,
9
+ type KeyboardEvent,
10
+ type ChangeEvent,
11
+ type ReactNode,
12
+ type MouseEvent,
13
+ } from 'react';
14
+ import { CloudUpload, Check, X } from 'lucide-react';
15
+ import clsx from 'clsx';
16
+ import { Button } from '../Button';
17
+ import { Progress } from '../Progress';
18
+ import { useTranslation } from '../../i18n/useTranslation';
19
+ import { formatBytes } from './formatBytes';
20
+ import { iconForFile } from './iconForFile';
21
+ import styles from './FileUpload.module.scss';
22
+
23
+ /** Lifecycle status for a file in the controlled `files` array. */
24
+ export type FileUploadStatus = 'pending' | 'uploading' | 'done' | 'error';
25
+
26
+ /** Reason a file was rejected by built-in or custom validation. */
27
+ export type FileRejectReason = 'invalid-type' | 'too-large' | 'too-many' | 'duplicate' | 'custom';
28
+
29
+ /** One entry in the controlled file list. */
30
+ export interface FileEntry {
31
+ /**
32
+ * Stable ID for React keys and consumer bookkeeping. Consumer assigns
33
+ * (typically `crypto.randomUUID()` when handling `onFilesAdded`).
34
+ * File objects don't have stable identity in JS, so this is required.
35
+ */
36
+ id: string;
37
+ /** The underlying browser File. */
38
+ file: File;
39
+ /** Lifecycle status. Drives the per-row rendering. */
40
+ status: FileUploadStatus;
41
+ /**
42
+ * 0–100. Only meaningful when status='uploading' (renders via `<Progress>`).
43
+ * Ignored otherwise. Omitting it during upload renders the row with an
44
+ * indeterminate `<Progress>` (no value).
45
+ */
46
+ progress?: number;
47
+ /** Error message rendered when status='error'. */
48
+ error?: string;
49
+ }
50
+
51
+ export interface FileUploadProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
52
+ /**
53
+ * Placed on the hidden native `<input type="file">`, not the root — it is
54
+ * the only labelable element, so a `<label for>` (Field / SettingRow wire
55
+ * one) opens the file picker when clicked (#568). A `role="button"` div is
56
+ * not labelable, so the id on the dropzone would make the label click a no-op.
57
+ * While the dropzone is hidden (single mode with a file) the id falls back
58
+ * to the root, so scroll-to-id / focus-first-invalid still find the field.
59
+ */
60
+ id?: string;
61
+ /** Marks the dropzone `aria-invalid` for Field / SettingRow composition. @default false */
62
+ invalid?: boolean;
63
+ /**
64
+ * Consumed for Field / SettingRow composition (they render the visible
65
+ * required marker). Not forwarded: `role="button"` does not support
66
+ * `aria-required`, and native `required` on the hidden input would block
67
+ * form submission with an invisible bubble — your state owns the files.
68
+ */
69
+ required?: boolean;
70
+ /** Controlled file list. The component renders this; consumer owns the state. */
71
+ files: FileEntry[];
72
+ /**
73
+ * Called when the user drops or picks new files AND those files pass
74
+ * validation. The component does NOT mutate state — consumer is responsible
75
+ * for adding the returned `File[]` to `files` (typically wrapped in
76
+ * `FileEntry` shape with new IDs).
77
+ */
78
+ onFilesAdded: (files: File[]) => void;
79
+ /** Called when the user clicks the X on a file row. Consumer removes from `files`. */
80
+ onFileRemove: (entry: FileEntry) => void;
81
+ /**
82
+ * Called when a file fails validation. `reason` is one of `FileRejectReason`;
83
+ * `message` is the custom message from `validator` (only present when
84
+ * reason='custom'). Use for toasts or inline error rendering.
85
+ */
86
+ onFileReject?: (file: File, reason: FileRejectReason, message?: string) => void;
87
+ /**
88
+ * Allow multi-file selection. Default `false`. In single mode (default),
89
+ * `files` should have at most 1 entry; the dropzone hides once a file is
90
+ * present and re-appears after removal.
91
+ */
92
+ multiple?: boolean;
93
+ /**
94
+ * Accepted file types — MIME types and/or extensions, comma-separated.
95
+ * Forwarded to the native `<input accept>` attribute AND used for the
96
+ * built-in `invalid-type` validation. Default: no filter.
97
+ *
98
+ * Examples:
99
+ * - `accept="image/*"` — any image
100
+ * - `accept=".csv,application/vnd.ms-excel"` — CSV or Excel
101
+ * - `accept="application/pdf,image/png,image/jpeg"` — explicit MIME list
102
+ */
103
+ accept?: string;
104
+ /**
105
+ * Per-file size cap in bytes. Default: no limit.
106
+ * Files exceeding this fire `onFileReject` with reason `'too-large'`.
107
+ */
108
+ maxSize?: number;
109
+ /**
110
+ * Total file count cap. Only meaningful when `multiple=true`. Counts existing
111
+ * entries in `files` PLUS new files being added. Default: no limit.
112
+ * Excess files fire `onFileReject` with reason `'too-many'`.
113
+ */
114
+ maxFiles?: number;
115
+ /**
116
+ * Custom validation hook. Return `null` for valid; return a string error
117
+ * message for invalid (fires `onFileReject` with reason='custom', message=
118
+ * the returned string). Runs LAST in the validation chain (after type,
119
+ * size, count, duplicate checks).
120
+ */
121
+ validator?: (file: File) => string | null;
122
+ /**
123
+ * Disable the entire component. Dropzone shows muted, drag/click handlers
124
+ * are no-ops, remove buttons disabled. Default `false`.
125
+ */
126
+ disabled?: boolean;
127
+ /**
128
+ * Override the dropzone's main label. Default: the i18n value at
129
+ * `fileUpload.dragHint`. Pass a ReactNode for richer content (e.g. with a
130
+ * `<Code>` for accepted extensions).
131
+ *
132
+ * **A11y note:** when `dropzoneLabel` is a plain string, the component
133
+ * uses it as `aria-label` on the dropzone. When it's a ReactNode, the
134
+ * component falls back to the i18n value at `fileUpload.upload` and the
135
+ * rich content is visible-only. To give screen readers the equivalent
136
+ * text, pass `aria-label` via the spread (e.g. `aria-label="Upload CSV
137
+ * or Excel files"`).
138
+ *
139
+ * An EMPTY string counts as unset, not as an explicit blank — it drives both
140
+ * the visible label and the dropzone's `aria-label`, so an empty one left a
141
+ * `role="button"` with no accessible name.
142
+ */
143
+ dropzoneLabel?: ReactNode;
144
+ /**
145
+ * Override the dropzone icon. Default: lucide `CloudUpload` at 32px.
146
+ * Pass an SVG ReactNode or another icon component instance.
147
+ */
148
+ dropzoneIcon?: ReactNode;
149
+ /**
150
+ * Optional secondary line under the main label, typically describing
151
+ * accepted formats / size limits. Renders muted at `--font-size-sm`.
152
+ * Default: undefined (no secondary text).
153
+ */
154
+ dropzoneHint?: ReactNode;
155
+ }
156
+
157
+ const STATUS_CLASS: Record<FileUploadStatus, string> = {
158
+ pending: styles.rowPending,
159
+ uploading: styles.rowUploading,
160
+ done: styles.rowDone,
161
+ error: styles.rowError,
162
+ };
163
+
164
+ /**
165
+ * Match a single file against an `accept` attribute string. Handles MIME
166
+ * patterns (`image/*`, `application/pdf`) and extension patterns (`.csv`,
167
+ * `.pdf`). Returns true if the file matches any entry in the comma-separated
168
+ * accept list, false otherwise.
169
+ *
170
+ * Private to FileUpload — encapsulated so the validation pipeline doesn't
171
+ * sprout an ad-hoc regex.
172
+ */
173
+ function matchesAccept(file: File, accept: string): boolean {
174
+ const tokens = accept
175
+ .split(',')
176
+ .map((t) => t.trim())
177
+ .filter(Boolean);
178
+ if (tokens.length === 0) return true;
179
+ const mime = file.type;
180
+ const ext = file.name.slice(file.name.lastIndexOf('.')).toLowerCase();
181
+ for (const token of tokens) {
182
+ if (token.startsWith('.')) {
183
+ // Extension match (case-insensitive)
184
+ if (ext === token.toLowerCase()) return true;
185
+ } else if (token.endsWith('/*')) {
186
+ // Wildcard MIME (image/*, video/*, etc.)
187
+ const prefix = token.slice(0, -1); // "image/"
188
+ if (mime.startsWith(prefix)) return true;
189
+ } else {
190
+ // Exact MIME match (case-sensitive per RFC)
191
+ if (mime === token) return true;
192
+ }
193
+ }
194
+ return false;
195
+ }
196
+
197
+ /**
198
+ * Controlled, dropzone-style file picker with built-in validation and per-file status rows.
199
+ * @see docs/components/FileUpload.md
200
+ */
201
+ export const FileUpload = forwardRef<HTMLDivElement, FileUploadProps>(function FileUpload(
202
+ {
203
+ files,
204
+ onFilesAdded,
205
+ onFileRemove,
206
+ onFileReject,
207
+ multiple = false,
208
+ accept,
209
+ maxSize,
210
+ maxFiles,
211
+ validator,
212
+ disabled = false,
213
+ dropzoneLabel,
214
+ dropzoneIcon,
215
+ dropzoneHint,
216
+ 'aria-labelledby': ariaLabelledBy,
217
+ 'aria-describedby': ariaDescribedBy,
218
+ id,
219
+ invalid = false,
220
+ // Consumed so it never lands on the root div (#568); see the prop's JSDoc.
221
+ required: _required = false,
222
+ className,
223
+ ...rest
224
+ },
225
+ ref,
226
+ ) {
227
+ const t = useTranslation();
228
+ const inputRef = useRef<HTMLInputElement>(null);
229
+ const dragCounter = useRef(0);
230
+ const [isDragOver, setIsDragOver] = useState(false);
231
+
232
+ // Batch announcement text. Derived from the controlled `files` array, and
233
+ // deferred so region and text never mount together.
234
+ const total = files.length;
235
+ const settled = files.filter((f) => f.status === 'done' || f.status === 'error').length;
236
+ const failed = files.filter((f) => f.status === 'error').length;
237
+ const inFlight = total > 0 && settled < total;
238
+ const [batchStatus, setBatchStatus] = useState('');
239
+ // A settled batch is only announced if this component SAW it in flight.
240
+ // Deriving it from `files` alone announced a batch that never happened: a
241
+ // form opened on an entity with three existing attachments mounted already
242
+ // settled, and the region went from '' to "Uploaded: 3 / 3" with nothing on
243
+ // screen having changed. Hard rule 10 rules that out directly — gate the
244
+ // region on what the user can SEE change, not on the raw prop.
245
+ //
246
+ // Tracked INSIDE the effect, not during render. Mutating a ref while
247
+ // rendering breaks React's purity rule and a discarded concurrent render
248
+ // would leave the flag set for a transfer the user never saw. In the effect
249
+ // the ordering still works: the in-flight pass sets it, and the pass that
250
+ // observes the batch settled reads what that earlier pass wrote.
251
+ //
252
+ // The IDS seen in flight, not a boolean. A boolean was reset only when the
253
+ // list emptied, so a controlled parent swapping one already-settled `files`
254
+ // array for another — switching entities on a shared instance after any
255
+ // upload — carried it across and announced the new entity's pre-existing
256
+ // attachments. That is the same defect this guard exists to close, surviving
257
+ // on the prop-change path. Announcing requires the batch on screen to be one
258
+ // this component actually watched.
259
+ // Keyed on the FILE OBJECT, not the id. Ids are the consumer's, and a
260
+ // consumer numbering them per entity — `f1`, `f2` — hands the next entity
261
+ // the same ones, which inherited the flag and announced its pre-existing
262
+ // attachments. That is the fourth defect in this guard and the third of the
263
+ // same shape, so it now keys on something the consumer cannot collide: the
264
+ // `File` instance itself. A WeakSet also fails SAFE — a consumer that
265
+ // rebuilds `File` objects per render simply never matches, and silence is
266
+ // the harmless direction.
267
+ const seenInFlight = useRef<WeakSet<File>>(new WeakSet());
268
+ /** Last counts this region reported, to tell a completion from a deletion. */
269
+ const lastAnnounced = useRef({ total: 0, settled: 0, failed: 0 });
270
+ // A signature that actually changes when the BATCH changes. Ids alone did
271
+ // not: swapping to another entity whose file has the same id, count and
272
+ // status left every dependency identical, so the effect never re-ran and the
273
+ // region kept the previous entity's text — the guard was correct and never
274
+ // got to run. Name and size distinguish the files behind equal ids.
275
+ const signature = files
276
+ .map((f) => `${f.id}:${f.file.name}:${f.file.size}:${f.status}`)
277
+ .join('\u0000');
278
+ useEffect(() => {
279
+ if (total === 0) {
280
+ seenInFlight.current = new WeakSet();
281
+ setBatchStatus('');
282
+ return;
283
+ }
284
+ // Only the files actually IN FLIGHT, not the whole batch. Recording all of
285
+ // them meant removing the last uploading row left a settled sibling still
286
+ // "seen", so a deletion announced "Uploaded: 1 / 1" for a transfer that
287
+ // never finished. Now the removal takes the watched id with it and the
288
+ // region stays silent.
289
+ if (inFlight)
290
+ for (const f of files)
291
+ if (f.status !== 'done' && f.status !== 'error') seenInFlight.current.add(f.file);
292
+ const watched = files.some((f) => seenInFlight.current.has(f.file));
293
+ // A SHRINKING batch is a deletion, not a completion. Removing the last
294
+ // in-flight row was fixed by tracking only in-flight files; removing a
295
+ // SETTLED sibling survived that, because a watched file remained and the
296
+ // counts simply recomputed — so deleting one of two finished uploads
297
+ // announced "Uploaded: 1 / 1." The premise was right and applied one row
298
+ // too narrowly: nothing settled, the array just got shorter.
299
+ const shrank = total < lastAnnounced.current.total;
300
+ const progressed =
301
+ settled > lastAnnounced.current.settled || failed > lastAnnounced.current.failed;
302
+ lastAnnounced.current = { total, settled, failed };
303
+ if (shrank && !progressed) {
304
+ // Cleared, not left alone: a deletion has nothing to report, and keeping
305
+ // the previous text would leave "Uploading: 1 / 2" describing a batch
306
+ // that no longer exists. Emptying a live region announces nothing.
307
+ setBatchStatus('');
308
+ return;
309
+ }
310
+ setBatchStatus(
311
+ inFlight
312
+ ? t('fileUpload.batchUploading', { done: settled, total })
313
+ : watched
314
+ ? t('fileUpload.batchSettled', { done: total - failed, total, failed })
315
+ : '',
316
+ );
317
+ // `signature` rather than `files`: a controlled parent commonly passes a
318
+ // new array identity for the same batch on every render.
319
+ // eslint-disable-next-line react-hooks/exhaustive-deps
320
+ }, [inFlight, settled, total, failed, signature, t]);
321
+
322
+ const showDropzone = multiple || files.length === 0;
323
+ // In single mode the implicit cap is 1; multi mode uses maxFiles (Infinity if not set).
324
+ const effectiveMaxFiles = multiple ? (maxFiles ?? Infinity) : 1;
325
+
326
+ const processFiles = useCallback(
327
+ (incoming: File[]) => {
328
+ if (disabled) return;
329
+ const accepted: File[] = [];
330
+ let acceptedSoFar = 0;
331
+ for (const file of incoming) {
332
+ // 1. Type check
333
+ if (accept && !matchesAccept(file, accept)) {
334
+ onFileReject?.(file, 'invalid-type');
335
+ continue;
336
+ }
337
+ // 2. Size check
338
+ if (typeof maxSize === 'number' && file.size > maxSize) {
339
+ onFileReject?.(file, 'too-large');
340
+ continue;
341
+ }
342
+ // 3. Count check — counts existing entries + accepted-so-far in this batch.
343
+ if (files.length + acceptedSoFar >= effectiveMaxFiles) {
344
+ onFileReject?.(file, 'too-many');
345
+ continue;
346
+ }
347
+ // 4. Duplicate check — same name+size as an existing entry.
348
+ const isDuplicate = files.some(
349
+ (e) => e.file.name === file.name && e.file.size === file.size,
350
+ );
351
+ if (isDuplicate) {
352
+ onFileReject?.(file, 'duplicate');
353
+ continue;
354
+ }
355
+ // 5. Custom validator runs LAST.
356
+ if (validator) {
357
+ const message = validator(file);
358
+ if (message !== null) {
359
+ onFileReject?.(file, 'custom', message);
360
+ continue;
361
+ }
362
+ }
363
+ accepted.push(file);
364
+ acceptedSoFar++;
365
+ }
366
+ if (accepted.length > 0) {
367
+ onFilesAdded(accepted);
368
+ }
369
+ },
370
+ [accept, disabled, effectiveMaxFiles, files, maxSize, onFileReject, onFilesAdded, validator],
371
+ );
372
+
373
+ const handleDragEnter = useCallback(
374
+ (e: DragEvent<HTMLDivElement>) => {
375
+ if (disabled) return;
376
+ e.preventDefault();
377
+ dragCounter.current++;
378
+ if (dragCounter.current === 1) setIsDragOver(true);
379
+ },
380
+ [disabled],
381
+ );
382
+
383
+ const handleDragLeave = useCallback(
384
+ (e: DragEvent<HTMLDivElement>) => {
385
+ if (disabled) return;
386
+ e.preventDefault();
387
+ dragCounter.current = Math.max(0, dragCounter.current - 1);
388
+ if (dragCounter.current === 0) setIsDragOver(false);
389
+ },
390
+ [disabled],
391
+ );
392
+
393
+ const handleDragOver = useCallback(
394
+ (e: DragEvent<HTMLDivElement>) => {
395
+ // preventDefault on dragover signals "this is a valid drop target" — without
396
+ // it the drop event never fires.
397
+ if (disabled) return;
398
+ e.preventDefault();
399
+ },
400
+ [disabled],
401
+ );
402
+
403
+ const handleDrop = useCallback(
404
+ (e: DragEvent<HTMLDivElement>) => {
405
+ if (disabled) return;
406
+ e.preventDefault();
407
+ dragCounter.current = 0;
408
+ setIsDragOver(false);
409
+ const dropped = Array.from(e.dataTransfer.files);
410
+ if (dropped.length > 0) processFiles(dropped);
411
+ },
412
+ [disabled, processFiles],
413
+ );
414
+
415
+ const handleClick = useCallback(
416
+ (e: MouseEvent<HTMLDivElement>) => {
417
+ // A label-for click activates the input itself and bubbles here; the
418
+ // picker is already opening, so don't click it a second time.
419
+ if (disabled || e.target === inputRef.current) return;
420
+ inputRef.current?.click();
421
+ },
422
+ [disabled],
423
+ );
424
+
425
+ const handleKeyDown = useCallback(
426
+ (e: KeyboardEvent<HTMLDivElement>) => {
427
+ if (disabled) return;
428
+ if (e.key === 'Enter' || e.key === ' ') {
429
+ e.preventDefault();
430
+ inputRef.current?.click();
431
+ }
432
+ },
433
+ [disabled],
434
+ );
435
+
436
+ const handleInputChange = useCallback(
437
+ (e: ChangeEvent<HTMLInputElement>) => {
438
+ const picked = Array.from(e.target.files ?? []);
439
+ // Reset the input so the same file can be re-picked (browsers cache the
440
+ // last value; without this, picking the same file twice in a row doesn't
441
+ // fire change a second time).
442
+ e.target.value = '';
443
+ if (picked.length > 0) processFiles(picked);
444
+ },
445
+ [processFiles],
446
+ );
447
+
448
+ // {...rest} last so consumer overrides win (Pattern A).
449
+ return (
450
+ <div
451
+ ref={ref}
452
+ // The id belongs on the file input (see the prop); while the dropzone is
453
+ // hidden (single mode with a file) the root keeps it so scroll-to-id /
454
+ // focus-first-invalid still find the field.
455
+ id={showDropzone ? undefined : id}
456
+ className={clsx(styles.root, disabled && styles.disabled, className)}
457
+ {...rest}
458
+ >
459
+ {/* ONE region for the whole batch. Per-row regions were the obvious
460
+ shape and the wrong one: twelve files resolving inside two seconds is
461
+ twelve announcements. Rendered unconditionally with its text deferred
462
+ so a FileUpload that mounts mid-transfer still announces — see
463
+ CLAUDE.md Hard rule 10 and #502. */}
464
+ <span role="status" aria-live="polite" className={styles.srOnly}>
465
+ {batchStatus}
466
+ </span>
467
+ {showDropzone && (
468
+ <div
469
+ role="button"
470
+ tabIndex={disabled ? -1 : 0}
471
+ aria-labelledby={ariaLabelledBy}
472
+ // `dropzoneLabel &&`, not just the typeof: `dropzoneLabel=""` is a
473
+ // string, so without it the dropzone got a literal `aria-label=""`,
474
+ // which names nothing and falls through to name-from-content — where
475
+ // the label span is empty for the same reason. A role="button" with
476
+ // no accessible name (#535).
477
+ aria-label={
478
+ typeof dropzoneLabel === 'string' && dropzoneLabel
479
+ ? dropzoneLabel
480
+ : t('fileUpload.upload')
481
+ }
482
+ aria-describedby={ariaDescribedBy}
483
+ aria-invalid={invalid || undefined}
484
+ aria-disabled={disabled || undefined}
485
+ className={clsx(styles.dropzone, isDragOver && styles.dragOver)}
486
+ onDragEnter={handleDragEnter}
487
+ onDragOver={handleDragOver}
488
+ onDragLeave={handleDragLeave}
489
+ onDrop={handleDrop}
490
+ onClick={handleClick}
491
+ onKeyDown={handleKeyDown}
492
+ >
493
+ <input
494
+ ref={inputRef}
495
+ id={id}
496
+ type="file"
497
+ multiple={multiple}
498
+ accept={accept}
499
+ disabled={disabled}
500
+ onChange={handleInputChange}
501
+ className={styles.hiddenInput}
502
+ tabIndex={-1}
503
+ aria-hidden
504
+ />
505
+ <span className={styles.dropzoneIcon}>
506
+ {dropzoneIcon ?? <CloudUpload size={32} aria-hidden />}
507
+ </span>
508
+ <span className={styles.dropzoneLabel}>{dropzoneLabel || t('fileUpload.dragHint')}</span>
509
+ {dropzoneHint && <span className={styles.dropzoneHint}>{dropzoneHint}</span>}
510
+ </div>
511
+ )}
512
+ {files.length > 0 && (
513
+ <ul className={styles.list}>
514
+ {files.map((entry) => {
515
+ const Icon = iconForFile(entry.file);
516
+ return (
517
+ <li key={entry.id} className={clsx(styles.row, STATUS_CLASS[entry.status])}>
518
+ <span className={styles.rowIcon}>
519
+ <Icon size={20} aria-hidden />
520
+ </span>
521
+ <span className={styles.rowName} title={entry.file.name}>
522
+ {entry.file.name}
523
+ </span>
524
+ <span className={styles.rowMeta}>{formatBytes(entry.file.size)}</span>
525
+ <span className={styles.rowStatus}>
526
+ {entry.status === 'uploading' && (
527
+ <Progress
528
+ size="sm"
529
+ value={entry.progress}
530
+ aria-label={t('fileUpload.uploadingAriaLabel', { name: entry.file.name })}
531
+ />
532
+ )}
533
+ {entry.status === 'error' && entry.error && (
534
+ <span className={styles.rowErrorMsg}>{entry.error}</span>
535
+ )}
536
+ {entry.status === 'done' && (
537
+ <Check
538
+ className={styles.rowDoneIcon}
539
+ size={16}
540
+ role="img"
541
+ aria-label={t('fileUpload.done')}
542
+ />
543
+ )}
544
+ </span>
545
+ <Button
546
+ variant="ghost"
547
+ size="xs"
548
+ iconOnly
549
+ onClick={() => onFileRemove(entry)}
550
+ disabled={disabled}
551
+ // A failed row's message is plain text in a non-focusable
552
+ // <li>, so a user tabbing here later learned nothing about
553
+ // it. Fold the failure into the one name they DO reach.
554
+ aria-label={
555
+ entry.status === 'error'
556
+ ? t('fileUpload.removeFailedAriaLabel', { name: entry.file.name })
557
+ : t('fileUpload.removeAriaLabel', { name: entry.file.name })
558
+ }
559
+ >
560
+ <X size={16} aria-hidden />
561
+ </Button>
562
+ </li>
563
+ );
564
+ })}
565
+ </ul>
566
+ )}
567
+ </div>
568
+ );
569
+ });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Format a byte count into a human-readable string with a unit.
3
+ *
4
+ * Uses the 1024-based (KiB) convention internally but labels as `B`/`KB`/`MB`/
5
+ * `GB` for consumer familiarity — the CRM presents file sizes the way Finder /
6
+ * Explorer / Drive present them (1024 bytes = "1 KB"), not the SI 1000-base.
7
+ *
8
+ * Examples:
9
+ * - `formatBytes(0)` → `"0 B"`
10
+ * - `formatBytes(512)` → `"512 B"`
11
+ * - `formatBytes(1024)` → `"1 KB"`
12
+ * - `formatBytes(1536)` → `"1.5 KB"`
13
+ * - `formatBytes(1024 * 1024 * 1.5)` → `"1.5 MB"`
14
+ * - `formatBytes(1024 * 1024 * 1024 * 2.3)` → `"2.3 GB"`
15
+ *
16
+ * Bytes show as integers (no decimals). KB/MB/GB show as integers when whole,
17
+ * otherwise one decimal place.
18
+ *
19
+ * Private to the FileUpload directory — not re-exported from the package.
20
+ */
21
+ export function formatBytes(bytes: number): string {
22
+ if (bytes < 1024) return `${bytes} B`;
23
+ if (bytes < 1024 * 1024) return formatWithUnit(bytes / 1024, 'KB');
24
+ if (bytes < 1024 * 1024 * 1024) return formatWithUnit(bytes / (1024 * 1024), 'MB');
25
+ return formatWithUnit(bytes / (1024 * 1024 * 1024), 'GB');
26
+ }
27
+
28
+ function formatWithUnit(value: number, unit: string): string {
29
+ // Integer when whole; one decimal otherwise. Avoids "1.0 KB" while keeping
30
+ // "1.5 KB" / "2.3 GB" precision.
31
+ const rounded = Math.round(value * 10) / 10;
32
+ const display = Number.isInteger(rounded) ? rounded.toString() : rounded.toFixed(1);
33
+ return `${display} ${unit}`;
34
+ }