@gunjo/ui 0.0.1-alpha.0 → 0.0.1-alpha.2

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 (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +90 -0
  3. package/README.md +52 -91
  4. package/package.json +47 -6
  5. package/src/components/display/Accordion.tsx +185 -0
  6. package/src/components/display/AccordionGroup.tsx +155 -0
  7. package/src/components/display/ActionDataTable.tsx +413 -0
  8. package/src/components/display/ActivityTimelineCard.tsx +483 -0
  9. package/src/components/display/AnalyticsCard.tsx +167 -0
  10. package/src/components/display/AssetCard.tsx +242 -0
  11. package/src/components/display/AssetGrid.tsx +164 -0
  12. package/src/components/display/Avatar.tsx +127 -0
  13. package/src/components/display/AvatarGroup.tsx +131 -0
  14. package/src/components/{atoms → display}/Badge.tsx +3 -3
  15. package/src/components/display/BarChart.tsx +247 -0
  16. package/src/components/{molecules → display}/Card.tsx +1 -1
  17. package/src/components/display/Carousel.tsx +593 -0
  18. package/src/components/display/ChartLegend.tsx +124 -0
  19. package/src/components/display/ChatMessage.tsx +382 -0
  20. package/src/components/display/ChoroplethMap.tsx +613 -0
  21. package/src/components/display/Code.tsx +42 -0
  22. package/src/components/display/CodeBlock.tsx +338 -0
  23. package/src/components/display/ColorSwatch.tsx +71 -0
  24. package/src/components/display/ConcentricProgressCard.tsx +545 -0
  25. package/src/components/display/DataTable.tsx +522 -0
  26. package/src/components/display/DistributionBar.tsx +102 -0
  27. package/src/components/display/DocNote.tsx +36 -0
  28. package/src/components/display/DonutChart.tsx +257 -0
  29. package/src/components/display/EmptyState.tsx +44 -0
  30. package/src/components/display/FileTree.tsx +180 -0
  31. package/src/components/display/GaugeChart.tsx +219 -0
  32. package/src/components/display/HeatmapChart.tsx +266 -0
  33. package/src/components/display/Icon.tsx +66 -0
  34. package/src/components/display/ImagePreview.tsx +140 -0
  35. package/src/components/{atoms → display}/Img.tsx +46 -12
  36. package/src/components/display/LabeledDonutCard.tsx +475 -0
  37. package/src/components/display/LineChart.tsx +464 -0
  38. package/src/components/{molecules → display}/List.tsx +20 -13
  39. package/src/components/display/MarkdownRenderer.tsx +157 -0
  40. package/src/components/display/MetadataList.tsx +81 -0
  41. package/src/components/display/MiniDistributionBarCard.tsx +314 -0
  42. package/src/components/display/PieChart.tsx +234 -0
  43. package/src/components/display/QuadrantMatrix.tsx +330 -0
  44. package/src/components/display/RadarChart.tsx +335 -0
  45. package/src/components/display/RadialBarChart.tsx +264 -0
  46. package/src/components/display/RetentionCohortCard.tsx +350 -0
  47. package/src/components/display/RibbonChart.tsx +618 -0
  48. package/src/components/display/SearchableAccordion.tsx +270 -0
  49. package/src/components/display/SegmentTimelineCard.tsx +452 -0
  50. package/src/components/display/SegmentedGaugeCard.tsx +607 -0
  51. package/src/components/display/Spacer.tsx +51 -0
  52. package/src/components/display/SparklineChart.tsx +394 -0
  53. package/src/components/display/StackedBarChart.tsx +393 -0
  54. package/src/components/display/Statistic.tsx +70 -0
  55. package/src/components/{molecules → display}/Table.tsx +22 -7
  56. package/src/components/display/Tag.tsx +80 -0
  57. package/src/components/display/TagEditor.tsx +141 -0
  58. package/src/components/display/Timeline.tsx +121 -0
  59. package/src/components/{atoms → display}/ToolPill.tsx +42 -18
  60. package/src/components/display/TreeView.tsx +226 -0
  61. package/src/components/display/chart-tooltip.tsx +423 -0
  62. package/src/components/display/chart-utils.ts +71 -0
  63. package/src/components/display/circular-chart-utils.ts +147 -0
  64. package/src/components/display/generated/default-variant-keys.ts +90 -0
  65. package/src/components/display/generated/variant-keys.ts +169 -0
  66. package/src/components/{atoms → feedback}/Alert.tsx +12 -5
  67. package/src/components/feedback/Banner.tsx +90 -0
  68. package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
  69. package/src/components/feedback/ProgressWidget.tsx +44 -0
  70. package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
  71. package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
  72. package/src/components/feedback/StatusScreen.tsx +148 -0
  73. package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
  74. package/src/components/feedback/Toast.tsx +108 -0
  75. package/src/components/feedback/ToastProvider.tsx +78 -0
  76. package/src/components/feedback/generated/default-variant-keys.ts +16 -0
  77. package/src/components/feedback/generated/variant-keys.ts +21 -0
  78. package/src/components/generated/component-manifest.ts +1568 -454
  79. package/src/components/generated/component-style-hints.ts +1958 -718
  80. package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
  81. package/src/components/inputs/Calendar.tsx +212 -0
  82. package/src/components/inputs/ChatComposer.tsx +75 -0
  83. package/src/components/inputs/ChatInput.tsx +528 -0
  84. package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
  85. package/src/components/inputs/Combobox.tsx +175 -0
  86. package/src/components/inputs/CopyButton.tsx +187 -0
  87. package/src/components/inputs/DatePicker.tsx +519 -0
  88. package/src/components/inputs/DateRangePicker.tsx +878 -0
  89. package/src/components/inputs/EditableField.tsx +182 -0
  90. package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
  91. package/src/components/inputs/FilterButton.tsx +163 -0
  92. package/src/components/{molecules → inputs}/Form.tsx +20 -3
  93. package/src/components/{atoms → inputs}/Input.tsx +2 -0
  94. package/src/components/inputs/InputOTP.tsx +75 -0
  95. package/src/components/inputs/Mention.tsx +279 -0
  96. package/src/components/inputs/NumberInput.tsx +109 -0
  97. package/src/components/inputs/PasswordGroup.tsx +138 -0
  98. package/src/components/inputs/PasswordInput.tsx +74 -0
  99. package/src/components/inputs/PasswordRequirementList.tsx +96 -0
  100. package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
  101. package/src/components/inputs/PhoneInput.tsx +99 -0
  102. package/src/components/inputs/PostalCodeInput.tsx +98 -0
  103. package/src/components/inputs/RangeSlider.tsx +129 -0
  104. package/src/components/inputs/SearchInput.tsx +76 -0
  105. package/src/components/inputs/Select.tsx +39 -0
  106. package/src/components/{atoms → inputs}/Slider.tsx +18 -5
  107. package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
  108. package/src/components/{atoms → inputs}/Switch.tsx +15 -4
  109. package/src/components/inputs/TagInput.tsx +114 -0
  110. package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
  111. package/src/components/inputs/TimePicker.tsx +150 -0
  112. package/src/components/inputs/Toggle.tsx +48 -0
  113. package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
  114. package/src/components/inputs/TooltipButton.tsx +148 -0
  115. package/src/components/inputs/VoiceInputButton.tsx +317 -0
  116. package/src/components/inputs/calendar-holidays.ts +56 -0
  117. package/src/components/inputs/generated/default-variant-keys.ts +32 -0
  118. package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
  119. package/src/components/layout/AspectRatio.tsx +12 -0
  120. package/src/components/layout/AssetInspectorPanel.tsx +416 -0
  121. package/src/components/layout/Cluster.tsx +56 -0
  122. package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
  123. package/src/components/layout/Container.tsx +43 -0
  124. package/src/components/layout/DeviceFrame.tsx +227 -0
  125. package/src/components/layout/Grid.tsx +65 -0
  126. package/src/components/layout/HStack.tsx +73 -0
  127. package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
  128. package/src/components/layout/MarqueeFrame.tsx +158 -0
  129. package/src/components/layout/Resizable.tsx +94 -0
  130. package/src/components/layout/ScrollArea.tsx +71 -0
  131. package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
  132. package/src/components/layout/VStack.tsx +69 -0
  133. package/src/components/layout/generated/default-variant-keys.ts +16 -0
  134. package/src/components/layout/generated/variant-keys.ts +21 -0
  135. package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
  136. package/src/components/navigation/Command.tsx +266 -0
  137. package/src/components/navigation/CommandPalette.tsx +83 -0
  138. package/src/components/navigation/DocumentPager.tsx +171 -0
  139. package/src/components/navigation/Footer.tsx +88 -0
  140. package/src/components/navigation/Header.tsx +80 -0
  141. package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
  142. package/src/components/navigation/NavigationMenu.tsx +128 -0
  143. package/src/components/navigation/PageAside.tsx +84 -0
  144. package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
  145. package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
  146. package/src/components/navigation/Sidebar.tsx +223 -0
  147. package/src/components/navigation/SidebarItem.tsx +160 -0
  148. package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
  149. package/src/components/navigation/TextLink.tsx +71 -0
  150. package/src/components/navigation/generated/default-variant-keys.ts +12 -0
  151. package/src/components/navigation/generated/variant-keys.ts +13 -0
  152. package/src/components/overlay/AIChatInput.tsx +5 -0
  153. package/src/components/overlay/AIChatMessage.tsx +6 -0
  154. package/src/components/overlay/AlertDialog.tsx +145 -0
  155. package/src/components/overlay/ChatPanel.tsx +180 -0
  156. package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
  157. package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
  158. package/src/components/overlay/Drawer.tsx +131 -0
  159. package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
  160. package/src/components/overlay/FloatingPanel.tsx +90 -0
  161. package/src/components/overlay/HoverCard.tsx +36 -0
  162. package/src/components/overlay/MediaLightbox.tsx +403 -0
  163. package/src/components/overlay/MediaPickerDialog.tsx +198 -0
  164. package/src/components/overlay/Modal.tsx +103 -0
  165. package/src/components/overlay/OnboardingFlow.tsx +172 -0
  166. package/src/components/overlay/Popover.tsx +36 -0
  167. package/src/components/overlay/ShareModal.tsx +324 -0
  168. package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
  169. package/src/components/overlay/Tooltip.tsx +130 -0
  170. package/src/components/overlay/generated/default-variant-keys.ts +14 -0
  171. package/src/components/overlay/generated/variant-keys.ts +17 -0
  172. package/src/components/patterns/BlogTemplate.tsx +46 -0
  173. package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
  174. package/src/components/patterns/DocsTemplate.tsx +41 -0
  175. package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
  176. package/src/components/patterns/OnboardingTemplate.tsx +32 -0
  177. package/src/components/patterns/PricingTemplate.tsx +106 -0
  178. package/src/globals.css +173 -22
  179. package/src/index.ts +177 -76
  180. package/tailwind-theme-extend.cjs +48 -3
  181. package/design/atoms-metadata.json +0 -82
  182. package/design/molecules-metadata.json +0 -130
  183. package/design/organisms-metadata.json +0 -38
  184. package/design/templates-metadata.json +0 -38
  185. package/src/components/atoms/Avatar.tsx +0 -57
  186. package/src/components/atoms/Select.tsx +0 -28
  187. package/src/components/atoms/generated/default-variant-keys.ts +0 -36
  188. package/src/components/molecules/AIChatInput.tsx +0 -140
  189. package/src/components/molecules/AIChatMessage.tsx +0 -109
  190. package/src/components/molecules/Accordion.tsx +0 -99
  191. package/src/components/molecules/Calendar.tsx +0 -60
  192. package/src/components/molecules/Carousel.tsx +0 -261
  193. package/src/components/molecules/Command.tsx +0 -152
  194. package/src/components/molecules/FilterButton.tsx +0 -133
  195. package/src/components/molecules/HoverCard.tsx +0 -29
  196. package/src/components/molecules/Modal.tsx +0 -66
  197. package/src/components/molecules/Popover.tsx +0 -31
  198. package/src/components/molecules/ProgressWidget.tsx +0 -40
  199. package/src/components/molecules/Resizable.tsx +0 -47
  200. package/src/components/molecules/ScrollArea.tsx +0 -48
  201. package/src/components/molecules/SidebarItem.tsx +0 -134
  202. package/src/components/molecules/Toast.tsx +0 -57
  203. package/src/components/molecules/Tooltip.tsx +0 -30
  204. package/src/components/molecules/generated/default-variant-keys.ts +0 -22
  205. package/src/components/molecules/generated/variant-keys.ts +0 -33
  206. package/src/components/organisms/CommandPalette.tsx +0 -58
  207. package/src/components/organisms/FloatingPanel.tsx +0 -46
  208. package/src/components/organisms/ShareModal.tsx +0 -182
  209. package/src/components/organisms/ToastProvider.tsx +0 -49
  210. /package/src/components/{atoms → display}/Kbd.tsx +0 -0
  211. /package/src/components/{atoms → display}/Separator.tsx +0 -0
  212. /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
  213. /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
  214. /package/src/components/{atoms → inputs}/Button.tsx +0 -0
  215. /package/src/components/{atoms → inputs}/Label.tsx +0 -0
  216. /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
  217. /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
  218. /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
  219. /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
  220. /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
  221. /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
  222. /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
  223. /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
  224. /package/src/components/{templates → patterns}/SettingsTemplate.tsx +0 -0
@@ -0,0 +1,394 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "../../lib/utils"
6
+ import type { ChartColor, ChartDataPoint } from "./chart-utils"
7
+ import {
8
+ chartLabelToString,
9
+ clamp,
10
+ defaultChartValueFormatter,
11
+ getChartColor,
12
+ getChartLabel,
13
+ getChartValue,
14
+ } from "./chart-utils"
15
+ import { ChartFloatingTooltip, ChartTooltip } from "./chart-tooltip"
16
+ import type { SparklineChartVariantKey } from "./generated/variant-keys"
17
+ import { sparklineChartDefaultVariantKey } from "./generated/default-variant-keys"
18
+
19
+ interface NormalizedPoint {
20
+ index: number
21
+ label: React.ReactNode
22
+ x: number
23
+ y: number
24
+ value: number
25
+ }
26
+
27
+ interface SparklineTooltipState {
28
+ label: React.ReactNode
29
+ value: React.ReactNode
30
+ }
31
+
32
+ export interface SparklineChartProps
33
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
34
+ data: Array<ChartDataPoint | number>
35
+ variant?: SparklineChartVariantKey
36
+ color?: ChartColor
37
+ referenceValue?: number
38
+ referenceLabel?: React.ReactNode
39
+ showGrid?: boolean
40
+ showDots?: boolean
41
+ strokeWidth?: number
42
+ formatValue?: (value: number) => React.ReactNode
43
+ }
44
+
45
+ const sparklineChartVariantClasses: Record<SparklineChartVariantKey, string> = {
46
+ area: "h-24 w-full p-0",
47
+ line: "h-24 w-full p-0",
48
+ step: "h-24 w-full p-0",
49
+ }
50
+
51
+ function buildPoints(
52
+ data: Array<ChartDataPoint | number>,
53
+ width: number,
54
+ height: number,
55
+ padding: number,
56
+ domain?: { min: number; max: number }
57
+ ) {
58
+ const normalizedData = data
59
+ .map((item, index) => ({
60
+ index,
61
+ label: getChartLabel(item, index),
62
+ value: getChartValue(item),
63
+ }))
64
+ .filter((point) => Number.isFinite(point.value))
65
+ if (normalizedData.length === 0) return []
66
+
67
+ const values = normalizedData.map((point) => point.value)
68
+ const min = domain?.min ?? Math.min(...values)
69
+ const max = domain?.max ?? Math.max(...values)
70
+ const span = max - min || 1
71
+ const usableWidth = width - padding * 2
72
+ const usableHeight = height - padding * 2
73
+
74
+ return normalizedData.map((point, index) => {
75
+ const x =
76
+ normalizedData.length === 1
77
+ ? width / 2
78
+ : padding + (index / (normalizedData.length - 1)) * usableWidth
79
+ const y = padding + (1 - (point.value - min) / span) * usableHeight
80
+ return { ...point, x, y }
81
+ })
82
+ }
83
+
84
+ function buildLinePoints(points: NormalizedPoint[]) {
85
+ return points
86
+ }
87
+
88
+ function buildStepPoints(points: NormalizedPoint[]) {
89
+ const steppedPoints: NormalizedPoint[] = []
90
+ if (points.length === 0) return steppedPoints
91
+
92
+ steppedPoints.push(points[0])
93
+ for (let index = 1; index < points.length; index += 1) {
94
+ const previous = points[index - 1]
95
+ const current = points[index]
96
+ const midpoint = {
97
+ index: previous.index,
98
+ label: previous.label,
99
+ x: previous.x + (current.x - previous.x) / 2,
100
+ y: previous.y,
101
+ value: previous.value,
102
+ }
103
+ const verticalPoint = {
104
+ index: current.index,
105
+ label: current.label,
106
+ x: midpoint.x,
107
+ y: current.y,
108
+ value: current.value,
109
+ }
110
+ steppedPoints.push(midpoint, verticalPoint, current)
111
+ }
112
+
113
+ return steppedPoints
114
+ }
115
+
116
+ function formatChartNumber(value: number) {
117
+ return Number(value.toFixed(3))
118
+ }
119
+
120
+ function buildSvgPointList(points: NormalizedPoint[]) {
121
+ return points
122
+ .map((point) => `${formatChartNumber(point.x)},${formatChartNumber(point.y)}`)
123
+ .join(" ")
124
+ }
125
+
126
+ function buildAreaPointList(points: NormalizedPoint[], height: number) {
127
+ if (points.length === 0) return undefined
128
+ const last = points[points.length - 1]
129
+ const first = points[0]
130
+
131
+ return [
132
+ buildSvgPointList(points),
133
+ `${formatChartNumber(last.x)},${formatChartNumber(height)}`,
134
+ `${formatChartNumber(first.x)},${formatChartNumber(height)}`,
135
+ ].join(" ")
136
+ }
137
+
138
+ function getPointHitRange(points: NormalizedPoint[], index: number, width: number) {
139
+ const point = points[index]
140
+ if (!point) return { start: 0, end: width }
141
+ if (points.length <= 1) return { start: 0, end: width }
142
+
143
+ const next = points[index + 1]
144
+
145
+ return {
146
+ start: index === 0 ? 0 : point.x,
147
+ end: next ? next.x : width,
148
+ }
149
+ }
150
+
151
+ function useElementSize<T extends HTMLElement>() {
152
+ const [node, setNode] = React.useState<T | null>(null)
153
+ const [size, setSize] = React.useState({ width: 0, height: 0 })
154
+
155
+ React.useEffect(() => {
156
+ if (!node) return undefined
157
+ const updateSize = () => {
158
+ const rect = node.getBoundingClientRect()
159
+ setSize({ width: rect.width, height: rect.height })
160
+ }
161
+ updateSize()
162
+
163
+ const observer = new ResizeObserver(updateSize)
164
+ observer.observe(node)
165
+ return () => observer.disconnect()
166
+ }, [node])
167
+
168
+ return [setNode, size] as const
169
+ }
170
+
171
+ const SparklineChart = React.forwardRef<HTMLDivElement, SparklineChartProps>(
172
+ (
173
+ {
174
+ className,
175
+ data,
176
+ variant = sparklineChartDefaultVariantKey,
177
+ color,
178
+ referenceValue,
179
+ referenceLabel = "Reference",
180
+ showGrid = true,
181
+ showDots = false,
182
+ strokeWidth = 2,
183
+ formatValue = defaultChartValueFormatter,
184
+ ...props
185
+ },
186
+ ref
187
+ ) => {
188
+ const [setRootNode, size] = useElementSize<HTMLDivElement>()
189
+ const [tooltipOpen, setTooltipOpen] = React.useState(false)
190
+ const [tooltipPosition, setTooltipPosition] = React.useState({
191
+ x: 50,
192
+ y: 20,
193
+ })
194
+ const [tooltipContent, setTooltipContent] =
195
+ React.useState<SparklineTooltipState | null>(null)
196
+ const width = size.width || 240
197
+ const height = size.height || 96
198
+ const padding = 6
199
+ const values = data.map(getChartValue).filter((value) => Number.isFinite(value))
200
+ const domainValues =
201
+ referenceValue === undefined ? values : [...values, referenceValue]
202
+ const domain =
203
+ domainValues.length > 0
204
+ ? {
205
+ min: Math.min(...domainValues),
206
+ max: Math.max(...domainValues),
207
+ }
208
+ : undefined
209
+ const points = buildPoints(data, width, height, padding, domain)
210
+ const strokeColor = getChartColor(color, 0)
211
+ const referencePoints =
212
+ referenceValue === undefined
213
+ ? []
214
+ : buildPoints([referenceValue, referenceValue], width, height, padding, domain)
215
+ const referenceY =
216
+ referencePoints.length === 2 ? (referencePoints[0].y / height) * 100 : null
217
+ const referenceText = chartLabelToString(referenceLabel, "Reference")
218
+ const linePoints = variant === "step" ? buildStepPoints(points) : buildLinePoints(points)
219
+ const linePointList = buildSvgPointList(linePoints)
220
+ const areaPointList = buildAreaPointList(linePoints, height)
221
+ const setRef = (node: HTMLDivElement | null) => {
222
+ setRootNode(node)
223
+ if (typeof ref === "function") {
224
+ ref(node)
225
+ } else if (ref) {
226
+ ref.current = node
227
+ }
228
+ }
229
+ const openPointTooltip = (
230
+ point: NormalizedPoint,
231
+ position: { x: number; y: number }
232
+ ) => {
233
+ setTooltipContent({
234
+ label: point.label,
235
+ value: formatValue(point.value),
236
+ })
237
+ setTooltipPosition(position)
238
+ setTooltipOpen(true)
239
+ }
240
+ const handlePointerMove = (
241
+ event:
242
+ | React.PointerEvent<HTMLDivElement>
243
+ | React.MouseEvent<HTMLDivElement>
244
+ ) => {
245
+ const rect = event.currentTarget.getBoundingClientRect()
246
+ const localX = event.clientX - rect.left
247
+ const localY = event.clientY - rect.top
248
+
249
+ for (let index = 0; index < points.length; index += 1) {
250
+ const point = points[index]
251
+ const range = getPointHitRange(points, index, width)
252
+ const isLast = index === points.length - 1
253
+
254
+ if (
255
+ localX >= range.start &&
256
+ (localX < range.end || isLast)
257
+ ) {
258
+ openPointTooltip(point, {
259
+ x: clamp((localX / rect.width) * 100),
260
+ y: clamp((localY / rect.height) * 100),
261
+ })
262
+ return
263
+ }
264
+ }
265
+
266
+ setTooltipOpen(false)
267
+ }
268
+
269
+ return (
270
+ <div
271
+ ref={setRef}
272
+ role="img"
273
+ className={cn(
274
+ sparklineChartVariantClasses[variant],
275
+ "relative",
276
+ className
277
+ )}
278
+ onPointerMove={handlePointerMove}
279
+ onMouseMove={handlePointerMove}
280
+ onMouseLeave={() => setTooltipOpen(false)}
281
+ onPointerLeave={() => setTooltipOpen(false)}
282
+ {...props}
283
+ >
284
+ {showGrid
285
+ ? [25, 50, 75].map((percent) => (
286
+ <span
287
+ key={percent}
288
+ className="pointer-events-none absolute inset-x-0 border-t border-border/55"
289
+ style={{ top: `${percent}%` }}
290
+ aria-hidden="true"
291
+ />
292
+ ))
293
+ : null}
294
+ {referenceY !== null ? (
295
+ <>
296
+ <span
297
+ className="pointer-events-none absolute inset-x-0 border-t border-dashed border-foreground/35"
298
+ style={{ top: `${referenceY}%` }}
299
+ aria-hidden="true"
300
+ />
301
+ <ChartTooltip
302
+ label={referenceLabel}
303
+ value={formatValue(referenceValue ?? 0)}
304
+ >
305
+ <span
306
+ className="absolute inset-x-0 z-20 h-5 -translate-y-1/2 rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
307
+ style={{ top: `${referenceY}%` }}
308
+ tabIndex={0}
309
+ aria-label={`${referenceText}: ${formatValue(referenceValue ?? 0)}`}
310
+ onPointerMove={(event) => {
311
+ event.stopPropagation()
312
+ setTooltipOpen(false)
313
+ }}
314
+ onMouseMove={(event) => {
315
+ event.stopPropagation()
316
+ setTooltipOpen(false)
317
+ }}
318
+ />
319
+ </ChartTooltip>
320
+ </>
321
+ ) : null}
322
+ <svg
323
+ className="pointer-events-none absolute inset-0 h-full w-full overflow-visible"
324
+ viewBox={`0 0 ${formatChartNumber(width)} ${formatChartNumber(height)}`}
325
+ preserveAspectRatio="none"
326
+ aria-hidden="true"
327
+ >
328
+ {variant === "area" && areaPointList ? (
329
+ <polygon
330
+ points={areaPointList}
331
+ fill={strokeColor}
332
+ opacity={0.12}
333
+ />
334
+ ) : null}
335
+ <polyline
336
+ points={linePointList}
337
+ fill="none"
338
+ stroke={strokeColor}
339
+ strokeWidth={strokeWidth}
340
+ strokeLinecap="round"
341
+ strokeLinejoin="round"
342
+ vectorEffect="non-scaling-stroke"
343
+ />
344
+ {showDots
345
+ ? points.map((point, index) => (
346
+ <circle
347
+ key={`${point.x}-${index}`}
348
+ cx={formatChartNumber(point.x)}
349
+ cy={formatChartNumber(point.y)}
350
+ r={4}
351
+ fill="hsl(var(--background))"
352
+ stroke={strokeColor}
353
+ strokeWidth={2}
354
+ vectorEffect="non-scaling-stroke"
355
+ />
356
+ ))
357
+ : null}
358
+ </svg>
359
+ {points.map((point, index) => {
360
+ const range = getPointHitRange(points, index, width)
361
+
362
+ return (
363
+ <span
364
+ key={`hit-${point.x}-${index}`}
365
+ className="pointer-events-none absolute inset-y-0 z-10 rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
366
+ style={{
367
+ left: `${(range.start / width) * 100}%`,
368
+ width: `${((range.end - range.start) / width) * 100}%`,
369
+ }}
370
+ tabIndex={0}
371
+ aria-label={`${chartLabelToString(point.label)}: ${formatValue(point.value)}`}
372
+ onFocus={() =>
373
+ openPointTooltip(point, {
374
+ x: (point.x / width) * 100,
375
+ y: (point.y / height) * 100,
376
+ })
377
+ }
378
+ onBlur={() => setTooltipOpen(false)}
379
+ />
380
+ )
381
+ })}
382
+ <ChartFloatingTooltip
383
+ label={tooltipContent?.label}
384
+ value={tooltipContent?.value}
385
+ position={tooltipPosition}
386
+ open={tooltipOpen}
387
+ />
388
+ </div>
389
+ )
390
+ }
391
+ )
392
+ SparklineChart.displayName = "SparklineChart"
393
+
394
+ export { SparklineChart }