@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,464 @@
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
+ defaultChartValueFormatter,
10
+ getChartColor,
11
+ getChartLabel,
12
+ getChartValue,
13
+ } from "./chart-utils"
14
+ import { ChartLegend } from "./ChartLegend"
15
+ import { ChartTooltip } from "./chart-tooltip"
16
+ import type { LineChartVariantKey } from "./generated/variant-keys"
17
+ import { lineChartDefaultVariantKey } from "./generated/default-variant-keys"
18
+
19
+ export interface LineChartSeries {
20
+ label?: React.ReactNode
21
+ data: Array<ChartDataPoint | number>
22
+ color?: ChartColor
23
+ }
24
+
25
+ export interface LineChartProps
26
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
27
+ series: LineChartSeries[]
28
+ variant?: LineChartVariantKey
29
+ min?: number
30
+ max?: number
31
+ referenceValue?: number
32
+ referenceLabel?: React.ReactNode
33
+ showGrid?: boolean
34
+ showDots?: boolean
35
+ showLegend?: boolean
36
+ showLabels?: boolean
37
+ strokeWidth?: number
38
+ formatValue?: (value: number) => React.ReactNode
39
+ }
40
+
41
+ interface LineChartPoint {
42
+ index: number
43
+ label: React.ReactNode
44
+ x: number
45
+ y: number
46
+ value: number
47
+ }
48
+
49
+ interface LineChartPadding {
50
+ top: number
51
+ right: number
52
+ bottom: number
53
+ left: number
54
+ }
55
+
56
+ const lineChartVariantClasses: Record<LineChartVariantKey, string> = {
57
+ area: "h-[260px] w-full p-0",
58
+ line: "h-[260px] w-full p-0",
59
+ }
60
+
61
+ const plotPadding: LineChartPadding = {
62
+ top: 14,
63
+ right: 12,
64
+ bottom: 10,
65
+ left: 28,
66
+ }
67
+
68
+ function getFiniteSeriesValues(series: LineChartSeries[]) {
69
+ return series.flatMap((item) =>
70
+ item.data.map(getChartValue).filter((value) => Number.isFinite(value))
71
+ )
72
+ }
73
+
74
+ function getChartDomain(
75
+ series: LineChartSeries[],
76
+ min: number | undefined,
77
+ max: number | undefined,
78
+ referenceValue: number | undefined
79
+ ) {
80
+ const values = getFiniteSeriesValues(series)
81
+ if (referenceValue !== undefined && Number.isFinite(referenceValue)) {
82
+ values.push(referenceValue)
83
+ }
84
+
85
+ if (values.length === 0 && min === undefined && max === undefined) {
86
+ return { min: 0, max: 1 }
87
+ }
88
+
89
+ const fallbackMin = values.length > 0 ? Math.min(...values) : 0
90
+ const fallbackMax = values.length > 0 ? Math.max(...values) : fallbackMin + 1
91
+ const resolvedMin =
92
+ typeof min === "number" && Number.isFinite(min) ? min : fallbackMin
93
+ const resolvedMax =
94
+ typeof max === "number" && Number.isFinite(max) ? max : fallbackMax
95
+
96
+ return {
97
+ min: Math.min(resolvedMin, resolvedMax),
98
+ max: Math.max(resolvedMin, resolvedMax),
99
+ }
100
+ }
101
+
102
+ function buildPoints(
103
+ data: Array<ChartDataPoint | number>,
104
+ width: number,
105
+ height: number,
106
+ padding: LineChartPadding,
107
+ domain: { min: number; max: number },
108
+ maxPointCount: number
109
+ ) {
110
+ const span = domain.max - domain.min || 1
111
+ const usableWidth = Math.max(1, width - padding.left - padding.right)
112
+ const usableHeight = Math.max(1, height - padding.top - padding.bottom)
113
+
114
+ return data
115
+ .map((item, index) => ({
116
+ index,
117
+ label: getChartLabel(item, index),
118
+ value: getChartValue(item),
119
+ }))
120
+ .filter((point) => Number.isFinite(point.value))
121
+ .map((point) => {
122
+ const x =
123
+ maxPointCount <= 1
124
+ ? width / 2
125
+ : padding.left + (point.index / (maxPointCount - 1)) * usableWidth
126
+ const y =
127
+ padding.top +
128
+ (1 - (point.value - domain.min) / span) * usableHeight
129
+
130
+ return { ...point, x, y }
131
+ })
132
+ }
133
+
134
+ function formatChartNumber(value: number) {
135
+ return Number(value.toFixed(3))
136
+ }
137
+
138
+ function buildSvgPointList(points: LineChartPoint[]) {
139
+ return points
140
+ .map((point) => `${formatChartNumber(point.x)},${formatChartNumber(point.y)}`)
141
+ .join(" ")
142
+ }
143
+
144
+ function buildAreaPointList(
145
+ points: LineChartPoint[],
146
+ height: number,
147
+ padding: LineChartPadding
148
+ ) {
149
+ if (points.length === 0) return undefined
150
+
151
+ const first = points[0]
152
+ const last = points[points.length - 1]
153
+ const baselineY = height - padding.bottom
154
+
155
+ return [
156
+ buildSvgPointList(points),
157
+ `${formatChartNumber(last.x)},${formatChartNumber(baselineY)}`,
158
+ `${formatChartNumber(first.x)},${formatChartNumber(baselineY)}`,
159
+ ].join(" ")
160
+ }
161
+
162
+ function getReferenceY(
163
+ value: number | undefined,
164
+ height: number,
165
+ padding: LineChartPadding,
166
+ domain: { min: number; max: number }
167
+ ) {
168
+ if (value === undefined || !Number.isFinite(value)) return null
169
+
170
+ const span = domain.max - domain.min || 1
171
+ return padding.top + (1 - (value - domain.min) / span) * (height - padding.top - padding.bottom)
172
+ }
173
+
174
+ function getSeriesLabel(series: LineChartSeries, index: number) {
175
+ return series.label ?? `Series ${index + 1}`
176
+ }
177
+
178
+ function getLineChartLabels(series: LineChartSeries[]) {
179
+ const longestSeries = series.reduce<LineChartSeries | null>((longest, item) => {
180
+ if (!longest || item.data.length > longest.data.length) return item
181
+ return longest
182
+ }, null)
183
+
184
+ return longestSeries?.data.map((item, index) => getChartLabel(item, index)) ?? []
185
+ }
186
+
187
+ function useElementSize<T extends HTMLElement>() {
188
+ const [node, setNode] = React.useState<T | null>(null)
189
+ const [size, setSize] = React.useState({ width: 0, height: 0 })
190
+
191
+ React.useEffect(() => {
192
+ if (!node) return undefined
193
+ const updateSize = () => {
194
+ const rect = node.getBoundingClientRect()
195
+ setSize({ width: rect.width, height: rect.height })
196
+ }
197
+ updateSize()
198
+
199
+ const observer = new ResizeObserver(updateSize)
200
+ observer.observe(node)
201
+ return () => observer.disconnect()
202
+ }, [node])
203
+
204
+ return [setNode, size] as const
205
+ }
206
+
207
+ const LineChart = React.forwardRef<HTMLDivElement, LineChartProps>(
208
+ (
209
+ {
210
+ className,
211
+ series,
212
+ variant = lineChartDefaultVariantKey,
213
+ min,
214
+ max,
215
+ referenceValue,
216
+ referenceLabel = "Reference",
217
+ showGrid = true,
218
+ showDots = true,
219
+ showLegend = false,
220
+ showLabels = true,
221
+ strokeWidth = 2,
222
+ formatValue = defaultChartValueFormatter,
223
+ ...props
224
+ },
225
+ ref
226
+ ) => {
227
+ const [setPlotNode, plotSize] = useElementSize<HTMLDivElement>()
228
+ const width = plotSize.width || 480
229
+ const height = plotSize.height || 212
230
+ const maxPointCount = Math.max(1, ...series.map((item) => item.data.length))
231
+ const domain = getChartDomain(series, min, max, referenceValue)
232
+ const labels = getLineChartLabels(series)
233
+ const referenceY = getReferenceY(referenceValue, height, plotPadding, domain)
234
+ const referenceText = chartLabelToString(referenceLabel, "Reference")
235
+ const legendItems = series.map((item, index) => {
236
+ const lastPoint = [...item.data]
237
+ .reverse()
238
+ .find((point) => Number.isFinite(getChartValue(point)))
239
+ const value =
240
+ lastPoint !== undefined ? formatValue(getChartValue(lastPoint)) : undefined
241
+
242
+ return {
243
+ label: getSeriesLabel(item, index),
244
+ value,
245
+ color: item.color,
246
+ description:
247
+ referenceValue !== undefined
248
+ ? [referenceLabel, ": ", formatValue(referenceValue)]
249
+ : undefined,
250
+ }
251
+ })
252
+
253
+ return (
254
+ <div
255
+ ref={ref}
256
+ role="img"
257
+ className={cn(
258
+ lineChartVariantClasses[variant],
259
+ "flex min-w-0 flex-col gap-3",
260
+ className
261
+ )}
262
+ {...props}
263
+ >
264
+ <div ref={setPlotNode} className="relative min-h-0 flex-1 overflow-hidden">
265
+ <svg
266
+ className="pointer-events-none absolute inset-0 h-full w-full overflow-visible"
267
+ viewBox={`0 0 ${formatChartNumber(width)} ${formatChartNumber(height)}`}
268
+ preserveAspectRatio="none"
269
+ aria-hidden="true"
270
+ >
271
+ {showGrid
272
+ ? [0, 25, 50, 75, 100].map((percent) => {
273
+ const y =
274
+ plotPadding.top +
275
+ ((height - plotPadding.top - plotPadding.bottom) *
276
+ percent) /
277
+ 100
278
+
279
+ return (
280
+ <line
281
+ key={`y-${percent}`}
282
+ x1={0}
283
+ x2={formatChartNumber(width)}
284
+ y1={formatChartNumber(y)}
285
+ y2={formatChartNumber(y)}
286
+ stroke="hsl(var(--border) / 0.65)"
287
+ strokeDasharray="4 4"
288
+ vectorEffect="non-scaling-stroke"
289
+ />
290
+ )
291
+ })
292
+ : null}
293
+ {showGrid && labels.length > 1
294
+ ? labels.map((label, index) => {
295
+ const x =
296
+ plotPadding.left +
297
+ (index / (labels.length - 1)) *
298
+ (width - plotPadding.left - plotPadding.right)
299
+
300
+ return (
301
+ <line
302
+ key={`x-${chartLabelToString(label, "Label")}-${index}`}
303
+ x1={formatChartNumber(x)}
304
+ x2={formatChartNumber(x)}
305
+ y1={0}
306
+ y2={formatChartNumber(height)}
307
+ stroke="hsl(var(--border) / 0.45)"
308
+ vectorEffect="non-scaling-stroke"
309
+ />
310
+ )
311
+ })
312
+ : null}
313
+ {referenceY !== null ? (
314
+ <line
315
+ x1={0}
316
+ x2={formatChartNumber(width)}
317
+ y1={formatChartNumber(referenceY)}
318
+ y2={formatChartNumber(referenceY)}
319
+ stroke="hsl(var(--foreground) / 0.35)"
320
+ strokeDasharray="4 4"
321
+ vectorEffect="non-scaling-stroke"
322
+ />
323
+ ) : null}
324
+ </svg>
325
+ {referenceY !== null ? (
326
+ <ChartTooltip
327
+ label={referenceLabel}
328
+ value={formatValue(referenceValue ?? 0)}
329
+ >
330
+ <span
331
+ className="absolute inset-x-0 z-10 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"
332
+ style={{ top: `${referenceY}px` }}
333
+ tabIndex={0}
334
+ aria-label={`${referenceText}: ${formatValue(referenceValue ?? 0)}`}
335
+ />
336
+ </ChartTooltip>
337
+ ) : null}
338
+ {series.map((item, seriesIndex) => {
339
+ const points = buildPoints(
340
+ item.data,
341
+ width,
342
+ height,
343
+ plotPadding,
344
+ domain,
345
+ maxPointCount
346
+ )
347
+ const color = getChartColor(item.color, seriesIndex)
348
+ const linePoints = buildSvgPointList(points)
349
+ const areaPoints = buildAreaPointList(
350
+ points,
351
+ height,
352
+ plotPadding
353
+ )
354
+
355
+ return (
356
+ <React.Fragment key={`${chartLabelToString(item.label, "Series")}-${seriesIndex}`}>
357
+ <svg
358
+ className="pointer-events-none absolute inset-0 h-full w-full overflow-visible"
359
+ viewBox={`0 0 ${formatChartNumber(width)} ${formatChartNumber(height)}`}
360
+ preserveAspectRatio="none"
361
+ aria-hidden="true"
362
+ >
363
+ {variant === "area" && areaPoints ? (
364
+ <polygon
365
+ points={areaPoints}
366
+ fill={color}
367
+ opacity={seriesIndex === 0 ? 0.14 : 0.08}
368
+ />
369
+ ) : null}
370
+ <polyline
371
+ points={linePoints}
372
+ fill="none"
373
+ stroke={color}
374
+ strokeWidth={strokeWidth}
375
+ strokeLinecap="round"
376
+ strokeLinejoin="round"
377
+ vectorEffect="non-scaling-stroke"
378
+ />
379
+ {showDots
380
+ ? points.map((point, index) => (
381
+ <circle
382
+ key={`${seriesIndex}-dot-${point.x}-${index}`}
383
+ cx={formatChartNumber(point.x)}
384
+ cy={formatChartNumber(point.y)}
385
+ r={4}
386
+ fill="hsl(var(--background))"
387
+ stroke={color}
388
+ strokeWidth={2}
389
+ vectorEffect="non-scaling-stroke"
390
+ />
391
+ ))
392
+ : null}
393
+ </svg>
394
+ {points.map((point, index) => {
395
+ const description =
396
+ referenceValue !== undefined
397
+ ? [referenceLabel, ": ", formatValue(referenceValue)]
398
+ : undefined
399
+ const descriptionText =
400
+ referenceValue !== undefined
401
+ ? ` (${referenceText}: ${formatValue(referenceValue)})`
402
+ : ""
403
+
404
+ return (
405
+ <ChartTooltip
406
+ key={`${seriesIndex}-hit-${point.x}-${index}`}
407
+ label={
408
+ <>
409
+ {getSeriesLabel(item, seriesIndex)}
410
+ {" / "}
411
+ {point.label}
412
+ </>
413
+ }
414
+ value={formatValue(point.value)}
415
+ description={description}
416
+ >
417
+ <span
418
+ className="absolute z-10 h-7 w-7 -translate-x-1/2 -translate-y-1/2 rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
419
+ style={{
420
+ left: `${(point.x / width) * 100}%`,
421
+ top: `${(point.y / height) * 100}%`,
422
+ }}
423
+ tabIndex={0}
424
+ aria-label={`${chartLabelToString(
425
+ getSeriesLabel(item, seriesIndex),
426
+ "Series"
427
+ )} ${chartLabelToString(point.label)}: ${formatValue(point.value)}${descriptionText}`}
428
+ />
429
+ </ChartTooltip>
430
+ )
431
+ })}
432
+ </React.Fragment>
433
+ )
434
+ })}
435
+ </div>
436
+ {showLabels && labels.length > 0 ? (
437
+ <div
438
+ className="grid text-xs text-muted-foreground"
439
+ style={{
440
+ gridTemplateColumns: `repeat(${labels.length}, minmax(0, 1fr))`,
441
+ paddingLeft: plotPadding.left,
442
+ paddingRight: plotPadding.right,
443
+ }}
444
+ >
445
+ {labels.map((label, index) => (
446
+ <span
447
+ key={`${chartLabelToString(label, "Label")}-${index}`}
448
+ className="min-w-0 truncate text-center"
449
+ >
450
+ {label}
451
+ </span>
452
+ ))}
453
+ </div>
454
+ ) : null}
455
+ {showLegend ? (
456
+ <ChartLegend items={legendItems} variant="horizontal" />
457
+ ) : null}
458
+ </div>
459
+ )
460
+ }
461
+ )
462
+ LineChart.displayName = "LineChart"
463
+
464
+ export { LineChart }
@@ -1,6 +1,6 @@
1
1
  import * as React from "react"
