@wealthx/shadcn 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +93 -84
- package/CHANGELOG.md +13 -0
- package/dist/{chunk-NXA3CZ7A.mjs → chunk-4Y6R4WEC.mjs} +2 -0
- package/dist/{chunk-AH52LG6N.mjs → chunk-7MMXNK3C.mjs} +2 -0
- package/dist/{chunk-4CX4SBRO.mjs → chunk-A6AAWBPF.mjs} +1 -1
- package/dist/{chunk-WOEHFRGB.mjs → chunk-BDYZCBRT.mjs} +4 -4
- package/dist/{chunk-5QQVZTVZ.mjs → chunk-BL3DXM2X.mjs} +2 -1
- package/dist/{chunk-PMB3A7V3.mjs → chunk-EI5F6FMT.mjs} +1 -1
- package/dist/{chunk-QVKWW6KE.mjs → chunk-GGM2UYGG.mjs} +2 -1
- package/dist/{chunk-KMCGSZTX.mjs → chunk-JNQORUPP.mjs} +2 -1
- package/dist/{chunk-PJHPSRYD.mjs → chunk-K3JYD4IU.mjs} +2 -1
- package/dist/{chunk-CSDO6VBW.mjs → chunk-LBMRIB3G.mjs} +10 -10
- package/dist/{chunk-SMQ3DG25.mjs → chunk-LHYCMLVA.mjs} +2 -1
- package/dist/{chunk-NLCKVHWB.mjs → chunk-OPNQAVVH.mjs} +2 -1
- package/dist/{chunk-2SF672SZ.mjs → chunk-RYCLWMZ7.mjs} +2 -1
- package/dist/{chunk-PG6K5XEC.mjs → chunk-S4QRUQNW.mjs} +1 -1
- package/dist/{chunk-YKPROFLB.mjs → chunk-SIZMLSRU.mjs} +2 -1
- package/dist/{chunk-LE6YFY6D.mjs → chunk-SWGT756Z.mjs} +2 -1
- package/dist/{chunk-6FCGKSZX.mjs → chunk-TS2ZX2VS.mjs} +2 -0
- package/dist/chunk-U4NDAF2P.mjs +207 -0
- package/dist/{chunk-DOH3EHX7.mjs → chunk-U5X52X37.mjs} +1 -1
- package/dist/{chunk-WA6O6EUR.mjs → chunk-URGMJAE3.mjs} +9 -9
- package/dist/{chunk-ZRO5JO3H.mjs → chunk-UT4KJR7V.mjs} +48 -12
- package/dist/{chunk-SYOD63OZ.mjs → chunk-VGSESELX.mjs} +2 -2
- package/dist/{chunk-RRBS6D63.mjs → chunk-VPBN3WOO.mjs} +2 -1
- package/dist/chunk-ZRSDX6OW.mjs +385 -0
- package/dist/chunk-ZSHYDDRB.mjs +249 -0
- package/dist/components/ui/add-column-modal.mjs +3 -3
- package/dist/components/ui/add-lead-modal.mjs +2 -2
- package/dist/components/ui/backoffice-alert-history-chart.js +1 -1
- package/dist/components/ui/backoffice-alert-history-chart.mjs +1 -1
- package/dist/components/ui/backoffice-contact-history-chart.js +1 -1
- package/dist/components/ui/backoffice-contact-history-chart.mjs +1 -1
- package/dist/components/ui/borrowing-capacity-line-chart.js +1 -0
- package/dist/components/ui/borrowing-capacity-line-chart.mjs +1 -1
- package/dist/components/ui/cash-balance-line-chart.js +1 -0
- package/dist/components/ui/cash-balance-line-chart.mjs +1 -1
- package/dist/components/ui/cashflow-bar-chart.js +1 -1
- package/dist/components/ui/cashflow-bar-chart.mjs +1 -1
- package/dist/components/ui/color-picker.js +417 -0
- package/dist/components/ui/color-picker.mjs +22 -0
- package/dist/components/ui/data-table.js +44 -12
- package/dist/components/ui/data-table.mjs +1 -1
- package/dist/components/ui/date-picker.mjs +2 -2
- package/dist/components/ui/expense-bar-chart.js +1 -1
- package/dist/components/ui/expense-bar-chart.mjs +1 -1
- package/dist/components/ui/form-primitives.js +4 -4
- package/dist/components/ui/form-primitives.mjs +3 -3
- package/dist/components/ui/income-bar-chart.js +1 -1
- package/dist/components/ui/income-bar-chart.mjs +1 -1
- package/dist/components/ui/opportunity-edit-modals.js +4 -4
- package/dist/components/ui/opportunity-edit-modals.mjs +8 -8
- package/dist/components/ui/opportunity-summary-tab.js +4 -4
- package/dist/components/ui/opportunity-summary-tab.mjs +9 -9
- package/dist/components/ui/pipeline-board.js +4 -4
- package/dist/components/ui/pipeline-board.mjs +3 -3
- package/dist/components/ui/pipeline-dialogs.js +4 -4
- package/dist/components/ui/pipeline-dialogs.mjs +5 -5
- package/dist/components/ui/property-cashflow-doughnut-chart.js +1 -1
- package/dist/components/ui/property-cashflow-doughnut-chart.mjs +1 -1
- package/dist/components/ui/property-debt-equity-doughnut-chart.js +1 -1
- package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +1 -1
- package/dist/components/ui/property-mobile-estimate-line-chart.js +1 -0
- package/dist/components/ui/property-mobile-estimate-line-chart.mjs +1 -1
- package/dist/components/ui/sidebar-nav.js +540 -0
- package/dist/components/ui/sidebar-nav.mjs +11 -0
- package/dist/components/ui/stepper.js +283 -0
- package/dist/components/ui/stepper.mjs +18 -0
- package/dist/components/ui/toggle-group.js +4 -4
- package/dist/components/ui/toggle-group.mjs +2 -2
- package/dist/components/ui/toggle.js +4 -4
- package/dist/components/ui/toggle.mjs +1 -1
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +1 -1
- package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +1 -1
- package/dist/components/ui/transactions-income-expense-bar-chart.js +1 -1
- package/dist/components/ui/transactions-income-expense-bar-chart.mjs +1 -1
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +1 -1
- package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +1 -1
- package/dist/index.js +2154 -1302
- package/dist/index.mjs +115 -83
- package/dist/lib/typography.js +10 -10
- package/dist/lib/typography.mjs +1 -1
- package/dist/styles.css +1 -1
- package/package.json +16 -1
- package/src/components/index.tsx +41 -0
- package/src/components/ui/backoffice-alert-history-chart.tsx +2 -1
- package/src/components/ui/backoffice-contact-history-chart.tsx +2 -1
- package/src/components/ui/borrowing-capacity-line-chart.tsx +2 -0
- package/src/components/ui/cash-balance-line-chart.tsx +2 -0
- package/src/components/ui/cashflow-bar-chart.tsx +2 -1
- package/src/components/ui/color-picker.tsx +307 -0
- package/src/components/ui/data-table.tsx +91 -11
- package/src/components/ui/expense-bar-chart.tsx +2 -1
- package/src/components/ui/income-bar-chart.tsx +2 -1
- package/src/components/ui/property-cashflow-doughnut-chart.tsx +2 -1
- package/src/components/ui/property-debt-equity-doughnut-chart.tsx +2 -1
- package/src/components/ui/property-mobile-estimate-line-chart.tsx +2 -0
- package/src/components/ui/sidebar-nav.tsx +517 -0
- package/src/components/ui/stepper.tsx +347 -0
- package/src/components/ui/toggle.tsx +4 -4
- package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +2 -1
- package/src/components/ui/transactions-income-expense-bar-chart.tsx +2 -1
- package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +2 -1
- package/src/lib/typography.ts +11 -11
- package/src/styles/globals.css +19 -19
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +3 -0
- package/dist/{chunk-KUDCQ4FI.mjs → chunk-5MEWU56Z.mjs} +3 -3
- package/dist/{chunk-PR6V5XKM.mjs → chunk-CGH4DRNG.mjs} +3 -3
- package/dist/{chunk-3WMX6KWS.mjs → chunk-Y4QFWRNR.mjs} +8 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wealthx/shadcn",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -248,6 +248,11 @@
|
|
|
248
248
|
"import": "./dist/components/ui/chip.mjs",
|
|
249
249
|
"require": "./dist/components/ui/chip.js"
|
|
250
250
|
},
|
|
251
|
+
"./color-picker": {
|
|
252
|
+
"types": "./src/components/ui/color-picker.tsx",
|
|
253
|
+
"import": "./dist/components/ui/color-picker.mjs",
|
|
254
|
+
"require": "./dist/components/ui/color-picker.js"
|
|
255
|
+
},
|
|
251
256
|
"./combobox": {
|
|
252
257
|
"types": "./src/components/ui/combobox.tsx",
|
|
253
258
|
"import": "./dist/components/ui/combobox.mjs",
|
|
@@ -413,6 +418,11 @@
|
|
|
413
418
|
"import": "./dist/components/ui/stage-timeline.mjs",
|
|
414
419
|
"require": "./dist/components/ui/stage-timeline.js"
|
|
415
420
|
},
|
|
421
|
+
"./stepper": {
|
|
422
|
+
"types": "./src/components/ui/stepper.tsx",
|
|
423
|
+
"import": "./dist/components/ui/stepper.mjs",
|
|
424
|
+
"require": "./dist/components/ui/stepper.js"
|
|
425
|
+
},
|
|
416
426
|
"./ai-assistant-drawer": {
|
|
417
427
|
"types": "./src/components/ui/ai-assistant-drawer.tsx",
|
|
418
428
|
"import": "./dist/components/ui/ai-assistant-drawer.mjs",
|
|
@@ -432,6 +442,11 @@
|
|
|
432
442
|
"types": "./src/components/ui/opportunity-summary-tab.tsx",
|
|
433
443
|
"import": "./dist/components/ui/opportunity-summary-tab.mjs",
|
|
434
444
|
"require": "./dist/components/ui/opportunity-summary-tab.js"
|
|
445
|
+
},
|
|
446
|
+
"./sidebar-nav": {
|
|
447
|
+
"types": "./src/components/ui/sidebar-nav.tsx",
|
|
448
|
+
"import": "./dist/components/ui/sidebar-nav.mjs",
|
|
449
|
+
"require": "./dist/components/ui/sidebar-nav.js"
|
|
435
450
|
}
|
|
436
451
|
}
|
|
437
452
|
}
|
package/src/components/index.tsx
CHANGED
|
@@ -153,6 +153,20 @@ export type { CheckboxProps, CheckboxCardProps } from "./ui/checkbox";
|
|
|
153
153
|
export { Chip } from "./ui/chip";
|
|
154
154
|
export type { ChipProps } from "./ui/chip";
|
|
155
155
|
|
|
156
|
+
export {
|
|
157
|
+
ColorPicker,
|
|
158
|
+
ColorPickerContent,
|
|
159
|
+
ColorSwatch,
|
|
160
|
+
COLOR_PICKER_PRESETS,
|
|
161
|
+
isValidHex,
|
|
162
|
+
normalizeHex,
|
|
163
|
+
} from "./ui/color-picker";
|
|
164
|
+
export type {
|
|
165
|
+
ColorPickerProps,
|
|
166
|
+
ColorPickerContentProps,
|
|
167
|
+
ColorSwatchProps,
|
|
168
|
+
} from "./ui/color-picker";
|
|
169
|
+
|
|
156
170
|
export {
|
|
157
171
|
Combobox,
|
|
158
172
|
ComboboxTrigger,
|
|
@@ -191,6 +205,7 @@ export {
|
|
|
191
205
|
export type {
|
|
192
206
|
DataTableProps,
|
|
193
207
|
ColumnDef,
|
|
208
|
+
PaginationState,
|
|
194
209
|
SortingState,
|
|
195
210
|
ColumnFiltersState,
|
|
196
211
|
VisibilityState,
|
|
@@ -639,6 +654,15 @@ export type {
|
|
|
639
654
|
export { Separator } from "./ui/separator";
|
|
640
655
|
export type { SeparatorProps } from "./ui/separator";
|
|
641
656
|
|
|
657
|
+
export { SidebarNav } from "./ui/sidebar-nav";
|
|
658
|
+
export type {
|
|
659
|
+
SidebarNavProps,
|
|
660
|
+
SidebarNavSubItem,
|
|
661
|
+
SidebarNavItem,
|
|
662
|
+
SidebarNavMetricItem,
|
|
663
|
+
SidebarNavMetricsGroup,
|
|
664
|
+
} from "./ui/sidebar-nav";
|
|
665
|
+
|
|
642
666
|
export {
|
|
643
667
|
Sheet,
|
|
644
668
|
SheetClose,
|
|
@@ -680,6 +704,23 @@ export type {
|
|
|
680
704
|
StageTimelineTask,
|
|
681
705
|
} from "./ui/stage-timeline";
|
|
682
706
|
|
|
707
|
+
export {
|
|
708
|
+
Stepper,
|
|
709
|
+
Step,
|
|
710
|
+
StepIndicator,
|
|
711
|
+
StepLabel,
|
|
712
|
+
StepContent,
|
|
713
|
+
StepItem,
|
|
714
|
+
} from "./ui/stepper";
|
|
715
|
+
export type {
|
|
716
|
+
StepperProps,
|
|
717
|
+
StepProps,
|
|
718
|
+
StepIndicatorProps,
|
|
719
|
+
StepLabelProps,
|
|
720
|
+
StepContentProps,
|
|
721
|
+
StepItemProps,
|
|
722
|
+
} from "./ui/stepper";
|
|
723
|
+
|
|
683
724
|
export { Switch, SwitchCard } from "./ui/switch";
|
|
684
725
|
export type { SwitchProps, SwitchCardProps } from "./ui/switch";
|
|
685
726
|
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Chart as ChartJS,
|
|
4
4
|
CategoryScale,
|
|
5
5
|
LinearScale,
|
|
6
|
+
BarController,
|
|
6
7
|
BarElement,
|
|
7
8
|
Tooltip,
|
|
8
9
|
type ChartOptions,
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
ChartPeriodButton,
|
|
25
26
|
} from "./chart-shared";
|
|
26
27
|
|
|
27
|
-
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);
|
|
28
|
+
ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip);
|
|
28
29
|
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
30
31
|
// Types
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Chart as ChartJS,
|
|
4
4
|
CategoryScale,
|
|
5
5
|
LinearScale,
|
|
6
|
+
BarController,
|
|
6
7
|
BarElement,
|
|
7
8
|
Tooltip,
|
|
8
9
|
type ChartOptions,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
ChartPeriodButton,
|
|
26
27
|
} from "./chart-shared";
|
|
27
28
|
|
|
28
|
-
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);
|
|
29
|
+
ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip);
|
|
29
30
|
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
31
32
|
// Types
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Chart as ChartJS,
|
|
4
4
|
CategoryScale,
|
|
5
5
|
LinearScale,
|
|
6
|
+
LineController,
|
|
6
7
|
LineElement,
|
|
7
8
|
PointElement,
|
|
8
9
|
Tooltip,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
ChartJS.register(
|
|
29
30
|
CategoryScale,
|
|
30
31
|
LinearScale,
|
|
32
|
+
LineController,
|
|
31
33
|
LineElement,
|
|
32
34
|
PointElement,
|
|
33
35
|
Tooltip,
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Chart as ChartJS,
|
|
4
4
|
CategoryScale,
|
|
5
5
|
LinearScale,
|
|
6
|
+
LineController,
|
|
6
7
|
LineElement,
|
|
7
8
|
PointElement,
|
|
8
9
|
Tooltip,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
ChartJS.register(
|
|
26
27
|
CategoryScale,
|
|
27
28
|
LinearScale,
|
|
29
|
+
LineController,
|
|
28
30
|
LineElement,
|
|
29
31
|
PointElement,
|
|
30
32
|
Tooltip,
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Chart as ChartJS,
|
|
4
4
|
CategoryScale,
|
|
5
5
|
LinearScale,
|
|
6
|
+
BarController,
|
|
6
7
|
BarElement,
|
|
7
8
|
Tooltip,
|
|
8
9
|
Legend,
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
ChartPeriodButton,
|
|
25
26
|
} from "./chart-shared";
|
|
26
27
|
|
|
27
|
-
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip, Legend);
|
|
28
|
+
ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip, Legend);
|
|
28
29
|
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
30
31
|
// Types
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import {
|
|
4
|
+
Popover,
|
|
5
|
+
PopoverContent,
|
|
6
|
+
PopoverTrigger,
|
|
7
|
+
} from "@/components/ui/popover";
|
|
8
|
+
import { Input } from "@/components/ui/input";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ColorPicker — WealthX Design System
|
|
12
|
+
*
|
|
13
|
+
* Popover-based color picker with preset swatches and a hex input field.
|
|
14
|
+
* Designed for tenant white-label brand color configuration.
|
|
15
|
+
*
|
|
16
|
+
* Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Preset palettes
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export const COLOR_PICKER_PRESETS = [
|
|
24
|
+
// Blues
|
|
25
|
+
"#1E40AF",
|
|
26
|
+
"#2563EB",
|
|
27
|
+
"#3B82F6",
|
|
28
|
+
"#60A5FA",
|
|
29
|
+
// Purples
|
|
30
|
+
"#7C3AED",
|
|
31
|
+
"#8B5CF6",
|
|
32
|
+
"#A78BFA",
|
|
33
|
+
"#C4B5FD",
|
|
34
|
+
// Greens
|
|
35
|
+
"#15803D",
|
|
36
|
+
"#16A34A",
|
|
37
|
+
"#22C55E",
|
|
38
|
+
"#4ADE80",
|
|
39
|
+
// Reds
|
|
40
|
+
"#B91C1C",
|
|
41
|
+
"#DC2626",
|
|
42
|
+
"#EF4444",
|
|
43
|
+
"#F87171",
|
|
44
|
+
// Oranges
|
|
45
|
+
"#C2410C",
|
|
46
|
+
"#EA580C",
|
|
47
|
+
"#F97316",
|
|
48
|
+
"#FB923C",
|
|
49
|
+
// Teals
|
|
50
|
+
"#0F766E",
|
|
51
|
+
"#0D9488",
|
|
52
|
+
"#14B8A6",
|
|
53
|
+
"#2DD4BF",
|
|
54
|
+
// Neutrals
|
|
55
|
+
"#111827",
|
|
56
|
+
"#374151",
|
|
57
|
+
"#6B7280",
|
|
58
|
+
"#D1D5DB",
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Utility
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
/** Returns true if the string is a valid 6-digit or 3-digit hex color. */
|
|
66
|
+
function isValidHex(value: string): boolean {
|
|
67
|
+
return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Ensures value always has a leading `#`. */
|
|
71
|
+
function normalizeHex(value: string): string {
|
|
72
|
+
const stripped = value.replace(/^#/, "");
|
|
73
|
+
return `#${stripped}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// ColorSwatch
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
interface ColorSwatchProps {
|
|
81
|
+
color: string;
|
|
82
|
+
selected?: boolean;
|
|
83
|
+
size?: "sm" | "md";
|
|
84
|
+
onClick?: (color: string) => void;
|
|
85
|
+
className?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ColorSwatch({
|
|
89
|
+
color,
|
|
90
|
+
selected,
|
|
91
|
+
size = "md",
|
|
92
|
+
onClick,
|
|
93
|
+
className,
|
|
94
|
+
}: ColorSwatchProps) {
|
|
95
|
+
return (
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
title={color}
|
|
99
|
+
aria-label={`Select color ${color}`}
|
|
100
|
+
aria-pressed={selected}
|
|
101
|
+
onClick={() => onClick?.(color)}
|
|
102
|
+
className={cn(
|
|
103
|
+
"relative shrink-0 transition-all outline-none shadow-[inset_0_0_0_1px_rgba(0,0,0,0.12)]",
|
|
104
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
105
|
+
size === "md" ? "size-7" : "size-5",
|
|
106
|
+
selected &&
|
|
107
|
+
"ring-2 ring-foreground ring-offset-1 ring-offset-background",
|
|
108
|
+
className,
|
|
109
|
+
)}
|
|
110
|
+
style={{ backgroundColor: color }}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// ColorPickerContent (inner panel shown in popover)
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
interface ColorPickerContentProps {
|
|
120
|
+
value: string;
|
|
121
|
+
onChange: (color: string) => void;
|
|
122
|
+
presets?: string[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function ColorPickerContent({
|
|
126
|
+
value,
|
|
127
|
+
onChange,
|
|
128
|
+
presets = COLOR_PICKER_PRESETS,
|
|
129
|
+
}: ColorPickerContentProps) {
|
|
130
|
+
const [hexInput, setHexInput] = React.useState(value);
|
|
131
|
+
|
|
132
|
+
React.useEffect(() => {
|
|
133
|
+
setHexInput(value);
|
|
134
|
+
}, [value]);
|
|
135
|
+
|
|
136
|
+
function handleHexInputChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
137
|
+
const raw = e.target.value;
|
|
138
|
+
setHexInput(raw);
|
|
139
|
+
const normalized = normalizeHex(raw);
|
|
140
|
+
if (isValidHex(normalized)) {
|
|
141
|
+
onChange(normalized);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function handleHexBlur() {
|
|
146
|
+
const normalized = normalizeHex(hexInput);
|
|
147
|
+
if (isValidHex(normalized)) {
|
|
148
|
+
setHexInput(normalized);
|
|
149
|
+
if (normalized !== value) onChange(normalized);
|
|
150
|
+
} else {
|
|
151
|
+
setHexInput(value);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const normalizedInput = normalizeHex(hexInput);
|
|
156
|
+
const isValid = isValidHex(normalizedInput);
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div data-slot="color-picker-content" className="flex flex-col gap-4">
|
|
160
|
+
<div>
|
|
161
|
+
<p className="mb-2 text-xs font-medium text-muted-foreground">
|
|
162
|
+
Presets
|
|
163
|
+
</p>
|
|
164
|
+
<div className="grid grid-cols-7 gap-1.5">
|
|
165
|
+
{presets.map((color) => (
|
|
166
|
+
<ColorSwatch
|
|
167
|
+
key={color}
|
|
168
|
+
color={color}
|
|
169
|
+
selected={value.toLowerCase() === color.toLowerCase()}
|
|
170
|
+
onClick={onChange}
|
|
171
|
+
/>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div>
|
|
177
|
+
<p className="mb-2 text-xs font-medium text-muted-foreground">
|
|
178
|
+
Custom hex
|
|
179
|
+
</p>
|
|
180
|
+
<div className="flex items-center gap-2">
|
|
181
|
+
<label
|
|
182
|
+
className="relative size-9 shrink-0 cursor-pointer border border-border"
|
|
183
|
+
title="Open color picker"
|
|
184
|
+
style={{
|
|
185
|
+
backgroundColor: isValid ? normalizedInput : value,
|
|
186
|
+
}}
|
|
187
|
+
>
|
|
188
|
+
<input
|
|
189
|
+
type="color"
|
|
190
|
+
aria-label="Native color picker"
|
|
191
|
+
value={isValid ? normalizedInput : value}
|
|
192
|
+
onChange={(e) => {
|
|
193
|
+
setHexInput(e.target.value);
|
|
194
|
+
onChange(e.target.value);
|
|
195
|
+
}}
|
|
196
|
+
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
|
197
|
+
/>
|
|
198
|
+
</label>
|
|
199
|
+
<Input
|
|
200
|
+
aria-label="Hex color value"
|
|
201
|
+
placeholder="#000000"
|
|
202
|
+
value={hexInput}
|
|
203
|
+
onChange={handleHexInputChange}
|
|
204
|
+
onBlur={handleHexBlur}
|
|
205
|
+
maxLength={7}
|
|
206
|
+
className="h-9 flex-1 font-mono text-sm uppercase"
|
|
207
|
+
/>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// ColorPicker (popover trigger + content)
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
interface ColorPickerProps {
|
|
219
|
+
/** The current color value (hex string, e.g. `"#3B82F6"`). */
|
|
220
|
+
value?: string;
|
|
221
|
+
/** Default value when uncontrolled. */
|
|
222
|
+
defaultValue?: string;
|
|
223
|
+
/** Called whenever the selected color changes. */
|
|
224
|
+
onChange?: (color: string) => void;
|
|
225
|
+
/** Preset colors shown in the swatch grid. Defaults to `COLOR_PICKER_PRESETS`. */
|
|
226
|
+
presets?: string[];
|
|
227
|
+
/** Disable the picker. */
|
|
228
|
+
disabled?: boolean;
|
|
229
|
+
/** Optional label shown above the trigger. */
|
|
230
|
+
label?: string;
|
|
231
|
+
className?: string;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function ColorPicker({
|
|
235
|
+
value: controlledValue,
|
|
236
|
+
defaultValue = "#3B82F6",
|
|
237
|
+
onChange,
|
|
238
|
+
presets,
|
|
239
|
+
disabled,
|
|
240
|
+
label,
|
|
241
|
+
className,
|
|
242
|
+
}: ColorPickerProps) {
|
|
243
|
+
const isControlled = controlledValue !== undefined;
|
|
244
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
245
|
+
const color = isControlled ? controlledValue : internalValue;
|
|
246
|
+
const [open, setOpen] = React.useState(false);
|
|
247
|
+
|
|
248
|
+
React.useEffect(() => {
|
|
249
|
+
if (disabled) setOpen(false);
|
|
250
|
+
}, [disabled]);
|
|
251
|
+
|
|
252
|
+
function handleChange(newColor: string) {
|
|
253
|
+
if (!isControlled) setInternalValue(newColor);
|
|
254
|
+
onChange?.(newColor);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<div
|
|
259
|
+
data-slot="color-picker"
|
|
260
|
+
className={cn("inline-flex flex-col gap-1.5 font-sans", className)}
|
|
261
|
+
>
|
|
262
|
+
{label && (
|
|
263
|
+
<span className="text-sm font-medium text-foreground">{label}</span>
|
|
264
|
+
)}
|
|
265
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
266
|
+
<PopoverTrigger
|
|
267
|
+
disabled={disabled}
|
|
268
|
+
className={cn(
|
|
269
|
+
"flex h-9 min-w-[140px] cursor-pointer items-center gap-2.5 border border-input bg-background px-3 text-sm outline-none transition-colors",
|
|
270
|
+
"hover:border-ring",
|
|
271
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
272
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
273
|
+
)}
|
|
274
|
+
>
|
|
275
|
+
<span
|
|
276
|
+
aria-hidden
|
|
277
|
+
className="size-5 shrink-0 border border-border/50"
|
|
278
|
+
style={{ backgroundColor: color }}
|
|
279
|
+
/>
|
|
280
|
+
<span className="font-mono text-xs uppercase tracking-wide">
|
|
281
|
+
{color}
|
|
282
|
+
</span>
|
|
283
|
+
</PopoverTrigger>
|
|
284
|
+
<PopoverContent
|
|
285
|
+
className="w-[220px] p-4"
|
|
286
|
+
align="start"
|
|
287
|
+
data-shadcn-scope
|
|
288
|
+
>
|
|
289
|
+
<ColorPickerContent
|
|
290
|
+
value={color}
|
|
291
|
+
onChange={handleChange}
|
|
292
|
+
presets={presets}
|
|
293
|
+
/>
|
|
294
|
+
</PopoverContent>
|
|
295
|
+
</Popover>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export {
|
|
301
|
+
ColorPicker,
|
|
302
|
+
ColorPickerContent,
|
|
303
|
+
ColorSwatch,
|
|
304
|
+
isValidHex,
|
|
305
|
+
normalizeHex,
|
|
306
|
+
};
|
|
307
|
+
export type { ColorPickerProps, ColorPickerContentProps, ColorSwatchProps };
|
|
@@ -19,6 +19,7 @@ import * as React from "react";
|
|
|
19
19
|
import {
|
|
20
20
|
type ColumnDef,
|
|
21
21
|
type ColumnFiltersState,
|
|
22
|
+
type PaginationState,
|
|
22
23
|
type SortingState,
|
|
23
24
|
type VisibilityState,
|
|
24
25
|
type RowSelectionState,
|
|
@@ -74,7 +75,7 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|
|
74
75
|
// Types
|
|
75
76
|
// ---------------------------------------------------------------------------
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
interface DataTableProps<TData, TValue> {
|
|
78
79
|
/** Column definitions — pass ColumnDef[] from \@tanstack/react-table */
|
|
79
80
|
columns: ColumnDef<TData, TValue>[];
|
|
80
81
|
/** Row data */
|
|
@@ -101,13 +102,35 @@ export interface DataTableProps<TData, TValue> {
|
|
|
101
102
|
toolbar?: (table: TanstackTable<TData>) => React.ReactNode;
|
|
102
103
|
/** Text shown when no results found */
|
|
103
104
|
emptyText?: string;
|
|
105
|
+
/** Enable server-side pagination (disables client-side pagination model) */
|
|
106
|
+
manualPagination?: boolean;
|
|
107
|
+
/** Enable server-side sorting (disables client-side sorting model) */
|
|
108
|
+
manualSorting?: boolean;
|
|
109
|
+
/** Enable server-side filtering (disables client-side filtering model) */
|
|
110
|
+
manualFiltering?: boolean;
|
|
111
|
+
/** Total page count (required for server-side pagination) */
|
|
112
|
+
pageCount?: number;
|
|
113
|
+
/** Total row count (alternative to pageCount for server-side pagination) */
|
|
114
|
+
rowCount?: number;
|
|
115
|
+
/** Controlled sorting state */
|
|
116
|
+
sorting?: SortingState;
|
|
117
|
+
/** Controlled pagination state */
|
|
118
|
+
pagination?: PaginationState;
|
|
119
|
+
/** Controlled column filters state */
|
|
120
|
+
columnFilters?: ColumnFiltersState;
|
|
121
|
+
/** Callback when sorting state changes */
|
|
122
|
+
onSortingChange?: (sorting: SortingState) => void;
|
|
123
|
+
/** Callback when pagination state changes */
|
|
124
|
+
onPaginationChange?: (pagination: PaginationState) => void;
|
|
125
|
+
/** Callback when column filters state changes */
|
|
126
|
+
onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
|
|
104
127
|
}
|
|
105
128
|
|
|
106
129
|
// ---------------------------------------------------------------------------
|
|
107
130
|
// Helper: sortable header
|
|
108
131
|
// ---------------------------------------------------------------------------
|
|
109
132
|
|
|
110
|
-
|
|
133
|
+
function DataTableColumnHeader({
|
|
111
134
|
column,
|
|
112
135
|
title,
|
|
113
136
|
className,
|
|
@@ -157,7 +180,7 @@ export function DataTableColumnHeader({
|
|
|
157
180
|
// Helper: selection column
|
|
158
181
|
// ---------------------------------------------------------------------------
|
|
159
182
|
|
|
160
|
-
|
|
183
|
+
function getSelectionColumn<TData>(): ColumnDef<TData> {
|
|
161
184
|
return {
|
|
162
185
|
id: "select",
|
|
163
186
|
header: ({ table }) => (
|
|
@@ -412,15 +435,38 @@ function DataTable<TData, TValue>({
|
|
|
412
435
|
className,
|
|
413
436
|
toolbar,
|
|
414
437
|
emptyText = "No results.",
|
|
438
|
+
manualPagination = false,
|
|
439
|
+
manualSorting = false,
|
|
440
|
+
manualFiltering = false,
|
|
441
|
+
pageCount,
|
|
442
|
+
rowCount,
|
|
443
|
+
sorting: controlledSorting,
|
|
444
|
+
pagination: controlledPagination,
|
|
445
|
+
columnFilters: controlledColumnFilters,
|
|
446
|
+
onSortingChange: onSortingChangeProp,
|
|
447
|
+
onPaginationChange: onPaginationChangeProp,
|
|
448
|
+
onColumnFiltersChange: onColumnFiltersChangeProp,
|
|
415
449
|
}: DataTableProps<TData, TValue>): ReactElement {
|
|
416
|
-
|
|
417
|
-
const [
|
|
450
|
+
// Internal state (used when not externally controlled)
|
|
451
|
+
const [internalSorting, setInternalSorting] = React.useState<SortingState>(
|
|
418
452
|
[],
|
|
419
453
|
);
|
|
454
|
+
const [internalColumnFilters, setInternalColumnFilters] =
|
|
455
|
+
React.useState<ColumnFiltersState>([]);
|
|
456
|
+
const [internalPagination, setInternalPagination] =
|
|
457
|
+
React.useState<PaginationState>({
|
|
458
|
+
pageIndex: 0,
|
|
459
|
+
pageSize: pageSizeOptions[0] ?? 10,
|
|
460
|
+
});
|
|
420
461
|
const [columnVisibility, setColumnVisibility] =
|
|
421
462
|
React.useState<VisibilityState>({});
|
|
422
463
|
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
|
|
423
464
|
|
|
465
|
+
// Resolve controlled vs uncontrolled
|
|
466
|
+
const sorting = controlledSorting ?? internalSorting;
|
|
467
|
+
const columnFilters = controlledColumnFilters ?? internalColumnFilters;
|
|
468
|
+
const pagination = controlledPagination ?? internalPagination;
|
|
469
|
+
|
|
424
470
|
// Prepend selection column if enabled
|
|
425
471
|
const resolvedColumns = React.useMemo(() => {
|
|
426
472
|
if (!enableRowSelection) return userColumns;
|
|
@@ -438,9 +484,26 @@ function DataTable<TData, TValue>({
|
|
|
438
484
|
columnFilters,
|
|
439
485
|
columnVisibility,
|
|
440
486
|
rowSelection,
|
|
487
|
+
pagination,
|
|
488
|
+
},
|
|
489
|
+
onSortingChange: (updater) => {
|
|
490
|
+
const next = typeof updater === "function" ? updater(sorting) : updater;
|
|
491
|
+
if (controlledSorting === undefined) setInternalSorting(next);
|
|
492
|
+
onSortingChangeProp?.(next);
|
|
493
|
+
},
|
|
494
|
+
onColumnFiltersChange: (updater) => {
|
|
495
|
+
const next =
|
|
496
|
+
typeof updater === "function" ? updater(columnFilters) : updater;
|
|
497
|
+
if (controlledColumnFilters === undefined)
|
|
498
|
+
setInternalColumnFilters(next);
|
|
499
|
+
onColumnFiltersChangeProp?.(next);
|
|
500
|
+
},
|
|
501
|
+
onPaginationChange: (updater) => {
|
|
502
|
+
const next =
|
|
503
|
+
typeof updater === "function" ? updater(pagination) : updater;
|
|
504
|
+
if (controlledPagination === undefined) setInternalPagination(next);
|
|
505
|
+
onPaginationChangeProp?.(next);
|
|
441
506
|
},
|
|
442
|
-
onSortingChange: setSorting,
|
|
443
|
-
onColumnFiltersChange: setColumnFilters,
|
|
444
507
|
onColumnVisibilityChange: setColumnVisibility,
|
|
445
508
|
onRowSelectionChange: (updater) => {
|
|
446
509
|
const next =
|
|
@@ -449,9 +512,17 @@ function DataTable<TData, TValue>({
|
|
|
449
512
|
onRowSelectionChange?.(next);
|
|
450
513
|
},
|
|
451
514
|
getCoreRowModel: getCoreRowModel(),
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
515
|
+
...(manualFiltering
|
|
516
|
+
? { manualFiltering: true as const }
|
|
517
|
+
: { getFilteredRowModel: getFilteredRowModel() }),
|
|
518
|
+
...(manualPagination
|
|
519
|
+
? { manualPagination: true as const }
|
|
520
|
+
: { getPaginationRowModel: getPaginationRowModel() }),
|
|
521
|
+
...(manualSorting
|
|
522
|
+
? { manualSorting: true as const }
|
|
523
|
+
: { getSortedRowModel: getSortedRowModel() }),
|
|
524
|
+
...(pageCount !== undefined && { pageCount }),
|
|
525
|
+
...(rowCount !== undefined && { rowCount }),
|
|
455
526
|
enableRowSelection,
|
|
456
527
|
});
|
|
457
528
|
|
|
@@ -539,11 +610,20 @@ function DataTable<TData, TValue>({
|
|
|
539
610
|
);
|
|
540
611
|
}
|
|
541
612
|
|
|
542
|
-
export {
|
|
613
|
+
export {
|
|
614
|
+
DataTable,
|
|
615
|
+
DataTableToolbar,
|
|
616
|
+
DataTablePagination,
|
|
617
|
+
DataTableSkeleton,
|
|
618
|
+
DataTableColumnHeader,
|
|
619
|
+
getSelectionColumn,
|
|
620
|
+
};
|
|
543
621
|
|
|
544
622
|
// Re-export tanstack types for consumer convenience
|
|
545
623
|
export type {
|
|
624
|
+
DataTableProps,
|
|
546
625
|
ColumnDef,
|
|
626
|
+
PaginationState,
|
|
547
627
|
SortingState,
|
|
548
628
|
ColumnFiltersState,
|
|
549
629
|
VisibilityState,
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Chart as ChartJS,
|
|
4
4
|
CategoryScale,
|
|
5
5
|
LinearScale,
|
|
6
|
+
BarController,
|
|
6
7
|
BarElement,
|
|
7
8
|
Tooltip,
|
|
8
9
|
Legend,
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
type ChartGranularity,
|
|
30
31
|
} from "./chart-shared";
|
|
31
32
|
|
|
32
|
-
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip, Legend);
|
|
33
|
+
ChartJS.register(CategoryScale, LinearScale, BarController, BarElement, Tooltip, Legend);
|
|
33
34
|
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
35
36
|
// Types
|