@flikk/ui 1.0.0-beta.30 → 1.0.0-beta.31

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 (295) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/dist/components/ai/AgentStatus/AgentStatus.d.ts +3 -0
  3. package/dist/components/ai/AgentStatus/AgentStatus.js +144 -0
  4. package/dist/components/ai/AgentStatus/AgentStatus.theme.d.ts +2 -0
  5. package/dist/components/ai/AgentStatus/AgentStatus.theme.js +37 -0
  6. package/dist/components/ai/AgentStatus/AgentStatus.types.d.ts +116 -0
  7. package/dist/components/ai/AgentStatus/index.d.ts +3 -0
  8. package/dist/components/ai/CodeBlock/CodeBlock.js +3 -3
  9. package/dist/components/ai/CodeBlock/CodeBlock.theme.js +1 -1
  10. package/dist/components/ai/PromptInput/PromptInput.js +13 -14
  11. package/dist/components/ai/PromptInput/PromptInput.theme.js +1 -1
  12. package/dist/components/ai/PromptInput/VoiceRecorder.js +3 -3
  13. package/dist/components/ai/Reasoning/Reasoning.d.ts +5 -0
  14. package/dist/components/ai/Reasoning/Reasoning.js +16 -0
  15. package/dist/components/ai/Reasoning/Reasoning.types.d.ts +20 -0
  16. package/dist/components/ai/Reasoning/index.d.ts +2 -0
  17. package/dist/components/ai/SourceCitation/SourceCitation.d.ts +4 -0
  18. package/dist/components/ai/SourceCitation/SourceCitation.js +29 -0
  19. package/dist/components/ai/SourceCitation/SourceCitation.theme.d.ts +2 -0
  20. package/dist/components/ai/SourceCitation/SourceCitation.theme.js +23 -0
  21. package/dist/components/ai/SourceCitation/SourceCitation.types.d.ts +55 -0
  22. package/dist/components/ai/SourceCitation/index.d.ts +3 -0
  23. package/dist/components/ai/StreamingResponse/ErrorDisplay.js +2 -2
  24. package/dist/components/ai/StreamingResponse/StreamingResponse.js +1 -3
  25. package/dist/components/ai/StreamingResponse/StreamingResponse.theme.js +3 -1
  26. package/dist/components/ai/TokenCounter/TokenCounter.js +2 -2
  27. package/dist/components/ai/index.d.ts +3 -0
  28. package/dist/components/ai/index.js +6 -3
  29. package/dist/components/ai/shared/StatusIndicator.d.ts +8 -0
  30. package/dist/components/ai/shared/StatusIndicator.js +34 -0
  31. package/dist/components/canvas/CanvasToolbar.js +2 -2
  32. package/dist/components/canvas/NodeControls.js +2 -2
  33. package/dist/components/canvas/NodeRenderer.js +2 -2
  34. package/dist/components/charts/ActivityRings/ActivityRings.js +2 -2
  35. package/dist/components/charts/AreaChart/AreaChart.js +21 -32
  36. package/dist/components/charts/AreaChart/AreaChart.theme.d.ts +1 -6
  37. package/dist/components/charts/AreaChart/AreaChart.theme.js +7 -19
  38. package/dist/components/charts/AreaChart/AreaChart.types.d.ts +18 -11
  39. package/dist/components/charts/AreaChart/AreaChart.types.js +10 -3
  40. package/dist/components/charts/AreaChart/index.d.ts +1 -1
  41. package/dist/components/charts/BarChart/BarChart.js +9 -28
  42. package/dist/components/charts/BarChart/BarChart.types.d.ts +20 -16
  43. package/dist/components/charts/BarChart/BarChart.types.js +11 -4
  44. package/dist/components/charts/ComboChart/BarRenderer.js +8 -5
  45. package/dist/components/charts/ComboChart/ComboChart.js +11 -24
  46. package/dist/components/charts/ComboChart/ComboChart.types.d.ts +15 -13
  47. package/dist/components/charts/ComboChart/ComboChart.types.js +14 -2
  48. package/dist/components/charts/ComboChart/LineRenderer.js +15 -10
  49. package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.d.ts +16 -0
  50. package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.js +237 -0
  51. package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.types.d.ts +62 -0
  52. package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.types.js +21 -0
  53. package/dist/components/charts/HorizontalBarChart/index.d.ts +3 -0
  54. package/dist/components/charts/LineChart/LineChart.js +9 -26
  55. package/dist/components/charts/LineChart/LineChart.types.d.ts +11 -14
  56. package/dist/components/charts/LineChart/LineChart.types.js +11 -3
  57. package/dist/components/charts/LollipopChart/LollipopChart.d.ts +3 -0
  58. package/dist/components/charts/LollipopChart/LollipopChart.js +361 -0
  59. package/dist/components/charts/LollipopChart/LollipopChart.types.d.ts +42 -0
  60. package/dist/components/charts/LollipopChart/LollipopChart.types.js +12 -0
  61. package/dist/components/charts/LollipopChart/index.d.ts +3 -0
  62. package/dist/components/charts/ScatterPlot/ScatterPlot.js +5 -4
  63. package/dist/components/charts/ScatterPlot/ScatterPlot.types.d.ts +14 -19
  64. package/dist/components/charts/ScatterPlot/ScatterPlot.types.js +9 -2
  65. package/dist/components/charts/StackedBarChart/StackedBarChart.js +150 -132
  66. package/dist/components/charts/StackedBarChart/StackedBarChart.types.d.ts +20 -18
  67. package/dist/components/charts/StackedBarChart/StackedBarChart.types.js +10 -4
  68. package/dist/components/charts/hooks/useChartAccessibility.d.ts +1 -1
  69. package/dist/components/charts/hooks/useChartAccessibility.js +3 -0
  70. package/dist/components/charts/hooks/useTooltipPosition.js +18 -6
  71. package/dist/components/charts/index.d.ts +6 -0
  72. package/dist/components/charts/index.js +6 -0
  73. package/dist/components/charts/shared/ChartAxis/CategoryYAxis.d.ts +14 -0
  74. package/dist/components/charts/shared/ChartAxis/CategoryYAxis.js +35 -0
  75. package/dist/components/charts/shared/ChartAxis/ChartAxis.types.d.ts +69 -8
  76. package/dist/components/charts/shared/ChartAxis/ValueXAxis.d.ts +14 -0
  77. package/dist/components/charts/shared/ChartAxis/ValueXAxis.js +64 -0
  78. package/dist/components/charts/shared/ChartAxis/XAxis.js +1 -1
  79. package/dist/components/charts/shared/ChartAxis/YAxis.js +2 -2
  80. package/dist/components/charts/shared/ChartAxis/index.d.ts +3 -1
  81. package/dist/components/charts/shared/ChartGrid/HorizontalGrid.d.ts +0 -2
  82. package/dist/components/charts/shared/ChartGrid/HorizontalGrid.js +1 -1
  83. package/dist/components/charts/shared/ChartGrid/VerticalGrid.d.ts +29 -0
  84. package/dist/components/charts/shared/ChartGrid/VerticalGrid.js +26 -0
  85. package/dist/components/charts/shared/ChartGrid/index.d.ts +2 -0
  86. package/dist/components/charts/shared/ChartMarker/ChartMarker.js +7 -1
  87. package/dist/components/charts/theme/chart.theme.js +18 -23
  88. package/dist/components/charts/types/chart.types.d.ts +9 -5
  89. package/dist/components/charts/types/chart.types.js +1 -7
  90. package/dist/components/charts/utils/animation-utils.js +1 -3
  91. package/dist/components/charts/utils/bar-emphasis.d.ts +32 -3
  92. package/dist/components/charts/utils/bar-emphasis.js +24 -8
  93. package/dist/components/charts/utils/index.d.ts +1 -0
  94. package/dist/components/charts/utils/series-color.d.ts +7 -0
  95. package/dist/components/charts/utils/series-color.js +20 -0
  96. package/dist/components/core/Accordion/Accordion.theme.js +2 -2
  97. package/dist/components/core/Accordion/AccordionTrigger.js +2 -2
  98. package/dist/components/core/Alert/Alert.animations.d.ts +2 -1
  99. package/dist/components/core/Alert/Alert.animations.js +21 -6
  100. package/dist/components/core/Alert/Alert.js +7 -7
  101. package/dist/components/core/Avatar/Avatar.js +50 -42
  102. package/dist/components/core/Avatar/Avatar.theme.d.ts +8 -4
  103. package/dist/components/core/Avatar/Avatar.theme.js +64 -8
  104. package/dist/components/core/Avatar/Avatar.types.d.ts +13 -2
  105. package/dist/components/core/Avatar/index.d.ts +1 -1
  106. package/dist/components/core/AvatarGroup/AvatarGroup.js +6 -8
  107. package/dist/components/core/AvatarGroup/AvatarGroup.theme.d.ts +1 -2
  108. package/dist/components/core/AvatarGroup/AvatarGroup.theme.js +2 -6
  109. package/dist/components/core/AvatarGroup/AvatarGroup.types.d.ts +0 -6
  110. package/dist/components/core/AvatarGroup/index.d.ts +1 -1
  111. package/dist/components/core/Badge/Badge.animations.js +4 -2
  112. package/dist/components/core/Badge/Badge.js +9 -4
  113. package/dist/components/core/Badge/Badge.theme.js +18 -12
  114. package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.d.ts +1 -4
  115. package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +2 -2
  116. package/dist/components/core/Button/Button.js +9 -17
  117. package/dist/components/core/Button/Button.ripple.js +2 -2
  118. package/dist/components/core/Button/Button.theme.d.ts +2 -0
  119. package/dist/components/core/Button/Button.theme.js +34 -21
  120. package/dist/components/core/Button/Button.types.d.ts +18 -1
  121. package/dist/components/core/Calendar/Calendar.js +20 -5
  122. package/dist/components/core/Calendar/Calendar.theme.js +14 -22
  123. package/dist/components/core/Calendar/Calendar.types.d.ts +0 -1
  124. package/dist/components/core/Calendar/CalendarMini/CalendarMini.js +3 -3
  125. package/dist/components/core/Card/Card.theme.js +12 -4
  126. package/dist/components/core/Card/CardBody.js +1 -1
  127. package/dist/components/core/Card/CardFooter.js +1 -1
  128. package/dist/components/core/Card/CardHeader.js +1 -1
  129. package/dist/components/core/Carousel/CarouselNext.js +2 -2
  130. package/dist/components/core/Carousel/CarouselPrevious.js +2 -2
  131. package/dist/components/core/CommandPalette/CommandPalette.js +5 -4
  132. package/dist/components/core/CommandPalette/CommandPalette.theme.js +3 -7
  133. package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +2 -0
  134. package/dist/components/core/ContextMenu/ContextMenuItem.js +2 -2
  135. package/dist/components/core/ContextMenu/ContextMenuSubTrigger.js +2 -2
  136. package/dist/components/core/Drawer/Drawer.theme.js +7 -4
  137. package/dist/components/core/Drawer/DrawerHeader.js +2 -2
  138. package/dist/components/core/Dropdown/Dropdown.theme.js +3 -1
  139. package/dist/components/core/Dropdown/DropdownItem.js +2 -2
  140. package/dist/components/core/Dropdown/DropdownTrigger.js +16 -5
  141. package/dist/components/core/HeroCard/HeroCard.theme.js +5 -2
  142. package/dist/components/core/Kbd/Kbd.theme.js +4 -2
  143. package/dist/components/core/Link/Link.js +6 -4
  144. package/dist/components/core/Message/Message.js +2 -2
  145. package/dist/components/core/Message/Message.theme.js +5 -7
  146. package/dist/components/core/Message/MessageAudio.js +2 -2
  147. package/dist/components/core/Message/MessageFile.js +2 -2
  148. package/dist/components/core/Message/MessageLink.js +2 -2
  149. package/dist/components/core/Message/MessageStatusIndicator.js +5 -5
  150. package/dist/components/core/Modal/Modal.d.ts +1 -1
  151. package/dist/components/core/Modal/Modal.js +2 -2
  152. package/dist/components/core/Modal/Modal.theme.js +9 -4
  153. package/dist/components/core/Modal/ModalHeader.js +2 -2
  154. package/dist/components/core/ModalStack/ModalStackModal.js +2 -2
  155. package/dist/components/core/NavItem/NavItem.js +6 -6
  156. package/dist/components/core/OfflineIndicator/OfflineIndicator.animations.d.ts +22 -0
  157. package/dist/components/core/OfflineIndicator/OfflineIndicator.animations.js +54 -0
  158. package/dist/components/core/OfflineIndicator/OfflineIndicator.js +23 -24
  159. package/dist/components/core/OfflineIndicator/OfflineIndicator.theme.js +25 -6
  160. package/dist/components/core/OfflineIndicator/OfflineIndicator.types.d.ts +17 -0
  161. package/dist/components/core/PageHeading/PageHeading.theme.js +1 -1
  162. package/dist/components/core/PageHeading/PageHeadingBackButton.js +2 -2
  163. package/dist/components/core/Pagination/Pagination.js +3 -3
  164. package/dist/components/core/Pagination/Pagination.theme.js +3 -3
  165. package/dist/components/core/Pill/Pill.js +4 -2
  166. package/dist/components/core/Pill/Pill.theme.js +6 -4
  167. package/dist/components/core/Rating/Rating.js +4 -5
  168. package/dist/components/core/Rating/Rating.theme.js +1 -1
  169. package/dist/components/core/Segmented/Segmented.theme.js +9 -6
  170. package/dist/components/core/Sidebar/Sidebar.types.d.ts +4 -2
  171. package/dist/components/core/Sidebar/SidebarHeader.js +2 -2
  172. package/dist/components/core/Sidebar/SidebarMobileTrigger.js +2 -2
  173. package/dist/components/core/Sidebar/SidebarSearch.js +3 -3
  174. package/dist/components/core/Sidebar/SidebarToggle.js +3 -3
  175. package/dist/components/core/Sidebar/SidebarUserProfile.js +2 -2
  176. package/dist/components/core/Sortable/Sortable.js +2 -2
  177. package/dist/components/core/Tag/Tag.js +2 -2
  178. package/dist/components/core/Tag/Tag.theme.js +4 -2
  179. package/dist/components/core/Toast/Toast.js +3 -3
  180. package/dist/components/core/Toast/Toast.types.d.ts +1 -1
  181. package/dist/components/core/Toast/useToast.js +5 -5
  182. package/dist/components/core/Tooltip/Tooltip.d.ts +1 -1
  183. package/dist/components/core/Tooltip/Tooltip.js +42 -10
  184. package/dist/components/core/Tooltip/Tooltip.types.d.ts +25 -0
  185. package/dist/components/core/Tree/Tree.js +2 -2
  186. package/dist/components/core/shared/interaction.animations.d.ts +51 -0
  187. package/dist/components/core/shared/interaction.animations.js +59 -0
  188. package/dist/components/data-display/Feed/Feed.js +2 -2
  189. package/dist/components/data-display/GanttChart/GanttChart.theme.js +1 -1
  190. package/dist/components/data-display/GanttChart/GanttChartPanel.js +2 -2
  191. package/dist/components/data-display/GanttChart/GanttTableGroupRow.js +3 -3
  192. package/dist/components/data-display/GanttChart/GanttTaskContextMenu.js +2 -2
  193. package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
  194. package/dist/components/data-display/KPI/KPI.js +2 -2
  195. package/dist/components/data-display/KPI/KPI.theme.js +4 -4
  196. package/dist/components/data-display/Metric/Metric.js +2 -2
  197. package/dist/components/data-display/Metric/Metric.theme.js +25 -8
  198. package/dist/components/data-display/Metric/Metric.types.d.ts +1 -1
  199. package/dist/components/data-display/Table/Table.theme.js +9 -9
  200. package/dist/components/data-display/Table/TableActions.js +2 -2
  201. package/dist/components/data-display/Table/TableActionsMenu.js +1 -1
  202. package/dist/components/data-display/Table/TableColumnManager.js +2 -2
  203. package/dist/components/data-display/Table/TableDeclarative.js +3 -3
  204. package/dist/components/data-display/Table/TableFilter.js +4 -4
  205. package/dist/components/data-display/Table/TableHeader.js +3 -3
  206. package/dist/components/data-display/Table/TableRow.js +2 -2
  207. package/dist/components/data-display/Table/TableSelectionHeader.js +3 -3
  208. package/dist/components/data-display/Timeline/TimelineMarker.js +2 -2
  209. package/dist/components/effects/CustomCursor/CustomCursor.js +2 -2
  210. package/dist/components/effects/CustomCursor/CustomCursor.theme.js +2 -2
  211. package/dist/components/effects/DotPattern/DotPattern.js +6 -0
  212. package/dist/components/effects/DotPattern/DotPattern.types.d.ts +4 -2
  213. package/dist/components/effects/MeshGradient/MeshGradient.utils.d.ts +1 -1
  214. package/dist/components/effects/MeshGradient/MeshGradient.utils.js +1 -1
  215. package/dist/components/forms/Checkbox/Checkbox.js +2 -1
  216. package/dist/components/forms/Checkbox/Checkbox.theme.js +2 -2
  217. package/dist/components/forms/ColorPicker/ColorPickerEyeDropper.js +2 -2
  218. package/dist/components/forms/ColorPicker/ColorPickerGradient.js +2 -2
  219. package/dist/components/forms/ColorPicker/ColorPickerHeader.js +2 -2
  220. package/dist/components/forms/ColorPicker/ColorPickerImage.js +3 -3
  221. package/dist/components/forms/ColorPicker/ColorPickerInput.js +2 -2
  222. package/dist/components/forms/ColorPicker/ColorPickerPresets.js +2 -2
  223. package/dist/components/forms/ColorPicker/ColorPickerSavedColors.js +2 -2
  224. package/dist/components/forms/Combobox/Combobox.js +3 -3
  225. package/dist/components/forms/Combobox/Combobox.theme.js +2 -2
  226. package/dist/components/forms/DatePicker/DatePicker.animations.js +16 -25
  227. package/dist/components/forms/DatePicker/DatePicker.theme.js +2 -2
  228. package/dist/components/forms/DatePicker/DatePickerBody.js +1 -1
  229. package/dist/components/forms/DatePicker/DatePickerTrigger.js +2 -2
  230. package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +3 -3
  231. package/dist/components/forms/DateRangePicker/DateRangePickerBody.js +1 -1
  232. package/dist/components/forms/DateRangePicker/DateRangePickerPresets.js +2 -2
  233. package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +2 -2
  234. package/dist/components/forms/FileUpload/FileUpload.js +4 -4
  235. package/dist/components/forms/FileUpload/FileUpload.theme.js +5 -1
  236. package/dist/components/forms/FileUpload/FileUploadProgress.js +7 -7
  237. package/dist/components/forms/Input/Input.js +6 -7
  238. package/dist/components/forms/Input/Input.theme.js +19 -15
  239. package/dist/components/forms/InputAddress/InputAddress.js +10 -3
  240. package/dist/components/forms/InputCounter/InputCounter.js +3 -3
  241. package/dist/components/forms/InputOTP/InputOTP.d.ts +3 -12
  242. package/dist/components/forms/InputOTP/InputOTP.js +140 -151
  243. package/dist/components/forms/InputOTP/InputOTP.theme.d.ts +5 -1
  244. package/dist/components/forms/InputOTP/InputOTP.theme.js +40 -5
  245. package/dist/components/forms/InputOTP/InputOTP.types.d.ts +45 -2
  246. package/dist/components/forms/InputOTP/index.d.ts +1 -1
  247. package/dist/components/forms/InputTag/InputTag.js +5 -5
  248. package/dist/components/forms/InputTag/InputTag.theme.js +1 -2
  249. package/dist/components/forms/Mention/Mention.theme.js +3 -5
  250. package/dist/components/forms/Radio/Radio.js +3 -2
  251. package/dist/components/forms/Radio/Radio.theme.js +2 -2
  252. package/dist/components/forms/RichTextEditor/RichTextEditor.js +22 -23
  253. package/dist/components/forms/Select/Select.d.ts +5 -1
  254. package/dist/components/forms/Select/Select.js +12 -11
  255. package/dist/components/forms/Select/Select.theme.js +4 -4
  256. package/dist/components/forms/Select/Select.types.d.ts +4 -1
  257. package/dist/components/forms/SelectableCard/SelectableCard.animations.d.ts +1 -2
  258. package/dist/components/forms/SelectableCard/SelectableCard.animations.js +6 -7
  259. package/dist/components/forms/SelectableCard/SelectableCard.js +1 -1
  260. package/dist/components/forms/Signature/Signature.js +1 -1
  261. package/dist/components/forms/Slider/Slider.js +24 -11
  262. package/dist/components/forms/Slider/Slider.theme.js +9 -3
  263. package/dist/components/forms/Switch/Switch.theme.js +1 -1
  264. package/dist/components/forms/TimePicker/TimePicker.theme.js +6 -7
  265. package/dist/components/forms/TimePicker/TimePickerContent.js +1 -1
  266. package/dist/components/forms/TimePicker/TimePickerTrigger.js +3 -3
  267. package/dist/components/forms/forms.theme.js +23 -12
  268. package/dist/components/forms/index.js +1 -1
  269. package/dist/components/generative/registry.js +195 -2
  270. package/dist/components/generative/resolvers.js +16 -16
  271. package/dist/components/generative/schema.generated.js +2687 -379
  272. package/dist/components/layout/Section/Section.theme.js +1 -1
  273. package/dist/generative.schema.json +2687 -379
  274. package/dist/hooks/index.d.ts +2 -0
  275. package/dist/hooks/useElapsedTime.d.ts +28 -0
  276. package/dist/hooks/useElapsedTime.js +37 -0
  277. package/dist/hooks/useSelectPortal.d.ts +10 -2
  278. package/dist/hooks/useSelectPortal.js +6 -3
  279. package/dist/index.js +1 -1
  280. package/dist/registry.json +835 -261
  281. package/dist/shadcn-compat.css +1 -1
  282. package/dist/styles.css +1 -1
  283. package/dist/theme.css +5 -1
  284. package/dist/themes/ember.css +131 -0
  285. package/dist/themes/iris.css +94 -0
  286. package/dist/themes/jade.css +139 -0
  287. package/dist/tools.json +2705 -361
  288. package/dist/utils/colorUtils.d.ts +6 -6
  289. package/dist/utils/colorUtils.js +8 -8
  290. package/package.json +9 -6
  291. package/src/global.scss +15 -4
  292. package/src/styles/theme.css +440 -152
  293. package/tailwind.preset.cjs +52 -34
  294. package/dist/components/core/Pill/Pill.animations.d.ts +0 -32
  295. package/dist/components/core/Pill/Pill.animations.js +0 -16