2
2
  import { cn } from "../../lib/utils"
3
- import { Check, Circle, Dot } from "lucide-react"
3
+ import { IconCheck as Check, IconCircle as Circle, IconPoint as Dot } from "@tabler/icons-react";
4
4
  import { listVariantKeys, type ListVariantKey } from "./generated/variant-keys"
5
5
  import { listDefaultVariantKey } from "./generated/default-variant-keys"
6
6
 
@@ -15,9 +15,9 @@ const markerIcons: Record<ListVariantKey, React.ReactNode> = {
15
15
  }
16
16
 
17
17
  const listLayoutClasses: Record<ListVariantKey, string> = {
18
- dot: "flex flex-col items-center w-fit gap-2",
19
- check: "flex flex-col items-center w-fit gap-2",
20
- circle: "flex flex-col items-center w-fit gap-2",
18
+ dot: "flex flex-col",
19
+ check: "flex flex-col",
20
+ circle: "flex flex-col",
21
21
  }
22
22
 
23
23
  function isListVariantKey(value: ListMarkerKey | undefined): value is ListVariantKey {
@@ -28,10 +28,11 @@ export interface ListProps extends React.HTMLAttributes<HTMLUListElement | HTMLO
28
28
  variant?: "unordered" | "ordered" | "none"
29
29
  marker?: ListMarkerKey
30
30
  spacing?: "default" | "tight" | "loose"
31
+ divided?: boolean
31
32
  }
