xertica-ui 2.1.2 → 2.1.4
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.
- package/CHANGELOG.md +46 -0
- package/README.md +1 -1
- package/bin/cli.ts +1 -1
- package/bin/generate-tokens.ts +13 -7
- package/components/assistant/xertica-assistant/index.ts +2 -0
- package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +97 -0
- package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -0
- package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -0
- package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +86 -0
- package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +77 -0
- package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +573 -0
- package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +65 -0
- package/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.tsx +41 -0
- package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +98 -0
- package/components/assistant/xertica-assistant/parts/index.ts +16 -0
- package/components/assistant/xertica-assistant/types.ts +139 -0
- package/components/assistant/xertica-assistant/use-assistant.ts +559 -0
- package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +198 -1460
- package/components/brand/theme-toggle/ThemeToggle.tsx +8 -27
- package/components/hooks/index.ts +3 -0
- package/components/hooks/use-layout-shortcuts.ts +46 -0
- package/components/layout/sidebar/index.ts +2 -0
- package/components/layout/sidebar/sidebar.stories.tsx +160 -8
- package/components/layout/sidebar/sidebar.tsx +606 -497
- package/components/layout/sidebar/use-sidebar.ts +104 -0
- package/components/media/audio-player/AudioPlayer.tsx +131 -206
- package/components/media/audio-player/use-audio-player.ts +298 -0
- package/components/pages/home-page/HomePage.tsx +1 -1
- package/components/pages/template-content/TemplateContent.tsx +5 -5
- package/components/pages/template-page/TemplatePage.tsx +5 -5
- package/components/shared/CustomTooltipContent.tsx +52 -0
- package/components/shared/layout-constants.ts +1 -1
- package/components/ui/chart/chart.stories.tsx +966 -7
- package/components/ui/chart/chart.tsx +918 -45
- package/components/ui/file-upload/file-upload.stories.tsx +100 -0
- package/components/ui/file-upload/file-upload.tsx +14 -74
- package/components/ui/file-upload/index.ts +1 -0
- package/components/ui/file-upload/use-file-upload.ts +181 -0
- package/components/ui/pagination/index.ts +2 -0
- package/components/ui/pagination/pagination.stories.tsx +94 -0
- package/components/ui/pagination/use-pagination.ts +194 -0
- package/components/ui/rich-text-editor/index.ts +2 -0
- package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
- package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
- package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
- package/components/ui/stepper/index.ts +3 -1
- package/components/ui/stepper/stepper.stories.tsx +116 -0
- package/components/ui/stepper/stepper.tsx +4 -4
- package/components/ui/stepper/use-stepper.ts +137 -0
- package/components/ui/tree-view/index.ts +4 -1
- package/components/ui/tree-view/tree-view.stories.tsx +110 -4
- package/components/ui/tree-view/tree-view.tsx +17 -125
- package/components/ui/tree-view/use-tree-view.ts +229 -0
- package/contexts/AssistenteContext.tsx +17 -54
- package/contexts/BrandColorsContext.tsx +6 -17
- package/contexts/LayoutContext.tsx +5 -31
- package/dist/AssistantChart-BAudAfne.cjs +3591 -0
- package/dist/AssistantChart-BP8upjMk.js +3565 -0
- package/dist/AudioPlayer-1ypwE2Wh.cjs +936 -0
- package/dist/AudioPlayer-DuKXrCfy.js +937 -0
- package/dist/CustomTooltipContent-DHjkY0ww.js +40 -0
- package/dist/CustomTooltipContent-c_K-DWRr.cjs +56 -0
- package/dist/LanguageContext-BwhwC3G2.js +657 -0
- package/dist/LanguageContext-DvUt5jBg.cjs +656 -0
- package/dist/LayoutContext-BDmcZfMH.cjs +84 -0
- package/dist/LayoutContext-dbQvdC4O.js +85 -0
- package/dist/ThemeContext-RTy1m2Uq.js +82 -0
- package/dist/ThemeContext-bSzuOit2.cjs +81 -0
- package/dist/VerifyEmailPage-C_ihbcth.js +2828 -0
- package/dist/VerifyEmailPage-Dt7zgA4w.cjs +2827 -0
- package/dist/XerticaProvider-CW9hpCdF.cjs +39 -0
- package/dist/XerticaProvider-siSt9uG2.js +40 -0
- package/dist/XerticaXLogo-D8jf0SNv.cjs +214 -0
- package/dist/XerticaXLogo-fAJMy3H4.js +215 -0
- package/dist/assistant.cjs.js +2 -1
- package/dist/assistant.es.js +3 -2
- package/dist/brand.cjs.js +2 -2
- package/dist/brand.es.js +2 -2
- package/dist/cli.js +14 -8
- package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantCollapsedView.d.ts +13 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantConversationList.d.ts +16 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.d.ts +17 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.d.ts +19 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantHeader.d.ts +11 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantMessageBubble.d.ts +29 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantTabBar.d.ts +13 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.d.ts +4 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.d.ts +17 -0
- package/dist/components/assistant/xertica-assistant/parts/index.d.ts +16 -0
- package/dist/components/assistant/xertica-assistant/types.d.ts +106 -0
- package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +125 -0
- package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +8 -97
- package/dist/components/hooks/index.d.ts +3 -0
- package/dist/components/hooks/use-layout-shortcuts.d.ts +22 -0
- package/dist/components/layout/sidebar/index.d.ts +2 -0
- package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
- package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
- package/dist/components/media/audio-player/AudioPlayer.d.ts +4 -1
- package/dist/components/media/audio-player/use-audio-player.d.ts +72 -0
- package/dist/components/shared/CustomTooltipContent.d.ts +20 -0
- package/dist/components/shared/layout-constants.d.ts +1 -1
- package/dist/components/ui/alert/alert.d.ts +1 -1
- package/dist/components/ui/badge/badge.d.ts +1 -1
- package/dist/components/ui/button/button.d.ts +2 -2
- package/dist/components/ui/chart/chart.d.ts +162 -5
- package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
- package/dist/components/ui/file-upload/index.d.ts +1 -0
- package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
- package/dist/components/ui/pagination/index.d.ts +2 -0
- package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
- package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
- package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
- package/dist/components/ui/stepper/index.d.ts +3 -1
- package/dist/components/ui/stepper/stepper.d.ts +2 -2
- package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
- package/dist/components/ui/tree-view/index.d.ts +4 -1
- package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
- package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
- package/dist/contexts/AssistenteContext.d.ts +10 -49
- package/dist/hooks.cjs.js +30 -10
- package/dist/hooks.es.js +25 -4
- package/dist/index.cjs.js +20 -9
- package/dist/index.es.js +38 -27
- package/dist/layout.cjs.js +82 -1
- package/dist/layout.es.js +83 -2
- package/dist/media.cjs.js +1 -1
- package/dist/media.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-BmsjY03B.js +2949 -0
- package/dist/rich-text-editor-GS2kpTAK.cjs +2966 -0
- package/dist/sidebar-CVUGHOS_.cjs +756 -0
- package/dist/sidebar-CmvwjnVb.js +757 -0
- package/dist/ui.cjs.js +12 -2
- package/dist/ui.es.js +24 -14
- package/dist/use-audio-player-Bkh23vQ3.js +177 -0
- package/dist/use-audio-player-Dn1NR9xN.cjs +176 -0
- package/dist/utils/color-utils.d.ts +51 -0
- package/dist/xertica-assistant-BMqdyRVi.js +2082 -0
- package/dist/xertica-assistant-Bj3vBCq_.cjs +2081 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/ai-usage.md +28 -10
- package/docs/architecture-improvements.md +463 -0
- package/docs/architecture.md +77 -1
- package/docs/components/assistant-chart.md +1 -1
- package/docs/components/assistant.md +159 -0
- package/docs/components/audio-player.md +46 -0
- package/docs/components/branding.md +251 -0
- package/docs/components/chart.md +354 -39
- package/docs/components/code-block.md +108 -0
- package/docs/components/file-upload.md +119 -2
- package/docs/components/formatted-document.md +113 -0
- package/docs/components/hooks.md +430 -0
- package/docs/components/image-with-fallback.md +106 -0
- package/docs/components/map-layers.md +140 -0
- package/docs/components/modern-chat-input.md +163 -0
- package/docs/components/pages.md +351 -0
- package/docs/components/pagination.md +187 -0
- package/docs/components/rich-text-editor.md +164 -0
- package/docs/components/sidebar.md +153 -4
- package/docs/components/stepper.md +157 -12
- package/docs/components/tree-view.md +164 -6
- package/docs/doc-audit.md +223 -0
- package/docs/getting-started.md +155 -1
- package/docs/guidelines.md +14 -8
- package/docs/layout.md +2 -2
- package/docs/llms.md +29 -9
- package/docs/patterns/detail-page.md +276 -0
- package/docs/patterns/settings.md +346 -0
- package/docs/patterns/wizard.md +217 -0
- package/guidelines/Guidelines.md +5 -3
- package/llms.txt +1 -1
- package/package.json +10 -10
- package/styles/xertica/tokens.css +41 -12
- package/templates/CLAUDE.md +16 -6
- package/templates/guidelines/Guidelines.md +16 -4
- package/templates/package.json +3 -3
- package/templates/src/styles/xertica/tokens.css +39 -10
- package/utils/color-utils.ts +72 -0
|
@@ -12,7 +12,13 @@ import {
|
|
|
12
12
|
InteractiveTimeSeriesChart,
|
|
13
13
|
ComboMetricChart,
|
|
14
14
|
DonutBreakdownChart,
|
|
15
|
+
SparklineChart,
|
|
16
|
+
RadarMetricChart,
|
|
17
|
+
PieMetricChart,
|
|
18
|
+
RadialBarMetricChart,
|
|
19
|
+
GaugeChart,
|
|
15
20
|
type ChartConfig,
|
|
21
|
+
type GaugeChartThreshold,
|
|
16
22
|
} from './chart';
|
|
17
23
|
import {
|
|
18
24
|
BarChart,
|
|
@@ -25,6 +31,7 @@ import {
|
|
|
25
31
|
} from 'recharts';
|
|
26
32
|
import { Card, CardHeader, CardTitle, CardContent } from '../card';
|
|
27
33
|
import React from 'react';
|
|
34
|
+
import { TrendingUp, TrendingDown, Minus } from 'lucide-react';
|
|
28
35
|
|
|
29
36
|
const meta: Meta<typeof DashboardBarChart> = {
|
|
30
37
|
title: 'UI/Chart',
|
|
@@ -55,6 +62,8 @@ const meta: Meta<typeof DashboardBarChart> = {
|
|
|
55
62
|
export default meta;
|
|
56
63
|
type Story = StoryObj<typeof DashboardBarChart>;
|
|
57
64
|
|
|
65
|
+
// ─── Shared data ──────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
58
67
|
const data = [
|
|
59
68
|
{ month: 'Jan', revenue: 4000, expenses: 2400 },
|
|
60
69
|
{ month: 'Feb', revenue: 3000, expenses: 1398 },
|
|
@@ -73,7 +82,7 @@ const richChartConfig: ChartConfig = {
|
|
|
73
82
|
revenue: { label: 'Revenue', color: 'var(--chart-1)' },
|
|
74
83
|
pipeline: { label: 'Pipeline', color: 'var(--chart-4)' },
|
|
75
84
|
conversion: { label: 'Conversion', color: 'var(--chart-2)' },
|
|
76
|
-
churn: { label: 'Churn', color: 'var(--chart-
|
|
85
|
+
churn: { label: 'Churn', color: 'var(--chart-5)' },
|
|
77
86
|
};
|
|
78
87
|
|
|
79
88
|
const richData = [
|
|
@@ -83,6 +92,8 @@ const richData = [
|
|
|
83
92
|
{ date: 'Apr 22', revenue: 5600, pipeline: 4300, conversion: 41, churn: 9 },
|
|
84
93
|
{ date: 'Apr 29', revenue: 7200, pipeline: 4700, conversion: 48, churn: 5 },
|
|
85
94
|
{ date: 'May 06', revenue: 7900, pipeline: 5300, conversion: 52, churn: 4 },
|
|
95
|
+
{ date: 'May 13', revenue: 8400, pipeline: 5800, conversion: 55, churn: 3 },
|
|
96
|
+
{ date: 'May 20', revenue: 9100, pipeline: 6200, conversion: 58, churn: 4 },
|
|
86
97
|
];
|
|
87
98
|
|
|
88
99
|
const breakdownConfig: ChartConfig = {
|
|
@@ -97,7 +108,10 @@ const breakdownData = [
|
|
|
97
108
|
{ name: 'startup', value: 17 },
|
|
98
109
|
];
|
|
99
110
|
|
|
111
|
+
// ─── Primitive stories ────────────────────────────────────────────────────────
|
|
112
|
+
|
|
100
113
|
export const BarChartExample: Story = {
|
|
114
|
+
name: 'Primitive / Bar Chart',
|
|
101
115
|
render: () => (
|
|
102
116
|
<Card className="w-full max-w-4xl">
|
|
103
117
|
<CardHeader>
|
|
@@ -120,6 +134,7 @@ export const BarChartExample: Story = {
|
|
|
120
134
|
};
|
|
121
135
|
|
|
122
136
|
export const LineChartExample: Story = {
|
|
137
|
+
name: 'Primitive / Line Chart',
|
|
123
138
|
render: () => (
|
|
124
139
|
<Card className="w-full max-w-4xl">
|
|
125
140
|
<CardHeader>
|
|
@@ -128,7 +143,7 @@ export const LineChartExample: Story = {
|
|
|
128
143
|
<CardContent>
|
|
129
144
|
<ChartContainer config={chartConfig} className="h-[300px] w-full">
|
|
130
145
|
<LineChart data={data}>
|
|
131
|
-
<CartesianGrid vertical={false} />
|
|
146
|
+
<CartesianGrid vertical={false} strokeDasharray="3 3" />
|
|
132
147
|
<XAxis dataKey="month" tickLine={false} axisLine={false} />
|
|
133
148
|
<ChartTooltip content={<ChartTooltipContent />} />
|
|
134
149
|
<Line
|
|
@@ -136,8 +151,8 @@ export const LineChartExample: Story = {
|
|
|
136
151
|
dataKey="revenue"
|
|
137
152
|
stroke="var(--color-revenue)"
|
|
138
153
|
strokeWidth={3}
|
|
139
|
-
dot={{ r: 4, fill: "var(--color-revenue)" }}
|
|
140
|
-
activeDot={{ r: 6 }}
|
|
154
|
+
dot={{ r: 4, fill: "var(--color-revenue)", strokeWidth: 0 }}
|
|
155
|
+
activeDot={{ r: 6, strokeWidth: 0 }}
|
|
141
156
|
/>
|
|
142
157
|
</LineChart>
|
|
143
158
|
</ChartContainer>
|
|
@@ -146,7 +161,10 @@ export const LineChartExample: Story = {
|
|
|
146
161
|
),
|
|
147
162
|
};
|
|
148
163
|
|
|
164
|
+
// ─── Dashboard chart stories ──────────────────────────────────────────────────
|
|
165
|
+
|
|
149
166
|
export const DashboardBarChartExample: Story = {
|
|
167
|
+
name: 'Bar Chart',
|
|
150
168
|
args: {
|
|
151
169
|
data: richData,
|
|
152
170
|
indexKey: 'date',
|
|
@@ -173,10 +191,11 @@ export const DashboardBarChartExample: Story = {
|
|
|
173
191
|
};
|
|
174
192
|
|
|
175
193
|
export const DashboardLineChartExample: Story = {
|
|
194
|
+
name: 'Line Chart',
|
|
176
195
|
render: () => (
|
|
177
196
|
<ChartCard
|
|
178
197
|
title="Growth Trend"
|
|
179
|
-
description="Multi-series line chart with
|
|
198
|
+
description="Multi-series line chart with smooth curves"
|
|
180
199
|
>
|
|
181
200
|
<DashboardLineChart
|
|
182
201
|
data={richData}
|
|
@@ -192,12 +211,16 @@ export const DashboardLineChartExample: Story = {
|
|
|
192
211
|
{ key: 'pipeline', label: 'Pipeline' },
|
|
193
212
|
{ key: 'conversion', label: 'Conversion' },
|
|
194
213
|
]}
|
|
214
|
+
showDots
|
|
215
|
+
curveType="monotone"
|
|
216
|
+
strokeWidth={2.5}
|
|
195
217
|
/>
|
|
196
218
|
</ChartCard>
|
|
197
219
|
),
|
|
198
220
|
};
|
|
199
221
|
|
|
200
222
|
export const HorizontalBarChartExample: Story = {
|
|
223
|
+
name: 'Horizontal Bar Chart',
|
|
201
224
|
render: () => (
|
|
202
225
|
<ChartCard
|
|
203
226
|
title="Top Segments"
|
|
@@ -222,6 +245,7 @@ export const HorizontalBarChartExample: Story = {
|
|
|
222
245
|
};
|
|
223
246
|
|
|
224
247
|
export const InteractiveTimeSeriesExample: Story = {
|
|
248
|
+
name: 'Interactive Time Series',
|
|
225
249
|
render: () => (
|
|
226
250
|
<ChartCard
|
|
227
251
|
title="Executive Trend"
|
|
@@ -237,12 +261,38 @@ export const InteractiveTimeSeriesExample: Story = {
|
|
|
237
261
|
{ key: 'pipeline', label: 'Pipeline' },
|
|
238
262
|
{ key: 'conversion', label: 'Conversion' },
|
|
239
263
|
]}
|
|
264
|
+
gradientFill
|
|
265
|
+
/>
|
|
266
|
+
</ChartCard>
|
|
267
|
+
),
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export const GradientAreaChartExample: Story = {
|
|
271
|
+
name: 'Gradient Area Chart',
|
|
272
|
+
render: () => (
|
|
273
|
+
<ChartCard
|
|
274
|
+
title="Revenue Over Time"
|
|
275
|
+
description="Area chart with gradient fill for a modern look"
|
|
276
|
+
>
|
|
277
|
+
<InteractiveTimeSeriesChart
|
|
278
|
+
data={richData}
|
|
279
|
+
indexKey="date"
|
|
280
|
+
config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
|
|
281
|
+
colors={['var(--chart-1)']}
|
|
282
|
+
series={[{ key: 'revenue', label: 'Revenue' }]}
|
|
283
|
+
gradientFill
|
|
284
|
+
showDots
|
|
285
|
+
periods={[
|
|
286
|
+
{ value: '7d', label: '7 days' },
|
|
287
|
+
{ value: '30d', label: '30 days' },
|
|
288
|
+
]}
|
|
240
289
|
/>
|
|
241
290
|
</ChartCard>
|
|
242
291
|
),
|
|
243
292
|
};
|
|
244
293
|
|
|
245
294
|
export const ComboMetricChartExample: Story = {
|
|
295
|
+
name: 'Combo Metric Chart',
|
|
246
296
|
render: () => (
|
|
247
297
|
<ChartCard
|
|
248
298
|
title="Pipeline Health"
|
|
@@ -266,7 +316,34 @@ export const ComboMetricChartExample: Story = {
|
|
|
266
316
|
),
|
|
267
317
|
};
|
|
268
318
|
|
|
319
|
+
export const ComboWithGradientAreaExample: Story = {
|
|
320
|
+
name: 'Combo Chart with Gradient Area',
|
|
321
|
+
render: () => (
|
|
322
|
+
<ChartCard
|
|
323
|
+
title="Revenue & Pipeline"
|
|
324
|
+
description="Gradient area for revenue, line for conversion rate"
|
|
325
|
+
>
|
|
326
|
+
<ComboMetricChart
|
|
327
|
+
data={richData}
|
|
328
|
+
indexKey="date"
|
|
329
|
+
config={richChartConfig}
|
|
330
|
+
colors={{
|
|
331
|
+
revenue: 'var(--chart-1)',
|
|
332
|
+
conversion: 'var(--chart-2)',
|
|
333
|
+
}}
|
|
334
|
+
series={[
|
|
335
|
+
{ key: 'revenue', type: 'area' },
|
|
336
|
+
{ key: 'conversion', type: 'line' },
|
|
337
|
+
]}
|
|
338
|
+
gradientFill
|
|
339
|
+
curveType="monotone"
|
|
340
|
+
/>
|
|
341
|
+
</ChartCard>
|
|
342
|
+
),
|
|
343
|
+
};
|
|
344
|
+
|
|
269
345
|
export const DonutBreakdownExample: Story = {
|
|
346
|
+
name: 'Donut Chart',
|
|
270
347
|
render: () => (
|
|
271
348
|
<ChartCard
|
|
272
349
|
title="Customer Mix"
|
|
@@ -283,7 +360,250 @@ export const DonutBreakdownExample: Story = {
|
|
|
283
360
|
),
|
|
284
361
|
};
|
|
285
362
|
|
|
363
|
+
// ─── Sparkline stories ────────────────────────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
const sparkData = [
|
|
366
|
+
{ v: 12 }, { v: 18 }, { v: 14 }, { v: 22 }, { v: 19 },
|
|
367
|
+
{ v: 28 }, { v: 24 }, { v: 31 }, { v: 27 }, { v: 35 },
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
const sparkDataDown = [
|
|
371
|
+
{ v: 35 }, { v: 30 }, { v: 28 }, { v: 22 }, { v: 25 },
|
|
372
|
+
{ v: 18 }, { v: 20 }, { v: 14 }, { v: 12 }, { v: 8 },
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
export const SparklineExample: Story = {
|
|
376
|
+
name: 'Sparkline Chart',
|
|
377
|
+
render: () => (
|
|
378
|
+
<div className="space-y-6 max-w-2xl">
|
|
379
|
+
<p className="text-sm text-muted-foreground">
|
|
380
|
+
Sparklines are minimal inline charts designed for stat cards and table cells.
|
|
381
|
+
</p>
|
|
382
|
+
|
|
383
|
+
{/* Stat cards with sparklines */}
|
|
384
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
385
|
+
<Card className="p-4">
|
|
386
|
+
<div className="flex items-start justify-between mb-3">
|
|
387
|
+
<div>
|
|
388
|
+
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wide">Revenue</p>
|
|
389
|
+
<p className="text-2xl font-bold mt-0.5">$35.2K</p>
|
|
390
|
+
</div>
|
|
391
|
+
<span className="inline-flex items-center gap-1 text-xs font-medium text-emerald-600 bg-emerald-50 dark:bg-emerald-950 dark:text-emerald-400 px-2 py-0.5 rounded-full">
|
|
392
|
+
<TrendingUp className="size-3" />
|
|
393
|
+
+12%
|
|
394
|
+
</span>
|
|
395
|
+
</div>
|
|
396
|
+
<SparklineChart
|
|
397
|
+
data={sparkData}
|
|
398
|
+
dataKey="v"
|
|
399
|
+
color="var(--chart-2)"
|
|
400
|
+
filled
|
|
401
|
+
showEndDot
|
|
402
|
+
className="h-12"
|
|
403
|
+
/>
|
|
404
|
+
</Card>
|
|
405
|
+
|
|
406
|
+
<Card className="p-4">
|
|
407
|
+
<div className="flex items-start justify-between mb-3">
|
|
408
|
+
<div>
|
|
409
|
+
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wide">Churn</p>
|
|
410
|
+
<p className="text-2xl font-bold mt-0.5">8.4%</p>
|
|
411
|
+
</div>
|
|
412
|
+
<span className="inline-flex items-center gap-1 text-xs font-medium text-red-600 bg-red-50 dark:bg-red-950 dark:text-red-400 px-2 py-0.5 rounded-full">
|
|
413
|
+
<TrendingDown className="size-3" />
|
|
414
|
+
-3%
|
|
415
|
+
</span>
|
|
416
|
+
</div>
|
|
417
|
+
<SparklineChart
|
|
418
|
+
data={sparkDataDown}
|
|
419
|
+
dataKey="v"
|
|
420
|
+
color="var(--chart-5)"
|
|
421
|
+
filled
|
|
422
|
+
showEndDot
|
|
423
|
+
className="h-12"
|
|
424
|
+
/>
|
|
425
|
+
</Card>
|
|
426
|
+
|
|
427
|
+
<Card className="p-4">
|
|
428
|
+
<div className="flex items-start justify-between mb-3">
|
|
429
|
+
<div>
|
|
430
|
+
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wide">Pipeline</p>
|
|
431
|
+
<p className="text-2xl font-bold mt-0.5">$128K</p>
|
|
432
|
+
</div>
|
|
433
|
+
<span className="inline-flex items-center gap-1 text-xs font-medium text-muted-foreground bg-muted px-2 py-0.5 rounded-full">
|
|
434
|
+
<Minus className="size-3" />
|
|
435
|
+
0%
|
|
436
|
+
</span>
|
|
437
|
+
</div>
|
|
438
|
+
<SparklineChart
|
|
439
|
+
data={[
|
|
440
|
+
{ v: 20 }, { v: 22 }, { v: 19 }, { v: 23 }, { v: 21 },
|
|
441
|
+
{ v: 24 }, { v: 22 }, { v: 25 }, { v: 23 }, { v: 24 },
|
|
442
|
+
]}
|
|
443
|
+
dataKey="v"
|
|
444
|
+
color="var(--chart-4)"
|
|
445
|
+
filled
|
|
446
|
+
showEndDot
|
|
447
|
+
className="h-12"
|
|
448
|
+
/>
|
|
449
|
+
</Card>
|
|
450
|
+
</div>
|
|
451
|
+
|
|
452
|
+
{/* Sparkline variants */}
|
|
453
|
+
<ChartCard title="Sparkline Variants" description="Line-only vs filled area">
|
|
454
|
+
<div className="space-y-4 py-2">
|
|
455
|
+
<div className="flex items-center gap-4">
|
|
456
|
+
<span className="text-xs text-muted-foreground w-24 shrink-0">Filled (default)</span>
|
|
457
|
+
<SparklineChart data={sparkData} dataKey="v" color="var(--chart-1)" filled className="h-10 flex-1" />
|
|
458
|
+
</div>
|
|
459
|
+
<div className="flex items-center gap-4">
|
|
460
|
+
<span className="text-xs text-muted-foreground w-24 shrink-0">Line only</span>
|
|
461
|
+
<SparklineChart data={sparkData} dataKey="v" color="var(--chart-3)" filled={false} showEndDot={false} className="h-10 flex-1" />
|
|
462
|
+
</div>
|
|
463
|
+
<div className="flex items-center gap-4">
|
|
464
|
+
<span className="text-xs text-muted-foreground w-24 shrink-0">Step curve</span>
|
|
465
|
+
<SparklineChart data={sparkData} dataKey="v" color="var(--chart-6)" filled curveType="step" className="h-10 flex-1" />
|
|
466
|
+
</div>
|
|
467
|
+
</div>
|
|
468
|
+
</ChartCard>
|
|
469
|
+
</div>
|
|
470
|
+
),
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// ─── Dashboard layout story ───────────────────────────────────────────────────
|
|
474
|
+
|
|
475
|
+
export const DashboardLayoutExample: Story = {
|
|
476
|
+
name: 'Dashboard Layout',
|
|
477
|
+
render: () => {
|
|
478
|
+
const kpiData = [
|
|
479
|
+
{ label: 'Total Revenue', value: '$128.4K', trend: '+18%', up: true, sparkData: sparkData, color: 'var(--chart-1)' },
|
|
480
|
+
{ label: 'Active Pipeline', value: '$62.1K', trend: '+9%', up: true, sparkData: sparkData.map(d => ({ v: d.v * 0.7 })), color: 'var(--chart-4)' },
|
|
481
|
+
{ label: 'Conversion Rate', value: '58%', trend: '+4%', up: true, sparkData: sparkData.map(d => ({ v: d.v * 0.5 })), color: 'var(--chart-2)' },
|
|
482
|
+
{ label: 'Churn Rate', value: '3.2%', trend: '-1%', up: false, sparkData: sparkDataDown, color: 'var(--chart-5)' },
|
|
483
|
+
];
|
|
484
|
+
|
|
485
|
+
return (
|
|
486
|
+
<div className="space-y-6">
|
|
487
|
+
{/* KPI row */}
|
|
488
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
489
|
+
{kpiData.map((kpi) => (
|
|
490
|
+
<Card key={kpi.label} className="p-4">
|
|
491
|
+
<div className="flex items-start justify-between mb-3">
|
|
492
|
+
<div>
|
|
493
|
+
<p className="text-xs text-muted-foreground font-medium">{kpi.label}</p>
|
|
494
|
+
<p className="text-xl font-bold mt-0.5">{kpi.value}</p>
|
|
495
|
+
</div>
|
|
496
|
+
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full ${kpi.up
|
|
497
|
+
? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-950 dark:text-emerald-400'
|
|
498
|
+
: 'text-red-600 bg-red-50 dark:bg-red-950 dark:text-red-400'
|
|
499
|
+
}`}>
|
|
500
|
+
{kpi.up ? <TrendingUp className="size-3" /> : <TrendingDown className="size-3" />}
|
|
501
|
+
{kpi.trend}
|
|
502
|
+
</span>
|
|
503
|
+
</div>
|
|
504
|
+
<SparklineChart
|
|
505
|
+
data={kpi.sparkData}
|
|
506
|
+
dataKey="v"
|
|
507
|
+
color={kpi.color}
|
|
508
|
+
filled
|
|
509
|
+
showEndDot
|
|
510
|
+
className="h-10"
|
|
511
|
+
/>
|
|
512
|
+
</Card>
|
|
513
|
+
))}
|
|
514
|
+
</div>
|
|
515
|
+
|
|
516
|
+
{/* Main charts row */}
|
|
517
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
|
518
|
+
<div className="lg:col-span-2">
|
|
519
|
+
<ChartCard
|
|
520
|
+
title="Revenue & Pipeline"
|
|
521
|
+
description="Weekly performance overview"
|
|
522
|
+
>
|
|
523
|
+
<InteractiveTimeSeriesChart
|
|
524
|
+
data={richData}
|
|
525
|
+
indexKey="date"
|
|
526
|
+
config={richChartConfig}
|
|
527
|
+
colors={['var(--chart-1)', 'var(--chart-4)', 'var(--chart-2)']}
|
|
528
|
+
series={[
|
|
529
|
+
{ key: 'revenue', label: 'Revenue' },
|
|
530
|
+
{ key: 'pipeline', label: 'Pipeline' },
|
|
531
|
+
{ key: 'conversion', label: 'Conversion' },
|
|
532
|
+
]}
|
|
533
|
+
gradientFill
|
|
534
|
+
periods={[
|
|
535
|
+
{ value: '7d', label: '7 days' },
|
|
536
|
+
{ value: '30d', label: '30 days' },
|
|
537
|
+
]}
|
|
538
|
+
/>
|
|
539
|
+
</ChartCard>
|
|
540
|
+
</div>
|
|
541
|
+
|
|
542
|
+
<ChartCard
|
|
543
|
+
title="Customer Mix"
|
|
544
|
+
description="Segment breakdown"
|
|
545
|
+
>
|
|
546
|
+
<DonutBreakdownChart
|
|
547
|
+
data={breakdownData}
|
|
548
|
+
config={breakdownConfig}
|
|
549
|
+
colors={['var(--chart-1)', 'var(--chart-4)', 'var(--chart-2)']}
|
|
550
|
+
centerValue="100%"
|
|
551
|
+
centerLabel="Accounts"
|
|
552
|
+
/>
|
|
553
|
+
</ChartCard>
|
|
554
|
+
</div>
|
|
555
|
+
|
|
556
|
+
{/* Bottom row */}
|
|
557
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
558
|
+
<ChartCard
|
|
559
|
+
title="Top Segments"
|
|
560
|
+
description="Revenue by customer segment"
|
|
561
|
+
>
|
|
562
|
+
<HorizontalBarChart
|
|
563
|
+
data={[
|
|
564
|
+
{ segment: 'Enterprise', revenue: 7600 },
|
|
565
|
+
{ segment: 'Mid-market', revenue: 5400 },
|
|
566
|
+
{ segment: 'Startup', revenue: 3100 },
|
|
567
|
+
{ segment: 'Partners', revenue: 2200 },
|
|
568
|
+
]}
|
|
569
|
+
indexKey="segment"
|
|
570
|
+
config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
|
|
571
|
+
colors={{ revenue: 'var(--chart-1)' }}
|
|
572
|
+
barSize="xl"
|
|
573
|
+
categoryWidth={110}
|
|
574
|
+
showLegend={false}
|
|
575
|
+
/>
|
|
576
|
+
</ChartCard>
|
|
577
|
+
|
|
578
|
+
<ChartCard
|
|
579
|
+
title="Pipeline Health"
|
|
580
|
+
description="Volume vs conversion rate"
|
|
581
|
+
>
|
|
582
|
+
<ComboMetricChart
|
|
583
|
+
data={richData}
|
|
584
|
+
indexKey="date"
|
|
585
|
+
config={richChartConfig}
|
|
586
|
+
colors={{
|
|
587
|
+
pipeline: 'var(--chart-4)',
|
|
588
|
+
conversion: 'var(--chart-2)',
|
|
589
|
+
}}
|
|
590
|
+
barSize="lg"
|
|
591
|
+
series={[
|
|
592
|
+
{ key: 'pipeline', type: 'bar' },
|
|
593
|
+
{ key: 'conversion', type: 'line' },
|
|
594
|
+
]}
|
|
595
|
+
/>
|
|
596
|
+
</ChartCard>
|
|
597
|
+
</div>
|
|
598
|
+
</div>
|
|
599
|
+
);
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// ─── State stories ────────────────────────────────────────────────────────────
|
|
604
|
+
|
|
286
605
|
export const EmptyStateExample: Story = {
|
|
606
|
+
name: 'State / Empty',
|
|
287
607
|
render: () => (
|
|
288
608
|
<ChartCard
|
|
289
609
|
title="Revenue by Week"
|
|
@@ -300,6 +620,7 @@ export const EmptyStateExample: Story = {
|
|
|
300
620
|
};
|
|
301
621
|
|
|
302
622
|
export const ConnectionErrorExample: Story = {
|
|
623
|
+
name: 'State / Connection Error',
|
|
303
624
|
render: () => (
|
|
304
625
|
<ChartCard
|
|
305
626
|
title="Pipeline Health"
|
|
@@ -316,10 +637,30 @@ export const ConnectionErrorExample: Story = {
|
|
|
316
637
|
),
|
|
317
638
|
};
|
|
318
639
|
|
|
640
|
+
export const LoadingStateExample: Story = {
|
|
641
|
+
name: 'State / Loading',
|
|
642
|
+
render: () => (
|
|
643
|
+
<ChartCard
|
|
644
|
+
title="Revenue by Week"
|
|
645
|
+
description="Loading skeleton while data is fetched"
|
|
646
|
+
>
|
|
647
|
+
<DashboardBarChart
|
|
648
|
+
data={[]}
|
|
649
|
+
indexKey="date"
|
|
650
|
+
config={richChartConfig}
|
|
651
|
+
isLoading
|
|
652
|
+
/>
|
|
653
|
+
</ChartCard>
|
|
654
|
+
),
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// ─── Variant stories ──────────────────────────────────────────────────────────
|
|
658
|
+
|
|
319
659
|
export const BarSizeVariantsExample: Story = {
|
|
660
|
+
name: 'Variants / Bar Sizes',
|
|
320
661
|
render: () => (
|
|
321
662
|
<div className="space-y-6">
|
|
322
|
-
<ChartCard title="Thin Bars" description="
|
|
663
|
+
<ChartCard title="Thin Bars" description='barSize="sm"'>
|
|
323
664
|
<DashboardBarChart
|
|
324
665
|
data={richData}
|
|
325
666
|
indexKey="date"
|
|
@@ -329,7 +670,7 @@ export const BarSizeVariantsExample: Story = {
|
|
|
329
670
|
showLegend={false}
|
|
330
671
|
/>
|
|
331
672
|
</ChartCard>
|
|
332
|
-
<ChartCard title="Wide Bars" description="
|
|
673
|
+
<ChartCard title="Wide Bars" description='barSize="xl"'>
|
|
333
674
|
<DashboardBarChart
|
|
334
675
|
data={richData}
|
|
335
676
|
indexKey="date"
|
|
@@ -342,3 +683,621 @@ export const BarSizeVariantsExample: Story = {
|
|
|
342
683
|
</div>
|
|
343
684
|
),
|
|
344
685
|
};
|
|
686
|
+
|
|
687
|
+
export const CurveTypeVariantsExample: Story = {
|
|
688
|
+
name: 'Variants / Curve Types',
|
|
689
|
+
render: () => (
|
|
690
|
+
<div className="space-y-6">
|
|
691
|
+
{(['monotone', 'linear', 'step', 'natural', 'basis'] as const).map((curve) => (
|
|
692
|
+
<ChartCard key={curve} title={`curveType="${curve}"`} description="Line chart curve interpolation">
|
|
693
|
+
<DashboardLineChart
|
|
694
|
+
data={richData}
|
|
695
|
+
indexKey="date"
|
|
696
|
+
config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
|
|
697
|
+
series={[{ key: 'revenue' }]}
|
|
698
|
+
curveType={curve}
|
|
699
|
+
showLegend={false}
|
|
700
|
+
showDots={curve === 'linear'}
|
|
701
|
+
/>
|
|
702
|
+
</ChartCard>
|
|
703
|
+
))}
|
|
704
|
+
</div>
|
|
705
|
+
),
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
export const ColorPaletteExample: Story = {
|
|
709
|
+
name: 'Variants / Color Palette',
|
|
710
|
+
render: () => {
|
|
711
|
+
const paletteConfig: ChartConfig = {
|
|
712
|
+
chart1: { label: 'chart-1 (indigo)', color: 'var(--chart-1)' },
|
|
713
|
+
chart2: { label: 'chart-2 (emerald)', color: 'var(--chart-2)' },
|
|
714
|
+
chart3: { label: 'chart-3 (amber)', color: 'var(--chart-3)' },
|
|
715
|
+
chart4: { label: 'chart-4 (blue)', color: 'var(--chart-4)' },
|
|
716
|
+
chart5: { label: 'chart-5 (red)', color: 'var(--chart-5)' },
|
|
717
|
+
chart6: { label: 'chart-6 (purple)', color: 'var(--chart-6)' },
|
|
718
|
+
chart7: { label: 'chart-7 (teal)', color: 'var(--chart-7)' },
|
|
719
|
+
chart8: { label: 'chart-8 (rose)', color: 'var(--chart-8)' },
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
const paletteData = [
|
|
723
|
+
{ name: 'A', chart1: 80, chart2: 70, chart3: 60, chart4: 75, chart5: 55, chart6: 65, chart7: 72, chart8: 68 },
|
|
724
|
+
];
|
|
725
|
+
|
|
726
|
+
return (
|
|
727
|
+
<ChartCard
|
|
728
|
+
title="Chart Color Palette"
|
|
729
|
+
description="All 8 chart tokens — vibrant, accessible, harmonious"
|
|
730
|
+
>
|
|
731
|
+
<DashboardBarChart
|
|
732
|
+
data={paletteData}
|
|
733
|
+
indexKey="name"
|
|
734
|
+
config={paletteConfig}
|
|
735
|
+
barSize="xl"
|
|
736
|
+
showGrid={false}
|
|
737
|
+
/>
|
|
738
|
+
</ChartCard>
|
|
739
|
+
);
|
|
740
|
+
},
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
// ─── Radar Chart Stories ───────────────────────────────────────────────────────
|
|
744
|
+
|
|
745
|
+
const radarData = [
|
|
746
|
+
{ skill: 'Performance', score: 88 },
|
|
747
|
+
{ skill: 'Reliability', score: 72 },
|
|
748
|
+
{ skill: 'Security', score: 95 },
|
|
749
|
+
{ skill: 'Scalability', score: 65 },
|
|
750
|
+
{ skill: 'Usability', score: 80 },
|
|
751
|
+
{ skill: 'Maintainability', score: 58 },
|
|
752
|
+
];
|
|
753
|
+
|
|
754
|
+
const radarMultiData = [
|
|
755
|
+
{ skill: 'Performance', teamA: 88, teamB: 62 },
|
|
756
|
+
{ skill: 'Reliability', teamA: 72, teamB: 85 },
|
|
757
|
+
{ skill: 'Security', teamA: 95, teamB: 70 },
|
|
758
|
+
{ skill: 'Scalability', teamA: 65, teamB: 90 },
|
|
759
|
+
{ skill: 'Usability', teamA: 80, teamB: 55 },
|
|
760
|
+
{ skill: 'Maintainability', teamA: 58, teamB: 78 },
|
|
761
|
+
];
|
|
762
|
+
|
|
763
|
+
export const RadarChartFilled: Story = {
|
|
764
|
+
name: 'Radar Chart / Filled',
|
|
765
|
+
render: () => (
|
|
766
|
+
<ChartCard
|
|
767
|
+
title="System Health Score"
|
|
768
|
+
description="Filled radar — single series"
|
|
769
|
+
>
|
|
770
|
+
<RadarMetricChart
|
|
771
|
+
data={radarData}
|
|
772
|
+
labelKey="skill"
|
|
773
|
+
series={[{ key: 'score', label: 'Score' }]}
|
|
774
|
+
filled
|
|
775
|
+
showLegend={false}
|
|
776
|
+
/>
|
|
777
|
+
</ChartCard>
|
|
778
|
+
),
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
export const RadarChartOutline: Story = {
|
|
782
|
+
name: 'Radar Chart / Outline',
|
|
783
|
+
render: () => (
|
|
784
|
+
<ChartCard
|
|
785
|
+
title="System Health Score"
|
|
786
|
+
description="Outline only — no fill"
|
|
787
|
+
>
|
|
788
|
+
<RadarMetricChart
|
|
789
|
+
data={radarData}
|
|
790
|
+
labelKey="skill"
|
|
791
|
+
series={[{ key: 'score', label: 'Score' }]}
|
|
792
|
+
filled={false}
|
|
793
|
+
showDots
|
|
794
|
+
showLegend={false}
|
|
795
|
+
/>
|
|
796
|
+
</ChartCard>
|
|
797
|
+
),
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
export const RadarChartMultiSeries: Story = {
|
|
801
|
+
name: 'Radar Chart / Multi-Series',
|
|
802
|
+
render: () => (
|
|
803
|
+
<ChartCard
|
|
804
|
+
title="Team Comparison"
|
|
805
|
+
description="Two teams across 6 dimensions"
|
|
806
|
+
>
|
|
807
|
+
<RadarMetricChart
|
|
808
|
+
data={radarMultiData}
|
|
809
|
+
labelKey="skill"
|
|
810
|
+
series={[
|
|
811
|
+
{ key: 'teamA', label: 'Team Alpha' },
|
|
812
|
+
{ key: 'teamB', label: 'Team Beta' },
|
|
813
|
+
]}
|
|
814
|
+
filled
|
|
815
|
+
fillOpacity={0.2}
|
|
816
|
+
showDots
|
|
817
|
+
/>
|
|
818
|
+
</ChartCard>
|
|
819
|
+
),
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
export const RadarChartVariants: Story = {
|
|
823
|
+
name: 'Radar Chart / All Variants',
|
|
824
|
+
render: () => (
|
|
825
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
826
|
+
<ChartCard title="Filled" description="Default filled area">
|
|
827
|
+
<RadarMetricChart
|
|
828
|
+
data={radarData}
|
|
829
|
+
labelKey="skill"
|
|
830
|
+
series={[{ key: 'score', label: 'Score' }]}
|
|
831
|
+
filled
|
|
832
|
+
showLegend={false}
|
|
833
|
+
/>
|
|
834
|
+
</ChartCard>
|
|
835
|
+
<ChartCard title="Outline + Dots" description="No fill, with dots">
|
|
836
|
+
<RadarMetricChart
|
|
837
|
+
data={radarData}
|
|
838
|
+
labelKey="skill"
|
|
839
|
+
series={[{ key: 'score', label: 'Score' }]}
|
|
840
|
+
filled={false}
|
|
841
|
+
showDots
|
|
842
|
+
showLegend={false}
|
|
843
|
+
/>
|
|
844
|
+
</ChartCard>
|
|
845
|
+
<ChartCard title="Multi-Series" description="Comparison overlay">
|
|
846
|
+
<RadarMetricChart
|
|
847
|
+
data={radarMultiData}
|
|
848
|
+
labelKey="skill"
|
|
849
|
+
series={[
|
|
850
|
+
{ key: 'teamA', label: 'Team A' },
|
|
851
|
+
{ key: 'teamB', label: 'Team B' },
|
|
852
|
+
]}
|
|
853
|
+
filled
|
|
854
|
+
fillOpacity={0.18}
|
|
855
|
+
/>
|
|
856
|
+
</ChartCard>
|
|
857
|
+
</div>
|
|
858
|
+
),
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
// ─── Pie Chart Stories ────────────────────────────────────────────────────────
|
|
862
|
+
|
|
863
|
+
const pieData = [
|
|
864
|
+
{ category: 'Infrastructure', spend: 42000 },
|
|
865
|
+
{ category: 'Engineering', spend: 31000 },
|
|
866
|
+
{ category: 'Marketing', spend: 18500 },
|
|
867
|
+
{ category: 'Operations', spend: 12000 },
|
|
868
|
+
{ category: 'Other', spend: 6500 },
|
|
869
|
+
];
|
|
870
|
+
|
|
871
|
+
export const PieChartSimple: Story = {
|
|
872
|
+
name: 'Pie Chart / Simple',
|
|
873
|
+
render: () => (
|
|
874
|
+
<ChartCard
|
|
875
|
+
title="Budget Breakdown"
|
|
876
|
+
description="Simple pie — no labels"
|
|
877
|
+
>
|
|
878
|
+
<PieMetricChart
|
|
879
|
+
data={pieData}
|
|
880
|
+
nameKey="category"
|
|
881
|
+
valueKey="spend"
|
|
882
|
+
valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
|
|
883
|
+
/>
|
|
884
|
+
</ChartCard>
|
|
885
|
+
),
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
export const PieChartWithLabels: Story = {
|
|
889
|
+
name: 'Pie Chart / With Labels',
|
|
890
|
+
render: () => (
|
|
891
|
+
<ChartCard
|
|
892
|
+
title="Budget Breakdown"
|
|
893
|
+
description="Pie with percentage labels"
|
|
894
|
+
>
|
|
895
|
+
<PieMetricChart
|
|
896
|
+
data={pieData}
|
|
897
|
+
nameKey="category"
|
|
898
|
+
valueKey="spend"
|
|
899
|
+
showLabels
|
|
900
|
+
valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
|
|
901
|
+
/>
|
|
902
|
+
</ChartCard>
|
|
903
|
+
),
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
export const PieChartExploded: Story = {
|
|
907
|
+
name: 'Pie Chart / Exploded Slice',
|
|
908
|
+
render: () => (
|
|
909
|
+
<ChartCard
|
|
910
|
+
title="Budget Breakdown"
|
|
911
|
+
description="Largest slice highlighted with drop shadow"
|
|
912
|
+
>
|
|
913
|
+
<PieMetricChart
|
|
914
|
+
data={pieData}
|
|
915
|
+
nameKey="category"
|
|
916
|
+
valueKey="spend"
|
|
917
|
+
showLabels
|
|
918
|
+
explodeIndex={0}
|
|
919
|
+
valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
|
|
920
|
+
/>
|
|
921
|
+
</ChartCard>
|
|
922
|
+
),
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
export const PieChartDonutVariant: Story = {
|
|
926
|
+
name: 'Pie Chart / Donut Variant',
|
|
927
|
+
render: () => (
|
|
928
|
+
<ChartCard
|
|
929
|
+
title="Budget Breakdown"
|
|
930
|
+
description="Pie with inner radius (donut style)"
|
|
931
|
+
>
|
|
932
|
+
<PieMetricChart
|
|
933
|
+
data={pieData}
|
|
934
|
+
nameKey="category"
|
|
935
|
+
valueKey="spend"
|
|
936
|
+
innerRadius="45%"
|
|
937
|
+
showLabels
|
|
938
|
+
valueFormatter={(v) => `$${(v as number).toLocaleString()}`}
|
|
939
|
+
/>
|
|
940
|
+
</ChartCard>
|
|
941
|
+
),
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
export const PieChartVariants: Story = {
|
|
945
|
+
name: 'Pie Chart / All Variants',
|
|
946
|
+
render: () => (
|
|
947
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
948
|
+
<ChartCard title="Simple" description="No labels">
|
|
949
|
+
<PieMetricChart data={pieData} nameKey="category" valueKey="spend" />
|
|
950
|
+
</ChartCard>
|
|
951
|
+
<ChartCard title="With Labels" description="Percentage labels">
|
|
952
|
+
<PieMetricChart data={pieData} nameKey="category" valueKey="spend" showLabels />
|
|
953
|
+
</ChartCard>
|
|
954
|
+
<ChartCard title="Exploded" description="First slice highlighted">
|
|
955
|
+
<PieMetricChart data={pieData} nameKey="category" valueKey="spend" showLabels explodeIndex={0} />
|
|
956
|
+
</ChartCard>
|
|
957
|
+
<ChartCard title="Donut" description="Inner radius 45%">
|
|
958
|
+
<PieMetricChart data={pieData} nameKey="category" valueKey="spend" innerRadius="45%" showLabels />
|
|
959
|
+
</ChartCard>
|
|
960
|
+
</div>
|
|
961
|
+
),
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
// ─── Radial Bar Chart Stories ─────────────────────────────────────────────────
|
|
965
|
+
|
|
966
|
+
const radialData = [
|
|
967
|
+
{ name: 'Infrastructure', value: 78 },
|
|
968
|
+
{ name: 'Engineering', value: 62 },
|
|
969
|
+
{ name: 'Marketing', value: 45 },
|
|
970
|
+
{ name: 'Operations', value: 91 },
|
|
971
|
+
];
|
|
972
|
+
|
|
973
|
+
export const RadialBarChartSingle: Story = {
|
|
974
|
+
name: 'Radial Bar / Single Bar',
|
|
975
|
+
render: () => (
|
|
976
|
+
<ChartCard
|
|
977
|
+
title="Deployment Success Rate"
|
|
978
|
+
description="Single radial bar — full circle"
|
|
979
|
+
>
|
|
980
|
+
<RadialBarMetricChart
|
|
981
|
+
data={[{ name: 'Success Rate', value: 87 }]}
|
|
982
|
+
dataKey="value"
|
|
983
|
+
nameKey="name"
|
|
984
|
+
showLegend={false}
|
|
985
|
+
innerRadius="60%"
|
|
986
|
+
outerRadius="90%"
|
|
987
|
+
valueFormatter={(v) => `${v}%`}
|
|
988
|
+
/>
|
|
989
|
+
</ChartCard>
|
|
990
|
+
),
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
export const RadialBarChartMulti: Story = {
|
|
994
|
+
name: 'Radial Bar / Multi-Bar',
|
|
995
|
+
render: () => (
|
|
996
|
+
<ChartCard
|
|
997
|
+
title="Department KPIs"
|
|
998
|
+
description="Multiple radial bars with background track"
|
|
999
|
+
>
|
|
1000
|
+
<RadialBarMetricChart
|
|
1001
|
+
data={radialData}
|
|
1002
|
+
dataKey="value"
|
|
1003
|
+
nameKey="name"
|
|
1004
|
+
innerRadius="20%"
|
|
1005
|
+
outerRadius="90%"
|
|
1006
|
+
valueFormatter={(v) => `${v}%`}
|
|
1007
|
+
/>
|
|
1008
|
+
</ChartCard>
|
|
1009
|
+
),
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
export const RadialBarChartSemiCircle: Story = {
|
|
1013
|
+
name: 'Radial Bar / Semi-Circle',
|
|
1014
|
+
render: () => (
|
|
1015
|
+
<ChartCard
|
|
1016
|
+
title="Department KPIs"
|
|
1017
|
+
description="Semi-circle layout (startAngle=180, endAngle=0)"
|
|
1018
|
+
>
|
|
1019
|
+
<RadialBarMetricChart
|
|
1020
|
+
data={radialData}
|
|
1021
|
+
dataKey="value"
|
|
1022
|
+
nameKey="name"
|
|
1023
|
+
innerRadius="30%"
|
|
1024
|
+
outerRadius="90%"
|
|
1025
|
+
startAngle={180}
|
|
1026
|
+
endAngle={0}
|
|
1027
|
+
valueFormatter={(v) => `${v}%`}
|
|
1028
|
+
/>
|
|
1029
|
+
</ChartCard>
|
|
1030
|
+
),
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
export const RadialBarChartVariants: Story = {
|
|
1034
|
+
name: 'Radial Bar / All Variants',
|
|
1035
|
+
render: () => (
|
|
1036
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
1037
|
+
<ChartCard title="Single Bar" description="Full circle">
|
|
1038
|
+
<RadialBarMetricChart
|
|
1039
|
+
data={[{ name: 'Success', value: 87 }]}
|
|
1040
|
+
dataKey="value"
|
|
1041
|
+
nameKey="name"
|
|
1042
|
+
showLegend={false}
|
|
1043
|
+
innerRadius="60%"
|
|
1044
|
+
outerRadius="90%"
|
|
1045
|
+
valueFormatter={(v) => `${v}%`}
|
|
1046
|
+
/>
|
|
1047
|
+
</ChartCard>
|
|
1048
|
+
<ChartCard title="Multi-Bar" description="Stacked radial bars">
|
|
1049
|
+
<RadialBarMetricChart
|
|
1050
|
+
data={radialData}
|
|
1051
|
+
dataKey="value"
|
|
1052
|
+
nameKey="name"
|
|
1053
|
+
innerRadius="20%"
|
|
1054
|
+
outerRadius="90%"
|
|
1055
|
+
valueFormatter={(v) => `${v}%`}
|
|
1056
|
+
/>
|
|
1057
|
+
</ChartCard>
|
|
1058
|
+
<ChartCard title="Semi-Circle" description="180° arc layout">
|
|
1059
|
+
<RadialBarMetricChart
|
|
1060
|
+
data={radialData}
|
|
1061
|
+
dataKey="value"
|
|
1062
|
+
nameKey="name"
|
|
1063
|
+
innerRadius="30%"
|
|
1064
|
+
outerRadius="90%"
|
|
1065
|
+
startAngle={180}
|
|
1066
|
+
endAngle={0}
|
|
1067
|
+
valueFormatter={(v) => `${v}%`}
|
|
1068
|
+
/>
|
|
1069
|
+
</ChartCard>
|
|
1070
|
+
</div>
|
|
1071
|
+
),
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
// ─── Gauge Chart Stories ──────────────────────────────────────────────────────
|
|
1075
|
+
|
|
1076
|
+
const gaugeThresholds: GaugeChartThreshold[] = [
|
|
1077
|
+
{ value: 33, color: 'var(--chart-5)', label: 'Critical' },
|
|
1078
|
+
{ value: 66, color: 'var(--chart-3)', label: 'Warning' },
|
|
1079
|
+
{ value: 100, color: 'var(--chart-2)', label: 'Healthy' },
|
|
1080
|
+
];
|
|
1081
|
+
|
|
1082
|
+
export const GaugeChartSimple: Story = {
|
|
1083
|
+
name: 'Gauge Chart / Simple',
|
|
1084
|
+
render: () => (
|
|
1085
|
+
<ChartCard
|
|
1086
|
+
title="CPU Usage"
|
|
1087
|
+
description="Simple gauge — single color"
|
|
1088
|
+
className="max-w-sm mx-auto"
|
|
1089
|
+
>
|
|
1090
|
+
<div className="flex justify-center py-4">
|
|
1091
|
+
<GaugeChart value={67} label="CPU Usage" />
|
|
1092
|
+
</div>
|
|
1093
|
+
</ChartCard>
|
|
1094
|
+
),
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
export const GaugeChartWithThresholds: Story = {
|
|
1098
|
+
name: 'Gauge Chart / With Thresholds',
|
|
1099
|
+
render: () => (
|
|
1100
|
+
<ChartCard
|
|
1101
|
+
title="System Health"
|
|
1102
|
+
description="Color zones: critical / warning / healthy"
|
|
1103
|
+
className="max-w-sm mx-auto"
|
|
1104
|
+
>
|
|
1105
|
+
<div className="flex justify-center py-4">
|
|
1106
|
+
<GaugeChart
|
|
1107
|
+
value={72}
|
|
1108
|
+
label="Overall Health"
|
|
1109
|
+
thresholds={gaugeThresholds}
|
|
1110
|
+
/>
|
|
1111
|
+
</div>
|
|
1112
|
+
</ChartCard>
|
|
1113
|
+
),
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
export const GaugeChartNoNeedle: Story = {
|
|
1117
|
+
name: 'Gauge Chart / No Needle',
|
|
1118
|
+
render: () => (
|
|
1119
|
+
<ChartCard
|
|
1120
|
+
title="Memory Usage"
|
|
1121
|
+
description="Arc only — no needle indicator"
|
|
1122
|
+
className="max-w-sm mx-auto"
|
|
1123
|
+
>
|
|
1124
|
+
<div className="flex justify-center py-4">
|
|
1125
|
+
<GaugeChart
|
|
1126
|
+
value={45}
|
|
1127
|
+
label="Memory"
|
|
1128
|
+
showNeedle={false}
|
|
1129
|
+
thresholds={gaugeThresholds}
|
|
1130
|
+
/>
|
|
1131
|
+
</div>
|
|
1132
|
+
</ChartCard>
|
|
1133
|
+
),
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
export const GaugeChartVariants: Story = {
|
|
1137
|
+
name: 'Gauge Chart / All Variants',
|
|
1138
|
+
render: () => (
|
|
1139
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
1140
|
+
<ChartCard title="Low (22%)" description="Critical zone">
|
|
1141
|
+
<div className="flex justify-center py-2">
|
|
1142
|
+
<GaugeChart value={22} label="CPU" thresholds={gaugeThresholds} />
|
|
1143
|
+
</div>
|
|
1144
|
+
</ChartCard>
|
|
1145
|
+
<ChartCard title="Medium (55%)" description="Warning zone">
|
|
1146
|
+
<div className="flex justify-center py-2">
|
|
1147
|
+
<GaugeChart value={55} label="Memory" thresholds={gaugeThresholds} />
|
|
1148
|
+
</div>
|
|
1149
|
+
</ChartCard>
|
|
1150
|
+
<ChartCard title="High (88%)" description="Healthy zone">
|
|
1151
|
+
<div className="flex justify-center py-2">
|
|
1152
|
+
<GaugeChart value={88} label="Disk I/O" thresholds={gaugeThresholds} />
|
|
1153
|
+
</div>
|
|
1154
|
+
</ChartCard>
|
|
1155
|
+
</div>
|
|
1156
|
+
),
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
export const GaugeChartDashboard: Story = {
|
|
1160
|
+
name: 'Gauge Chart / Infrastructure Dashboard',
|
|
1161
|
+
render: () => {
|
|
1162
|
+
const metrics = [
|
|
1163
|
+
{ label: 'CPU', value: 34, unit: '%' },
|
|
1164
|
+
{ label: 'Memory', value: 71, unit: '%' },
|
|
1165
|
+
{ label: 'Disk', value: 58, unit: '%' },
|
|
1166
|
+
{ label: 'Network', value: 22, unit: '%' },
|
|
1167
|
+
];
|
|
1168
|
+
|
|
1169
|
+
return (
|
|
1170
|
+
<div className="space-y-4">
|
|
1171
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
1172
|
+
{metrics.map((m) => (
|
|
1173
|
+
<ChartCard key={m.label} title={m.label} description={`Current ${m.label} utilization`}>
|
|
1174
|
+
<div className="flex justify-center py-2">
|
|
1175
|
+
<GaugeChart
|
|
1176
|
+
value={m.value}
|
|
1177
|
+
label={m.label}
|
|
1178
|
+
thresholds={gaugeThresholds}
|
|
1179
|
+
valueFormatter={(v) => `${v}${m.unit}`}
|
|
1180
|
+
/>
|
|
1181
|
+
</div>
|
|
1182
|
+
</ChartCard>
|
|
1183
|
+
))}
|
|
1184
|
+
</div>
|
|
1185
|
+
</div>
|
|
1186
|
+
);
|
|
1187
|
+
},
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
// ─── Complete Chart Library Overview ─────────────────────────────────────────
|
|
1191
|
+
|
|
1192
|
+
export const CompleteLibraryOverview: Story = {
|
|
1193
|
+
name: 'Complete Library / All Chart Types',
|
|
1194
|
+
render: () => (
|
|
1195
|
+
<div className="space-y-6">
|
|
1196
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
1197
|
+
{/* Bar */}
|
|
1198
|
+
<ChartCard title="Bar Chart" description="Categorical comparison">
|
|
1199
|
+
<DashboardBarChart
|
|
1200
|
+
data={[
|
|
1201
|
+
{ month: 'Jan', revenue: 42000 },
|
|
1202
|
+
{ month: 'Feb', revenue: 38000 },
|
|
1203
|
+
{ month: 'Mar', revenue: 51000 },
|
|
1204
|
+
{ month: 'Apr', revenue: 47000 },
|
|
1205
|
+
{ month: 'May', revenue: 55000 },
|
|
1206
|
+
]}
|
|
1207
|
+
indexKey="month"
|
|
1208
|
+
config={{ revenue: { label: 'Revenue', color: 'var(--chart-1)' } }}
|
|
1209
|
+
series={[{ key: 'revenue', label: 'Revenue' }]}
|
|
1210
|
+
showLegend={false}
|
|
1211
|
+
/>
|
|
1212
|
+
</ChartCard>
|
|
1213
|
+
|
|
1214
|
+
{/* Line */}
|
|
1215
|
+
<ChartCard title="Line Chart" description="Trend over time">
|
|
1216
|
+
<DashboardLineChart
|
|
1217
|
+
data={[
|
|
1218
|
+
{ month: 'Jan', users: 1200 },
|
|
1219
|
+
{ month: 'Feb', users: 1450 },
|
|
1220
|
+
{ month: 'Mar', users: 1380 },
|
|
1221
|
+
{ month: 'Apr', users: 1620 },
|
|
1222
|
+
{ month: 'May', users: 1890 },
|
|
1223
|
+
]}
|
|
1224
|
+
indexKey="month"
|
|
1225
|
+
config={{ users: { label: 'Users', color: 'var(--chart-2)' } }}
|
|
1226
|
+
series={[{ key: 'users', label: 'Users' }]}
|
|
1227
|
+
showLegend={false}
|
|
1228
|
+
/>
|
|
1229
|
+
</ChartCard>
|
|
1230
|
+
|
|
1231
|
+
{/* Donut */}
|
|
1232
|
+
<ChartCard title="Donut Chart" description="Part-to-whole">
|
|
1233
|
+
<DonutBreakdownChart
|
|
1234
|
+
data={[
|
|
1235
|
+
{ name: 'Active', value: 68 },
|
|
1236
|
+
{ name: 'Idle', value: 22 },
|
|
1237
|
+
{ name: 'Offline', value: 10 },
|
|
1238
|
+
]}
|
|
1239
|
+
nameKey="name"
|
|
1240
|
+
valueKey="value"
|
|
1241
|
+
config={{
|
|
1242
|
+
Active: { label: 'Active', color: 'var(--chart-2)' },
|
|
1243
|
+
Idle: { label: 'Idle', color: 'var(--chart-3)' },
|
|
1244
|
+
Offline: { label: 'Offline', color: 'var(--chart-5)' },
|
|
1245
|
+
}}
|
|
1246
|
+
centerValue="68%"
|
|
1247
|
+
centerLabel="Active"
|
|
1248
|
+
showLegend={false}
|
|
1249
|
+
/>
|
|
1250
|
+
</ChartCard>
|
|
1251
|
+
|
|
1252
|
+
{/* Radar */}
|
|
1253
|
+
<ChartCard title="Radar Chart" description="Multi-dimensional">
|
|
1254
|
+
<RadarMetricChart
|
|
1255
|
+
data={radarData}
|
|
1256
|
+
labelKey="skill"
|
|
1257
|
+
series={[{ key: 'score', label: 'Score' }]}
|
|
1258
|
+
filled
|
|
1259
|
+
showLegend={false}
|
|
1260
|
+
/>
|
|
1261
|
+
</ChartCard>
|
|
1262
|
+
|
|
1263
|
+
{/* Pie */}
|
|
1264
|
+
<ChartCard title="Pie Chart" description="Proportional slices">
|
|
1265
|
+
<PieMetricChart
|
|
1266
|
+
data={pieData}
|
|
1267
|
+
nameKey="category"
|
|
1268
|
+
valueKey="spend"
|
|
1269
|
+
showLabels
|
|
1270
|
+
showLegend={false}
|
|
1271
|
+
/>
|
|
1272
|
+
</ChartCard>
|
|
1273
|
+
|
|
1274
|
+
{/* Radial Bar */}
|
|
1275
|
+
<ChartCard title="Radial Bar" description="Progress rings">
|
|
1276
|
+
<RadialBarMetricChart
|
|
1277
|
+
data={radialData}
|
|
1278
|
+
dataKey="value"
|
|
1279
|
+
nameKey="name"
|
|
1280
|
+
innerRadius="20%"
|
|
1281
|
+
outerRadius="90%"
|
|
1282
|
+
valueFormatter={(v) => `${v}%`}
|
|
1283
|
+
/>
|
|
1284
|
+
</ChartCard>
|
|
1285
|
+
</div>
|
|
1286
|
+
|
|
1287
|
+
{/* Gauge row */}
|
|
1288
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
1289
|
+
{[
|
|
1290
|
+
{ label: 'CPU', value: 34 },
|
|
1291
|
+
{ label: 'Memory', value: 71 },
|
|
1292
|
+
{ label: 'Disk', value: 58 },
|
|
1293
|
+
].map((m) => (
|
|
1294
|
+
<ChartCard key={m.label} title={`Gauge — ${m.label}`} description="Semicircle gauge">
|
|
1295
|
+
<div className="flex justify-center py-2">
|
|
1296
|
+
<GaugeChart value={m.value} label={m.label} thresholds={gaugeThresholds} />
|
|
1297
|
+
</div>
|
|
1298
|
+
</ChartCard>
|
|
1299
|
+
))}
|
|
1300
|
+
</div>
|
|
1301
|
+
</div>
|
|
1302
|
+
),
|
|
1303
|
+
};
|