@@ -0,0 +1,361 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useId, useState, useEffect, useMemo, useRef } from 'react';
4
+ import { useReducedMotion, motion, AnimatePresence } from 'motion/react';
5
+ import { cn } from '../../../utils/cn.js';
6
+ import { contrastTone } from '../../../utils/colorUtils.js';
7
+ import { useChartDimensions } from '../hooks/useChartDimensions.js';
8
+ import { useChartValidation } from '../hooks/useChartValidation.js';
9
+ import { useChartAccessibility } from '../hooks/useChartAccessibility.js';
10
+ import { useTooltipPosition } from '../hooks/useTooltipPosition.js';
11
+ import { ChartErrorBoundary } from '../shared/ChartErrorBoundary/ChartErrorBoundary.js';
12
+ import { ChartTooltip } from '../shared/ChartTooltip/ChartTooltip.js';
13
+ import { HorizontalGrid } from '../shared/ChartGrid/HorizontalGrid.js';
14
+ import { YAxis } from '../shared/ChartAxis/YAxis.js';
15
+ import { XAxis } from '../shared/ChartAxis/XAxis.js';
16
+ import { CARTESIAN_ENTRANCE_DURATION, LINE_DOT_STAGGER, dotEntranceTransition, RISE_FROM_BASELINE, barEntranceTransition, BAR_GROUP_STAGGER } from '../utils/animation-utils.js';
17
+ import { springs } from '../../../theme/animations.js';
18
+ import { X_AXIS_MARGIN } from '../types/chart.types.js';
19
+ import { LOLLIPOP_CHART_DEFAULTS } from './LollipopChart.types.js';
20
+ import { Warning } from '@phosphor-icons/react';
21
+
22
+ /** Top margin reserved so the selected mark's pill has clearance from the top edge. */
23
+ const LOLLIPOP_TOP_MARGIN = 48;
24
+ const LollipopChart = ({ data, config, showGrid = LOLLIPOP_CHART_DEFAULTS.showGrid, showXAxis = LOLLIPOP_CHART_DEFAULTS.showXAxis, showYAxis = LOLLIPOP_CHART_DEFAULTS.showYAxis, minValue: propMinValue, maxValue: propMaxValue, dotRadius = LOLLIPOP_CHART_DEFAULTS.dotRadius, stemWidth = LOLLIPOP_CHART_DEFAULTS.stemWidth, color, showAnimation = LOLLIPOP_CHART_DEFAULTS.showAnimation, enableKeyboardNavigation = LOLLIPOP_CHART_DEFAULTS.enableKeyboardNavigation, showValidationWarnings = process.env.NODE_ENV === "development", darkMode = false, selectedIndex: selectedIndexProp, defaultSelectedIndex = LOLLIPOP_CHART_DEFAULTS.defaultSelectedIndex, onSelectedIndexChange, valueFormatter, className, title, description, width, height, ...props }) => {
25
+ var _a, _b, _c;
26
+ const chartId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
27
+ const shouldReduceMotion = useReducedMotion();
28
+ const [hasAnimated, setHasAnimated] = useState(false);
29
+ useEffect(() => {
30
+ setHasAnimated(true);
31
+ }, [shouldReduceMotion, showAnimation]);
32
+ // Single-series only: warn (dev-only) if the caller passes more than one
33
+ // config key, and always render the first.
34
+ const dataKeys = useMemo(() => Object.keys(config || {}), [config]);
35
+ if (process.env.NODE_ENV !== "production" &&
36
+ dataKeys.length > 1) {
37
+ console.warn("[LollipopChart]: `config` should have exactly one key — LollipopChart is single-series only. " +
38
+ `Rendering "${dataKeys[0]}" and ignoring the rest.`);
39
+ }
40
+ const dataKey = dataKeys[0];
41
+ const isSelectionControlled = selectedIndexProp !== undefined;
42
+ const [internalSelectedIndex, setInternalSelectedIndex] = useState(defaultSelectedIndex);
43
+ const resolvedSelectedIndex = isSelectionControlled ? selectedIndexProp : internalSelectedIndex;
44
+ if (process.env.NODE_ENV !== "production") {
45
+ if (isSelectionControlled && defaultSelectedIndex !== LOLLIPOP_CHART_DEFAULTS.defaultSelectedIndex) {
46
+ console.warn("[LollipopChart]: both `selectedIndex` and `defaultSelectedIndex` were provided — `selectedIndex` (controlled) wins.");
47
+ }
48
+ if (isSelectionControlled && !onSelectedIndexChange) {
49
+ console.warn("[LollipopChart]: `selectedIndex` was provided without `onSelectedIndexChange` — the chart will not be able to update selection.");
50
+ }
51
+ }
52
+ const handleSelect = (index) => {
53
+ if (!isSelectionControlled) {
54
+ setInternalSelectedIndex(index);
55
+ }
56
+ onSelectedIndexChange === null || onSelectedIndexChange === void 0 ? void 0 : onSelectedIndexChange(index);
57
+ };
58
+ const formatValue = (value, item) => valueFormatter ? valueFormatter(value, item) : String(value);
59
+ const { validation, sanitizedData, isValid, hasWarnings, safeScale, safeMath, } = useChartValidation(data, config, { autoSanitize: true });
60
+ const seriesColor = (_b = color !== null && color !== void 0 ? color : (dataKey && ((_a = config[dataKey]) === null || _a === void 0 ? void 0 : _a.color))) !== null && _b !== void 0 ? _b : "var(--color-primary)";
61
+ const containerRef = useRef(null);
62
+ const { dimensions, measured } = useChartDimensions(containerRef);
63
+ const isMobile = typeof window !== "undefined" &&
64
+ ("ontouchstart" in window || navigator.maxTouchPoints > 0);
65
+ const { tooltipData, tooltipRef, handleMouseEnter, handleMouseMove, handleMouseLeave, } = useTooltipPosition({ containerRef, isMobile });
66
+ const { chartAccessibilityProps, descriptionProps, focusedElementIndex, isKeyboardMode, handleMouseInteraction, } = useChartAccessibility({
67
+ chartType: "lollipop",
68
+ data: sanitizedData,
69
+ config,
70
+ title,
71
+ description,
72
+ enableKeyboardNavigation,
73
+ });
74
+ const [hoveredIndex, setHoveredIndex] = useState(null);
75
+ const activeIndex = hoveredIndex !== null
76
+ ? hoveredIndex
77
+ : isKeyboardMode && focusedElementIndex >= 0
78
+ ? focusedElementIndex
79
+ : null;
80
+ const isAnyActive = activeIndex !== null;
81
+ const margin = useMemo(() => ({
82
+ top: LOLLIPOP_TOP_MARGIN,
83
+ right: showYAxis ? 20 : 16,
84
+ bottom: showXAxis ? X_AXIS_MARGIN : 0,
85
+ left: showYAxis ? 60 : 16,
86
+ }), [showXAxis, showYAxis]);
87
+ const innerWidth = Math.max(0, dimensions.width - margin.left - margin.right);
88
+ const innerHeight = Math.max(0, dimensions.height - margin.top - margin.bottom);
89
+ const scaleRange = safeScale.calculateRange({
90
+ minValue: propMinValue,
91
+ maxValue: propMaxValue,
92
+ includeZero: true,
93
+ });
94
+ const { min: minValue, max: maxValue, hasValidData } = scaleRange;
95
+ useEffect(() => {
96
+ var _a, _b;
97
+ if (isKeyboardMode &&
98
+ focusedElementIndex >= 0 &&
99
+ focusedElementIndex < sanitizedData.length &&
100
+ dataKey) {
101
+ const item = sanitizedData[focusedElementIndex];
102
+ const rawValue = item[dataKey];
103
+ if (typeof rawValue === "number" && isFinite(rawValue)) {
104
+ const categoryGap = 24;
105
+ const totalGap = Math.max(0, (sanitizedData.length - 1) * categoryGap);
106
+ const categoryWidth = safeMath.divide(Math.max(0, innerWidth - totalGap), sanitizedData.length, 0);
107
+ const cx = margin.left + focusedElementIndex * (categoryWidth + categoryGap) + categoryWidth / 2;
108
+ const range = maxValue - minValue;
109
+ const scaledZero = range > 0 ? safeMath.scale(0, minValue, maxValue, 0, innerHeight) : 0;
110
+ const zeroY = margin.top + innerHeight - scaledZero;
111
+ const scaledValue = range > 0 ? safeMath.scale(rawValue, minValue, maxValue, 0, innerHeight) : 0;
112
+ const isNeg = rawValue < 0;
113
+ const dotY = isNeg
114
+ ? zeroY + Math.abs(scaledValue - scaledZero)
115
+ : zeroY - Math.abs(scaledValue - scaledZero);
116
+ const syntheticEvent = new MouseEvent("mouseenter", { clientX: cx, clientY: dotY });
117
+ handleMouseEnter(syntheticEvent, {
118
+ category: ((_a = item.name) !== null && _a !== void 0 ? _a : `Category ${focusedElementIndex + 1}`).toString(),
119
+ series: [
120
+ { key: dataKey, label: ((_b = config[dataKey]) === null || _b === void 0 ? void 0 : _b.label) || dataKey, value: rawValue, color: seriesColor },
121
+ ],
122
+ }, { x: cx, y: dotY, dimensions });
123
+ }
124
+ }
125
+ else if (isKeyboardMode && focusedElementIndex === -1) {
126
+ handleMouseLeave();
127
+ }
128
+ }, [
129
+ isKeyboardMode, focusedElementIndex, sanitizedData, config, dimensions, margin,
130
+ innerWidth, innerHeight, minValue, maxValue, handleMouseEnter, handleMouseLeave,
131
+ dataKey, seriesColor, safeMath,
132
+ ]);
133
+ // --- Every hook above this line runs unconditionally, every render — same
134
+ // discipline BarChart documents (BarChart.tsx ~line 674): a chart going
135
+ // from populated -> empty/invalid data must never change its hook count.
136
+ if (!isValid) {
137
+ return (jsxs("div", { className: cn("chart-container flex flex-col items-center justify-center p-6 border border-[var(--color-border)] rounded-lg text-2xl", className), role: "alert", "aria-live": "polite", ...props, children: [jsxs("div", { className: "flex flex-col items-center gap-4 mb-1", children: [jsx(Warning, { className: "size-6 text-[var(--color-danger)]" }), jsx("div", { className: "text-base font-semibold text-[var(--color-text-secondary)]", children: "Invalid Chart Data" })] }), jsx("p", { className: "text-base text-[var(--color-text-secondary)]/70 mb-2", children: ((_c = validation.errors[0]) === null || _c === void 0 ? void 0 : _c.message) ||
138
+ "Unable to render chart due to data validation errors" })] }));
139
+ }
140
+ if (!sanitizedData || sanitizedData.length === 0) {
141
+ return (jsx("div", { className: cn("chart-container flex items-center justify-center text-[var(--color-text-muted)]", className), role: "img", "aria-label": "Empty lollipop chart", ...props, children: jsx("span", { children: "No data available" }) }));
142
+ }
143
+ if (!hasValidData) {
144
+ return (jsx("div", { className: cn("chart-container flex items-center justify-center text-[var(--color-text-muted)]", className), role: "img", "aria-label": "Empty lollipop chart - no valid data", ...props, children: jsx("span", { children: "No valid numeric data available" }) }));
145
+ }
146
+ return (jsx(ChartErrorBoundary, { className: className, showErrorDetails: process.env.NODE_ENV === "development", children: jsxs("div", { ref: containerRef, className: cn("chart-container w-full h-full flex flex-col", darkMode && "dark", className), style: {
147
+ width: width !== undefined
148
+ ? typeof width === "number"
149
+ ? `${width}px`
150
+ : width
151
+ : undefined,
152
+ height: height !== undefined
153
+ ? typeof height === "number"
154
+ ? `${height}px`
155
+ : height
156
+ : undefined,
157
+ }, ...chartAccessibilityProps, onKeyDown: (e) => {
158
+ chartAccessibilityProps.onKeyDown(e);
159
+ if ((e.key === "Enter" || e.key === " ") && isKeyboardMode && focusedElementIndex >= 0) {
160
+ e.preventDefault();
161
+ handleSelect(resolvedSelectedIndex === focusedElementIndex ? null : focusedElementIndex);
162
+ }
163
+ }, onMouseEnter: handleMouseInteraction, onMouseMove: handleMouseInteraction, ...props, children: [descriptionProps && jsx("div", { ...descriptionProps }), showValidationWarnings && hasWarnings && (jsxs("div", { className: "absolute top-2 left-2 right-2 z-10 p-2 bg-[var(--color-warning-50)] border border-[var(--color-warning-200)] rounded text-sm text-[var(--color-warning)] shadow-sm", children: [jsx("strong", { children: "Chart Warnings:" }), jsx("ul", { className: "mt-1", children: validation.warnings.map((warning, index) => (jsxs("li", { children: ["\u2022 ", warning.message] }, index))) })] })), jsx("div", { className: "relative flex-1 min-h-0", children: jsxs("svg", { width: "100%", height: "100%", viewBox: `0 0 ${dimensions.width} ${dimensions.height}`, style: { overflow: "visible" }, children: [jsxs("defs", { children: [jsxs("linearGradient", { id: `${chartId}-lollipop-stem-gradient`, gradientUnits: "userSpaceOnUse", x1: 0, y1: margin.top, x2: 0, y2: margin.top + innerHeight, children: [jsx("stop", { offset: "0%", stopColor: seriesColor, stopOpacity: 1 }), jsx("stop", { offset: "100%", stopColor: seriesColor, stopOpacity: 0.4 })] }), jsx("filter", { id: `${chartId}-lollipop-stem-glow`, filterUnits: "userSpaceOnUse", x: -20, y: margin.top - 20, width: dimensions.width + 40, height: innerHeight + 40, colorInterpolationFilters: "sRGB", children: jsx("feDropShadow", { dx: "0", dy: "0", stdDeviation: "4", floodColor: seriesColor, floodOpacity: "0.45" }) })] }), jsx(HorizontalGrid, { show: showGrid, x: margin.left, y: margin.top, width: innerWidth, height: innerHeight, lineCount: 5 }), jsx(YAxis, { show: showYAxis, min: minValue, max: maxValue, x: margin.left, y: margin.top, height: innerHeight, showGrid: false, gridWidth: innerWidth }), (() => {
164
+ const categoryGap = 24;
165
+ const totalGap = Math.max(0, (sanitizedData.length - 1) * categoryGap);
166
+ const categoryWidth = safeMath.divide(Math.max(0, innerWidth - totalGap), sanitizedData.length, 0);
167
+ return (jsx(XAxis, { show: showXAxis, data: sanitizedData, x: margin.left, y: margin.top + innerHeight, width: innerWidth, categoryWidth: categoryWidth, categoryGap: categoryGap }));
168
+ })(), measured &&
169
+ (() => {
170
+ const categoryGap = 24;
171
+ const totalGap = Math.max(0, (sanitizedData.length - 1) * categoryGap);
172
+ const categoryWidth = safeMath.divide(Math.max(0, innerWidth - totalGap), sanitizedData.length, 0);
173
+ const range = maxValue - minValue;
174
+ const scaledZero = range > 0 ? safeMath.scale(0, minValue, maxValue, 0, innerHeight) : 0;
175
+ const zeroY = margin.top + innerHeight - scaledZero;
176
+ return (jsx("g", { className: "chart-lollipops", children: sanitizedData.map((item, index) => {
177
+ const rawValue = item[dataKey];
178
+ const value = typeof rawValue === "number" && isFinite(rawValue) ? rawValue : 0;
179
+ const isNull = rawValue === null ||
180
+ rawValue === undefined ||
181
+ (typeof rawValue === "number" && !isFinite(rawValue));
182
+ const cx = margin.left + index * (categoryWidth + categoryGap) + categoryWidth / 2;
183
+ // Hoisted above the null gate on purpose: these depend only on the
184
+ // index, never on the value, and the null marker must dim off the
185
+ // SAME expression as every other mark. Recomputing it inline there
186
+ // drifted — `isAnyActive ? 0.35 : 1` misses the
187
+ // !isCurrentActiveOrSelected term, so a null sitting under an
188
+ // explicit `defaultSelectedIndex` dimmed while a real mark wouldn't.
189
+ const isSelected = resolvedSelectedIndex === index;
190
+ const isCurrentActiveOrSelected = index === activeIndex || index === resolvedSelectedIndex;
191
+ const lineOpacity = isAnyActive && !isCurrentActiveOrSelected ? 0.35 : 1;
192
+ // A null category has nothing to plot, so it gets a marker resting on
193
+ // the baseline and no stem — the axis label then anchors to something
194
+ // instead of hanging under empty space.
195
+ //
196
+ // 🔴 This is deliberately INDISTINGUISHABLE from a real `value: 0`,
197
+ // whose dot also lands on the baseline (dotY === zeroY once
198
+ // scaledValue === scaledZero). That collision is a known, accepted
199
+ // trade-off — don't "fix" it by nudging this marker off the axis.
200
+ //
201
+ // No hit area, stem, tooltip or selection: there is no value to
202
+ // report, so the marker is presentational only, and null stays
203
+ // skipped everywhere it would have to state a number.
204
+ if (isNull) {
205
+ return (jsx(motion.g, {
206
+ // initial/animate/transition mirror the real mark's motion.g
207
+ // exactly — same springs, same opacity timing — only `y` differs
208
+ // (pinned to the baseline instead of a plotted markerY). Keep
209
+ // them in lockstep: every past divergence here was invisible in
210
+ // a screenshot and only showed up in motion.
211
+ initial: showAnimation && !shouldReduceMotion
212
+ ? { x: cx, y: zeroY, scale: 0, opacity: 0 }
213
+ : { x: cx, y: zeroY, scale: 1, opacity: 1 }, animate: {
214
+ x: cx,
215
+ y: zeroY,
216
+ scale: 1,
217
+ opacity: lineOpacity,
218
+ }, transition: {
219
+ ...dotEntranceTransition(index, shouldReduceMotion),
220
+ // x/y ride springs.snappy so the marker tracks a resize or a
221
+ // data change identically to its neighbours; left to the
222
+ // entrance spring it lagged behind them.
223
+ x: springs.snappy,
224
+ y: springs.snappy,
225
+ // Opacity needs its own transition too. Left to the entrance
226
+ // spring it inherits that spring's per-index stagger delay and
227
+ // the hover dim crawls — measured at 0.58 a full second in,
228
+ // while every real mark had long since settled on 0.35.
229
+ opacity: {
230
+ delay: hasAnimated
231
+ ? 0
232
+ : shouldReduceMotion
233
+ ? 0
234
+ : CARTESIAN_ENTRANCE_DURATION + index * LINE_DOT_STAGGER,
235
+ duration: 0.2,
236
+ },
237
+ }, style: {
238
+ originX: "50%",
239
+ originY: "50%",
240
+ transformBox: "fill-box",
241
+ }, children: jsx("rect", { x: -dotRadius, y: -dotRadius, width: dotRadius * 2, height: dotRadius * 2, rx: dotRadius, fill: seriesColor, stroke: "var(--color-surface)", strokeWidth: 2 }) }, index));
242
+ }
243
+ const scaledValue = range > 0
244
+ ? safeMath.scale(value, minValue, maxValue, 0, innerHeight)
245
+ : 0;
246
+ const isNeg = value < 0;
247
+ const dotY = isNeg
248
+ ? zeroY + Math.abs(scaledValue - scaledZero)
249
+ : zeroY - Math.abs(scaledValue - scaledZero);
250
+ const delay = index * BAR_GROUP_STAGGER;
251
+ return (jsxs("g", { onClick: () => handleSelect(isSelected ? null : index), onMouseEnter: (e) => {
252
+ var _a, _b;
253
+ setHoveredIndex(index);
254
+ if (isSelected)
255
+ return;
256
+ handleMouseEnter(e, {
257
+ category: ((_a = item.name) !== null && _a !== void 0 ? _a : `Category ${index + 1}`).toString(),
258
+ series: [
259
+ {
260
+ key: dataKey,
261
+ label: ((_b = config[dataKey]) === null || _b === void 0 ? void 0 : _b.label) || dataKey,
262
+ value,
263
+ color: seriesColor,
264
+ },
265
+ ],
266
+ }, { x: cx, y: dotY, dimensions });
267
+ }, onMouseMove: (e) => {
268
+ if (isSelected)
269
+ return;
270
+ handleMouseMove(e, { x: cx, y: dotY, dimensions });
271
+ }, onMouseLeave: () => {
272
+ setHoveredIndex(null);
273
+ if (isSelected)
274
+ return;
275
+ handleMouseLeave();
276
+ }, style: { cursor: "pointer" }, children: [jsx("rect", { x: cx - categoryWidth / 2, y: margin.top, width: categoryWidth, height: innerHeight, fill: "transparent" }), jsx(motion.line, { x1: cx, x2: cx, y1: zeroY, y2: dotY, stroke: `url(#${chartId}-lollipop-stem-gradient)`, strokeWidth: stemWidth,
277
+ // butt, not round: a round cap overhangs BOTH ends by
278
+ // strokeWidth/2, so the stem bled past the baseline and sat
279
+ // visibly below the axis rule. Butt terminates it exactly on
280
+ // the axis. The flat top costs nothing — the dot marker
281
+ // covers it.
282
+ strokeLinecap: "butt", filter: `url(#${chartId}-lollipop-stem-glow)`, initial: showAnimation && !shouldReduceMotion
283
+ ? { scaleY: 0 }
284
+ : { scaleY: 1 }, animate: {
285
+ scaleY: 1,
286
+ x1: cx,
287
+ x2: cx,
288
+ y1: zeroY,
289
+ y2: dotY,
290
+ opacity: lineOpacity,
291
+ }, transition: showAnimation && !shouldReduceMotion
292
+ ? {
293
+ scaleY: barEntranceTransition(delay, hasAnimated),
294
+ x1: springs.premium,
295
+ x2: springs.premium,
296
+ y1: springs.premium,
297
+ y2: springs.premium,
298
+ opacity: { duration: 0.2 },
299
+ }
300
+ : undefined, style: { ...RISE_FROM_BASELINE, transformBox: "fill-box" } }), (() => {
301
+ // The selected mark morphs from a small dot into a
302
+ // pill showing the value, in place of the old
303
+ // separate floating badge — same seriesColor on
304
+ // both shapes so the swap reads as one mark
305
+ // changing shape, not a switch to a different
306
+ // element. Pill dimensions clamp to the plot's
307
+ // vertical bounds so a very high/low value's pill
308
+ // never clips past the top or into the axis row.
309
+ const badgeText = formatValue(value, item);
310
+ const pillWidth = Math.max(56, badgeText.length * 9 + 24);
311
+ const pillHeight = 28;
312
+ const pillCenterY = Math.min(Math.max(dotY, margin.top + pillHeight / 2), margin.top + innerHeight - pillHeight / 2);
313
+ const markerY = isSelected ? pillCenterY : dotY;
314
+ // When seriesColor is the (default) primary token,
315
+ // reuse the design system's own vetted contrast
316
+ // pairing — the same one every other filled-primary
317
+ // surface (Button, Badge, ...) uses — rather than a
318
+ // runtime luminance heuristic. A custom per-series
319
+ // `color`/config color isn't necessarily primary,
320
+ // so it still falls back to `contrastTone()`.
321
+ const pillTextFill = seriesColor === "var(--color-primary)"
322
+ ? "var(--color-primary-contrast)"
323
+ : contrastTone(seriesColor) === "dark"
324
+ ? "var(--color-mono-950)"
325
+ : "var(--color-mono-50)";
326
+ return (jsxs(motion.g, { initial: showAnimation && !shouldReduceMotion
327
+ ? { x: cx, y: markerY, scale: 0, opacity: 0 }
328
+ : { x: cx, y: markerY, scale: 1, opacity: 1 }, animate: {
329
+ x: cx,
330
+ y: markerY,
331
+ scale: 1,
332
+ opacity: lineOpacity,
333
+ }, transition: {
334
+ ...dotEntranceTransition(index, shouldReduceMotion),
335
+ x: springs.snappy,
336
+ y: springs.snappy,
337
+ opacity: {
338
+ delay: hasAnimated
339
+ ? 0
340
+ : shouldReduceMotion
341
+ ? 0
342
+ : CARTESIAN_ENTRANCE_DURATION + index * LINE_DOT_STAGGER,
343
+ duration: 0.2,
344
+ },
345
+ }, style: {
346
+ originX: "50%",
347
+ originY: "50%",
348
+ transformBox: "fill-box",
349
+ }, children: [jsx(motion.rect, { animate: {
350
+ width: isSelected ? pillWidth : dotRadius * 2,
351
+ height: isSelected ? pillHeight : dotRadius * 2,
352
+ rx: isSelected ? pillHeight / 2 : dotRadius,
353
+ x: isSelected ? -pillWidth / 2 : -dotRadius,
354
+ y: isSelected ? -pillHeight / 2 : -dotRadius,
355
+ }, transition: springs.snappy, fill: seriesColor, stroke: "var(--color-surface)", strokeWidth: 2 }), jsx(AnimatePresence, { children: isSelected && (jsx(motion.text, { initial: { opacity: 0, scale: 0.6 }, animate: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.6 }, transition: springs.snappy, x: 0, y: 0, textAnchor: "middle", dominantBaseline: "central", className: "text-sm font-semibold select-none pointer-events-none", fill: pillTextFill, children: badgeText }, "text")) })] }));
356
+ })()] }, index));
357
+ }) }));
358
+ })()] }) }), jsx(ChartTooltip, { tooltipRef: tooltipRef, content: tooltipData === null || tooltipData === void 0 ? void 0 : tooltipData.content, active: !!tooltipData, position: tooltipData ? { x: tooltipData.x, y: tooltipData.y } : { x: 0, y: 0 } })] }) }));
359
+ };
360
+
361
+ export { LollipopChart };
@@ -0,0 +1,42 @@
1
+ import { BaseChartProps, ChartConfig, ChartDataPoint } from "../types/chart.types";
2
+ export interface LollipopChartProps extends BaseChartProps {
3
+ config: ChartConfig;
4
+ showGrid?: boolean;
5
+ showXAxis?: boolean;
6
+ showYAxis?: boolean;
7
+ minValue?: number;
8
+ maxValue?: number;
9
+ /** Controlled selected category index. `null`/`undefined` = no selection. */
10
+ selectedIndex?: number | null;
11
+ /** Uncontrolled initial selection. */
12
+ defaultSelectedIndex?: number | null;
13
+ /** Payload is the index, not the event (CLAUDE.md §9 naming convention). */
14
+ onSelectedIndexChange?: (index: number | null) => void;
15
+ /** Formats the pinned badge's value text. Defaults to `String(value)`. */
16
+ valueFormatter?: (value: number, item: ChartDataPoint) => string;
17
+ dotRadius?: number;
18
+ stemWidth?: number;
19
+ /** Series color for the dot + stem. Falls back to the single `config` key's
20
+ * `color`, then `var(--color-primary)`. */
21
+ color?: string;
22
+ showAnimation?: boolean;
23
+ enableKeyboardNavigation?: boolean;
24
+ showValidationWarnings?: boolean;
25
+ darkMode?: boolean;
26
+ className?: string;
27
+ title?: string;
28
+ description?: string;
29
+ width?: string | number;
30
+ height?: string | number;
31
+ }
32
+ export declare const LOLLIPOP_CHART_DEFAULTS: {
33
+ readonly showGrid: false;
34
+ readonly showXAxis: true;
35
+ readonly showYAxis: false;
36
+ readonly dotRadius: 6;
37
+ readonly stemWidth: 3;
38
+ readonly defaultSelectedIndex: number | null;
39
+ readonly showAnimation: true;
40
+ readonly enableKeyboardNavigation: true;
41
+ };
42
+ export type LollipopChartData = ChartDataPoint;
@@ -0,0 +1,12 @@
1
+ const LOLLIPOP_CHART_DEFAULTS = {
2
+ showGrid: false,
3
+ showXAxis: true,
4
+ showYAxis: false,
5
+ dotRadius: 6,
6
+ stemWidth: 3,
7
+ defaultSelectedIndex: null,
8
+ showAnimation: true,
9
+ enableKeyboardNavigation: true,
10
+ };
11
+
12
+ export { LOLLIPOP_CHART_DEFAULTS };
@@ -0,0 +1,3 @@
1
+ export { LollipopChart } from "./LollipopChart";
2
+ export type { LollipopChartProps, LollipopChartData } from "./LollipopChart.types";
3
+ export { LOLLIPOP_CHART_DEFAULTS } from "./LollipopChart.types";
@@ -9,7 +9,7 @@ import { useChartDimensions } from '../hooks/useChartDimensions.js';
9
9
  import { XAxis } from '../shared/ChartAxis/XAxis.js';