32
33
 
33
34
  const List = React.forwardRef<HTMLElement, ListProps>(
34
- ({ className, variant = "unordered", marker, spacing = "default", children, ...props }, ref) => {
35
+ ({ className, variant = "unordered", marker, spacing = "default", divided = false, children, ...props }, ref) => {
35
36
  const Comp = variant === "ordered" ? "ol" : "ul"
36
37
 
37
38
  // Determine default marker based on variant if not specified
@@ -42,16 +43,22 @@ const List = React.forwardRef<HTMLElement, ListProps>(
42
43
  : variant === "unordered"
43
44
  ? listDefaultVariantKey
44
45
  : "none");
46
+ const usesCustomMarker = isListVariantKey(effectiveMarker)
47
+ const customMarkerLayout = usesCustomMarker ? listLayoutClasses[effectiveMarker] : undefined
45
48
 
46
49
  return (
47
50
  <Comp
48
51
  ref={ref as any}
49
52
  className={cn(
50
53
  "text-muted-foreground",
51
- isListVariantKey(effectiveMarker) && listLayoutClasses[effectiveMarker],
52
- spacing === "default" && "space-y-2",
53
- spacing === "tight" && "space-y-1",
54
- spacing === "loose" && "space-y-4",
54
+ divided ? "flex flex-col divide-y divide-border/70" : customMarkerLayout,
55
+ divided && spacing === "default" && "[&>li]:py-2",
56
+ divided && spacing === "tight" && "[&>li]:py-1.5",
57
+ divided && spacing === "loose" && "[&>li]:py-3",
58
+ divided && "[&>li:first-child]:pt-0 [&>li:last-child]:pb-0",
59
+ !divided && spacing === "default" && (usesCustomMarker ? "gap-2" : "space-y-2"),
60
+ !divided && spacing === "tight" && (usesCustomMarker ? "gap-1" : "space-y-1"),
61
+ !divided && spacing === "loose" && (usesCustomMarker ? "gap-4" : "space-y-4"),
55
62
  className
56
63
  )}
57
64
  {...props}
@@ -102,15 +109,15 @@ const ListItem = React.forwardRef<HTMLLIElement, ListItemProps>(
102
109
  <li
103
110
  ref={ref}
104
111
  className={cn(
105
- "flex items-center gap-2",
106
- isStandardOrdered && "list-decimal list-inside ml-4 list-item", // Restore standard list behavior
107
- !isStandardOrdered && "list-none",
112
+ isStandardOrdered
113
+ ? "ml-4 list-inside list-decimal"
114
+ : "flex list-none items-center gap-2",
108
115
  className
109
116
  )}
110
117
  {...props}
111
118
  >
112
119
  {!isStandardOrdered && markerEl}
113
- <span className="flex-1 text-sm text-muted-foreground">{children}</span>
120
+ <span className="min-w-0 flex-1 text-sm text-muted-foreground">{children}</span>
114
121
  </li>
115
122
  )
116
123
  }