@alpic-ai/ui 0.0.0-dev.g2338b91 → 0.0.0-dev.g23fdbf3

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 (113) hide show
  1. package/dist/components/accordion-card.d.mts +5 -6
  2. package/dist/components/accordion.d.mts +5 -6
  3. package/dist/components/alert.d.mts +9 -11
  4. package/dist/components/area-chart.d.mts +62 -0
  5. package/dist/components/area-chart.mjs +269 -0
  6. package/dist/components/attachment-tile.d.mts +1 -3
  7. package/dist/components/avatar.d.mts +8 -10
  8. package/dist/components/badge.d.mts +2 -4
  9. package/dist/components/bar-chart.d.mts +48 -0
  10. package/dist/components/bar-chart.mjs +245 -0
  11. package/dist/components/bar-list.d.mts +28 -0
  12. package/dist/components/bar-list.mjs +98 -0
  13. package/dist/components/breadcrumb.d.mts +10 -11
  14. package/dist/components/button.d.mts +6 -8
  15. package/dist/components/card.d.mts +9 -10
  16. package/dist/components/chart-card.d.mts +25 -0
  17. package/dist/components/chart-card.mjs +48 -0
  18. package/dist/components/chart-container.d.mts +20 -0
  19. package/dist/components/chart-container.mjs +37 -0
  20. package/dist/components/chart-legend.d.mts +16 -0
  21. package/dist/components/chart-legend.mjs +26 -0
  22. package/dist/components/chart-tooltip.d.mts +33 -0
  23. package/dist/components/chart-tooltip.mjs +52 -0
  24. package/dist/components/checkbox.d.mts +2 -3
  25. package/dist/components/collapsible.d.mts +4 -5
  26. package/dist/components/combobox.d.mts +12 -11
  27. package/dist/components/combobox.mjs +7 -4
  28. package/dist/components/command.d.mts +9 -10
  29. package/dist/components/copyable.d.mts +2 -3
  30. package/dist/components/description-list.d.mts +5 -6
  31. package/dist/components/dialog.d.mts +15 -17
  32. package/dist/components/donut-chart.d.mts +46 -0
  33. package/dist/components/donut-chart.mjs +185 -0
  34. package/dist/components/dropdown-menu.d.mts +18 -20
  35. package/dist/components/form.d.mts +38 -21
  36. package/dist/components/form.mjs +6 -6
  37. package/dist/components/github-button.d.mts +1 -2
  38. package/dist/components/grid-fx.d.mts +13 -0
  39. package/dist/components/grid-fx.mjs +188 -0
  40. package/dist/components/heatmap-chart.d.mts +40 -0
  41. package/dist/components/heatmap-chart.mjs +198 -0
  42. package/dist/components/input-group.d.mts +5 -7
  43. package/dist/components/input.d.mts +4 -5
  44. package/dist/components/input.mjs +2 -2
  45. package/dist/components/label.d.mts +2 -3
  46. package/dist/components/line-chart.d.mts +55 -0
  47. package/dist/components/line-chart.mjs +211 -0
  48. package/dist/components/page-loader.d.mts +1 -3
  49. package/dist/components/pagination.d.mts +3 -4
  50. package/dist/components/popover.d.mts +5 -6
  51. package/dist/components/radio-group.d.mts +3 -4
  52. package/dist/components/scroll-area.d.mts +3 -4
  53. package/dist/components/select-trigger-variants.d.mts +1 -3
  54. package/dist/components/select.d.mts +9 -10
  55. package/dist/components/separator.d.mts +2 -3
  56. package/dist/components/sheet.d.mts +11 -12
  57. package/dist/components/shimmer-text.d.mts +2 -2
  58. package/dist/components/sidebar.d.mts +34 -36
  59. package/dist/components/sidebar.mjs +10 -10
  60. package/dist/components/skeleton.d.mts +2 -4
  61. package/dist/components/sonner.d.mts +5 -6
  62. package/dist/components/spinner.d.mts +3 -5
  63. package/dist/components/stat.d.mts +30 -0
  64. package/dist/components/stat.mjs +107 -0
  65. package/dist/components/status-dot.d.mts +2 -4
  66. package/dist/components/switch.d.mts +2 -3
  67. package/dist/components/table.d.mts +10 -11
  68. package/dist/components/tabs.d.mts +12 -14
  69. package/dist/components/tag.d.mts +3 -5
  70. package/dist/components/task-progress.d.mts +1 -3
  71. package/dist/components/textarea.d.mts +3 -4
  72. package/dist/components/textarea.mjs +2 -2
  73. package/dist/components/toggle-group.d.mts +4 -6
  74. package/dist/components/toggle-group.mjs +3 -3
  75. package/dist/components/tooltip-icon-button.d.mts +1 -2
  76. package/dist/components/tooltip.d.mts +5 -6
  77. package/dist/components/typography.d.mts +4 -5
  78. package/dist/components/wizard.d.mts +4 -5
  79. package/dist/hooks/use-chart-theme.d.mts +18 -0
  80. package/dist/hooks/use-chart-theme.mjs +57 -0
  81. package/dist/hooks/use-mobile.mjs +3 -3
  82. package/dist/hooks/use-reduced-motion.d.mts +4 -0
  83. package/dist/hooks/use-reduced-motion.mjs +16 -0
  84. package/dist/lib/chart-palette.d.mts +4 -0
  85. package/dist/lib/chart-palette.mjs +95 -0
  86. package/dist/lib/chart.d.mts +14 -0
  87. package/dist/lib/chart.mjs +27 -0
  88. package/package.json +30 -29
  89. package/src/components/area-chart.tsx +339 -0
  90. package/src/components/bar-chart.tsx +300 -0
  91. package/src/components/bar-list.tsx +150 -0
  92. package/src/components/chart-card.tsx +63 -0
  93. package/src/components/chart-container.tsx +49 -0
  94. package/src/components/chart-legend.tsx +41 -0
  95. package/src/components/chart-tooltip.tsx +93 -0
  96. package/src/components/combobox.tsx +9 -2
  97. package/src/components/donut-chart.tsx +217 -0
  98. package/src/components/grid-fx.tsx +238 -0
  99. package/src/components/heatmap-chart.tsx +287 -0
  100. package/src/components/line-chart.tsx +264 -0
  101. package/src/components/stat.tsx +109 -0
  102. package/src/hooks/use-chart-theme.ts +75 -0
  103. package/src/hooks/use-reduced-motion.ts +17 -0
  104. package/src/lib/chart-palette.ts +110 -0
  105. package/src/lib/chart.ts +56 -0
  106. package/src/stories/area-chart.stories.tsx +200 -0
  107. package/src/stories/bar-chart.stories.tsx +169 -0
  108. package/src/stories/bar-list.stories.tsx +85 -0
  109. package/src/stories/donut-chart.stories.tsx +112 -0
  110. package/src/stories/heatmap-chart.stories.tsx +107 -0
  111. package/src/stories/line-chart.stories.tsx +146 -0
  112. package/src/stories/stat.stories.tsx +64 -0
  113. package/src/styles/tokens.css +63 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpic-ai/ui",