10
10
  import { YAxis } from '../shared/ChartAxis/YAxis.js';
11
11
  import { ChartTooltip } from '../shared/ChartTooltip/ChartTooltip.js';
12
- import { ChartLegend } from '../shared/ChartLegend/ChartLegend.js';
12
+ import { resolveLegendPosition, ChartLegend } from '../shared/ChartLegend/ChartLegend.js';
13
13
  import { SCATTER_PLOT_DEFAULTS } from './ScatterPlot.types.js';
14
14
  import { getCategoryColor, scatterPlotTheme } from './ScatterPlot.theme.js';
15
15
  import { SCATTER_POINT_DURATION, SCATTER_POINT_STAGGER } from '../utils/animation-utils.js';
@@ -21,7 +21,7 @@ import { SCATTER_POINT_DURATION, SCATTER_POINT_STAGGER } from '../utils/animatio
21
21
  * Now uses shared axis components with tick marks and value labels like other charts.
22
22
  */
23
23
  const ScatterPlot = ({ data, xLabel, yLabel, dotRadius: propDotRadius, pointSize, // @deprecated
24
- showGrid = SCATTER_PLOT_DEFAULTS.showGrid, showXAxis = SCATTER_PLOT_DEFAULTS.showXAxis, showYAxis = SCATTER_PLOT_DEFAULTS.showYAxis, showTooltip = SCATTER_PLOT_DEFAULTS.showTooltip, colors, minValue: propMinValue, maxValue: propMaxValue, theme: themeOverrides, className, svgClassName, dotClassName, gridClassName, axisClassName, title, description, enableKeyboardNavigation = SCATTER_PLOT_DEFAULTS.enableKeyboardNavigation, width, height, ...props }) => {
24
+ showGrid = SCATTER_PLOT_DEFAULTS.showGrid, showXAxis = SCATTER_PLOT_DEFAULTS.showXAxis, showYAxis = SCATTER_PLOT_DEFAULTS.showYAxis, showTooltip = SCATTER_PLOT_DEFAULTS.showTooltip, legend, colors, minValue: propMinValue, maxValue: propMaxValue, theme: themeOverrides, className, svgClassName, dotClassName, gridClassName, axisClassName, title, description, enableKeyboardNavigation = SCATTER_PLOT_DEFAULTS.enableKeyboardNavigation, width, height, ...props }) => {
25
25
  var _a;
26
26
  const theme = { ...scatterPlotTheme, ...(themeOverrides || {}) };
27
27
  const chartColors = colors || theme.colors || scatterPlotTheme.colors;
@@ -114,6 +114,7 @@ showGrid = SCATTER_PLOT_DEFAULTS.showGrid, showXAxis = SCATTER_PLOT_DEFAULTS.sho
114
114
  label: cat,
115
115
  color: getCategoryColor(chartColors, idx),
116
116
  })), [categories, chartColors]);
