@wealthx/shadcn 1.0.2 → 1.1.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.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +154 -138
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +82 -0
  4. package/dist/chunk-3EQP72AW.mjs +58 -0
  5. package/dist/chunk-5JGQAAQV.mjs +212 -0
  6. package/dist/chunk-GLW2UO6O.mjs +212 -0
  7. package/dist/chunk-RN67642N.mjs +171 -0
  8. package/dist/chunk-UEL4RD5P.mjs +272 -0
  9. package/dist/chunk-YBXCIF5Q.mjs +198 -0
  10. package/dist/components/ui/cashflow-bar-chart.js +596 -0
  11. package/dist/components/ui/cashflow-bar-chart.mjs +16 -0
  12. package/dist/components/ui/combobox.js +261 -0
  13. package/dist/components/ui/combobox.mjs +28 -0
  14. package/dist/components/ui/data-table.mjs +3 -3
  15. package/dist/components/ui/expense-bar-chart.js +543 -0
  16. package/dist/components/ui/expense-bar-chart.mjs +16 -0
  17. package/dist/components/ui/field.mjs +2 -2
  18. package/dist/components/ui/income-bar-chart.js +543 -0
  19. package/dist/components/ui/income-bar-chart.mjs +16 -0
  20. package/dist/components/ui/transactions-income-expense-bar-chart.js +478 -0
  21. package/dist/components/ui/transactions-income-expense-bar-chart.mjs +16 -0
  22. package/dist/index.js +1685 -725
  23. package/dist/index.mjs +152 -111
  24. package/dist/styles.css +1 -1
  25. package/package.json +30 -2
  26. package/src/components/index.tsx +56 -0
  27. package/src/components/ui/cashflow-bar-chart.tsx +336 -0
  28. package/src/components/ui/chart-shared.tsx +100 -0
  29. package/src/components/ui/combobox.tsx +217 -0
  30. package/src/components/ui/expense-bar-chart.tsx +278 -0
  31. package/src/components/ui/income-bar-chart.tsx +278 -0
  32. package/src/components/ui/transactions-income-expense-bar-chart.tsx +198 -0
  33. package/src/styles/styles-css.ts +1 -1
  34. package/tsup.config.ts +5 -0
  35. package/dist/{chunk-K76E2TQU.mjs → chunk-CJ46PDXE.mjs} +5 -5
  36. package/dist/{chunk-HUVTPUV2.mjs → chunk-NLLKTU4B.mjs} +3 -3
