@mbao01/common 0.8.1 → 0.9.1
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/dist/types/components/ActivityFeed/ActivityFeed.d.ts +6 -0
- package/dist/types/components/ActivityFeed/constants.d.ts +6 -0
- package/dist/types/components/ActivityFeed/index.d.ts +2 -0
- package/dist/types/components/ActivityFeed/types.d.ts +21 -0
- package/dist/types/components/Amount/Amount.d.ts +6 -0
- package/dist/types/components/Amount/index.d.ts +2 -0
- package/dist/types/components/Amount/types.d.ts +19 -0
- package/dist/types/components/AnimatedCounter/AnimatedCounter.d.ts +6 -0
- package/dist/types/components/AnimatedCounter/index.d.ts +2 -0
- package/dist/types/components/AnimatedCounter/types.d.ts +13 -0
- package/dist/types/components/AnimatedGroup/AnimatedGroup.d.ts +6 -0
- package/dist/types/components/AnimatedGroup/index.d.ts +2 -0
- package/dist/types/components/AnimatedGroup/types.d.ts +22 -0
- package/dist/types/components/AnimatedList/AnimatedList.d.ts +6 -0
- package/dist/types/components/AnimatedList/index.d.ts +2 -0
- package/dist/types/components/AnimatedList/types.d.ts +13 -0
- package/dist/types/components/BorderBeam/BorderBeam.d.ts +6 -0
- package/dist/types/components/BorderBeam/index.d.ts +2 -0
- package/dist/types/components/BorderBeam/types.d.ts +13 -0
- package/dist/types/components/Box/Box.d.ts +4 -0
- package/dist/types/components/Box/constants.d.ts +4 -0
- package/dist/types/components/CalendarHeatmap/CalendarHeatmap.d.ts +6 -0
- package/dist/types/components/CalendarHeatmap/index.d.ts +2 -0
- package/dist/types/components/CalendarHeatmap/types.d.ts +25 -0
- package/dist/types/components/Chart/stories/examples/AreaChart.d.ts +6 -0
- package/dist/types/components/Chart/stories/examples/BarChart.d.ts +11 -0
- package/dist/types/components/Chart/stories/examples/LineChart.d.ts +10 -0
- package/dist/types/components/Chart/stories/examples/PieChart.d.ts +5 -0
- package/dist/types/components/Chart/stories/examples/RadialChart.d.ts +5 -0
- package/dist/types/components/CircularProgress/CircularProgress.d.ts +6 -0
- package/dist/types/components/CircularProgress/constants.d.ts +5 -0
- package/dist/types/components/CircularProgress/index.d.ts +2 -0
- package/dist/types/components/CircularProgress/types.d.ts +17 -0
- package/dist/types/components/ComparisonBar/ComparisonBar.d.ts +6 -0
- package/dist/types/components/ComparisonBar/constants.d.ts +4 -0
- package/dist/types/components/ComparisonBar/index.d.ts +2 -0
- package/dist/types/components/ComparisonBar/types.d.ts +21 -0
- package/dist/types/components/Confetti/Confetti.d.ts +6 -0
- package/dist/types/components/Confetti/index.d.ts +2 -0
- package/dist/types/components/Confetti/types.d.ts +19 -0
- package/dist/types/components/CountdownTimer/CountdownTimer.d.ts +6 -0
- package/dist/types/components/CountdownTimer/index.d.ts +2 -0
- package/dist/types/components/CountdownTimer/types.d.ts +13 -0
- package/dist/types/components/DataList/DataList.d.ts +6 -0
- package/dist/types/components/DataList/constants.d.ts +12 -0
- package/dist/types/components/DataList/index.d.ts +2 -0
- package/dist/types/components/DataList/types.d.ts +15 -0
- package/dist/types/components/DatePicker/DateRangePresetPicker.d.ts +2 -0
- package/dist/types/components/DatePicker/index.d.ts +1 -0
- package/dist/types/components/DatePicker/types.d.ts +19 -0
- package/dist/types/components/Description/Description.d.ts +2 -1
- package/dist/types/components/Description/DescriptionGroup.d.ts +6 -0
- package/dist/types/components/Description/constants.d.ts +23 -0
- package/dist/types/components/Description/index.d.ts +2 -0
- package/dist/types/components/Description/types.d.ts +28 -3
- package/dist/types/components/Form/DatetimeInput/DatetimeInput.d.ts +1 -1
- package/dist/types/components/GlowCard/GlowCard.d.ts +6 -0
- package/dist/types/components/GlowCard/index.d.ts +2 -0
- package/dist/types/components/GlowCard/types.d.ts +8 -0
- package/dist/types/components/GradientText/GradientText.d.ts +6 -0
- package/dist/types/components/GradientText/index.d.ts +2 -0
- package/dist/types/components/GradientText/types.d.ts +14 -0
- package/dist/types/components/Greeting/Greeting.d.ts +6 -0
- package/dist/types/components/Greeting/index.d.ts +2 -0
- package/dist/types/components/Greeting/types.d.ts +9 -0
- package/dist/types/components/IconContainer/IconContainer.d.ts +2 -0
- package/dist/types/components/IconContainer/constants.d.ts +15 -0
- package/dist/types/components/IconContainer/index.d.ts +1 -0
- package/dist/types/components/IconContainer/types.d.ts +3 -0
- package/dist/types/components/KPICard/KPICard.d.ts +6 -0
- package/dist/types/components/KPICard/index.d.ts +2 -0
- package/dist/types/components/KPICard/types.d.ts +17 -0
- package/dist/types/components/Marquee/Marquee.d.ts +6 -0
- package/dist/types/components/Marquee/index.d.ts +2 -0
- package/dist/types/components/Marquee/types.d.ts +12 -0
- package/dist/types/components/Meteors/Meteors.d.ts +6 -0
- package/dist/types/components/Meteors/index.d.ts +2 -0
- package/dist/types/components/Meteors/types.d.ts +5 -0
- package/dist/types/components/MiniAreaChart/MiniAreaChart.d.ts +6 -0
- package/dist/types/components/MiniAreaChart/index.d.ts +2 -0
- package/dist/types/components/MiniAreaChart/types.d.ts +10 -0
- package/dist/types/components/MiniBarChart/MiniBarChart.d.ts +6 -0
- package/dist/types/components/MiniBarChart/index.d.ts +2 -0
- package/dist/types/components/MiniBarChart/types.d.ts +15 -0
- package/dist/types/components/MiniDonutChart/MiniDonutChart.d.ts +6 -0
- package/dist/types/components/MiniDonutChart/index.d.ts +2 -0
- package/dist/types/components/MiniDonutChart/types.d.ts +15 -0
- package/dist/types/components/MiniStackedBar/MiniStackedBar.d.ts +6 -0
- package/dist/types/components/MiniStackedBar/index.d.ts +2 -0
- package/dist/types/components/MiniStackedBar/types.d.ts +13 -0
- package/dist/types/components/NumberTicker/NumberTicker.d.ts +6 -0
- package/dist/types/components/NumberTicker/index.d.ts +2 -0
- package/dist/types/components/NumberTicker/types.d.ts +11 -0
- package/dist/types/components/Pulse/Pulse.d.ts +6 -0
- package/dist/types/components/Pulse/constants.d.ts +10 -0
- package/dist/types/components/Pulse/index.d.ts +2 -0
- package/dist/types/components/Pulse/types.d.ts +11 -0
- package/dist/types/components/ShinyButton/ShinyButton.d.ts +6 -0
- package/dist/types/components/ShinyButton/index.d.ts +2 -0
- package/dist/types/components/ShinyButton/types.d.ts +4 -0
- package/dist/types/components/Sparkline/Sparkline.d.ts +6 -0
- package/dist/types/components/Sparkline/index.d.ts +2 -0
- package/dist/types/components/Sparkline/types.d.ts +17 -0
- package/dist/types/components/SpotlightCard/SpotlightCard.d.ts +6 -0
- package/dist/types/components/SpotlightCard/index.d.ts +2 -0
- package/dist/types/components/SpotlightCard/types.d.ts +7 -0
- package/dist/types/components/StatCard/StatCard.d.ts +6 -0
- package/dist/types/components/StatCard/index.d.ts +2 -0
- package/dist/types/components/StatCard/types.d.ts +15 -0
- package/dist/types/components/TextShimmer/TextShimmer.d.ts +6 -0
- package/dist/types/components/TextShimmer/index.d.ts +2 -0
- package/dist/types/components/TextShimmer/types.d.ts +7 -0
- package/dist/types/components/TrendBadge/TrendBadge.d.ts +6 -0
- package/dist/types/components/TrendBadge/constants.d.ts +7 -0
- package/dist/types/components/TrendBadge/index.d.ts +2 -0
- package/dist/types/components/TrendBadge/types.d.ts +15 -0
- package/dist/types/components/WidgetShell/WidgetShell.d.ts +6 -0
- package/dist/types/components/WidgetShell/index.d.ts +2 -0
- package/dist/types/components/WidgetShell/types.d.ts +20 -0
- package/dist/types/index.d.ts +34 -0
- package/package.json +2 -1
- package/src/components/Accordion/constants.ts +1 -1
- package/src/components/ActivityFeed/ActivityFeed.tsx +51 -0
- package/src/components/ActivityFeed/constants.ts +19 -0
- package/src/components/ActivityFeed/index.ts +2 -0
- package/src/components/ActivityFeed/types.ts +23 -0
- package/src/components/Alert/constants.ts +1 -1
- package/src/components/AlertDialog/constants.ts +1 -1
- package/src/components/Amount/Amount.tsx +50 -0
- package/src/components/Amount/index.ts +2 -0
- package/src/components/Amount/types.ts +20 -0
- package/src/components/AnimatedCounter/AnimatedCounter.tsx +68 -0
- package/src/components/AnimatedCounter/index.ts +2 -0
- package/src/components/AnimatedCounter/types.ts +14 -0
- package/src/components/AnimatedGroup/AnimatedGroup.tsx +97 -0
- package/src/components/AnimatedGroup/index.ts +2 -0
- package/src/components/AnimatedGroup/types.ts +21 -0
- package/src/components/AnimatedList/AnimatedList.tsx +42 -0
- package/src/components/AnimatedList/index.ts +2 -0
- package/src/components/AnimatedList/types.ts +15 -0
- package/src/components/Badge/constants.ts +1 -1
- package/src/components/Banner/constants.ts +1 -1
- package/src/components/BorderBeam/BorderBeam.tsx +41 -0
- package/src/components/BorderBeam/index.ts +2 -0
- package/src/components/BorderBeam/types.ts +14 -0
- package/src/components/Box/Box.tsx +8 -2
- package/src/components/Box/constants.ts +35 -0
- package/src/components/Button/constants.ts +66 -63
- package/src/components/CalendarHeatmap/CalendarHeatmap.tsx +141 -0
- package/src/components/CalendarHeatmap/index.ts +2 -0
- package/src/components/CalendarHeatmap/types.ts +27 -0
- package/src/components/Card/constants.ts +24 -21
- package/src/components/Carousel/constants.ts +2 -2
- package/src/components/Chart/stories/examples/AreaChart.tsx +55 -0
- package/src/components/Chart/stories/examples/BarChart.tsx +95 -0
- package/src/components/Chart/stories/examples/LineChart.tsx +111 -0
- package/src/components/Chart/stories/examples/PieChart.tsx +55 -0
- package/src/components/Chart/stories/examples/RadialChart.tsx +65 -0
- package/src/components/CircularProgress/CircularProgress.tsx +46 -0
- package/src/components/CircularProgress/constants.ts +32 -0
- package/src/components/CircularProgress/index.ts +2 -0
- package/src/components/CircularProgress/types.ts +18 -0
- package/src/components/Command/constants.ts +1 -1
- package/src/components/ComparisonBar/ComparisonBar.tsx +65 -0
- package/src/components/ComparisonBar/constants.ts +23 -0
- package/src/components/ComparisonBar/index.ts +2 -0
- package/src/components/ComparisonBar/types.ts +23 -0
- package/src/components/Confetti/Confetti.tsx +82 -0
- package/src/components/Confetti/index.ts +2 -0
- package/src/components/Confetti/types.ts +20 -0
- package/src/components/CountdownTimer/CountdownTimer.tsx +91 -0
- package/src/components/CountdownTimer/index.ts +2 -0
- package/src/components/CountdownTimer/types.ts +14 -0
- package/src/components/DataList/DataList.tsx +32 -0
- package/src/components/DataList/constants.ts +47 -0
- package/src/components/DataList/index.ts +2 -0
- package/src/components/DataList/types.ts +17 -0
- package/src/components/DatePicker/DateRangePresetPicker.tsx +122 -0
- package/src/components/DatePicker/index.ts +1 -0
- package/src/components/DatePicker/types.ts +22 -0
- package/src/components/Description/Description.tsx +67 -5
- package/src/components/Description/DescriptionGroup.tsx +39 -0
- package/src/components/Description/constants.ts +128 -0
- package/src/components/Description/index.ts +10 -0
- package/src/components/Description/types.ts +31 -3
- package/src/components/Dialog/constants.ts +2 -2
- package/src/components/Dock/constants.ts +2 -2
- package/src/components/Drawer/constants.ts +2 -2
- package/src/components/Form/Checkbox/constants.ts +1 -1
- package/src/components/Form/DatetimeInput/constants.ts +1 -1
- package/src/components/Form/Input/constants.ts +1 -1
- package/src/components/Form/MultiSelect/constants.ts +1 -1
- package/src/components/Form/NativeSelect/constants.ts +1 -1
- package/src/components/Form/Radio/constants.ts +1 -1
- package/src/components/Form/Select/constants.ts +1 -1
- package/src/components/Form/Slider/constants.ts +1 -1
- package/src/components/Form/Switch/constants.ts +1 -1
- package/src/components/Form/Textarea/constants.ts +1 -1
- package/src/components/GlowCard/GlowCard.tsx +46 -0
- package/src/components/GlowCard/index.ts +2 -0
- package/src/components/GlowCard/types.ts +9 -0
- package/src/components/GradientText/GradientText.tsx +36 -0
- package/src/components/GradientText/index.ts +2 -0
- package/src/components/GradientText/types.ts +15 -0
- package/src/components/Greeting/Greeting.tsx +46 -0
- package/src/components/Greeting/index.ts +2 -0
- package/src/components/Greeting/types.ts +10 -0
- package/src/components/IconContainer/IconContainer.tsx +44 -0
- package/src/components/IconContainer/constants.ts +112 -0
- package/src/components/IconContainer/index.ts +1 -0
- package/src/components/IconContainer/types.ts +5 -0
- package/src/components/KPICard/KPICard.tsx +75 -0
- package/src/components/KPICard/index.ts +2 -0
- package/src/components/KPICard/types.ts +18 -0
- package/src/components/Marquee/Marquee.tsx +45 -0
- package/src/components/Marquee/index.ts +2 -0
- package/src/components/Marquee/types.ts +13 -0
- package/src/components/Menu/Menubar/constants.ts +2 -2
- package/src/components/Menu/NavigationMenu/constants.ts +2 -2
- package/src/components/Meteors/Meteors.tsx +38 -0
- package/src/components/Meteors/index.ts +2 -0
- package/src/components/Meteors/types.ts +6 -0
- package/src/components/MiniAreaChart/MiniAreaChart.tsx +68 -0
- package/src/components/MiniAreaChart/index.ts +2 -0
- package/src/components/MiniAreaChart/types.ts +11 -0
- package/src/components/MiniBarChart/MiniBarChart.tsx +49 -0
- package/src/components/MiniBarChart/index.ts +2 -0
- package/src/components/MiniBarChart/types.ts +16 -0
- package/src/components/MiniDonutChart/MiniDonutChart.tsx +87 -0
- package/src/components/MiniDonutChart/index.ts +2 -0
- package/src/components/MiniDonutChart/types.ts +17 -0
- package/src/components/MiniStackedBar/MiniStackedBar.tsx +61 -0
- package/src/components/MiniStackedBar/index.ts +2 -0
- package/src/components/MiniStackedBar/types.ts +15 -0
- package/src/components/NumberTicker/NumberTicker.tsx +58 -0
- package/src/components/NumberTicker/index.ts +2 -0
- package/src/components/NumberTicker/types.ts +12 -0
- package/src/components/Pagination/constants.ts +2 -2
- package/src/components/Progress/constants.ts +1 -1
- package/src/components/Pulse/Pulse.tsx +26 -0
- package/src/components/Pulse/constants.ts +55 -0
- package/src/components/Pulse/index.ts +2 -0
- package/src/components/Pulse/types.ts +12 -0
- package/src/components/Resizable/constants.ts +1 -1
- package/src/components/Sheet/constants.ts +1 -1
- package/src/components/ShinyButton/ShinyButton.tsx +57 -0
- package/src/components/ShinyButton/index.ts +2 -0
- package/src/components/ShinyButton/types.ts +8 -0
- package/src/components/Skeleton/constants.ts +1 -1
- package/src/components/Sonner/constants.ts +1 -1
- package/src/components/Sparkline/Sparkline.tsx +108 -0
- package/src/components/Sparkline/index.ts +2 -0
- package/src/components/Sparkline/types.ts +18 -0
- package/src/components/SpotlightCard/SpotlightCard.tsx +56 -0
- package/src/components/SpotlightCard/index.ts +2 -0
- package/src/components/SpotlightCard/types.ts +8 -0
- package/src/components/Stat/constants.ts +1 -1
- package/src/components/StatCard/StatCard.tsx +59 -0
- package/src/components/StatCard/index.ts +2 -0
- package/src/components/StatCard/types.ts +16 -0
- package/src/components/Tabs/constants.ts +1 -1
- package/src/components/TextShimmer/TextShimmer.tsx +34 -0
- package/src/components/TextShimmer/index.ts +2 -0
- package/src/components/TextShimmer/types.ts +8 -0
- package/src/components/Timeline/constants.ts +1 -1
- package/src/components/Toggle/constants.ts +1 -1
- package/src/components/Tooltip/constants.ts +1 -1
- package/src/components/TrendBadge/TrendBadge.tsx +40 -0
- package/src/components/TrendBadge/constants.ts +38 -0
- package/src/components/TrendBadge/index.ts +2 -0
- package/src/components/TrendBadge/types.ts +16 -0
- package/src/components/WidgetShell/WidgetShell.tsx +101 -0
- package/src/components/WidgetShell/index.ts +2 -0
- package/src/components/WidgetShell/types.ts +22 -0
- package/src/index.ts +36 -0
- package/src/stylesheets/tailwind.css +208 -0
- package/src/utilities/getSubpaths/getSubpaths.ts +1 -2
|
@@ -137,3 +137,114 @@ export const WithLegendLineChartExample = (props: LineChartProps) => {
|
|
|
137
137
|
</Chart>
|
|
138
138
|
);
|
|
139
139
|
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Smooth curved line chart with gradient stroke and refined active dot.
|
|
143
|
+
* Shows a single data series with emphasis on the curve smoothness.
|
|
144
|
+
*/
|
|
145
|
+
export const SmoothLineChartExample = (props: LineChartProps) => {
|
|
146
|
+
const chartConfig = {
|
|
147
|
+
value: {
|
|
148
|
+
label: "Revenue",
|
|
149
|
+
color: "hsl(var(--chart-1))",
|
|
150
|
+
},
|
|
151
|
+
} satisfies ChartConfig;
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<Chart config={chartConfig} className="h-[250px] w-full">
|
|
155
|
+
<LineChart {...props.lineChart}>
|
|
156
|
+
<CartesianGrid vertical={false} strokeDasharray="3 3" />
|
|
157
|
+
<XAxis
|
|
158
|
+
dataKey="month"
|
|
159
|
+
tickLine={false}
|
|
160
|
+
axisLine={false}
|
|
161
|
+
tickMargin={8}
|
|
162
|
+
{...props.xAxis}
|
|
163
|
+
/>
|
|
164
|
+
<YAxis
|
|
165
|
+
tickLine={false}
|
|
166
|
+
axisLine={false}
|
|
167
|
+
tickMargin={8}
|
|
168
|
+
{...props.yAxis}
|
|
169
|
+
/>
|
|
170
|
+
<ChartTooltip
|
|
171
|
+
content={<ChartTooltipContent indicator="dot" />}
|
|
172
|
+
/>
|
|
173
|
+
<Line
|
|
174
|
+
dataKey="value"
|
|
175
|
+
type="natural"
|
|
176
|
+
stroke="var(--color-value)"
|
|
177
|
+
strokeWidth={2.5}
|
|
178
|
+
dot={false}
|
|
179
|
+
activeDot={{
|
|
180
|
+
r: 6,
|
|
181
|
+
strokeWidth: 2,
|
|
182
|
+
stroke: "var(--color-value)",
|
|
183
|
+
fill: "white",
|
|
184
|
+
}}
|
|
185
|
+
{...props.line}
|
|
186
|
+
/>
|
|
187
|
+
</LineChart>
|
|
188
|
+
</Chart>
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Multi-series comparison line chart with dashed reference line.
|
|
194
|
+
* Useful for budget vs actual, target vs performance comparisons.
|
|
195
|
+
*/
|
|
196
|
+
export const ComparisonLineChartExample = (props: LineChartProps) => {
|
|
197
|
+
const chartConfig = {
|
|
198
|
+
actual: {
|
|
199
|
+
label: "Actual",
|
|
200
|
+
color: "hsl(var(--chart-1))",
|
|
201
|
+
},
|
|
202
|
+
target: {
|
|
203
|
+
label: "Target",
|
|
204
|
+
color: "hsl(var(--chart-2))",
|
|
205
|
+
},
|
|
206
|
+
} satisfies ChartConfig;
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<Chart config={chartConfig} className="h-[250px] w-full">
|
|
210
|
+
<LineChart {...props.lineChart}>
|
|
211
|
+
<CartesianGrid vertical={false} strokeDasharray="3 3" />
|
|
212
|
+
<XAxis
|
|
213
|
+
dataKey="month"
|
|
214
|
+
tickLine={false}
|
|
215
|
+
axisLine={false}
|
|
216
|
+
tickMargin={8}
|
|
217
|
+
tickFormatter={(value: string) => value.slice(0, 3)}
|
|
218
|
+
{...props.xAxis}
|
|
219
|
+
/>
|
|
220
|
+
<YAxis
|
|
221
|
+
tickLine={false}
|
|
222
|
+
axisLine={false}
|
|
223
|
+
tickMargin={8}
|
|
224
|
+
tickFormatter={(v: number) => `$${(v / 1000).toFixed(0)}k`}
|
|
225
|
+
{...props.yAxis}
|
|
226
|
+
/>
|
|
227
|
+
<ChartTooltip content={<ChartTooltipContent />} />
|
|
228
|
+
<ChartLegend content={<ChartLegendContent />} />
|
|
229
|
+
<Line
|
|
230
|
+
dataKey="target"
|
|
231
|
+
type="monotone"
|
|
232
|
+
stroke="var(--color-target)"
|
|
233
|
+
strokeWidth={2}
|
|
234
|
+
strokeDasharray="6 3"
|
|
235
|
+
dot={false}
|
|
236
|
+
{...props.line}
|
|
237
|
+
/>
|
|
238
|
+
<Line
|
|
239
|
+
dataKey="actual"
|
|
240
|
+
type="monotone"
|
|
241
|
+
stroke="var(--color-actual)"
|
|
242
|
+
strokeWidth={2.5}
|
|
243
|
+
dot={false}
|
|
244
|
+
activeDot={{ r: 5, strokeWidth: 2, fill: "var(--color-actual)" }}
|
|
245
|
+
{...props.line}
|
|
246
|
+
/>
|
|
247
|
+
</LineChart>
|
|
248
|
+
</Chart>
|
|
249
|
+
);
|
|
250
|
+
};
|
|
@@ -169,6 +169,61 @@ export const ActiveDonutPieChartExample = (props: Partial<PieChartProps>) => {
|
|
|
169
169
|
);
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Beautiful donut chart with a center metric label.
|
|
174
|
+
* Perfect for portfolio allocation, budget breakdown, etc.
|
|
175
|
+
*/
|
|
176
|
+
export const CenterLabelDonutExample = (props: Partial<PieChartProps>) => {
|
|
177
|
+
const chartConfig = {
|
|
178
|
+
allocation: {
|
|
179
|
+
label: "Allocation",
|
|
180
|
+
},
|
|
181
|
+
stocks: {
|
|
182
|
+
label: "Stocks",
|
|
183
|
+
color: "hsl(var(--chart-1))",
|
|
184
|
+
},
|
|
185
|
+
bonds: {
|
|
186
|
+
label: "Bonds",
|
|
187
|
+
color: "hsl(var(--chart-2))",
|
|
188
|
+
},
|
|
189
|
+
real_estate: {
|
|
190
|
+
label: "Real Estate",
|
|
191
|
+
color: "hsl(var(--chart-3))",
|
|
192
|
+
},
|
|
193
|
+
crypto: {
|
|
194
|
+
label: "Crypto",
|
|
195
|
+
color: "hsl(var(--chart-4))",
|
|
196
|
+
},
|
|
197
|
+
cash: {
|
|
198
|
+
label: "Cash",
|
|
199
|
+
color: "hsl(var(--chart-5))",
|
|
200
|
+
},
|
|
201
|
+
} satisfies ChartConfig;
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<Chart config={chartConfig} className="mx-auto aspect-square h-[250px]">
|
|
205
|
+
<PieChart {...props.pieChart}>
|
|
206
|
+
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
|
|
207
|
+
<Pie
|
|
208
|
+
dataKey="value"
|
|
209
|
+
nameKey="category"
|
|
210
|
+
innerRadius={65}
|
|
211
|
+
outerRadius={90}
|
|
212
|
+
strokeWidth={3}
|
|
213
|
+
stroke="hsl(var(--b1, 0 0% 100%))"
|
|
214
|
+
paddingAngle={2}
|
|
215
|
+
cornerRadius={4}
|
|
216
|
+
{...props.pie}
|
|
217
|
+
/>
|
|
218
|
+
<ChartLegend
|
|
219
|
+
content={<ChartLegendContent nameKey="category" />}
|
|
220
|
+
className="-translate-y-2 flex-wrap gap-2 *:basis-1/3 *:justify-center"
|
|
221
|
+
/>
|
|
222
|
+
</PieChart>
|
|
223
|
+
</Chart>
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
|
|
172
227
|
export const StackedPieChartExample = (props: Partial<PieChartProps>) => {
|
|
173
228
|
const desktopData = [
|
|
174
229
|
{ month: "january", desktop: 186, fill: "var(--color-january)" },
|
|
@@ -190,6 +190,71 @@ export const ShapeRadialChartExample = (props: Partial<RadialBarChartProps>) =>
|
|
|
190
190
|
);
|
|
191
191
|
};
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* KPI gauge — a single-value radial chart with a large center metric
|
|
195
|
+
* and background track. Great for goal progress, budget usage, etc.
|
|
196
|
+
*/
|
|
197
|
+
export const KPIGaugeRadialChartExample = (props: Partial<RadialBarChartProps>) => {
|
|
198
|
+
const chartData = [{ name: "progress", value: 73, fill: "var(--color-progress)" }];
|
|
199
|
+
const chartConfig = {
|
|
200
|
+
progress: {
|
|
201
|
+
label: "Progress",
|
|
202
|
+
color: "hsl(var(--chart-1))",
|
|
203
|
+
},
|
|
204
|
+
} satisfies ChartConfig;
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<Chart config={chartConfig} className="mx-auto aspect-square h-[250px]">
|
|
208
|
+
<RadialBarChart
|
|
209
|
+
data={chartData}
|
|
210
|
+
startAngle={180}
|
|
211
|
+
endAngle={0}
|
|
212
|
+
innerRadius={80}
|
|
213
|
+
outerRadius={110}
|
|
214
|
+
barSize={14}
|
|
215
|
+
{...props.radialBarChart}
|
|
216
|
+
>
|
|
217
|
+
<PolarGrid
|
|
218
|
+
gridType="circle"
|
|
219
|
+
radialLines={false}
|
|
220
|
+
stroke="none"
|
|
221
|
+
className="first:fill-base-200 last:fill-base-100"
|
|
222
|
+
polarRadius={[86, 74]}
|
|
223
|
+
/>
|
|
224
|
+
<RadialBar
|
|
225
|
+
dataKey="value"
|
|
226
|
+
cornerRadius={10}
|
|
227
|
+
fill="var(--color-progress)"
|
|
228
|
+
background={{ fill: "hsl(var(--b2, 0 0% 90%))" }}
|
|
229
|
+
{...props.radialBar}
|
|
230
|
+
/>
|
|
231
|
+
<PolarRadiusAxis tick={false} tickLine={false} axisLine={false}>
|
|
232
|
+
<Label
|
|
233
|
+
content={({ viewBox }) => {
|
|
234
|
+
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
|
|
235
|
+
return (
|
|
236
|
+
<text x={viewBox.cx} y={viewBox.cy} textAnchor="middle" dominantBaseline="middle">
|
|
237
|
+
<tspan
|
|
238
|
+
x={viewBox.cx}
|
|
239
|
+
y={(viewBox.cy ?? 0) - 12}
|
|
240
|
+
className="fill-base-content text-4xl font-bold"
|
|
241
|
+
>
|
|
242
|
+
73%
|
|
243
|
+
</tspan>
|
|
244
|
+
<tspan x={viewBox.cx} y={(viewBox.cy ?? 0) + 16} className="fill-base-content/60 text-sm">
|
|
245
|
+
of target
|
|
246
|
+
</tspan>
|
|
247
|
+
</text>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}}
|
|
251
|
+
/>
|
|
252
|
+
</PolarRadiusAxis>
|
|
253
|
+
</RadialBarChart>
|
|
254
|
+
</Chart>
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
|
|
193
258
|
export const StackedRadialChartExample = (props: Partial<RadialBarChartProps>) => {
|
|
194
259
|
const chartData = [{ month: "january", desktop: 1260, mobile: 570 }];
|
|
195
260
|
const chartConfig = {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import { cn } from "../../utilities";
|
|
3
|
+
import { getCircularProgressClasses, getCircularProgressLabelClasses } from "./constants";
|
|
4
|
+
import type { CircularProgressProps } from "./types";
|
|
5
|
+
|
|
6
|
+
const CircularProgress = ({
|
|
7
|
+
value,
|
|
8
|
+
className,
|
|
9
|
+
size,
|
|
10
|
+
thickness,
|
|
11
|
+
variant,
|
|
12
|
+
showLabel = true,
|
|
13
|
+
label,
|
|
14
|
+
preset = "md",
|
|
15
|
+
...props
|
|
16
|
+
}: CircularProgressProps) => {
|
|
17
|
+
const clampedValue = Math.max(0, Math.min(100, value));
|
|
18
|
+
|
|
19
|
+
const style: Record<string, string | number> = {
|
|
20
|
+
"--value": clampedValue,
|
|
21
|
+
};
|
|
22
|
+
if (size) style["--size"] = size;
|
|
23
|
+
if (thickness) style["--thickness"] = thickness;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
className={cn(getCircularProgressClasses({ variant, preset }), className)}
|
|
28
|
+
style={style as CSSProperties}
|
|
29
|
+
role="progressbar"
|
|
30
|
+
aria-valuenow={clampedValue}
|
|
31
|
+
aria-valuemin={0}
|
|
32
|
+
aria-valuemax={100}
|
|
33
|
+
{...props}
|
|
34
|
+
>
|
|
35
|
+
{showLabel && (
|
|
36
|
+
<span className={getCircularProgressLabelClasses()}>
|
|
37
|
+
{label ?? `${Math.round(clampedValue)}%`}
|
|
38
|
+
</span>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
CircularProgress.displayName = "CircularProgress";
|
|
45
|
+
|
|
46
|
+
export { CircularProgress };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { cva } from "../../libs";
|
|
2
|
+
import { createVariants } from "../../utilities";
|
|
3
|
+
|
|
4
|
+
export const getCircularProgressClasses = cva(
|
|
5
|
+
"radial-progress transition-all duration-500",
|
|
6
|
+
{
|
|
7
|
+
variants: createVariants({
|
|
8
|
+
variant: {
|
|
9
|
+
primary: "text-primary",
|
|
10
|
+
secondary: "text-secondary",
|
|
11
|
+
accent: "text-accent",
|
|
12
|
+
info: "text-info",
|
|
13
|
+
success: "text-success",
|
|
14
|
+
warning: "text-warning",
|
|
15
|
+
error: "text-error",
|
|
16
|
+
neutral: "text-neutral",
|
|
17
|
+
},
|
|
18
|
+
preset: {
|
|
19
|
+
xs: "[--size:2rem] [--thickness:2px] text-xs",
|
|
20
|
+
sm: "[--size:3rem] [--thickness:3px] text-xs",
|
|
21
|
+
md: "[--size:5rem] [--thickness:4px] text-sm",
|
|
22
|
+
lg: "[--size:7rem] [--thickness:5px] text-base",
|
|
23
|
+
xl: "[--size:9rem] [--thickness:6px] text-lg",
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
preset: "md",
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const getCircularProgressLabelClasses = cva("font-semibold tabular-nums");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export type CircularProgressProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
4
|
+
/** Progress value from 0 to 100 */
|
|
5
|
+
value: number;
|
|
6
|
+
/** Size of the circle (CSS value) */
|
|
7
|
+
size?: string;
|
|
8
|
+
/** Thickness of the progress ring (CSS value) */
|
|
9
|
+
thickness?: string;
|
|
10
|
+
/** Color variant */
|
|
11
|
+
variant?: "primary" | "secondary" | "accent" | "info" | "success" | "warning" | "error" | "neutral";
|
|
12
|
+
/** Whether to show the value label */
|
|
13
|
+
showLabel?: boolean;
|
|
14
|
+
/** Custom label to display instead of percentage */
|
|
15
|
+
label?: ReactNode;
|
|
16
|
+
/** Size preset */
|
|
17
|
+
preset?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
18
|
+
};
|
|
@@ -27,7 +27,7 @@ export const getCommandGroupClasses = cva(
|
|
|
27
27
|
export const getCommandSeparatorClasses = cva("-mx-1 h-px bg-border");
|
|
28
28
|
|
|
29
29
|
export const getCommandItemClasses = cva(
|
|
30
|
-
"relative flex cursor-pointer select-none items-center rounded-xs px-2 py-1.5 text-sm outline-hidden transition-
|
|
30
|
+
"relative flex cursor-pointer select-none items-center rounded-xs px-2 py-1.5 text-sm outline-hidden transition-all duration-150 aria-selected:bg-base-300 aria-selected:text-base-content aria-disabled:underline aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:cursor-default"
|
|
31
31
|
);
|
|
32
32
|
|
|
33
33
|
export const getCommandShortcutClasses = cva(
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { cn } from "../../utilities";
|
|
3
|
+
import { getComparisonBarTrackClasses, getComparisonBarSegmentClasses } from "./constants";
|
|
4
|
+
import type { ComparisonBarProps } from "./types";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_COLORS = [
|
|
7
|
+
"oklch(0.7 0.15 250)",
|
|
8
|
+
"oklch(0.7 0.15 150)",
|
|
9
|
+
"oklch(0.7 0.15 50)",
|
|
10
|
+
"oklch(0.7 0.15 330)",
|
|
11
|
+
"oklch(0.6 0.15 200)",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const ComparisonBar = ({
|
|
15
|
+
segments,
|
|
16
|
+
className,
|
|
17
|
+
showLabels = true,
|
|
18
|
+
showValues = false,
|
|
19
|
+
size = "md",
|
|
20
|
+
formatValue = (v) => v.toLocaleString(),
|
|
21
|
+
...props
|
|
22
|
+
}: ComparisonBarProps) => {
|
|
23
|
+
const total = useMemo(() => segments.reduce((sum, s) => sum + s.value, 0), [segments]);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className={cn("flex flex-col gap-2", className)} {...props}>
|
|
27
|
+
<div className={getComparisonBarTrackClasses({ size })}>
|
|
28
|
+
{segments.map((segment, i) => {
|
|
29
|
+
const percentage = total > 0 ? (segment.value / total) * 100 : 0;
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
key={i}
|
|
33
|
+
className={getComparisonBarSegmentClasses()}
|
|
34
|
+
style={{
|
|
35
|
+
width: `${percentage}%`,
|
|
36
|
+
backgroundColor: segment.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length],
|
|
37
|
+
}}
|
|
38
|
+
title={`${segment.label}: ${formatValue(segment.value)} (${percentage.toFixed(1)}%)`}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
})}
|
|
42
|
+
</div>
|
|
43
|
+
{showLabels && (
|
|
44
|
+
<div className="flex flex-wrap gap-x-4 gap-y-1">
|
|
45
|
+
{segments.map((segment, i) => (
|
|
46
|
+
<div key={i} className="flex items-center gap-1.5">
|
|
47
|
+
<span
|
|
48
|
+
className="size-2.5 rounded-full"
|
|
49
|
+
style={{ backgroundColor: segment.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length] }}
|
|
50
|
+
/>
|
|
51
|
+
<span className="text-xs text-base-content/60">{segment.label}</span>
|
|
52
|
+
{showValues && (
|
|
53
|
+
<span className="text-xs font-medium tabular-nums">{formatValue(segment.value)}</span>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
ComparisonBar.displayName = "ComparisonBar";
|
|
64
|
+
|
|
65
|
+
export { ComparisonBar };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { cva } from "../../libs";
|
|
2
|
+
import { createVariants } from "../../utilities";
|
|
3
|
+
|
|
4
|
+
export const getComparisonBarTrackClasses = cva(
|
|
5
|
+
"flex w-full overflow-hidden rounded-full bg-base-200",
|
|
6
|
+
{
|
|
7
|
+
variants: createVariants({
|
|
8
|
+
size: {
|
|
9
|
+
xs: "h-1.5",
|
|
10
|
+
sm: "h-2",
|
|
11
|
+
md: "h-3",
|
|
12
|
+
lg: "h-4",
|
|
13
|
+
},
|
|
14
|
+
}),
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
size: "md",
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const getComparisonBarSegmentClasses = cva(
|
|
22
|
+
"transition-all duration-500 ease-out first:rounded-l-full last:rounded-r-full"
|
|
23
|
+
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
export type ComparisonBarSegment = {
|
|
4
|
+
/** Segment label */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Segment value */
|
|
7
|
+
value: number;
|
|
8
|
+
/** Color (CSS color or Tailwind class) */
|
|
9
|
+
color?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ComparisonBarProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
13
|
+
/** Segments to display */
|
|
14
|
+
segments: ComparisonBarSegment[];
|
|
15
|
+
/** Whether to show labels */
|
|
16
|
+
showLabels?: boolean;
|
|
17
|
+
/** Whether to show values */
|
|
18
|
+
showValues?: boolean;
|
|
19
|
+
/** Height variant */
|
|
20
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
21
|
+
/** Format function for values */
|
|
22
|
+
formatValue?: (value: number) => string;
|
|
23
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import type { ConfettiProps } from "./types";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_COLORS = [
|
|
7
|
+
"oklch(0.7 0.25 250)",
|
|
8
|
+
"oklch(0.7 0.2 330)",
|
|
9
|
+
"oklch(0.75 0.2 150)",
|
|
10
|
+
"oklch(0.8 0.15 60)",
|
|
11
|
+
"oklch(0.7 0.25 25)",
|
|
12
|
+
"oklch(0.7 0.15 200)",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const Confetti = ({
|
|
16
|
+
active = false,
|
|
17
|
+
count = 50,
|
|
18
|
+
duration = 3,
|
|
19
|
+
colors = DEFAULT_COLORS,
|
|
20
|
+
}: ConfettiProps) => {
|
|
21
|
+
const [visible, setVisible] = useState(false);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (active) {
|
|
25
|
+
setVisible(true);
|
|
26
|
+
const timer = setTimeout(() => setVisible(false), duration * 1000);
|
|
27
|
+
return () => clearTimeout(timer);
|
|
28
|
+
}
|
|
29
|
+
setVisible(false);
|
|
30
|
+
}, [active, duration]);
|
|
31
|
+
|
|
32
|
+
const pieces = useMemo(
|
|
33
|
+
() =>
|
|
34
|
+
Array.from({ length: count }, (_, i) => ({
|
|
35
|
+
id: i,
|
|
36
|
+
x: Math.random() * 100,
|
|
37
|
+
delay: Math.random() * 0.5,
|
|
38
|
+
duration: duration * 0.6 + Math.random() * duration * 0.4,
|
|
39
|
+
color: colors[i % colors.length],
|
|
40
|
+
size: 4 + Math.random() * 6,
|
|
41
|
+
rotation: Math.random() * 360,
|
|
42
|
+
})),
|
|
43
|
+
[count, duration, colors]
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (!visible) return null;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="pointer-events-none fixed inset-0 z-[100] overflow-hidden" aria-hidden="true">
|
|
50
|
+
{pieces.map((piece) => (
|
|
51
|
+
<div
|
|
52
|
+
key={piece.id}
|
|
53
|
+
className="absolute animate-confetti"
|
|
54
|
+
style={{
|
|
55
|
+
left: `${piece.x}%`,
|
|
56
|
+
top: "-5%",
|
|
57
|
+
width: piece.size,
|
|
58
|
+
height: piece.size * 0.6,
|
|
59
|
+
backgroundColor: piece.color,
|
|
60
|
+
borderRadius: "1px",
|
|
61
|
+
transform: `rotate(${piece.rotation}deg)`,
|
|
62
|
+
animationDelay: `${piece.delay}s`,
|
|
63
|
+
animationDuration: `${piece.duration}s`,
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
))}
|
|
67
|
+
<style>{`
|
|
68
|
+
@keyframes confetti-fall {
|
|
69
|
+
0% { transform: translateY(0) rotate(0deg); opacity: 1; }
|
|
70
|
+
100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }
|
|
71
|
+
}
|
|
72
|
+
.animate-confetti {
|
|
73
|
+
animation: confetti-fall var(--tw-duration, 3s) cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
|
74
|
+
}
|
|
75
|
+
`}</style>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
Confetti.displayName = "Confetti";
|
|
81
|
+
|
|
82
|
+
export { Confetti };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ConfettiPiece = {
|
|
2
|
+
id: number;
|
|
3
|
+
x: number;
|
|
4
|
+
delay: number;
|
|
5
|
+
duration: number;
|
|
6
|
+
color: string;
|
|
7
|
+
size: number;
|
|
8
|
+
rotation: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type ConfettiProps = {
|
|
12
|
+
/** Whether to show confetti */
|
|
13
|
+
active?: boolean;
|
|
14
|
+
/** Number of confetti pieces */
|
|
15
|
+
count?: number;
|
|
16
|
+
/** Duration in seconds */
|
|
17
|
+
duration?: number;
|
|
18
|
+
/** Custom colors */
|
|
19
|
+
colors?: string[];
|
|
20
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import type { CountdownTimerProps } from "./types";
|
|
5
|
+
import { cn } from "../../utilities";
|
|
6
|
+
|
|
7
|
+
type TimeLeft = { days: number; hours: number; minutes: number; seconds: number };
|
|
8
|
+
|
|
9
|
+
const unitClasses = {
|
|
10
|
+
sm: "text-[10px]",
|
|
11
|
+
md: "text-xs",
|
|
12
|
+
lg: "text-sm",
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
const Unit = ({ value, unit, size }: { value: number; unit: string; size: "sm" | "md" | "lg" }) => (
|
|
16
|
+
<div className="flex flex-col items-center">
|
|
17
|
+
<span className="font-bold tabular-nums">{String(value).padStart(2, "0")}</span>
|
|
18
|
+
<span className={cn("text-base-content/50 uppercase tracking-wider", unitClasses[size])}>
|
|
19
|
+
{unit}
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const Separator = () => <span className="self-start font-bold text-base-content/30 pt-0.5">:</span>;
|
|
25
|
+
|
|
26
|
+
const CountdownTimer = ({
|
|
27
|
+
targetDate,
|
|
28
|
+
className,
|
|
29
|
+
label,
|
|
30
|
+
onComplete,
|
|
31
|
+
showSeconds = true,
|
|
32
|
+
size = "md",
|
|
33
|
+
...props
|
|
34
|
+
}: CountdownTimerProps) => {
|
|
35
|
+
const calcTimeLeft = useCallback((): TimeLeft => {
|
|
36
|
+
const diff = Math.max(0, targetDate.getTime() - Date.now());
|
|
37
|
+
return {
|
|
38
|
+
days: Math.floor(diff / (1000 * 60 * 60 * 24)),
|
|
39
|
+
hours: Math.floor((diff / (1000 * 60 * 60)) % 24),
|
|
40
|
+
minutes: Math.floor((diff / (1000 * 60)) % 60),
|
|
41
|
+
seconds: Math.floor((diff / 1000) % 60),
|
|
42
|
+
};
|
|
43
|
+
}, [targetDate]);
|
|
44
|
+
|
|
45
|
+
const [timeLeft, setTimeLeft] = useState<TimeLeft>(calcTimeLeft);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const timer = setInterval(() => {
|
|
49
|
+
const tl = calcTimeLeft();
|
|
50
|
+
setTimeLeft(tl);
|
|
51
|
+
if (tl.days === 0 && tl.hours === 0 && tl.minutes === 0 && tl.seconds === 0) {
|
|
52
|
+
clearInterval(timer);
|
|
53
|
+
onComplete?.();
|
|
54
|
+
}
|
|
55
|
+
}, 1000);
|
|
56
|
+
return () => clearInterval(timer);
|
|
57
|
+
}, [calcTimeLeft, onComplete]);
|
|
58
|
+
|
|
59
|
+
const sizeClasses = {
|
|
60
|
+
sm: "text-lg gap-1",
|
|
61
|
+
md: "text-2xl gap-2",
|
|
62
|
+
lg: "text-4xl gap-3",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className={cn("flex flex-col items-center gap-1", className)} {...props}>
|
|
67
|
+
{label && <span className="text-sm text-base-content/60">{label}</span>}
|
|
68
|
+
<div className={cn("flex items-center", sizeClasses[size])}>
|
|
69
|
+
{timeLeft.days > 0 && (
|
|
70
|
+
<>
|
|
71
|
+
<Unit size={size} value={timeLeft.days} unit="days" />
|
|
72
|
+
<Separator />
|
|
73
|
+
</>
|
|
74
|
+
)}
|
|
75
|
+
<Unit size={size} value={timeLeft.hours} unit="hrs" />
|
|
76
|
+
<Separator />
|
|
77
|
+
<Unit size={size} value={timeLeft.minutes} unit="min" />
|
|
78
|
+
{showSeconds && (
|
|
79
|
+
<>
|
|
80
|
+
<Separator />
|
|
81
|
+
<Unit size={size} value={timeLeft.seconds} unit="sec" />
|
|
82
|
+
</>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
CountdownTimer.displayName = "CountdownTimer";
|
|
90
|
+
|
|
91
|
+
export { CountdownTimer };
|