117
+ const resolvedLegendPosition = resolveLegendPosition(legend, config);
117
118
  const chartDataForAccessibility = useMemo(() => {
118
119
  return validData.map((point) => ({
119
120
  category: point.category || 'Point',
@@ -155,7 +156,7 @@ showGrid = SCATTER_PLOT_DEFAULTS.showGrid, showXAxis = SCATTER_PLOT_DEFAULTS.sho
155
156
  minHeight: height === undefined ? '400px' : undefined,
156
157
  width: width !== undefined ? (typeof width === "number" ? `${width}px` : width) : undefined,
157
158
  height: height !== undefined ? (typeof height === "number" ? `${height}px` : height) : undefined,
158
- }, ...chartAccessibilityProps, ...props, children: [jsxs("svg", { width: dimensions.width, height: dimensions.height, className: cn(theme.svgStyle, svgClassName), ...descriptionProps, children: [jsx(YAxis, { show: showYAxis, min: yMin, max: yMax, x: margin.left, y: margin.top, height: innerHeight, showGrid: showGrid, gridWidth: innerWidth, tickCount: 5, className: cn(axisClassName) }), measured &&
159
+ }, ...chartAccessibilityProps, ...props, children: [resolvedLegendPosition === 'top' && (jsx(ChartLegend, { items: legendItems, className: "shrink-0 mb-4" })), jsxs("svg", { width: dimensions.width, height: dimensions.height, className: cn(theme.svgStyle, svgClassName), ...descriptionProps, children: [jsx(YAxis, { show: showYAxis, min: yMin, max: yMax, x: margin.left, y: margin.top, height: innerHeight, showGrid: showGrid, gridWidth: innerWidth, gridClassName: gridClassName, tickCount: 5, className: cn(axisClassName) }), measured &&
159
160
  validData.map((point, index) => {
160
161
  const cx = scaleX(point.x);
161
162
  const cy = scaleY(point.y);
@@ -200,7 +201,7 @@ showGrid = SCATTER_PLOT_DEFAULTS.showGrid, showXAxis = SCATTER_PLOT_DEFAULTS.sho
200
201
  }
201
202
  handleMouseInteraction();
202
203
  }, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave }, index));
203
- }), jsx(XAxis, { show: showXAxis, data: xAxisData, x: margin.left, y: margin.top + innerHeight, width: innerWidth, categoryWidth: innerWidth / (xAxisData.length - 1), categoryGap: 0, alignWithEdge: true, className: cn(axisClassName) }), xLabel && showXAxis && (jsx("text", { x: margin.left + innerWidth / 2, y: dimensions.height - 5, textAnchor: "middle", className: "text-sm fill-[var(--color-text-secondary)]", children: xLabel })), yLabel && showYAxis && (jsx("text", { x: 15, y: margin.top + innerHeight / 2, textAnchor: "middle", className: "text-sm fill-[var(--color-text-secondary)]", transform: `rotate(-90 15 ${margin.top + innerHeight / 2})`, children: yLabel }))] }), jsx(ChartLegend, { items: legendItems, className: "mt-4" }), showTooltip && (jsx(ChartTooltip, { active: !!tooltipData, position: tooltipData ? { x: tooltipData.x, y: tooltipData.y } : { x: 0, y: 0 }, tooltipRef: tooltipRef, content: tooltipData === null || tooltipData === void 0 ? void 0 : tooltipData.content, chartType: "scatter" }))] }));
204
+ }), jsx(XAxis, { show: showXAxis, data: xAxisData, x: margin.left, y: margin.top + innerHeight, width: innerWidth, categoryWidth: innerWidth / (xAxisData.length - 1), categoryGap: 0, alignWithEdge: true, className: cn(axisClassName) }), xLabel && showXAxis && (jsx("text", { x: margin.left + innerWidth / 2, y: dimensions.height - 5, textAnchor: "middle", className: "text-sm fill-[var(--color-text-secondary)]", children: xLabel })), yLabel && showYAxis && (jsx("text", { x: 15, y: margin.top + innerHeight / 2, textAnchor: "middle", className: "text-sm fill-[var(--color-text-secondary)]", transform: `rotate(-90 15 ${margin.top + innerHeight / 2})`, children: yLabel }))] }), resolvedLegendPosition === 'bottom' && (jsx(ChartLegend, { items: legendItems, className: "shrink-0 mt-4" })), showTooltip && (jsx(ChartTooltip, { active: !!tooltipData, position: tooltipData ? { x: tooltipData.x, y: tooltipData.y } : { x: 0, y: 0 }, tooltipRef: tooltipRef, content: tooltipData === null || tooltipData === void 0 ? void 0 : tooltipData.content, chartType: "scatter" }))] }));
204
205
  };