3
- "version": "0.0.0-dev.g2338b91",
3
+ "version": "0.0.0-dev.g23fdbf3",
4
4
  "description": "Alpic design system — shared UI components",
5
5
  "type": "module",
6
6
  "exports": {
@@ -23,48 +23,49 @@
23
23
  "src"
24
24
  ],
25
25
  "peerDependencies": {
26
- "lucide-react": "^1.14.0",
27
- "react": "^19.2.6",
28
- "react-dom": "^19.2.6",
29
- "react-hook-form": "^7.75.0",
26
+ "lucide-react": "^1.18.0",
27
+ "react": "^19.2.7",
28
+ "react-dom": "^19.2.7",
29
+ "react-hook-form": "^7.79.0",
30
30
  "sonner": "^2.0.7",
31
- "tailwindcss": "^4.3.0",
31
+ "tailwindcss": "^4.3.1",
32
32
  "tw-animate-css": "^1.4.0"
33
33
  },
34
34
  "dependencies": {
35
- "@radix-ui/react-accordion": "^1.2.12",
36
- "@radix-ui/react-avatar": "^1.1.11",
37
- "@radix-ui/react-checkbox": "^1.3.3",
38
- "@radix-ui/react-collapsible": "^1.1.12",
39
- "@radix-ui/react-dialog": "^1.1.15",
40
- "@radix-ui/react-dropdown-menu": "^2.1.16",
41
- "@radix-ui/react-label": "^2.1.8",
42
- "@radix-ui/react-popover": "^1.1.15",
43
- "@radix-ui/react-radio-group": "^1.3.8",
44
- "@radix-ui/react-scroll-area": "^1.2.10",
45
- "@radix-ui/react-select": "^2.2.6",
46
- "@radix-ui/react-separator": "^1.1.8",
47
- "@radix-ui/react-slot": "^1.2.4",
48
- "@radix-ui/react-switch": "^1.2.6",
49
- "@radix-ui/react-tabs": "^1.1.13",
50
- "@radix-ui/react-toggle-group": "^1.1.11",
51
- "@radix-ui/react-tooltip": "^1.2.8",
35
+ "@radix-ui/react-accordion": "^1.2.13",
36
+ "@radix-ui/react-avatar": "^1.1.12",
37
+ "@radix-ui/react-checkbox": "^1.3.4",
38
+ "@radix-ui/react-collapsible": "^1.1.13",
39
+ "@radix-ui/react-dialog": "^1.1.16",
40
+ "@radix-ui/react-dropdown-menu": "^2.1.17",
41
+ "@radix-ui/react-label": "^2.1.9",
42
+ "@radix-ui/react-popover": "^1.1.16",
43
+ "@radix-ui/react-radio-group": "^1.4.0",
44
+ "@radix-ui/react-scroll-area": "^1.2.11",
45
+ "@radix-ui/react-select": "^2.3.0",
46
+ "@radix-ui/react-separator": "^1.1.9",
47
+ "@radix-ui/react-slot": "^1.2.5",
48
+ "@radix-ui/react-switch": "^1.3.0",
49
+ "@radix-ui/react-tabs": "^1.1.14",
50
+ "@radix-ui/react-toggle-group": "^1.1.12",
51
+ "@radix-ui/react-tooltip": "^1.2.9",
52
52
  "class-variance-authority": "^0.7.1",
53
53
  "clsx": "^2.1.1",
54
54
  "cmdk": "^1.1.1",
55
+ "recharts": "^3.8.1",
55
56
  "tailwind-merge": "^3.6.0"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@ladle/react": "^5.1.1",
59
- "@tailwindcss/postcss": "^4.3.0",
60
- "@types/react": "19.2.14",
60
+ "@tailwindcss/postcss": "^4.3.1",
61
+ "@types/react": "19.2.17",
61
62
  "@types/react-dom": "19.2.3",
62
- "lucide-react": "^1.14.0",
63
- "react-hook-form": "^7.75.0",
63
+ "lucide-react": "^1.18.0",
64
+ "react-hook-form": "^7.79.0",
64
65
  "shx": "^0.4.0",
65
66
  "sonner": "^2.0.7",
66
- "tailwindcss": "^4.3.0",
67
- "tsdown": "^0.22.0",
67
+ "tailwindcss": "^4.3.1",
68
+ "tsdown": "^0.22.2",
68
69
  "tw-animate-css": "^1.4.0",
69
70
  "typescript": "^6.0.3"
70
71
  },
