@wealthx/shadcn 1.0.2 → 1.2.0
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/.turbo/turbo-build.log +235 -138
- package/CHANGELOG.md +12 -0
- package/README.md +82 -0
- package/dist/{chunk-6OJF6XRN.mjs → chunk-24FUO7TD.mjs} +4 -8
- package/dist/{chunk-4AJ5HWHD.mjs → chunk-2I5S2AMY.mjs} +3 -3
- package/dist/chunk-2SF672SZ.mjs +161 -0
- package/dist/{chunk-GPRJQ24C.mjs → chunk-34NWQURD.mjs} +2 -2
- package/dist/{chunk-MQ72DIBH.mjs → chunk-3GF7OVTP.mjs} +14 -5
- package/dist/chunk-3WMX6KWS.mjs +245 -0
- package/dist/{chunk-PMKODV6M.mjs → chunk-462HMNO4.mjs} +6 -10
- package/dist/chunk-4CX4SBRO.mjs +153 -0
- package/dist/chunk-4MN6UQHG.mjs +443 -0
- package/dist/chunk-5QQVZTVZ.mjs +233 -0
- package/dist/{chunk-BGP2N52Z.mjs → chunk-66MI7Q4B.mjs} +5 -5
- package/dist/chunk-6FCGKSZX.mjs +268 -0
- package/dist/{chunk-CGOKTPXU.mjs → chunk-6JQFUE5I.mjs} +20 -23
- package/dist/{chunk-Z3MK2KKZ.mjs → chunk-7DHU4VGG.mjs} +7 -3
- package/dist/{chunk-VZ2NR7L3.mjs → chunk-7PYJD5JI.mjs} +35 -27
- package/dist/{chunk-JU2RUWHF.mjs → chunk-7XJHLGUV.mjs} +1 -1
- package/dist/{chunk-BMFN37JH.mjs → chunk-7YAU5CY6.mjs} +1 -1
- package/dist/chunk-A56YQQHG.mjs +402 -0
- package/dist/chunk-AH52LG6N.mjs +315 -0
- package/dist/{chunk-SLWCCURD.mjs → chunk-CLIN5525.mjs} +8 -4
- package/dist/{chunk-3VQNJ235.mjs → chunk-CSDO6VBW.mjs} +7 -0
- package/dist/chunk-D4ILTPOG.mjs +293 -0
- package/dist/{chunk-HS7TFG7V.mjs → chunk-D6ID6M4V.mjs} +1 -1
- package/dist/chunk-DOH3EHX7.mjs +378 -0
- package/dist/{chunk-MJIEMGRD.mjs → chunk-EFRENWEJ.mjs} +9 -17
- package/dist/chunk-ERGGHC2V.mjs +185 -0
- package/dist/{chunk-OXQQNQZI.mjs → chunk-FEZKMUCF.mjs} +10 -1
- package/dist/{chunk-55CEW76V.mjs → chunk-FH6QVUVZ.mjs} +1 -1
- package/dist/chunk-FMAXJ2SI.mjs +71 -0
- package/dist/chunk-FZIXGLMV.mjs +173 -0
- package/dist/{chunk-DS2AMHN2.mjs → chunk-GYMYRIZP.mjs} +2 -2
- package/dist/{chunk-KQDD5MU3.mjs → chunk-H45TKD34.mjs} +5 -5
- package/dist/{chunk-BBJBJSXQ.mjs → chunk-J5UICVJS.mjs} +1 -1
- package/dist/{chunk-RL772EH7.mjs → chunk-JHJHG4GO.mjs} +4 -12
- package/dist/chunk-KMCGSZTX.mjs +177 -0
- package/dist/{chunk-FHNT55I5.mjs → chunk-KUDCQ4FI.mjs} +4 -4
- package/dist/chunk-LE6YFY6D.mjs +209 -0
- package/dist/{chunk-HUVTPUV2.mjs → chunk-LLVQKSU3.mjs} +23 -19
- package/dist/{chunk-KKHTJNMM.mjs → chunk-MARPPFOJ.mjs} +8 -4
- package/dist/{chunk-6AFMNC42.mjs → chunk-N2PT566P.mjs} +15 -11
- package/dist/chunk-NLCKVHWB.mjs +161 -0
- package/dist/{chunk-YN5SYTOO.mjs → chunk-NQPOYKAQ.mjs} +9 -5
- package/dist/{chunk-ZZV5JVNW.mjs → chunk-NSLMILBT.mjs} +3 -7
- package/dist/chunk-NXA3CZ7A.mjs +248 -0
- package/dist/chunk-OGOYQ7BG.mjs +150 -0
- package/dist/{chunk-3NQGYJEZ.mjs → chunk-P6AM5V7O.mjs} +10 -18
- package/dist/{chunk-CZ3BW5GL.mjs → chunk-P76HMUI6.mjs} +5 -11
- package/dist/chunk-PCPLO5HT.mjs +671 -0
- package/dist/chunk-PG6K5XEC.mjs +475 -0
- package/dist/chunk-PJHPSRYD.mjs +234 -0
- package/dist/{chunk-DDPA2XXS.mjs → chunk-PMB3A7V3.mjs} +2 -2
- package/dist/chunk-PR6V5XKM.mjs +209 -0
- package/dist/{chunk-46OFHMQA.mjs → chunk-Q76O3RIQ.mjs} +10 -6
- package/dist/chunk-QVKWW6KE.mjs +272 -0
- package/dist/chunk-RGU7HOEC.mjs +140 -0
- package/dist/{chunk-JF4PHPD5.mjs → chunk-RGVKLTLH.mjs} +4 -4
- package/dist/{chunk-VG6UF6UT.mjs → chunk-RP3SQYA3.mjs} +2 -2
- package/dist/chunk-RRBS6D63.mjs +163 -0
- package/dist/chunk-SMQ3DG25.mjs +285 -0
- package/dist/chunk-SPJ5KXW7.mjs +199 -0
- package/dist/chunk-SYOD63OZ.mjs +225 -0
- package/dist/chunk-UFYSFDER.mjs +42 -0
- package/dist/chunk-VACKZOMY.mjs +190 -0
- package/dist/chunk-VLQZANBF.mjs +42 -0
- package/dist/chunk-WA6O6EUR.mjs +1885 -0
- package/dist/{chunk-E3K6O4FZ.mjs → chunk-WAZD7NFU.mjs} +5 -2
- package/dist/chunk-WG6JGJXB.mjs +165 -0
- package/dist/{chunk-I64K754C.mjs → chunk-WNGWBVLV.mjs} +2 -2
- package/dist/{chunk-3U7SD3MS.mjs → chunk-WOEHFRGB.mjs} +3 -3
- package/dist/{chunk-DKZRJOMF.mjs → chunk-XIRTEFKH.mjs} +12 -12
- package/dist/chunk-Y6DWJSKZ.mjs +79 -0
- package/dist/chunk-YKPROFLB.mjs +161 -0
- package/dist/{chunk-K76E2TQU.mjs → chunk-ZRO5JO3H.mjs} +107 -67
- package/dist/{chunk-VYMHBV6D.mjs → chunk-ZU4NV6RG.mjs} +5 -3
- package/dist/components/ui/accordion.js +40 -4
- package/dist/components/ui/accordion.mjs +2 -2
- package/dist/components/ui/add-column-modal.js +789 -0
- package/dist/components/ui/add-column-modal.mjs +17 -0
- package/dist/components/ui/add-lead-modal.js +647 -0
- package/dist/components/ui/add-lead-modal.mjs +16 -0
- package/dist/components/ui/ai-assistant-drawer.js +686 -0
- package/dist/components/ui/ai-assistant-drawer.mjs +16 -0
- package/dist/components/ui/alert-dialog.js +37 -5
- package/dist/components/ui/alert-dialog.mjs +4 -4
- package/dist/components/ui/alert.js +37 -11
- package/dist/components/ui/alert.mjs +2 -2
- package/dist/components/ui/avatar.js +36 -8
- package/dist/components/ui/avatar.mjs +2 -2
- package/dist/components/ui/backoffice-alert-history-chart.js +624 -0
- package/dist/components/ui/backoffice-alert-history-chart.mjs +16 -0
- package/dist/components/ui/backoffice-contact-history-chart.js +687 -0
- package/dist/components/ui/backoffice-contact-history-chart.mjs +16 -0
- package/dist/components/ui/badge.js +37 -2
- package/dist/components/ui/badge.mjs +2 -2
- package/dist/components/ui/borrowing-capacity-line-chart.js +639 -0
- package/dist/components/ui/borrowing-capacity-line-chart.mjs +16 -0
- package/dist/components/ui/button.js +35 -3
- package/dist/components/ui/button.mjs +2 -2
- package/dist/components/ui/calendar.js +43 -19
- package/dist/components/ui/calendar.mjs +3 -3
- package/dist/components/ui/card.js +40 -4
- package/dist/components/ui/card.mjs +2 -2
- package/dist/components/ui/cash-balance-line-chart.js +627 -0
- package/dist/components/ui/cash-balance-line-chart.mjs +16 -0
- package/dist/components/ui/cashflow-bar-chart.js +650 -0
- package/dist/components/ui/cashflow-bar-chart.mjs +16 -0
- package/dist/components/ui/checkbox.js +36 -5
- package/dist/components/ui/checkbox.mjs +2 -3
- package/dist/components/ui/chip.js +37 -2
- package/dist/components/ui/chip.mjs +3 -3
- package/dist/components/ui/combobox.js +280 -0
- package/dist/components/ui/combobox.mjs +28 -0
- package/dist/components/ui/data-table.js +160 -88
- package/dist/components/ui/data-table.mjs +10 -11
- package/dist/components/ui/date-picker.js +44 -20
- package/dist/components/ui/date-picker.mjs +6 -7
- package/dist/components/ui/dialog.js +44 -12
- package/dist/components/ui/dialog.mjs +4 -4
- package/dist/components/ui/drawer.js +46 -10
- package/dist/components/ui/drawer.mjs +3 -3
- package/dist/components/ui/dropdown-menu.js +40 -16
- package/dist/components/ui/dropdown-menu.mjs +3 -3
- package/dist/components/ui/empty.js +41 -5
- package/dist/components/ui/empty.mjs +2 -2
- package/dist/components/ui/expense-bar-chart.js +642 -0
- package/dist/components/ui/expense-bar-chart.mjs +16 -0
- package/dist/components/ui/field.js +53 -21
- package/dist/components/ui/field.mjs +4 -4
- package/dist/components/ui/financial-cards.js +1002 -0
- package/dist/components/ui/financial-cards.mjs +24 -0
- package/dist/components/ui/financial-drawers.js +637 -0
- package/dist/components/ui/financial-drawers.mjs +17 -0
- package/dist/components/ui/financial-primitives.js +218 -0
- package/dist/components/ui/financial-primitives.mjs +22 -0
- package/dist/components/ui/financial-sections.js +1422 -0
- package/dist/components/ui/financial-sections.mjs +30 -0
- package/dist/components/ui/form-primitives.js +682 -0
- package/dist/components/ui/form-primitives.mjs +19 -0
- package/dist/components/ui/income-bar-chart.js +641 -0
- package/dist/components/ui/income-bar-chart.mjs +16 -0
- package/dist/components/ui/input-group.js +43 -7
- package/dist/components/ui/input-group.mjs +5 -5
- package/dist/components/ui/input-otp.js +39 -3
- package/dist/components/ui/input-otp.mjs +2 -2
- package/dist/components/ui/input.js +34 -2
- package/dist/components/ui/input.mjs +2 -2
- package/dist/components/ui/kanban-column.js +1143 -0
- package/dist/components/ui/kanban-column.mjs +20 -0
- package/dist/components/ui/label.js +35 -7
- package/dist/components/ui/label.mjs +2 -2
- package/dist/components/ui/opportunity-card.js +960 -0
- package/dist/components/ui/opportunity-card.mjs +20 -0
- package/dist/components/ui/opportunity-edit-modals.js +3360 -0
- package/dist/components/ui/opportunity-edit-modals.mjs +37 -0
- package/dist/components/ui/opportunity-summary-tab.js +4365 -0
- package/dist/components/ui/opportunity-summary-tab.mjs +34 -0
- package/dist/components/ui/pagination.js +35 -3
- package/dist/components/ui/pagination.mjs +3 -3
- package/dist/components/ui/pipeline-alerts.js +103 -0
- package/dist/components/ui/pipeline-alerts.mjs +8 -0
- package/dist/components/ui/pipeline-board.js +1408 -0
- package/dist/components/ui/pipeline-board.mjs +24 -0
- package/dist/components/ui/pipeline-chart.js +216 -0
- package/dist/components/ui/pipeline-chart.mjs +10 -0
- package/dist/components/ui/pipeline-dialogs.js +1183 -0
- package/dist/components/ui/pipeline-dialogs.mjs +23 -0
- package/dist/components/ui/pipeline-primitives.js +300 -0
- package/dist/components/ui/pipeline-primitives.mjs +11 -0
- package/dist/components/ui/popover.js +45 -4
- package/dist/components/ui/popover.mjs +3 -3
- package/dist/components/ui/progress.js +33 -1
- package/dist/components/ui/progress.mjs +2 -2
- package/dist/components/ui/property-cashflow-doughnut-chart.js +523 -0
- package/dist/components/ui/property-cashflow-doughnut-chart.mjs +16 -0
- package/dist/components/ui/property-debt-equity-doughnut-chart.js +521 -0
- package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +16 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.js +682 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.mjs +16 -0
- package/dist/components/ui/radio-group.js +33 -1
- package/dist/components/ui/radio-group.mjs +2 -2
- package/dist/components/ui/select.js +66 -26
- package/dist/components/ui/select.mjs +3 -3
- package/dist/components/ui/separator.js +33 -1
- package/dist/components/ui/separator.mjs +2 -2
- package/dist/components/ui/sheet.js +37 -9
- package/dist/components/ui/sheet.mjs +3 -3
- package/dist/components/ui/skeleton.js +33 -1
- package/dist/components/ui/skeleton.mjs +2 -2
- package/dist/components/ui/slider.js +86 -102
- package/dist/components/ui/slider.mjs +2 -2
- package/dist/components/ui/spinner.js +33 -1
- package/dist/components/ui/spinner.mjs +2 -2
- package/dist/components/ui/stage-timeline.js +579 -0
- package/dist/components/ui/stage-timeline.mjs +15 -0
- package/dist/components/ui/switch.js +37 -4
- package/dist/components/ui/switch.mjs +2 -3
- package/dist/components/ui/table.js +37 -5
- package/dist/components/ui/table.mjs +2 -2
- package/dist/components/ui/tabs.js +36 -12
- package/dist/components/ui/tabs.mjs +2 -2
- package/dist/components/ui/textarea.js +34 -2
- package/dist/components/ui/textarea.mjs +2 -2
- package/dist/components/ui/toggle-group.js +35 -4
- package/dist/components/ui/toggle-group.mjs +3 -4
- package/dist/components/ui/toggle.js +35 -4
- package/dist/components/ui/toggle.mjs +2 -3
- package/dist/components/ui/tooltip.js +51 -22
- package/dist/components/ui/tooltip.mjs +3 -3
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +528 -0
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +16 -0
- package/dist/components/ui/transactions-income-expense-bar-chart.js +516 -0
- package/dist/components/ui/transactions-income-expense-bar-chart.mjs +16 -0
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +528 -0
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +16 -0
- package/dist/index.js +11613 -2868
- package/dist/index.mjs +377 -164
- package/dist/lib/theme-provider.js +10 -1
- package/dist/lib/theme-provider.mjs +1 -1
- package/dist/lib/typography.js +8 -0
- package/dist/lib/typography.mjs +3 -1
- package/dist/lib/utils.js +33 -1
- package/dist/lib/utils.mjs +1 -1
- package/dist/styles.css +1 -1
- package/package.json +169 -6
- package/src/components/index.tsx +323 -13
- package/src/components/ui/accordion.tsx +6 -3
- package/src/components/ui/add-column-modal.tsx +339 -0
- package/src/components/ui/add-lead-modal.tsx +290 -0
- package/src/components/ui/ai-assistant-drawer.tsx +408 -0
- package/src/components/ui/alert-dialog.tsx +80 -54
- package/src/components/ui/alert.tsx +28 -28
- package/src/components/ui/avatar.tsx +30 -29
- package/src/components/ui/backoffice-alert-history-chart.tsx +260 -0
- package/src/components/ui/backoffice-contact-history-chart.tsx +325 -0
- package/src/components/ui/badge.tsx +17 -15
- package/src/components/ui/borrowing-capacity-line-chart.tsx +357 -0
- package/src/components/ui/button.tsx +30 -27
- package/src/components/ui/calendar.tsx +53 -67
- package/src/components/ui/card.tsx +27 -24
- package/src/components/ui/cash-balance-line-chart.tsx +302 -0
- package/src/components/ui/cashflow-bar-chart.tsx +363 -0
- package/src/components/ui/chart-shared.tsx +261 -0
- package/src/components/ui/checkbox.tsx +30 -26
- package/src/components/ui/combobox.tsx +223 -0
- package/src/components/ui/data-table.tsx +160 -99
- package/src/components/ui/date-picker.tsx +0 -2
- package/src/components/ui/dialog.tsx +70 -60
- package/src/components/ui/drawer.tsx +57 -48
- package/src/components/ui/dropdown-menu.tsx +90 -82
- package/src/components/ui/empty.tsx +31 -27
- package/src/components/ui/expense-bar-chart.tsx +296 -0
- package/src/components/ui/field.tsx +70 -62
- package/src/components/ui/financial-cards.tsx +830 -0
- package/src/components/ui/financial-drawers.tsx +339 -0
- package/src/components/ui/financial-primitives.tsx +331 -0
- package/src/components/ui/financial-sections.tsx +672 -0
- package/src/components/ui/form-primitives.tsx +536 -0
- package/src/components/ui/income-bar-chart.tsx +297 -0
- package/src/components/ui/input-group.tsx +41 -34
- package/src/components/ui/input-otp.tsx +29 -24
- package/src/components/ui/input.tsx +8 -8
- package/src/components/ui/kanban-column.tsx +333 -0
- package/src/components/ui/label.tsx +9 -12
- package/src/components/ui/opportunity-card.tsx +616 -0
- package/src/components/ui/opportunity-edit-modals.tsx +2528 -0
- package/src/components/ui/opportunity-summary-tab.tsx +579 -0
- package/src/components/ui/pipeline-alerts.tsx +74 -0
- package/src/components/ui/pipeline-board.tsx +268 -0
- package/src/components/ui/pipeline-chart.tsx +173 -0
- package/src/components/ui/pipeline-dialogs.tsx +303 -0
- package/src/components/ui/pipeline-primitives.tsx +108 -0
- package/src/components/ui/popover.tsx +41 -36
- package/src/components/ui/property-cashflow-doughnut-chart.tsx +188 -0
- package/src/components/ui/property-debt-equity-doughnut-chart.tsx +185 -0
- package/src/components/ui/property-mobile-estimate-line-chart.tsx +393 -0
- package/src/components/ui/select.tsx +65 -52
- package/src/components/ui/sheet.tsx +55 -52
- package/src/components/ui/slider.tsx +54 -77
- package/src/components/ui/stage-timeline.tsx +205 -0
- package/src/components/ui/switch.tsx +42 -29
- package/src/components/ui/table.tsx +28 -28
- package/src/components/ui/tabs.tsx +22 -28
- package/src/components/ui/textarea.tsx +8 -8
- package/src/components/ui/toggle-group.tsx +0 -2
- package/src/components/ui/toggle.tsx +13 -15
- package/src/components/ui/tooltip.tsx +30 -28
- package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +191 -0
- package/src/components/ui/transactions-income-expense-bar-chart.tsx +205 -0
- package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +191 -0
- package/src/lib/theme-provider.tsx +10 -0
- package/src/lib/typography.ts +9 -0
- package/src/lib/utils.ts +41 -3
- package/src/styles/globals.css +371 -124
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +32 -0
- package/dist/chunk-K74JRTJR.mjs +0 -105
- package/dist/chunk-V7CNWJT3.mjs +0 -10
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Chart as ChartJS,
|
|
4
|
+
CategoryScale,
|
|
5
|
+
LinearScale,
|
|
6
|
+
BarElement,
|
|
7
|
+
Tooltip,
|
|
8
|
+
Legend,
|
|
9
|
+
type ChartOptions,
|
|
10
|
+
type ChartData,
|
|
11
|
+
} from "chart.js";
|
|
12
|
+
import { Chart } from "react-chartjs-2";
|
|
13
|
+
import { useThemeVars } from "@/lib/theme-provider";
|
|
14
|
+
import { Card, CardContent, CardHeader, CardTitle, CardAction } from "./card";
|
|
15
|
+
import { Empty, EmptyDescription } from "./empty";
|
|
16
|
+
import { Skeleton } from "./skeleton";
|
|
17
|
+
import { cn } from "@/lib/utils";
|
|
18
|
+
import {
|
|
19
|
+
hexToRgba,
|
|
20
|
+
DATASET_ALPHAS,
|
|
21
|
+
FALLBACK_TICK,
|
|
22
|
+
FALLBACK_SECONDARY,
|
|
23
|
+
CHART_SLICE_COUNT,
|
|
24
|
+
CHART_PERIODS,
|
|
25
|
+
formatTooltipDate,
|
|
26
|
+
ChartLegendItem,
|
|
27
|
+
ChartPeriodButton,
|
|
28
|
+
type ChartPeriod,
|
|
29
|
+
type ChartGranularity,
|
|
30
|
+
} from "./chart-shared";
|
|
31
|
+
|
|
32
|
+
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip, Legend);
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Types
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
export interface ExpenseDataset {
|
|
39
|
+
/** Expense category label, e.g. "Housing", "Food", "Transport" */
|
|
40
|
+
label: string;
|
|
41
|
+
/** One value per data point, aligned to the months array */
|
|
42
|
+
data: number[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ExpenseBarChartData {
|
|
46
|
+
/** Display labels, e.g. ["Jul", "Aug"] for monthly or ["Mar 8", "Mar 9"] for daily */
|
|
47
|
+
months: string[];
|
|
48
|
+
/** Optional ISO date strings per point — used for the tooltip title */
|
|
49
|
+
dates?: string[];
|
|
50
|
+
datasets: ExpenseDataset[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type ExpensePeriod = ChartPeriod;
|
|
54
|
+
export type ExpenseGranularity = ChartGranularity;
|
|
55
|
+
|
|
56
|
+
export interface ExpenseBarChartProps {
|
|
57
|
+
/** Full dataset — sliced to the selected period */
|
|
58
|
+
expenseData: ExpenseBarChartData | null;
|
|
59
|
+
title?: string;
|
|
60
|
+
/** Show or hide the chart legend */
|
|
61
|
+
showLegend?: boolean;
|
|
62
|
+
/** Show or hide X axis labels */
|
|
63
|
+
showXAxis?: boolean;
|
|
64
|
+
/** Show or hide Y axis labels */
|
|
65
|
+
showYAxis?: boolean;
|
|
66
|
+
/** Legend placement relative to chart */
|
|
67
|
+
legendPosition?: "top" | "bottom";
|
|
68
|
+
/** Default period selector value */
|
|
69
|
+
defaultPeriod?: ExpensePeriod;
|
|
70
|
+
/**
|
|
71
|
+
* Data granularity — controls available period buttons and slice counts.
|
|
72
|
+
* "monthly" (default): shows 3M / 6M / 12M, slices by month count.
|
|
73
|
+
* "daily": shows 1M / 3M / 6M / 12M, slices by day count (1M=30, 3M=90, etc.).
|
|
74
|
+
*/
|
|
75
|
+
granularity?: ExpenseGranularity;
|
|
76
|
+
/** Chart canvas height in pixels */
|
|
77
|
+
height?: number;
|
|
78
|
+
/** Width of the card in pixels */
|
|
79
|
+
width?: number | string;
|
|
80
|
+
className?: string;
|
|
81
|
+
/** Show skeleton loading state instead of the chart */
|
|
82
|
+
isLoading?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Component
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
export function ExpenseBarChart({
|
|
91
|
+
expenseData,
|
|
92
|
+
title = "Expenses",
|
|
93
|
+
showLegend = true,
|
|
94
|
+
showXAxis = true,
|
|
95
|
+
showYAxis = true,
|
|
96
|
+
legendPosition = "top",
|
|
97
|
+
defaultPeriod = 6,
|
|
98
|
+
granularity = "monthly",
|
|
99
|
+
height = 280,
|
|
100
|
+
width = "100%",
|
|
101
|
+
className,
|
|
102
|
+
isLoading = false,
|
|
103
|
+
}: ExpenseBarChartProps) {
|
|
104
|
+
const periods = CHART_PERIODS[granularity];
|
|
105
|
+
const [period, setPeriod] = useState<ExpensePeriod>(defaultPeriod);
|
|
106
|
+
|
|
107
|
+
// Reset period when granularity changes, but not on initial mount
|
|
108
|
+
// (defaultPeriod handles the initial value).
|
|
109
|
+
const isFirstRender = useRef(true);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (isFirstRender.current) {
|
|
112
|
+
isFirstRender.current = false;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
setPeriod(CHART_PERIODS[granularity][0]);
|
|
116
|
+
}, [granularity]);
|
|
117
|
+
|
|
118
|
+
const themeVars = useThemeVars();
|
|
119
|
+
const brandSecondary: string =
|
|
120
|
+
(themeVars["--theme-secondary"] as string | undefined) ||
|
|
121
|
+
FALLBACK_SECONDARY;
|
|
122
|
+
const fontFamily: string =
|
|
123
|
+
(themeVars["--font-sans"] as string | undefined) || "Figtree, sans-serif";
|
|
124
|
+
|
|
125
|
+
const sliced = useMemo<ExpenseBarChartData | null>(() => {
|
|
126
|
+
if (!expenseData?.months?.length || !expenseData.datasets.length)
|
|
127
|
+
return null;
|
|
128
|
+
const count = Math.min(
|
|
129
|
+
CHART_SLICE_COUNT[granularity][period],
|
|
130
|
+
expenseData.months.length,
|
|
131
|
+
);
|
|
132
|
+
const start = expenseData.months.length - count;
|
|
133
|
+
return {
|
|
134
|
+
months: expenseData.months.slice(start),
|
|
135
|
+
dates: expenseData.dates?.slice(start),
|
|
136
|
+
datasets: expenseData.datasets.map((ds) => ({
|
|
137
|
+
...ds,
|
|
138
|
+
data: ds.data.slice(start),
|
|
139
|
+
})),
|
|
140
|
+
};
|
|
141
|
+
}, [expenseData, period, granularity]);
|
|
142
|
+
|
|
143
|
+
const datasetColors = useMemo(
|
|
144
|
+
() =>
|
|
145
|
+
sliced?.datasets.map((_, i) =>
|
|
146
|
+
hexToRgba(brandSecondary, DATASET_ALPHAS[i % DATASET_ALPHAS.length]),
|
|
147
|
+
) ?? [],
|
|
148
|
+
[sliced, brandSecondary],
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const chartData = useMemo<ChartData<"bar">>(() => {
|
|
152
|
+
if (!sliced) return { labels: [], datasets: [] };
|
|
153
|
+
return {
|
|
154
|
+
labels: sliced.months,
|
|
155
|
+
datasets: sliced.datasets.map((ds, i) => ({
|
|
156
|
+
label: ds.label,
|
|
157
|
+
data: ds.data,
|
|
158
|
+
backgroundColor: datasetColors[i],
|
|
159
|
+
hoverBackgroundColor: hexToRgba(
|
|
160
|
+
brandSecondary,
|
|
161
|
+
Math.min(DATASET_ALPHAS[i % DATASET_ALPHAS.length] + 0.15, 1),
|
|
162
|
+
),
|
|
163
|
+
borderWidth: 0,
|
|
164
|
+
borderRadius: 0,
|
|
165
|
+
borderSkipped: false,
|
|
166
|
+
barPercentage: 0.75,
|
|
167
|
+
categoryPercentage: 0.7,
|
|
168
|
+
stack: "expense",
|
|
169
|
+
})),
|
|
170
|
+
};
|
|
171
|
+
}, [sliced, datasetColors, brandSecondary]);
|
|
172
|
+
|
|
173
|
+
const options = useMemo<ChartOptions<"bar">>(
|
|
174
|
+
() => ({
|
|
175
|
+
responsive: true,
|
|
176
|
+
maintainAspectRatio: false,
|
|
177
|
+
animation: { duration: 800, easing: "easeOutQuart" },
|
|
178
|
+
layout: { padding: 0 },
|
|
179
|
+
plugins: {
|
|
180
|
+
legend: { display: false },
|
|
181
|
+
tooltip: {
|
|
182
|
+
mode: "index",
|
|
183
|
+
intersect: false,
|
|
184
|
+
padding: 12,
|
|
185
|
+
cornerRadius: 0,
|
|
186
|
+
titleFont: { size: 11, weight: 600 },
|
|
187
|
+
bodyFont: { size: 12, weight: 500 },
|
|
188
|
+
callbacks: {
|
|
189
|
+
title: (tooltipItems) => {
|
|
190
|
+
const idx = tooltipItems[0]?.dataIndex;
|
|
191
|
+
if (idx != null && sliced?.dates?.[idx]) {
|
|
192
|
+
return formatTooltipDate(sliced.dates[idx], granularity);
|
|
193
|
+
}
|
|
194
|
+
return tooltipItems[0]?.label ?? "";
|
|
195
|
+
},
|
|
196
|
+
label: (ctx) => {
|
|
197
|
+
const val = ctx.raw as number;
|
|
198
|
+
if (val === 0) return;
|
|
199
|
+
return ` ${ctx.dataset.label}: $${val.toLocaleString()}`;
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
scales: {
|
|
205
|
+
x: {
|
|
206
|
+
display: showXAxis,
|
|
207
|
+
stacked: true,
|
|
208
|
+
grid: { display: false },
|
|
209
|
+
border: { display: false },
|
|
210
|
+
ticks: { font: { size: 10 }, color: FALLBACK_TICK },
|
|
211
|
+
},
|
|
212
|
+
y: {
|
|
213
|
+
display: showYAxis,
|
|
214
|
+
stacked: true,
|
|
215
|
+
grid: { display: false },
|
|
216
|
+
border: { display: false },
|
|
217
|
+
ticks: {
|
|
218
|
+
font: { size: 10 },
|
|
219
|
+
color: FALLBACK_TICK,
|
|
220
|
+
maxTicksLimit: 5,
|
|
221
|
+
padding: 8,
|
|
222
|
+
callback: (v) => `$${Number(v).toLocaleString()}`,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
[showXAxis, showYAxis, sliced, granularity],
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<Card
|
|
232
|
+
className={cn("w-full py-4 sm:py-6 gap-2", className)}
|
|
233
|
+
style={{ maxWidth: width, fontFamily }}
|
|
234
|
+
>
|
|
235
|
+
<CardHeader className="px-3 sm:px-6">
|
|
236
|
+
<CardTitle className="text-sm sm:text-base">{title}</CardTitle>
|
|
237
|
+
<CardAction>
|
|
238
|
+
<div className="flex gap-0.5 sm:gap-1">
|
|
239
|
+
{periods.map((p) => (
|
|
240
|
+
<ChartPeriodButton
|
|
241
|
+
key={p}
|
|
242
|
+
period={p}
|
|
243
|
+
active={period === p}
|
|
244
|
+
onClick={() => setPeriod(p)}
|
|
245
|
+
/>
|
|
246
|
+
))}
|
|
247
|
+
</div>
|
|
248
|
+
</CardAction>
|
|
249
|
+
</CardHeader>
|
|
250
|
+
|
|
251
|
+
<CardContent className="px-3 sm:px-6">
|
|
252
|
+
{isLoading ? (
|
|
253
|
+
<Skeleton style={{ height, width: "100%" }} />
|
|
254
|
+
) : !sliced ? (
|
|
255
|
+
<Empty className="flex-none p-4" style={{ height }}>
|
|
256
|
+
<EmptyDescription>No data available</EmptyDescription>
|
|
257
|
+
</Empty>
|
|
258
|
+
) : (
|
|
259
|
+
<div className="flex flex-col gap-2">
|
|
260
|
+
{showLegend && legendPosition === "top" && (
|
|
261
|
+
<div className="flex flex-wrap gap-x-3 gap-y-1.5 pb-2">
|
|
262
|
+
{sliced.datasets.map((ds, i) => (
|
|
263
|
+
<ChartLegendItem
|
|
264
|
+
key={ds.label}
|
|
265
|
+
label={ds.label}
|
|
266
|
+
color={datasetColors[i]}
|
|
267
|
+
/>
|
|
268
|
+
))}
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
<div style={{ height, width: "100%", position: "relative" }}>
|
|
272
|
+
<Chart
|
|
273
|
+
key={brandSecondary}
|
|
274
|
+
type="bar"
|
|
275
|
+
data={chartData}
|
|
276
|
+
options={options}
|
|
277
|
+
aria-label={title}
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
{showLegend && legendPosition === "bottom" && (
|
|
281
|
+
<div className="flex flex-wrap gap-x-3 gap-y-1.5 pt-2">
|
|
282
|
+
{sliced.datasets.map((ds, i) => (
|
|
283
|
+
<ChartLegendItem
|
|
284
|
+
key={ds.label}
|
|
285
|
+
label={ds.label}
|
|
286
|
+
color={datasetColors[i]}
|
|
287
|
+
/>
|
|
288
|
+
))}
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
291
|
+
</div>
|
|
292
|
+
)}
|
|
293
|
+
</CardContent>
|
|
294
|
+
</Card>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type ReactElement, useMemo } from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
4
|
-
import { Label } from "@/components/ui/label"
|
|
5
|
-
import { Separator } from "@/components/ui/separator"
|
|
1
|
+
import { type ReactElement, useMemo } from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { Label } from "@/components/ui/label";
|
|
5
|
+
import { Separator } from "@/components/ui/separator";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Field — shadcn/WealthX
|
|
@@ -14,7 +14,7 @@ import { Separator } from "@/components/ui/separator"
|
|
|
14
14
|
* - FieldError.uniqueErrors: Array.from() instead of spread on Map iterator (es5 compat)
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
export type FieldSetProps = React.ComponentProps<"fieldset"
|
|
17
|
+
export type FieldSetProps = React.ComponentProps<"fieldset">;
|
|
18
18
|
|
|
19
19
|
function FieldSet({ className, ...props }: FieldSetProps): ReactElement {
|
|
20
20
|
return (
|
|
@@ -22,15 +22,17 @@ function FieldSet({ className, ...props }: FieldSetProps): ReactElement {
|
|
|
22
22
|
className={cn(
|
|
23
23
|
"flex flex-col gap-6",
|
|
24
24
|
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
|
|
25
|
-
className
|
|
25
|
+
className,
|
|
26
26
|
)}
|
|
27
27
|
data-slot="field-set"
|
|
28
28
|
{...props}
|
|
29
29
|
/>
|
|
30
|
-
)
|
|
30
|
+
);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export type FieldLegendProps = React.ComponentProps<"legend"> & {
|
|
33
|
+
export type FieldLegendProps = React.ComponentProps<"legend"> & {
|
|
34
|
+
variant?: "legend" | "label";
|
|
35
|
+
};
|
|
34
36
|
|
|
35
37
|
function FieldLegend({
|
|
36
38
|
className,
|
|
@@ -40,31 +42,31 @@ function FieldLegend({
|
|
|
40
42
|
return (
|
|
41
43
|
<legend
|
|
42
44
|
className={cn(
|
|
43
|
-
"mb-3
|
|
44
|
-
"data-[variant=legend]:text-
|
|
45
|
-
"data-[variant=label]:text-
|
|
46
|
-
className
|
|
45
|
+
"mb-3",
|
|
46
|
+
"data-[variant=legend]:text-label-large",
|
|
47
|
+
"data-[variant=label]:text-label-medium",
|
|
48
|
+
className,
|
|
47
49
|
)}
|
|
48
50
|
data-slot="field-legend"
|
|
49
51
|
data-variant={variant}
|
|
50
52
|
{...props}
|
|
51
53
|
/>
|
|
52
|
-
)
|
|
54
|
+
);
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
export type FieldGroupProps = React.ComponentProps<"div"
|
|
57
|
+
export type FieldGroupProps = React.ComponentProps<"div">;
|
|
56
58
|
|
|
57
59
|
function FieldGroup({ className, ...props }: FieldGroupProps): ReactElement {
|
|
58
60
|
return (
|
|
59
61
|
<div
|
|
60
62
|
className={cn(
|
|
61
63
|
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
|
|
62
|
-
className
|
|
64
|
+
className,
|
|
63
65
|
)}
|
|
64
66
|
data-slot="field-group"
|
|
65
67
|
{...props}
|
|
66
68
|
/>
|
|
67
|
-
)
|
|
69
|
+
);
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
const fieldVariants = cva(
|
|
@@ -88,10 +90,11 @@ const fieldVariants = cva(
|
|
|
88
90
|
defaultVariants: {
|
|
89
91
|
orientation: "vertical",
|
|
90
92
|
},
|
|
91
|
-
}
|
|
92
|
-
)
|
|
93
|
+
},
|
|
94
|
+
);
|
|
93
95
|
|
|
94
|
-
export type FieldProps = React.ComponentProps<"div"> &
|
|
96
|
+
export type FieldProps = React.ComponentProps<"div"> &
|
|
97
|
+
VariantProps<typeof fieldVariants>;
|
|
95
98
|
|
|
96
99
|
function Field({
|
|
97
100
|
className,
|
|
@@ -106,79 +109,82 @@ function Field({
|
|
|
106
109
|
role="group"
|
|
107
110
|
{...props}
|
|
108
111
|
/>
|
|
109
|
-
)
|
|
112
|
+
);
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
export type FieldContentProps = React.ComponentProps<"div"
|
|
115
|
+
export type FieldContentProps = React.ComponentProps<"div">;
|
|
113
116
|
|
|
114
|
-
function FieldContent({
|
|
117
|
+
function FieldContent({
|
|
118
|
+
className,
|
|
119
|
+
...props
|
|
120
|
+
}: FieldContentProps): ReactElement {
|
|
115
121
|
return (
|
|
116
122
|
<div
|
|
117
123
|
className={cn(
|
|
118
124
|
"group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
|
|
119
|
-
className
|
|
125
|
+
className,
|
|
120
126
|
)}
|
|
121
127
|
data-slot="field-content"
|
|
122
128
|
{...props}
|
|
123
129
|
/>
|
|
124
|
-
)
|
|
130
|
+
);
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
export type FieldLabelProps = React.ComponentProps<typeof Label
|
|
133
|
+
export type FieldLabelProps = React.ComponentProps<typeof Label>;
|
|
128
134
|
|
|
129
|
-
function FieldLabel({
|
|
130
|
-
className,
|
|
131
|
-
...props
|
|
132
|
-
}: FieldLabelProps): ReactElement {
|
|
135
|
+
function FieldLabel({ className, ...props }: FieldLabelProps): ReactElement {
|
|
133
136
|
return (
|
|
134
137
|
<Label
|
|
135
138
|
className={cn(
|
|
136
139
|
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
137
140
|
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
|
|
138
141
|
"has-data-checked:border-primary has-data-checked:bg-primary/5 dark:has-data-checked:bg-primary/10",
|
|
139
|
-
className
|
|
142
|
+
className,
|
|
140
143
|
)}
|
|
141
144
|
data-slot="field-label"
|
|
142
145
|
{...props}
|
|
143
146
|
/>
|
|
144
|
-
)
|
|
147
|
+
);
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
export type FieldTitleProps = React.ComponentProps<"div"
|
|
150
|
+
export type FieldTitleProps = React.ComponentProps<"div">;
|
|
148
151
|
|
|
149
152
|
function FieldTitle({ className, ...props }: FieldTitleProps): ReactElement {
|
|
150
153
|
return (
|
|
151
154
|
<div
|
|
152
155
|
className={cn(
|
|
153
|
-
"flex w-fit items-center gap-2 text-
|
|
154
|
-
className
|
|
156
|
+
"flex w-fit items-center gap-2 text-label-medium leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
157
|
+
className,
|
|
155
158
|
)}
|
|
156
159
|
data-slot="field-label"
|
|
157
160
|
{...props}
|
|
158
161
|
/>
|
|
159
|
-
)
|
|
162
|
+
);
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
export type FieldDescriptionProps = React.ComponentProps<"p"
|
|
165
|
+
export type FieldDescriptionProps = React.ComponentProps<"p">;
|
|
163
166
|
|
|
164
|
-
function FieldDescription({
|
|
167
|
+
function FieldDescription({
|
|
168
|
+
className,
|
|
169
|
+
...props
|
|
170
|
+
}: FieldDescriptionProps): ReactElement {
|
|
165
171
|
return (
|
|
166
172
|
<p
|
|
167
173
|
className={cn(
|
|
168
|
-
"text-
|
|
174
|
+
"text-caption leading-normal text-muted-foreground group-has-[[data-orientation=horizontal]]/field:text-balance",
|
|
169
175
|
"last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
|
|
170
176
|
"[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
|
|
171
|
-
className
|
|
177
|
+
className,
|
|
172
178
|
)}
|
|
173
179
|
data-slot="field-description"
|
|
174
180
|
{...props}
|
|
175
181
|
/>
|
|
176
|
-
)
|
|
182
|
+
);
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
export type FieldSeparatorProps = React.ComponentProps<"div"> & {
|
|
180
|
-
children?: React.ReactNode
|
|
181
|
-
}
|
|
186
|
+
children?: React.ReactNode;
|
|
187
|
+
};
|
|
182
188
|
|
|
183
189
|
function FieldSeparator({
|
|
184
190
|
children,
|
|
@@ -188,27 +194,29 @@ function FieldSeparator({
|
|
|
188
194
|
return (
|
|
189
195
|
<div
|
|
190
196
|
className={cn(
|
|
191
|
-
"relative -my-2 h-5 text-
|
|
192
|
-
className
|
|
197
|
+
"relative -my-2 h-5 text-body-small group-data-[variant=outline]/field-group:-mb-2",
|
|
198
|
+
className,
|
|
193
199
|
)}
|
|
194
200
|
data-content={Boolean(children)}
|
|
195
201
|
data-slot="field-separator"
|
|
196
202
|
{...props}
|
|
197
203
|
>
|
|
198
204
|
<Separator className="absolute inset-0 top-1/2" />
|
|
199
|
-
{children ?
|
|
205
|
+
{children ? (
|
|
206
|
+
<span
|
|
200
207
|
className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
|
|
201
208
|
data-slot="field-separator-content"
|
|
202
209
|
>
|
|
203
210
|
{children}
|
|
204
|
-
</span>
|
|
211
|
+
</span>
|
|
212
|
+
) : null}
|
|
205
213
|
</div>
|
|
206
|
-
)
|
|
214
|
+
);
|
|
207
215
|
}
|
|
208
216
|
|
|
209
217
|
export type FieldErrorProps = React.ComponentProps<"div"> & {
|
|
210
|
-
errors?: ({ message?: string } | undefined)[]
|
|
211
|
-
}
|
|
218
|
+
errors?: ({ message?: string } | undefined)[];
|
|
219
|
+
};
|
|
212
220
|
|
|
213
221
|
function FieldError({
|
|
214
222
|
className,
|
|
@@ -218,45 +226,45 @@ function FieldError({
|
|
|
218
226
|
}: FieldErrorProps): ReactElement | null {
|
|
219
227
|
const content = useMemo(() => {
|
|
220
228
|
if (children) {
|
|
221
|
-
return children
|
|
229
|
+
return children;
|
|
222
230
|
}
|
|
223
231
|
|
|
224
232
|
if (!errors?.length) {
|
|
225
|
-
return null
|
|
233
|
+
return null;
|
|
226
234
|
}
|
|
227
235
|
|
|
228
236
|
const uniqueErrors = Array.from(
|
|
229
|
-
new Map(errors.map((error) => [error?.message, error])).values()
|
|
230
|
-
)
|
|
237
|
+
new Map(errors.map((error) => [error?.message, error])).values(),
|
|
238
|
+
);
|
|
231
239
|
|
|
232
240
|
if (uniqueErrors.length === 1) {
|
|
233
|
-
return uniqueErrors[0]?.message
|
|
241
|
+
return uniqueErrors[0]?.message;
|
|
234
242
|
}
|
|
235
243
|
|
|
236
244
|
return (
|
|
237
245
|
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
238
246
|
{uniqueErrors.map(
|
|
239
247
|
(error) =>
|
|
240
|
-
error?.message && <li key={error.message}>{error.message}</li
|
|
248
|
+
error?.message && <li key={error.message}>{error.message}</li>,
|
|
241
249
|
)}
|
|
242
250
|
</ul>
|
|
243
|
-
)
|
|
244
|
-
}, [children, errors])
|
|
251
|
+
);
|
|
252
|
+
}, [children, errors]);
|
|
245
253
|
|
|
246
254
|
if (!content) {
|
|
247
|
-
return null
|
|
255
|
+
return null;
|
|
248
256
|
}
|
|
249
257
|
|
|
250
258
|
return (
|
|
251
259
|
<div
|
|
252
|
-
className={cn("text-
|
|
260
|
+
className={cn("text-caption text-destructive", className)}
|
|
253
261
|
data-slot="field-error"
|
|
254
262
|
role="alert"
|
|
255
263
|
{...props}
|
|
256
264
|
>
|
|
257
265
|
{content}
|
|
258
266
|
</div>
|
|
259
|
-
)
|
|
267
|
+
);
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
export {
|
|
@@ -270,4 +278,4 @@ export {
|
|
|
270
278
|
FieldSet,
|
|
271
279
|
FieldContent,
|
|
272
280
|
FieldTitle,
|
|
273
|
-
}
|
|
281
|
+
};
|