205
206
  ScatterPlot.displayName = 'ScatterPlot';
206
207
 
@@ -16,8 +16,13 @@ export interface ScatterPlotDataPoint {
16
16
  }
17
17
  /**
18
18
  * ScatterPlot props - extends standardized chart props
19
+ *
20
+ * 🔴 `variant` is Omitted, not inherited: scatter points are flat, single-colour marks —
21
+ * there is no gradient/minimal treatment to select. Leaving the inherited prop in place
22
+ * would publish a control that silently does nothing (and would reach the generative
23
+ * schema, where an LLM would emit it). Add it back only alongside an implementation.
19
24
  */
20
- export interface ScatterPlotProps extends StandardChartDisplayProps, Omit<BaseChartProps, 'data' | 'config'> {
25
+ export interface ScatterPlotProps extends Omit<StandardChartDisplayProps, 'variant'>, Omit<BaseChartProps, 'data' | 'config'> {
21
26
  /** Data points */
22
27
  data: ScatterPlotDataPoint[];
23
28
  /** X-axis label */
@@ -38,28 +43,18 @@ export interface ScatterPlotProps extends StandardChartDisplayProps, Omit<BaseCh
38
43
  axisClassName?: string;
39
44
  }
40
45
  /**
41
- * Default values using centralized defaults
46
+ * Default values using centralized defaults.
47
+ *
48
+ * 🔴 Picks explicitly rather than spreading `...CHART_DEFAULTS` — the spread pulled in a
49
+ * default for every other chart's props, so this object advertised values ScatterPlot
50
+ * never reads. List only what the component actually consumes.
42
51
  */
43
52
  export declare const SCATTER_PLOT_DEFAULTS: {
44
- readonly dotRadius: 4;
45
- readonly pointSize: 4;
46
- readonly showTooltip: true;
47
53
  readonly showGrid: true;
48
54
  readonly showXAxis: true;
49
55
  readonly showYAxis: true;
50
- readonly barRadius: 32;
51
- readonly cellRadius: 4;
52
- readonly radius: 20;
53
- readonly strokeWidth: 2;
54
- readonly gap: 4;
55
- readonly categoryGap: 16;
56
- readonly curved: true;
57
- readonly curveType: import("..").CurveType;
58
- readonly fillOpacity: 0.3;
59
- readonly showStroke: true;
60
- readonly showDots: false;
61
- readonly orientation: "vertical" | "horizontal";
62
- readonly stacked: false;
63
- readonly variant: import("..").ChartVariant;
64
56
  readonly enableKeyboardNavigation: true;
57
+ readonly dotRadius: 4;
58
+ readonly pointSize: 4;
59
+ readonly showTooltip: true;
65
60
  };
@@ -1,10 +1,17 @@
1
1
  import { CHART_DEFAULTS } from '../types/chart.types.js';
2
2
 
3
3
  /**
4
- * Default values using centralized defaults
4
+ * Default values using centralized defaults.
5
+ *
6
+ * 🔴 Picks explicitly rather than spreading `...CHART_DEFAULTS` — the spread pulled in a
7
+ * default for every other chart's props, so this object advertised values ScatterPlot
8
+ * never reads. List only what the component actually consumes.
5
9
  */
6
10
  const SCATTER_PLOT_DEFAULTS = {
7
- ...CHART_DEFAULTS,
11
+ showGrid: CHART_DEFAULTS.showGrid,
12
+ showXAxis: CHART_DEFAULTS.showXAxis,
13
+ showYAxis: CHART_DEFAULTS.showYAxis,
14
+ enableKeyboardNavigation: CHART_DEFAULTS.enableKeyboardNavigation,
8
15
  dotRadius: 4,
9
16
  showTooltip: true,
10
17
  };