@@ -0,0 +1,272 @@
1
+ import {
2
+ ChartPeriodButton,
3
+ FALLBACK_TICK,
4
+ formatTooltipDate,
5
+ hexToRgba
6
+ } from "./chunk-3EQP72AW.mjs";
7
+ import {
8
+ Skeleton
9
+ } from "./chunk-HS7TFG7V.mjs";
10
+ import {
11
+ Empty,
12
+ EmptyDescription
13
+ } from "./chunk-YN5SYTOO.mjs";
14
+ import {
15
+ Card,
16
+ CardAction,
17
+ CardContent,
18
+ CardHeader,
19
+ CardTitle
20
+ } from "./chunk-SLWCCURD.mjs";
21
+ import {
22
+ useThemeVars
23
+ } from "./chunk-OXQQNQZI.mjs";
24
+ import {
25
+ cn
26
+ } from "./chunk-V7CNWJT3.mjs";
27
+
28
+ // src/components/ui/cashflow-bar-chart.tsx
29
+ import { useMemo, useState } from "react";
30
+ import {
31
+ Chart as ChartJS,
32
+ CategoryScale,
33
+ LinearScale,
34
+ BarElement,
35
+ Tooltip,
36
+ Legend
37
+ } from "chart.js";
38
+ import { Chart } from "react-chartjs-2";
39
+ import { jsx, jsxs } from "react/jsx-runtime";
40
+ ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip, Legend);
41
+ function LegendItem({ label, fillColor, strokeColor, strokeWidth = 1.5 }) {
42
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
43
+ /* @__PURE__ */ jsx(
44
+ "div",
45
+ {
46
+ style: {
47
+ width: 10,
48
+ height: 10,
49
+ backgroundColor: fillColor,
50
+ border: strokeWidth > 0 ? `${strokeWidth}px solid ${strokeColor}` : "none",
51
+ flexShrink: 0
52
+ }
53
+ }
54
+ ),
55
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground leading-none", children: label })
56
+ ] });
57
+ }
58
+ function ChartLegend({ primary, secondary }) {
59
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-3 gap-y-1.5 pb-2", children: [
60
+ /* @__PURE__ */ jsx(
61
+ LegendItem,
62
+ {
63
+ label: "Income",
64
+ fillColor: hexToRgba(primary, 0.2),
65
+ strokeColor: primary
66
+ }
67
+ ),
68
+ /* @__PURE__ */ jsx(
69
+ LegendItem,
70
+ {
71
+ label: "Expenses and Liabilities",
72
+ fillColor: hexToRgba(secondary, 0.2),
73
+ strokeColor: secondary
74
+ }
75
+ ),
76
+ /* @__PURE__ */ jsx(
77
+ LegendItem,
78
+ {
79
+ label: "Surplus Income",
80
+ fillColor: primary,
81
+ strokeColor: primary,
82
+ strokeWidth: 0
83
+ }
84
+ ),
85
+ /* @__PURE__ */ jsx(
86
+ LegendItem,
87
+ {
88
+ label: "Over Spending",
89
+ fillColor: secondary,
90
+ strokeColor: secondary,
91
+ strokeWidth: 0
92
+ }
93
+ )
94
+ ] });
95
+ }
96
+ var PERIODS = [3, 6, 12];
97
+ var FALLBACK_PRIMARY = "#33FF99";
98
+ var FALLBACK_SECONDARY = "#162029";
99
+ function CashflowBarChart({
100
+ cashflowData,
101
+ title = "Cashflow",
102
+ showLegend = true,
103
+ showXAxis = true,
104
+ showYAxis = true,
105
+ legendPosition = "top",
106
+ defaultPeriod = 6,
107
+ height = 280,
108
+ width = "100%",
109
+ className,
110
+ isLoading = false
111
+ }) {
112
+ const [period, setPeriod] = useState(defaultPeriod);
113
+ const themeVars = useThemeVars();
114
+ const brandPrimary = themeVars["--theme-primary"] || FALLBACK_PRIMARY;
115
+ const brandSecondary = themeVars["--theme-secondary"] || FALLBACK_SECONDARY;
116
+ const sliced = useMemo(() => {
117
+ var _a;
118
+ if (!((_a = cashflowData == null ? void 0 : cashflowData.data) == null ? void 0 : _a.length)) return null;
119
+ const count = Math.min(period, cashflowData.data.length);
120
+ const start = cashflowData.data.length - count;
121
+ return {
122
+ months: cashflowData.months.slice(start),
123
+ data: cashflowData.data.slice(start)
124
+ };
125
+ }, [cashflowData, period]);
126
+ const chartData = useMemo(() => {
127
+ if (!sliced) return { labels: [], datasets: [] };
128
+ return {
129
+ labels: sliced.months,
130
+ datasets: [
131
+ {
132
+ label: "Income",
133
+ data: sliced.data.map((d) => d.income),
134
+ backgroundColor: hexToRgba(brandPrimary, 0.2),
135
+ hoverBackgroundColor: hexToRgba(brandPrimary, 0.35),
136
+ borderColor: brandPrimary,
137
+ borderWidth: 1.5,
138
+ borderRadius: 0,
139
+ borderSkipped: false,
140
+ barPercentage: 0.75,
141
+ categoryPercentage: 0.7
142
+ },
143
+ {
144
+ label: "Expenses and Liabilities",
145
+ data: sliced.data.map((d) => d.expenses),
146
+ backgroundColor: hexToRgba(brandSecondary, 0.2),
147
+ hoverBackgroundColor: hexToRgba(brandSecondary, 0.35),
148
+ borderColor: brandSecondary,
149
+ borderWidth: 1.5,
150
+ borderRadius: 0,
151
+ borderSkipped: false,
152
+ barPercentage: 0.75,
153
+ categoryPercentage: 0.7
154
+ },
155
+ {
156
+ label: "_thirdBar",
157
+ data: sliced.data.map(
158
+ (d) => d.overspending > 0 ? d.overspending : d.surplus
159
+ ),
160
+ backgroundColor: sliced.data.map(
161
+ (d) => d.overspending > 0 ? brandSecondary : brandPrimary
162
+ ),
163
+ hoverBackgroundColor: sliced.data.map(
164
+ (d) => d.overspending > 0 ? hexToRgba(brandSecondary, 0.8) : hexToRgba(brandPrimary, 0.8)
165
+ ),
166
+ borderWidth: 0,
167
+ borderRadius: 0,
168
+ borderSkipped: false,
169
+ barPercentage: 0.75,
170
+ categoryPercentage: 0.7
171
+ }
172
+ ]
173
+ };
174
+ }, [sliced, brandPrimary, brandSecondary]);
175
+ const options = useMemo(() => ({
176
+ responsive: true,
177
+ maintainAspectRatio: false,
178
+ animation: { duration: 800, easing: "easeOutQuart" },
179
+ layout: { padding: 0 },
180
+ plugins: {
181
+ legend: { display: false },
182
+ tooltip: {
183
+ mode: "index",
184
+ intersect: false,
185
+ padding: 12,
186
+ cornerRadius: 0,
187
+ titleFont: { size: 11, weight: "600" },
188
+ bodyFont: { size: 12, weight: "500" },
189
+ callbacks: {
190
+ title: (tooltipItems) => {
191
+ var _a, _b, _c, _d;
192
+ const idx = (_a = tooltipItems[0]) == null ? void 0 : _a.dataIndex;
193
+ if (idx != null && ((_b = sliced == null ? void 0 : sliced.data[idx]) == null ? void 0 : _b.date)) {
194
+ return formatTooltipDate(sliced.data[idx].date, "monthly");
195
+ }
196
+ return (_d = (_c = tooltipItems[0]) == null ? void 0 : _c.label) != null ? _d : "";
197
+ },
198
+ label: (ctx) => {
199
+ const val = ctx.raw;
200
+ if (val === 0) return null;
201
+ if (ctx.datasetIndex === 2) {
202
+ const d = sliced == null ? void 0 : sliced.data[ctx.dataIndex];
203
+ if (!d) return null;
204
+ const lbl = d.overspending > 0 ? "Over Spending" : "Surplus Income";
205
+ return ` ${lbl}: $${val.toLocaleString()}`;
206
+ }
207
+ return ` ${ctx.dataset.label}: $${val.toLocaleString()}`;
208
+ }
209
+ }
210
+ }
211
+ },
212
+ scales: {
213
+ x: {
214
+ display: showXAxis,
215
+ grid: { display: false },
216
+ border: { display: false },
217
+ ticks: { font: { size: 10 }, color: FALLBACK_TICK }
218
+ },
219
+ y: {
220
+ display: showYAxis,
221
+ grid: { display: false },
222
+ border: { display: false },
223
+ ticks: {
224
+ font: { size: 10 },
225
+ color: FALLBACK_TICK,
226
+ maxTicksLimit: 5,
227
+ padding: 8,
228
+ callback: (v) => `$${Number(v).toLocaleString()}`
229
+ }
230
+ }
231
+ }
232
+ }), [showXAxis, showYAxis, sliced]);
233
+ return /* @__PURE__ */ jsxs(
234
+ Card,
235
+ {
236
+ className: cn("w-full py-4 sm:py-6 gap-2", className),
237
+ style: { maxWidth: width },
238
+ children: [
239
+ /* @__PURE__ */ jsxs(CardHeader, { className: "px-3 sm:px-6", children: [
240
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-sm sm:text-base", children: title }),
241
+ /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx("div", { className: "flex gap-0.5 sm:gap-1", children: PERIODS.map((p) => /* @__PURE__ */ jsx(
242
+ ChartPeriodButton,
243
+ {
244
+ period: p,
245
+ active: period === p,
246
+ onClick: () => setPeriod(p)
247
+ },
248
+ p
249
+ )) }) })
250
+ ] }),
251
+ /* @__PURE__ */ jsx(CardContent, { className: "px-3 sm:px-6", children: isLoading ? /* @__PURE__ */ jsx(Skeleton, { style: { height, width: "100%" } }) : !sliced ? /* @__PURE__ */ jsx(Empty, { className: "flex-none p-4", style: { height }, children: /* @__PURE__ */ jsx(EmptyDescription, { children: "No data available" }) }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
252
+ showLegend && legendPosition === "top" && /* @__PURE__ */ jsx(ChartLegend, { primary: brandPrimary, secondary: brandSecondary }),
253
+ /* @__PURE__ */ jsx("div", { style: { height, width: "100%", position: "relative" }, children: /* @__PURE__ */ jsx(
254
+ Chart,
255
+ {
256
+ type: "bar",
257
+ data: chartData,
258
+ options,
259
+ "aria-label": title
260
+ },
261
+ `${brandPrimary}__${brandSecondary}`
262
+ ) }),
263
+ showLegend && legendPosition === "bottom" && /* @__PURE__ */ jsx(ChartLegend, { primary: brandPrimary, secondary: brandSecondary })
264
+ ] }) })
265
+ ]
266
+ }
267
+ );
268
+ }
269
+
270
+ export {
271
+ CashflowBarChart
272
+ };
@@ -0,0 +1,198 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-V7CNWJT3.mjs";
4
+ import {
5
+ __objRest,
6
+ __spreadProps,
7
+ __spreadValues
8
+ } from "./chunk-FWCSY2DS.mjs";
9
+
10
+ // src/components/ui/combobox.tsx
11
+ import { CheckIcon, ChevronDownIcon, SearchIcon } from "lucide-react";
12
+ import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox";
13
+ import { jsx, jsxs } from "react/jsx-runtime";
14
+ function Combobox(_a) {
15
+ var props = __objRest(_a, []);
16
+ return /* @__PURE__ */ jsx(ComboboxPrimitive.Root, __spreadValues({ "data-slot": "combobox" }, props));
17
+ }
18
+ function ComboboxTrigger(_a) {
19
+ var _b = _a, {
20
+ className,
21
+ size = "default",
22
+ children
23
+ } = _b, props = __objRest(_b, [
24
+ "className",
25
+ "size",
26
+ "children"
27
+ ]);
28
+ return /* @__PURE__ */ jsxs(
29
+ ComboboxPrimitive.Trigger,
30
+ __spreadProps(__spreadValues({
31
+ "data-slot": "combobox-trigger",
32
+ "data-size": size,
33
+ className: cn(
34
+ "flex w-fit items-center justify-between gap-2 border border-input bg-transparent px-3 py-2 text-body-small whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-primary/20 data-[popup-open]:border-primary data-[popup-open]:ring-[3px] data-[popup-open]:ring-primary/20 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=combobox-value]:line-clamp-1 *:data-[slot=combobox-value]:flex *:data-[slot=combobox-value]:items-center *:data-[slot=combobox-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
35
+ className
36
+ )
37
+ }, props), {
38
+ children: [
39
+ children,
40
+ /* @__PURE__ */ jsx(ComboboxPrimitive.Icon, { className: "transition-transform duration-200 data-[popup-open]:rotate-180", children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4 opacity-50" }) })
41
+ ]
42
+ })
43
+ );
44
+ }
45
+ function ComboboxValue(_a) {
46
+ var props = __objRest(_a, []);
47
+ return /* @__PURE__ */ jsx(ComboboxPrimitive.Value, __spreadValues({ "data-slot": "combobox-value" }, props));
48
+ }
49
+ function ComboboxInput(_a) {
50
+ var _b = _a, {
51
+ className
52
+ } = _b, props = __objRest(_b, [
53
+ "className"
54
+ ]);
55
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "combobox-input-wrapper", className: "flex items-center gap-2 border-b border-border px-3", children: [
56
+ /* @__PURE__ */ jsx(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
57
+ /* @__PURE__ */ jsx(
58
+ ComboboxPrimitive.Input,
59
+ __spreadValues({
60
+ "data-slot": "combobox-input",
61
+ className: cn(
62
+ "h-9 w-full min-w-0 bg-transparent py-1 text-body-small outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
63
+ className
64
+ )
65
+ }, props)
66
+ )
67
+ ] });
68
+ }
69
+ function ComboboxContent(_a) {
70
+ var _b = _a, {
71
+ className,
72
+ children
73
+ } = _b, props = __objRest(_b, [
74
+ "className",
75
+ "children"
76
+ ]);
77
+ return /* @__PURE__ */ jsx(ComboboxPrimitive.Portal, { children: /* @__PURE__ */ jsx(ComboboxPrimitive.Positioner, { sideOffset: 4, align: "start", children: /* @__PURE__ */ jsx(
78
+ ComboboxPrimitive.Popup,
79
+ __spreadProps(__spreadValues({
80
+ "data-slot": "combobox-content",
81
+ className: cn(
82
+ "relative z-50 max-h-[var(--available-height)] min-w-[8rem] overflow-hidden border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[ending-style]:animate-out data-[ending-style]:fade-out-0 data-[ending-style]:zoom-out-95 data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95",
83
+ className
84
+ )
85
+ }, props), {
86
+ children
87
+ })
88
+ ) }) });
89
+ }
90
+ function ComboboxList(_a) {
91
+ var _b = _a, {
92
+ className
93
+ } = _b, props = __objRest(_b, [
94
+ "className"
95
+ ]);
96
+ return /* @__PURE__ */ jsx(
97
+ ComboboxPrimitive.List,
98
+ __spreadValues({
99
+ "data-slot": "combobox-list",
100
+ className: cn(
101
+ "max-h-[min(var(--available-height),18rem)] overflow-y-auto p-1",
102
+ className
103
+ )
104
+ }, props)
105
+ );
106
+ }
107
+ function ComboboxItem(_a) {
108
+ var _b = _a, {
109
+ className,
110
+ children
111
+ } = _b, props = __objRest(_b, [
112
+ "className",
113
+ "children"
114
+ ]);
115
+ return /* @__PURE__ */ jsxs(
116
+ ComboboxPrimitive.Item,
117
+ __spreadProps(__spreadValues({
118
+ "data-slot": "combobox-item",
119
+ className: cn(
120
+ "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-body-small outline-hidden select-none data-[highlighted]:bg-primary/5 data-[highlighted]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
121
+ className
122
+ )
123
+ }, props), {
124
+ children: [
125
+ /* @__PURE__ */ jsx(
126
+ "span",
127
+ {
128
+ "data-slot": "combobox-item-indicator",
129
+ className: "absolute right-2 flex size-3.5 items-center justify-center",
130
+ children: /* @__PURE__ */ jsx(ComboboxPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-4" }) })
131
+ }
132
+ ),
133
+ children
134
+ ]
135
+ })
136
+ );
137
+ }
138
+ function ComboboxEmpty(_a) {
139
+ var _b = _a, {
140
+ className
141
+ } = _b, props = __objRest(_b, [
142
+ "className"
143
+ ]);
144
+ return /* @__PURE__ */ jsx(
145
+ ComboboxPrimitive.Empty,
146
+ __spreadValues({
147
+ "data-slot": "combobox-empty",
148
+ className: `text-body-small ${cn("py-6 text-center text-muted-foreground empty:hidden", className)}`
149
+ }, props)
150
+ );
151
+ }
152
+ function ComboboxGroup(_a) {
153
+ var props = __objRest(_a, []);
154
+ return /* @__PURE__ */ jsx(ComboboxPrimitive.Group, __spreadValues({ "data-slot": "combobox-group" }, props));
155
+ }
156
+ function ComboboxGroupLabel(_a) {
157
+ var _b = _a, {
158
+ className
159
+ } = _b, props = __objRest(_b, [
160
+ "className"
161
+ ]);
162
+ return /* @__PURE__ */ jsx(
163
+ ComboboxPrimitive.GroupLabel,
164
+ __spreadValues({
165
+ "data-slot": "combobox-group-label",
166
+ className: `text-label-small ${cn("px-2 py-1.5 uppercase text-muted-foreground", className)}`
167
+ }, props)
168
+ );
169
+ }
170
+ function ComboboxSeparator(_a) {
171
+ var _b = _a, {
172
+ className
173
+ } = _b, props = __objRest(_b, [
174
+ "className"
175
+ ]);
176
+ return /* @__PURE__ */ jsx(
177
+ "div",
178
+ __spreadValues({
179
+ role: "separator",
180
+ "data-slot": "combobox-separator",
181
+ className: cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)
182
+ }, props)
183
+ );
184
+ }
185
+
186
+ export {
187
+ Combobox,
188
+ ComboboxTrigger,
189
+ ComboboxValue,
190
+ ComboboxInput,
191
+ ComboboxContent,
192
+ ComboboxList,
193
+ ComboboxItem,
194
+ ComboboxEmpty,
195
+ ComboboxGroup,
196
+ ComboboxGroupLabel,
197
+ ComboboxSeparator
198
+ };