@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.
Files changed (110) hide show
  1. package/.turbo/turbo-build.log +93 -84
  2. package/CHANGELOG.md +13 -0
  3. package/dist/{chunk-NXA3CZ7A.mjs → chunk-4Y6R4WEC.mjs} +2 -0
  4. package/dist/{chunk-AH52LG6N.mjs → chunk-7MMXNK3C.mjs} +2 -0
  5. package/dist/{chunk-4CX4SBRO.mjs → chunk-A6AAWBPF.mjs} +1 -1
  6. package/dist/{chunk-WOEHFRGB.mjs → chunk-BDYZCBRT.mjs} +4 -4
  7. package/dist/{chunk-5QQVZTVZ.mjs → chunk-BL3DXM2X.mjs} +2 -1
  8. package/dist/{chunk-PMB3A7V3.mjs → chunk-EI5F6FMT.mjs} +1 -1
  9. package/dist/{chunk-QVKWW6KE.mjs → chunk-GGM2UYGG.mjs} +2 -1
  10. package/dist/{chunk-KMCGSZTX.mjs → chunk-JNQORUPP.mjs} +2 -1
  11. package/dist/{chunk-PJHPSRYD.mjs → chunk-K3JYD4IU.mjs} +2 -1
  12. package/dist/{chunk-CSDO6VBW.mjs → chunk-LBMRIB3G.mjs} +10 -10
  13. package/dist/{chunk-SMQ3DG25.mjs → chunk-LHYCMLVA.mjs} +2 -1
  14. package/dist/{chunk-NLCKVHWB.mjs → chunk-OPNQAVVH.mjs} +2 -1
  15. package/dist/{chunk-2SF672SZ.mjs → chunk-RYCLWMZ7.mjs} +2 -1
  16. package/dist/{chunk-PG6K5XEC.mjs → chunk-S4QRUQNW.mjs} +1 -1
  17. package/dist/{chunk-YKPROFLB.mjs → chunk-SIZMLSRU.mjs} +2 -1
  18. package/dist/{chunk-LE6YFY6D.mjs → chunk-SWGT756Z.mjs} +2 -1
  19. package/dist/{chunk-6FCGKSZX.mjs → chunk-TS2ZX2VS.mjs} +2 -0
  20. package/dist/chunk-U4NDAF2P.mjs +207 -0
  21. package/dist/{chunk-DOH3EHX7.mjs → chunk-U5X52X37.mjs} +1 -1
  22. package/dist/{chunk-WA6O6EUR.mjs → chunk-URGMJAE3.mjs} +9 -9
  23. package/dist/{chunk-ZRO5JO3H.mjs → chunk-UT4KJR7V.mjs} +48 -12
  24. package/dist/{chunk-SYOD63OZ.mjs → chunk-VGSESELX.mjs} +2 -2
  25. package/dist/{chunk-RRBS6D63.mjs → chunk-VPBN3WOO.mjs} +2 -1
  26. package/dist/chunk-ZRSDX6OW.mjs +385 -0
  27. package/dist/chunk-ZSHYDDRB.mjs +249 -0
  28. package/dist/components/ui/add-column-modal.mjs +3 -3
  29. package/dist/components/ui/add-lead-modal.mjs +2 -2
  30. package/dist/components/ui/backoffice-alert-history-chart.js +1 -1
  31. package/dist/components/ui/backoffice-alert-history-chart.mjs +1 -1
  32. package/dist/components/ui/backoffice-contact-history-chart.js +1 -1
  33. package/dist/components/ui/backoffice-contact-history-chart.mjs +1 -1
  34. package/dist/components/ui/borrowing-capacity-line-chart.js +1 -0
  35. package/dist/components/ui/borrowing-capacity-line-chart.mjs +1 -1
  36. package/dist/components/ui/cash-balance-line-chart.js +1 -0
  37. package/dist/components/ui/cash-balance-line-chart.mjs +1 -1
  38. package/dist/components/ui/cashflow-bar-chart.js +1 -1
  39. package/dist/components/ui/cashflow-bar-chart.mjs +1 -1
  40. package/dist/components/ui/color-picker.js +417 -0
  41. package/dist/components/ui/color-picker.mjs +22 -0
  42. package/dist/components/ui/data-table.js +44 -12
  43. package/dist/components/ui/data-table.mjs +1 -1
  44. package/dist/components/ui/date-picker.mjs +2 -2
  45. package/dist/components/ui/expense-bar-chart.js +1 -1
  46. package/dist/components/ui/expense-bar-chart.mjs +1 -1
  47. package/dist/components/ui/form-primitives.js +4 -4
  48. package/dist/components/ui/form-primitives.mjs +3 -3
  49. package/dist/components/ui/income-bar-chart.js +1 -1
  50. package/dist/components/ui/income-bar-chart.mjs +1 -1
  51. package/dist/components/ui/opportunity-edit-modals.js +4 -4
  52. package/dist/components/ui/opportunity-edit-modals.mjs +8 -8
  53. package/dist/components/ui/opportunity-summary-tab.js +4 -4
  54. package/dist/components/ui/opportunity-summary-tab.mjs +9 -9
  55. package/dist/components/ui/pipeline-board.js +4 -4
  56. package/dist/components/ui/pipeline-board.mjs +3 -3
  57. package/dist/components/ui/pipeline-dialogs.js +4 -4
  58. package/dist/components/ui/pipeline-dialogs.mjs +5 -5
  59. package/dist/components/ui/property-cashflow-doughnut-chart.js +1 -1
  60. package/dist/components/ui/property-cashflow-doughnut-chart.mjs +1 -1
  61. package/dist/components/ui/property-debt-equity-doughnut-chart.js +1 -1
  62. package/dist/components/ui/property-debt-equity-doughnut-chart.mjs +1 -1
  63. package/dist/components/ui/property-mobile-estimate-line-chart.js +1 -0
  64. package/dist/components/ui/property-mobile-estimate-line-chart.mjs +1 -1
  65. package/dist/components/ui/sidebar-nav.js +540 -0
  66. package/dist/components/ui/sidebar-nav.mjs +11 -0
  67. package/dist/components/ui/stepper.js +283 -0
  68. package/dist/components/ui/stepper.mjs +18 -0
  69. package/dist/components/ui/toggle-group.js +4 -4
  70. package/dist/components/ui/toggle-group.mjs +2 -2
  71. package/dist/components/ui/toggle.js +4 -4
  72. package/dist/components/ui/toggle.mjs +1 -1
  73. package/dist/components/ui/transactions-expense-categories-doughnut-chart.js +1 -1
  74. package/dist/components/ui/transactions-expense-categories-doughnut-chart.mjs +1 -1
  75. package/dist/components/ui/transactions-income-expense-bar-chart.js +1 -1
  76. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +1 -1
  77. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.js +1 -1
  78. package/dist/components/ui/transactions-liabilities-breakdown-doughnut-chart.mjs +1 -1
  79. package/dist/index.js +2154 -1302
  80. package/dist/index.mjs +115 -83
  81. package/dist/lib/typography.js +10 -10
  82. package/dist/lib/typography.mjs +1 -1
  83. package/dist/styles.css +1 -1
  84. package/package.json +16 -1
  85. package/src/components/index.tsx +41 -0
  86. package/src/components/ui/backoffice-alert-history-chart.tsx +2 -1
  87. package/src/components/ui/backoffice-contact-history-chart.tsx +2 -1
  88. package/src/components/ui/borrowing-capacity-line-chart.tsx +2 -0
  89. package/src/components/ui/cash-balance-line-chart.tsx +2 -0
  90. package/src/components/ui/cashflow-bar-chart.tsx +2 -1
  91. package/src/components/ui/color-picker.tsx +307 -0
  92. package/src/components/ui/data-table.tsx +91 -11
  93. package/src/components/ui/expense-bar-chart.tsx +2 -1
  94. package/src/components/ui/income-bar-chart.tsx +2 -1
  95. package/src/components/ui/property-cashflow-doughnut-chart.tsx +2 -1
  96. package/src/components/ui/property-debt-equity-doughnut-chart.tsx +2 -1
  97. package/src/components/ui/property-mobile-estimate-line-chart.tsx +2 -0
  98. package/src/components/ui/sidebar-nav.tsx +517 -0
  99. package/src/components/ui/stepper.tsx +347 -0
  100. package/src/components/ui/toggle.tsx +4 -4
  101. package/src/components/ui/transactions-expense-categories-doughnut-chart.tsx +2 -1
  102. package/src/components/ui/transactions-income-expense-bar-chart.tsx +2 -1
  103. package/src/components/ui/transactions-liabilities-breakdown-doughnut-chart.tsx +2 -1
  104. package/src/lib/typography.ts +11 -11
  105. package/src/styles/globals.css +19 -19
  106. package/src/styles/styles-css.ts +1 -1
  107. package/tsup.config.ts +3 -0
  108. package/dist/{chunk-KUDCQ4FI.mjs → chunk-5MEWU56Z.mjs} +3 -3
  109. package/dist/{chunk-PR6V5XKM.mjs → chunk-CGH4DRNG.mjs} +3 -3
  110. 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.0",
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
  }
@@ -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
- export interface DataTableProps<TData, TValue> {
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
- export function DataTableColumnHeader({
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
- export function getSelectionColumn<TData>(): ColumnDef<TData> {
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
- const [sorting, setSorting] = React.useState<SortingState>([]);
417
- const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
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
- getFilteredRowModel: getFilteredRowModel(),
453
- getPaginationRowModel: getPaginationRowModel(),
454
- getSortedRowModel: getSortedRowModel(),
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 { DataTable, DataTableToolbar, DataTablePagination, DataTableSkeleton };
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