@@ -0,0 +1,339 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import {
5
+ Area,
6
+ CartesianGrid,
7
+ LabelList,
8
+ AreaChart as RechartsAreaChart,
9
+ ReferenceArea,
10
+ ReferenceDot,
11
+ ReferenceLine,
12
+ ResponsiveContainer,
13
+ Tooltip,
14
+ XAxis,
15
+ YAxis,
16
+ } from "recharts";
17
+
18
+ import { useReducedMotion } from "../hooks/use-reduced-motion";
19
+ import { type ChartSeries, orderByLuminance, resolveSeries } from "../lib/chart";
20
+ import type { ChartPaletteName } from "../lib/chart-palette";
21
+ import { cn } from "../lib/cn";
22
+ import { useChartContext } from "./chart-container";
23
+ import { ChartLegend } from "./chart-legend";
24
+ import { ChartTooltipContent } from "./chart-tooltip";
25
+
26
+ const CURVE_TYPE = { monotone: "monotone", linear: "linear", step: "stepAfter" } as const;
27
+
28
+ export interface ChartMarker {
29
+ x: string | number;
30
+ y: number;
31
+ label?: string;
32
+ color?: string;
33
+ }
34
+
35
+ export interface AreaChartProps {
36
+ data: ReadonlyArray<Record<string, string | number | null | undefined>>;
37
+ index: string;
38
+ series: ChartSeries[];
39
+ variant?: "stacked" | "grouped" | "expand";
40
+ curve?: keyof typeof CURVE_TYPE;
41
+ legend?: boolean;
42
+ valueFlags?: boolean;
43
+ height?: number;
44
+ yAxisWidth?: number;
45
+ palette?: ChartPaletteName;
46
+ referenceLine?: { y: number; label?: string; band?: boolean };
47
+ markers?: ChartMarker[];
48
+ lastValueLabel?: boolean;
49
+ texture?: boolean;
50
+ loading?: boolean;
51
+ valueFormatter?: (value: number) => string;
52
+ labelFormatter?: (label: string | number) => string;
53
+ className?: string;
54
+ }
55
+
56
+ function AreaChart({
57
+ data,
58
+ index,
59
+ series,
60
+ variant = "stacked",
61
+ curve = "monotone",
62
+ legend = false,
63
+ valueFlags = false,
64
+ height = 200,
65
+ yAxisWidth = 48,
66
+ palette,
67
+ referenceLine,
68
+ markers,
69
+ lastValueLabel = false,
70
+ texture = false,
71
+ loading = false,
72
+ valueFormatter = (value) => value.toLocaleString("en-US"),
73
+ labelFormatter,
74
+ className,
75
+ }: AreaChartProps) {
76
+ const { palette: paletteColors, theme } = useChartContext(palette);
77
+ const reactId = React.useId().replace(/:/g, "");
78
+ const reducedMotion = useReducedMotion();
79
+ const animated = !reducedMotion;
80
+
81
+ const resolved = resolveSeries(series, paletteColors, theme);
82
+ const stacked = variant === "stacked" || variant === "expand";
83
+ const rendered = stacked ? orderByLuminance(resolved) : resolved;
84
+
85
+ const strokeOnly = variant === "grouped" && rendered.length > 1;
86
+ const filled = !strokeOnly;
87
+ const lead = resolved[0];
88
+ const withTotal = stacked && rendered.length > 1;
89
+
90
+ // Stacked bands must reach the stack height (sum per x-point), not the tallest
91
+ // single series, or the reference band stops short of the chart top.
92
+ const numericMax = React.useMemo(() => {
93
+ let max = 0;
94
+ for (const row of data) {
95
+ let rowTotal = 0;
96
+ for (const entry of series) {
97
+ const value = Number(row[entry.key]);
98
+ if (Number.isFinite(value)) {
99
+ rowTotal += value;
100
+ if (!stacked && value > max) {
101
+ max = value;
102
+ }
103
+ }
104
+ }
105
+ if (stacked && rowTotal > max) {
106
+ max = rowTotal;
107
+ }
108
+ }
109
+ return max;
110
+ }, [data, series, stacked]);
111
+
112
+ const curveType = CURVE_TYPE[curve];
113
+ const margin = { top: markers?.length ? 18 : 8, right: lastValueLabel ? 56 : 8, bottom: 2, left: 0 };
114
+
115
+ const axis = {
116
+ stroke: theme.border,
117
+ tick: { fill: theme.axisForeground, fontSize: 10, fontFamily: theme.fontMono },
118
+ tickLine: false as const,
119
+ axisLine: { stroke: theme.border, strokeOpacity: 0.6 },
120
+ };
121
+
122
+ const legendItems = resolved.map((entry) => ({ name: entry.name, color: entry.color, dashed: entry.dashed }));
123
+
124
+ const activeDotFor = (entry: (typeof rendered)[number]) =>
125
+ valueFlags
126
+ ? (dotProps: { cx?: number; cy?: number; value?: number | string }) => {
127
+ if (dotProps.cx == null || dotProps.cy == null) {
128
+ return <g />;
129
+ }
130
+ return (
131
+ <g>
132
+ <circle
133
+ cx={dotProps.cx}
134
+ cy={dotProps.cy}
135
+ r={3.5}
136
+ fill={entry.color}
137
+ stroke={theme.card}
138
+ strokeWidth={2}
139
+ />
140
+ <text
141
+ x={dotProps.cx}
142
+ y={dotProps.cy - 8}
143
+ textAnchor="middle"
144
+ fill={entry.color}
145
+ fontFamily={theme.fontMono}
146
+ fontSize={10}
147
+ style={{ fontVariantNumeric: "tabular-nums" }}
148
+ >
149
+ {valueFormatter(Number(dotProps.value ?? 0))}
150
+ </text>
151
+ </g>
152
+ );
153
+ }
154
+ : { r: 3.5, fill: entry.color, stroke: theme.card, strokeWidth: 2 };
155
+
156
+ const fillFor = (entry: (typeof rendered)[number], slot: number) => {
157
+ if (!filled) {
158
+ return "none";
159
+ }
160
+ if (texture && lead && entry.key === lead.key) {
161
+ return `url(#hatch-${reactId})`;
162
+ }
163
+ return `url(#area-${reactId}-${slot})`;
164
+ };
165
+
166
+ const renderLastLabel =
167
+ (color: string) =>
168
+ (props: {
169
+ x?: string | number;
170
+ y?: string | number;
171
+ value?: string | number | boolean | Array<string | number | boolean> | null;
172
+ index?: number;
173
+ }) => {
174
+ if (props.index !== data.length - 1 || props.x == null || props.y == null) {
175
+ return null;
176
+ }
177
+ return (
178
+ <text
179
+ x={Number(props.x) + 6}
180
+ y={Number(props.y)}
181
+ dy={3}
182
+ fill={color}
183
+ fontFamily={theme.fontMono}
184
+ fontSize={10}
185
+ textAnchor="start"
186
+ style={{ fontVariantNumeric: "tabular-nums" }}
187
+ >
188
+ {valueFormatter(Number(props.value ?? 0))}
189
+ </text>
190
+ );
191
+ };
192
+
193
+ const isEmpty = data.length === 0 || rendered.length === 0;
194
+
195
+ return (
196
+ <div data-slot="area-chart" className={cn("flex w-full flex-col gap-3", className)}>
197
+ <div className="w-full" style={{ height }}>
198
+ {loading ? (
199
+ <div className="flex h-full items-center justify-center gap-2.5 font-mono text-quaternary-foreground text-text-xs">
200
+ <span className="size-4 animate-spin rounded-full border-2 border-border border-t-foreground" />
201
+ loading…
202
+ </div>
203
+ ) : isEmpty ? (
204
+ <div className="flex h-full items-center justify-center rounded-lg border border-border border-dashed font-mono text-quaternary-foreground text-text-xs">
205
+ no data in range
206
+ </div>
207
+ ) : (
208
+ <ResponsiveContainer width="100%" height="100%">
209
+ <RechartsAreaChart
210
+ data={data as Record<string, string | number>[]}
211
+ stackOffset={variant === "expand" ? "expand" : "none"}
212
+ margin={margin}
213
+ >
214
+ <defs>
215
+ {filled &&
216
+ rendered.map((entry, slot) => (
217
+ <linearGradient key={entry.key} id={`area-${reactId}-${slot}`} x1="0" y1="0" x2="0" y2="1">
218
+ <stop offset="0%" stopColor={entry.color} stopOpacity={stacked ? 0.78 : 0.5} />
219
+ <stop offset="100%" stopColor={entry.color} stopOpacity={stacked ? 0.32 : 0.04} />
220
+ </linearGradient>
221
+ ))}
222
+ {texture && filled && lead && (
223
+ <pattern
224
+ id={`hatch-${reactId}`}
225
+ patternUnits="userSpaceOnUse"
226
+ width={6}
227
+ height={6}
228
+ patternTransform="rotate(45)"
229
+ >
230
+ <rect width={6} height={6} fill={lead.color} fillOpacity={0.18} />
231
+ <line x1={0} y1={0} x2={0} y2={6} stroke={lead.color} strokeWidth={1.2} strokeOpacity={0.6} />
232
+ </pattern>
233
+ )}
234
+ </defs>
235
+
236
+ <CartesianGrid vertical={false} stroke={theme.grid} strokeDasharray="2 4" />
237
+ <XAxis dataKey={index} {...axis} interval="preserveStartEnd" minTickGap={44} />
238
+ <YAxis
239
+ {...axis}
240
+ width={yAxisWidth}
241
+ tickFormatter={(value: number) =>
242
+ variant === "expand" ? `${Math.round(value * 100)}%` : valueFormatter(value)
243
+ }
244
+ />
245
+ <Tooltip
246
+ offset={12}
247
+ allowEscapeViewBox={{ x: false, y: false }}
248
+ cursor={{ stroke: theme.axisForeground, strokeWidth: 1, strokeDasharray: "3 3" }}
249
+ content={
250
+ <ChartTooltipContent
251
+ valueFormatter={valueFormatter}
252
+ labelFormatter={labelFormatter}
253
+ showTotal={withTotal}
254
+ />
255
+ }
256
+ />
257
+ {referenceLine?.band && (
258
+ <ReferenceArea
259
+ y1={referenceLine.y}
260
+ y2={numericMax}
261
+ fill={theme.warning}
262
+ fillOpacity={0.06}
263
+ ifOverflow="extendDomain"
264
+ />
265
+ )}
266
+ {referenceLine && (
267
+ <ReferenceLine
268
+ y={referenceLine.y}
269
+ stroke={theme.warning}
270
+ strokeDasharray="4 4"
271
+ strokeOpacity={0.6}
272
+ label={
273
+ referenceLine.label
274
+ ? {
275
+ value: referenceLine.label,
276
+ fill: theme.warning,
277
+ fontSize: 9,
278
+ fontFamily: theme.fontMono,
279
+ position: "insideBottomRight",
280
+ }
281
+ : undefined
282
+ }
283
+ />
284
+ )}
285
+
286
+ {rendered.map((entry, slot) => (
287
+ <Area
288
+ key={entry.key}
289
+ type={curveType}
290
+ dataKey={entry.key}
291
+ name={entry.name}
292
+ stackId={stacked ? "stack" : undefined}
293
+ stroke={entry.color}
294
+ strokeWidth={entry.dashed ? 2 : 1.6}
295
+ strokeDasharray={entry.dashed ? "5 3" : undefined}
296
+ fill={fillFor(entry, slot)}
297
+ dot={false}
298
+ activeDot={activeDotFor(entry)}
299
+ isAnimationActive={animated}
300
+ animationDuration={650}
301
+ animationEasing="ease-out"
302
+ >
303
+ {lastValueLabel && <LabelList dataKey={entry.key} content={renderLastLabel(entry.color)} />}
304
+ </Area>
305
+ ))}
306
+
307
+ {markers?.map((marker) => (
308
+ <ReferenceDot
309
+ key={`${marker.x}-${marker.y}`}
310
+ x={marker.x}
311
+ y={marker.y}
312
+ r={3.5}
313
+ fill={marker.color ?? theme.foreground}
314
+ stroke={theme.card}
315
+ strokeWidth={2}
316
+ label={
317
+ marker.label
318
+ ? {
319
+ value: marker.label,
320
+ fill: marker.color ?? theme.foreground,
321
+ fontSize: 9,
322
+ fontFamily: theme.fontMono,
323
+ position: "top",
324
+ }
325
+ : undefined
326
+ }
327
+ />
328
+ ))}
329
+ </RechartsAreaChart>
330
+ </ResponsiveContainer>
331
+ )}
332
+ </div>
333
+
334
+ {legend && !isEmpty && <ChartLegend items={legendItems} style={{ paddingLeft: yAxisWidth }} />}
335
+ </div>
336
+ );
337
+ }
338
+
339
+ export { AreaChart };