@siyahi/ui 0.1.8-next.4 → 0.1.9-next.10
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/README.md +75 -23
- package/THIRD_PARTY_NOTICES.md +11 -10
- package/dist/components/registry/charts/area-chart-card.d.ts +4 -3
- package/dist/components/registry/charts/area-chart-card.js +2 -2
- package/dist/components/registry/charts/bar-chart-card.d.ts +7 -1
- package/dist/components/registry/charts/bar-chart-card.js +35 -14
- package/dist/components/registry/charts/line-chart-card.d.ts +5 -2
- package/dist/components/registry/charts/line-chart-card.js +2 -2
- package/dist/components/registry/dashboard-01/section-cards.d.ts +11 -2
- package/dist/components/registry/dashboard-01/section-cards.js +44 -26
- package/dist/components/registry/dashboardcn/bar-list.d.ts +3 -1
- package/dist/components/registry/dashboardcn/bar-list.js +3 -3
- package/dist/components/registry/dashboardcn/distribution-bar.d.ts +38 -0
- package/dist/components/registry/dashboardcn/distribution-bar.js +118 -0
- package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
- package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
- package/dist/components/registry/dashboardcn/sparkline.d.ts +18 -0
- package/dist/components/registry/dashboardcn/sparkline.js +95 -0
- package/dist/components/registry/minidev/copy-button.d.ts +11 -0
- package/dist/components/registry/minidev/copy-button.js +34 -0
- package/dist/components/registry/niko-table/components/data-table-search-filter.d.ts +35 -0
- package/dist/components/registry/niko-table/components/data-table-search-filter.js +44 -0
- package/dist/components/registry/niko-table/filters/table-search-filter.d.ts +38 -0
- package/dist/components/registry/niko-table/filters/table-search-filter.js +103 -0
- package/dist/components/registry/niko-table/ui/table.js +2 -2
- package/dist/components/registry/sidebar-07/nav-main.d.ts +1 -1
- package/dist/components/siyahi/data-bar.d.ts +19 -0
- package/dist/components/siyahi/data-bar.js +29 -0
- package/dist/components/siyahi/data-list.d.ts +14 -0
- package/dist/components/siyahi/data-list.js +25 -0
- package/dist/components/siyahi/data-table-row-count.d.ts +11 -0
- package/dist/components/siyahi/data-table-row-count.js +18 -0
- package/dist/components/siyahi/data-table-sort-header.d.ts +15 -0
- package/dist/components/siyahi/data-table-sort-header.js +36 -0
- package/dist/components/siyahi/inline-code.d.ts +7 -0
- package/dist/components/siyahi/inline-code.js +11 -0
- package/dist/components/siyahi/page-section.d.ts +21 -0
- package/dist/components/siyahi/page-section.js +39 -0
- package/dist/components/siyahi/panel-grid.d.ts +22 -0
- package/dist/components/siyahi/panel-grid.js +34 -0
- package/dist/components/siyahi/siyahi-provider.d.ts +13 -2
- package/dist/components/siyahi/siyahi-provider.js +17 -4
- package/dist/components/siyahi/tone-progress.d.ts +15 -0
- package/dist/components/siyahi/tone-progress.js +18 -0
- package/dist/components/siyahi/treemap.d.ts +7 -0
- package/dist/components/siyahi/treemap.js +69 -2
- package/dist/components/ui/accordion.js +5 -5
- package/dist/components/ui/alert.js +2 -2
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/breadcrumb.js +2 -2
- package/dist/components/ui/button.js +10 -10
- package/dist/components/ui/calendar.js +1 -1
- package/dist/components/ui/card.js +4 -4
- package/dist/components/ui/chart.js +1 -1
- package/dist/components/ui/code-block.d.ts +28 -0
- package/dist/components/ui/code-block.js +123 -0
- package/dist/components/ui/command.js +7 -6
- package/dist/components/ui/context-menu.js +7 -7
- package/dist/components/ui/dialog.js +5 -5
- package/dist/components/ui/dropdown-menu.js +8 -8
- package/dist/components/ui/input-group.js +11 -11
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/popover.js +3 -3
- package/dist/components/ui/progress.js +1 -1
- package/dist/components/ui/select.js +6 -6
- package/dist/components/ui/sheet.js +5 -5
- package/dist/components/ui/sidebar.js +17 -18
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/slider.js +2 -2
- package/dist/components/ui/table.js +2 -2
- package/dist/components/ui/tabs.js +2 -2
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/toggle-group.js +2 -2
- package/dist/components/ui/toggle.js +4 -4
- package/dist/components/ui/tooltip.js +2 -2
- package/dist/index.d.ts +25 -11
- package/dist/index.js +20 -6
- package/dist/lib/chart-frame.d.ts +18 -4
- package/dist/lib/chart-frame.js +183 -56
- package/dist/lib/chart-series.d.ts +58 -3
- package/dist/lib/format.d.ts +3 -0
- package/dist/lib/format.js +6 -1
- package/dist/lib/treemap.d.ts +4 -0
- package/dist/lib/treemap.js +9 -4
- package/package.json +1 -1
- package/registry.lock.json +48 -0
- package/report.css +11 -11
- package/styles/siyahi.css +53 -11
- package/dist/lib/recharts.d.ts +0 -2
- package/dist/lib/recharts.js +0 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useLocale } from "../../siyahi/siyahi-provider.js";
|
|
3
|
+
import { pct } from "../../../lib/format.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import "react";
|
|
6
|
+
//#region src/components/registry/dashboardcn/distribution-bar.tsx
|
|
7
|
+
const defaultColors = [
|
|
8
|
+
"var(--chart-1)",
|
|
9
|
+
"var(--chart-2)",
|
|
10
|
+
"var(--chart-3)",
|
|
11
|
+
"var(--chart-4)",
|
|
12
|
+
"var(--chart-5)"
|
|
13
|
+
];
|
|
14
|
+
function DistributionBar({ segments, valueFormatter, showLegend = true, showValues = false, max, min, markers = [], highlighted = null, onHover, className, "aria-label": ariaLabel, ...props }) {
|
|
15
|
+
const locale = useLocale();
|
|
16
|
+
const format = valueFormatter ?? ((value) => new Intl.NumberFormat(locale.numberLocale, {
|
|
17
|
+
notation: "compact",
|
|
18
|
+
maximumFractionDigits: 1
|
|
19
|
+
}).format(value));
|
|
20
|
+
const total = segments.reduce((sum, segment) => sum + Math.abs(segment.value), 0);
|
|
21
|
+
const gains = segments.reduce((sum, segment) => sum + Math.max(segment.value, 0), 0);
|
|
22
|
+
const losses = segments.reduce((sum, segment) => sum + Math.min(segment.value, 0), 0);
|
|
23
|
+
const bottom = min ?? losses;
|
|
24
|
+
const top = max ?? gains;
|
|
25
|
+
const span = top - bottom || 1;
|
|
26
|
+
const position = (value) => (value - bottom) / span * 100;
|
|
27
|
+
const shown = markers.filter((marker) => marker.at >= bottom && marker.at <= top).map((marker, index) => ({
|
|
28
|
+
...marker,
|
|
29
|
+
key: `${marker.at}:${marker.label ?? ""}:${index}`
|
|
30
|
+
}));
|
|
31
|
+
let above = 0;
|
|
32
|
+
let below = 0;
|
|
33
|
+
const resolved = segments.map((segment, index) => {
|
|
34
|
+
const key = segment.key ?? segment.name;
|
|
35
|
+
const size = Math.abs(segment.value);
|
|
36
|
+
const start = segment.value < 0 ? below -= size : (above += size) - size;
|
|
37
|
+
return {
|
|
38
|
+
...segment,
|
|
39
|
+
key,
|
|
40
|
+
color: segment.color ?? defaultColors[index % defaultColors.length],
|
|
41
|
+
share: total > 0 ? size / total : 0,
|
|
42
|
+
left: position(start),
|
|
43
|
+
width: size / span * 100
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
const figure = (segment) => max === void 0 ? pct(segment.share, locale) : format(segment.value);
|
|
47
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
48
|
+
"data-slot": "distribution-bar",
|
|
49
|
+
className: cn("flex flex-col gap-3", className),
|
|
50
|
+
...props,
|
|
51
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
52
|
+
className: "relative",
|
|
53
|
+
children: [
|
|
54
|
+
shown.some((m) => m.label) ? /* @__PURE__ */ jsx("div", {
|
|
55
|
+
className: "text-muted-foreground relative h-4 font-mono text-xs",
|
|
56
|
+
children: shown.map((marker) => marker.label ? /* @__PURE__ */ jsx("span", {
|
|
57
|
+
className: cn("absolute top-0 whitespace-nowrap", position(marker.at) <= 5 ? "translate-x-0" : position(marker.at) >= 95 ? "-translate-x-full" : "-translate-x-1/2"),
|
|
58
|
+
style: { left: `${position(marker.at)}%` },
|
|
59
|
+
children: marker.label
|
|
60
|
+
}, marker.key) : null)
|
|
61
|
+
}) : null,
|
|
62
|
+
/* @__PURE__ */ jsx("div", {
|
|
63
|
+
role: "img",
|
|
64
|
+
"aria-label": ariaLabel ?? resolved.map((segment) => `${segment.name} ${figure(segment)}`).join(", "),
|
|
65
|
+
className: "bg-muted relative h-2.5 w-full overflow-hidden rounded-full",
|
|
66
|
+
onMouseLeave: onHover ? () => onHover(null) : void 0,
|
|
67
|
+
children: resolved.map((segment) => segment.width > 0 ? /* @__PURE__ */ jsx("div", {
|
|
68
|
+
"data-slot": "distribution-segment",
|
|
69
|
+
className: "border-card absolute inset-y-0 border-x transition-[width,left,opacity] duration-300",
|
|
70
|
+
style: {
|
|
71
|
+
left: `${segment.left}%`,
|
|
72
|
+
width: `${segment.width}%`,
|
|
73
|
+
backgroundColor: segment.color,
|
|
74
|
+
opacity: highlighted === null || highlighted === segment.key ? 1 : .35
|
|
75
|
+
},
|
|
76
|
+
onMouseEnter: onHover ? () => onHover(segment) : void 0
|
|
77
|
+
}, segment.key) : null)
|
|
78
|
+
}),
|
|
79
|
+
shown.map((marker) => /* @__PURE__ */ jsx("div", {
|
|
80
|
+
"data-slot": "distribution-marker",
|
|
81
|
+
"aria-hidden": "true",
|
|
82
|
+
className: "bg-foreground absolute bottom-0 h-2.5 w-0.5 -translate-x-1/2",
|
|
83
|
+
style: { left: `${position(marker.at)}%` }
|
|
84
|
+
}, marker.key))
|
|
85
|
+
]
|
|
86
|
+
}), showLegend ? /* @__PURE__ */ jsx("ul", {
|
|
87
|
+
className: "flex flex-wrap gap-x-4 gap-y-1.5 text-sm",
|
|
88
|
+
children: resolved.map((segment) => /* @__PURE__ */ jsxs("li", {
|
|
89
|
+
className: "flex items-center gap-2",
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ jsx("span", {
|
|
92
|
+
"aria-hidden": "true",
|
|
93
|
+
className: "size-2.5 shrink-0 rounded-xs",
|
|
94
|
+
style: { backgroundColor: segment.color }
|
|
95
|
+
}),
|
|
96
|
+
/* @__PURE__ */ jsx("span", {
|
|
97
|
+
className: "text-muted-foreground",
|
|
98
|
+
children: segment.name
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ jsx("span", {
|
|
101
|
+
className: "font-medium tabular-nums",
|
|
102
|
+
children: figure(segment)
|
|
103
|
+
}),
|
|
104
|
+
showValues && max === void 0 ? /* @__PURE__ */ jsxs("span", {
|
|
105
|
+
className: "text-muted-foreground tabular-nums",
|
|
106
|
+
children: [
|
|
107
|
+
"(",
|
|
108
|
+
format(segment.value),
|
|
109
|
+
")"
|
|
110
|
+
]
|
|
111
|
+
}) : null
|
|
112
|
+
]
|
|
113
|
+
}, segment.key))
|
|
114
|
+
}) : null]
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { DistributionBar };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Card } from "../../ui/card.js";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
//#region src/components/registry/dashboardcn/kpi-row-card.d.ts
|
|
4
|
+
export interface MetricStripItem {
|
|
5
|
+
/** Muted label above the figure. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** The figure, formatted by the page: money(4512300). */
|
|
8
|
+
value: React$1.ReactNode;
|
|
9
|
+
/** The signed change, drawn as a Delta under the figure; its sign colours the trend. For "pct", a fraction. */
|
|
10
|
+
change?: number;
|
|
11
|
+
changeFormat?: "pct" | "money";
|
|
12
|
+
/** Muted words after the change, such as "vs last month". */
|
|
13
|
+
note?: React$1.ReactNode;
|
|
14
|
+
/** Recent values, oldest first, drawn as a sparkline beside the figure. */
|
|
15
|
+
trend?: number[];
|
|
16
|
+
}
|
|
17
|
+
export interface MetricStripProps extends Omit<React$1.ComponentProps<typeof Card>, "title"> {
|
|
18
|
+
title?: React$1.ReactNode;
|
|
19
|
+
description?: React$1.ReactNode;
|
|
20
|
+
/** A range toggle, top right, that changes every figure. */
|
|
21
|
+
action?: React$1.ReactNode;
|
|
22
|
+
items: MetricStripItem[];
|
|
23
|
+
/** Lines under the strip, such as what the figures cover. */
|
|
24
|
+
footer?: React$1.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
/** A card holding a row of small figures, each with its change and a sparkline: more numbers in less room than stat tiles. */
|
|
27
|
+
declare function MetricStrip({ title, description, action, items, footer, className, ...props }: MetricStripProps): React$1.JSX.Element;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MetricStrip };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../../ui/card.js";
|
|
2
|
+
import { cn } from "../../../lib/utils.js";
|
|
3
|
+
import { Delta } from "../../siyahi/delta.js";
|
|
4
|
+
import { Sparkline } from "./sparkline.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import "react";
|
|
7
|
+
//#region src/components/registry/dashboardcn/kpi-row-card.tsx
|
|
8
|
+
const COLUMNS = {
|
|
9
|
+
1: "basis-full",
|
|
10
|
+
2: "basis-full @2xs/strip:basis-1/2",
|
|
11
|
+
3: "basis-full @lg/strip:basis-1/3",
|
|
12
|
+
4: "basis-full @2xs/strip:basis-1/2 @3xl/strip:basis-1/4",
|
|
13
|
+
5: "basis-full @2xs/strip:basis-1/2 @3xl/strip:basis-1/5",
|
|
14
|
+
6: "basis-full @2xs/strip:basis-1/2 @xl/strip:basis-1/3 @5xl/strip:basis-1/6",
|
|
15
|
+
8: "basis-full @2xs/strip:basis-1/2 @3xl/strip:basis-1/4 @7xl/strip:basis-1/8"
|
|
16
|
+
};
|
|
17
|
+
function trendColor(change) {
|
|
18
|
+
if (change === void 0) return "var(--chart-1)";
|
|
19
|
+
if (change > 0) return "var(--positive)";
|
|
20
|
+
if (change < 0) return "var(--negative)";
|
|
21
|
+
return "var(--muted-foreground)";
|
|
22
|
+
}
|
|
23
|
+
/** A card holding a row of small figures, each with its change and a sparkline: more numbers in less room than stat tiles. */
|
|
24
|
+
function MetricStrip({ title, description, action, items, footer, className, ...props }) {
|
|
25
|
+
const columns = COLUMNS[Math.min(items.length, 8)] ?? COLUMNS[8];
|
|
26
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
27
|
+
className: cn("gap-4", className),
|
|
28
|
+
...props,
|
|
29
|
+
children: [
|
|
30
|
+
(title || description || action) && /* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
31
|
+
title && /* @__PURE__ */ jsx(CardTitle, { children: title }),
|
|
32
|
+
description && /* @__PURE__ */ jsx(CardDescription, { children: description }),
|
|
33
|
+
action && /* @__PURE__ */ jsx(CardAction, { children: action })
|
|
34
|
+
] }),
|
|
35
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("div", {
|
|
36
|
+
"data-slot": "metric-strip",
|
|
37
|
+
className: "@container/strip -mx-4 -my-3 overflow-hidden",
|
|
38
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
39
|
+
className: "-mt-px -ml-px flex flex-wrap",
|
|
40
|
+
children: items.map((item) => /* @__PURE__ */ jsxs("div", {
|
|
41
|
+
"data-slot": "metric-strip-item",
|
|
42
|
+
className: cn("flex min-w-0 flex-col gap-1 border-t border-l px-4 py-3", columns),
|
|
43
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
44
|
+
className: "truncate text-xs text-muted-foreground",
|
|
45
|
+
children: item.label
|
|
46
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
47
|
+
className: "flex flex-wrap items-end justify-between gap-x-3 gap-y-1",
|
|
48
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
49
|
+
className: "flex min-w-0 flex-col gap-0.5",
|
|
50
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
51
|
+
className: "text-xl font-semibold tracking-tight tabular-nums",
|
|
52
|
+
children: item.value
|
|
53
|
+
}), (item.change !== void 0 || item.note) && /* @__PURE__ */ jsxs("div", {
|
|
54
|
+
className: "flex flex-wrap items-center gap-x-1.5 text-xs",
|
|
55
|
+
children: [item.change !== void 0 && /* @__PURE__ */ jsx(Delta, {
|
|
56
|
+
value: item.change,
|
|
57
|
+
format: item.changeFormat
|
|
58
|
+
}), item.note && /* @__PURE__ */ jsx("span", {
|
|
59
|
+
className: "text-muted-foreground",
|
|
60
|
+
children: item.note
|
|
61
|
+
})]
|
|
62
|
+
})]
|
|
63
|
+
}), item.trend && item.trend.length > 1 && /* @__PURE__ */ jsx(Sparkline, {
|
|
64
|
+
data: item.trend,
|
|
65
|
+
variant: "line",
|
|
66
|
+
color: trendColor(item.change),
|
|
67
|
+
className: "h-8 w-16 shrink-0"
|
|
68
|
+
})]
|
|
69
|
+
})]
|
|
70
|
+
}, item.label))
|
|
71
|
+
})
|
|
72
|
+
}) }),
|
|
73
|
+
footer && /* @__PURE__ */ jsx(CardFooter, {
|
|
74
|
+
className: "flex-col items-start gap-2 text-sm",
|
|
75
|
+
children: footer
|
|
76
|
+
})
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { MetricStrip };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ChartColor } from "../../../lib/chart-series.js";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
//#region src/components/registry/dashboardcn/sparkline.d.ts
|
|
4
|
+
export interface SparklineProps extends React$1.ComponentProps<"div"> {
|
|
5
|
+
/** Series to plot, oldest first. */
|
|
6
|
+
data: number[];
|
|
7
|
+
variant?: "area" | "line";
|
|
8
|
+
/** A chart slot, or var(--positive) or var(--negative) for a gain or a loss. Defaults to var(--chart-1). */
|
|
9
|
+
color?: ChartColor;
|
|
10
|
+
curve?: "monotone" | "linear" | "step";
|
|
11
|
+
strokeWidth?: number;
|
|
12
|
+
/** Animate after hydration and when data changes. Respects reduced motion. */
|
|
13
|
+
animate?: boolean;
|
|
14
|
+
animationDuration?: number;
|
|
15
|
+
}
|
|
16
|
+
declare function Sparkline({ data, variant, color, curve, strokeWidth, animate, animationDuration, className, ...props }: SparklineProps): React$1.JSX.Element;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Sparkline };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React$1 from "react";
|
|
4
|
+
import { Area, AreaChart, Line, LineChart, ResponsiveContainer, YAxis } from "recharts";
|
|
5
|
+
//#region src/components/registry/dashboardcn/sparkline.tsx
|
|
6
|
+
const reducedMotionQuery = "(prefers-reduced-motion: reduce)";
|
|
7
|
+
function subscribeToReducedMotion(onChange) {
|
|
8
|
+
const query = window.matchMedia(reducedMotionQuery);
|
|
9
|
+
query.addEventListener("change", onChange);
|
|
10
|
+
return () => query.removeEventListener("change", onChange);
|
|
11
|
+
}
|
|
12
|
+
function getReducedMotionSnapshot() {
|
|
13
|
+
return window.matchMedia(reducedMotionQuery).matches;
|
|
14
|
+
}
|
|
15
|
+
function getReducedMotionServerSnapshot() {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
function Sparkline({ data, variant = "area", color = "var(--chart-1)", curve = "monotone", strokeWidth = 1.5, animate = true, animationDuration = 500, className, ...props }) {
|
|
19
|
+
const id = React$1.useId();
|
|
20
|
+
const reducedMotion = React$1.useSyncExternalStore(subscribeToReducedMotion, getReducedMotionSnapshot, getReducedMotionServerSnapshot);
|
|
21
|
+
const animationActive = animate && !reducedMotion;
|
|
22
|
+
const points = React$1.useMemo(() => data.map((y, i) => ({
|
|
23
|
+
i,
|
|
24
|
+
y
|
|
25
|
+
})), [data]);
|
|
26
|
+
const margin = {
|
|
27
|
+
top: 2,
|
|
28
|
+
right: 0,
|
|
29
|
+
bottom: 2,
|
|
30
|
+
left: 0
|
|
31
|
+
};
|
|
32
|
+
const axis = /* @__PURE__ */ jsx(YAxis, {
|
|
33
|
+
hide: true,
|
|
34
|
+
domain: ["dataMin", "dataMax"]
|
|
35
|
+
});
|
|
36
|
+
return /* @__PURE__ */ jsx("div", {
|
|
37
|
+
"data-slot": "sparkline",
|
|
38
|
+
"aria-hidden": "true",
|
|
39
|
+
className: cn("h-10 w-full", className),
|
|
40
|
+
...props,
|
|
41
|
+
children: /* @__PURE__ */ jsx(ResponsiveContainer, {
|
|
42
|
+
width: "100%",
|
|
43
|
+
height: "100%",
|
|
44
|
+
children: variant === "line" ? /* @__PURE__ */ jsxs(LineChart, {
|
|
45
|
+
data: points,
|
|
46
|
+
margin,
|
|
47
|
+
children: [axis, /* @__PURE__ */ jsx(Line, {
|
|
48
|
+
type: curve,
|
|
49
|
+
dataKey: "y",
|
|
50
|
+
stroke: color,
|
|
51
|
+
strokeWidth,
|
|
52
|
+
dot: false,
|
|
53
|
+
isAnimationActive: animationActive,
|
|
54
|
+
animationDuration,
|
|
55
|
+
animationEasing: "ease-out"
|
|
56
|
+
})]
|
|
57
|
+
}) : /* @__PURE__ */ jsxs(AreaChart, {
|
|
58
|
+
data: points,
|
|
59
|
+
margin,
|
|
60
|
+
children: [
|
|
61
|
+
axis,
|
|
62
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", {
|
|
63
|
+
id,
|
|
64
|
+
x1: "0",
|
|
65
|
+
y1: "0",
|
|
66
|
+
x2: "0",
|
|
67
|
+
y2: "1",
|
|
68
|
+
children: [/* @__PURE__ */ jsx("stop", {
|
|
69
|
+
offset: "0%",
|
|
70
|
+
stopColor: color,
|
|
71
|
+
stopOpacity: .3
|
|
72
|
+
}), /* @__PURE__ */ jsx("stop", {
|
|
73
|
+
offset: "100%",
|
|
74
|
+
stopColor: color,
|
|
75
|
+
stopOpacity: 0
|
|
76
|
+
})]
|
|
77
|
+
}) }),
|
|
78
|
+
/* @__PURE__ */ jsx(Area, {
|
|
79
|
+
type: curve,
|
|
80
|
+
dataKey: "y",
|
|
81
|
+
stroke: color,
|
|
82
|
+
strokeWidth,
|
|
83
|
+
fill: `url(#${id})`,
|
|
84
|
+
dot: false,
|
|
85
|
+
isAnimationActive: animationActive,
|
|
86
|
+
animationDuration,
|
|
87
|
+
animationEasing: "ease-out"
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { Sparkline };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { buttonVariants } from "../../ui/button.js";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
import { VariantProps } from "class-variance-authority";
|
|
4
|
+
//#region src/components/registry/minidev/copy-button.d.ts
|
|
5
|
+
declare function CopyButton({ value, className, variant, size, label, copiedLabel, ...props }: {
|
|
6
|
+
value: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
copiedLabel?: string;
|
|
9
|
+
} & React$1.ComponentProps<"button"> & VariantProps<typeof buttonVariants>): React$1.JSX.Element;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { CopyButton };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button } from "../../ui/button.js";
|
|
3
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import * as React$1 from "react";
|
|
7
|
+
import "class-variance-authority";
|
|
8
|
+
//#region src/components/registry/minidev/copy-button.tsx
|
|
9
|
+
function CopyButton({ value, className, variant = "outline", size = "sm", label = "Copy", copiedLabel = "Copied", ...props }) {
|
|
10
|
+
const [copied, setCopied] = React$1.useState(false);
|
|
11
|
+
React$1.useEffect(() => {
|
|
12
|
+
if (!copied) return;
|
|
13
|
+
const timeout = setTimeout(() => setCopied(false), 2e3);
|
|
14
|
+
return () => clearTimeout(timeout);
|
|
15
|
+
}, [copied]);
|
|
16
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
17
|
+
type: "button",
|
|
18
|
+
variant,
|
|
19
|
+
size,
|
|
20
|
+
"data-slot": "copy-button",
|
|
21
|
+
className: cn(className),
|
|
22
|
+
"aria-label": copied ? copiedLabel : label,
|
|
23
|
+
onClick: async () => {
|
|
24
|
+
try {
|
|
25
|
+
await navigator.clipboard.writeText(value);
|
|
26
|
+
setCopied(true);
|
|
27
|
+
} catch {}
|
|
28
|
+
},
|
|
29
|
+
...props,
|
|
30
|
+
children: [copied ? /* @__PURE__ */ jsx(CheckIcon, { "data-icon": "inline-start" }) : /* @__PURE__ */ jsx(CopyIcon, { "data-icon": "inline-start" }), copied ? copiedLabel : label]
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { CopyButton };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TableSearchFilterProps } from "../filters/table-search-filter.js";
|
|
2
|
+
import { RowData } from "@tanstack/react-table";
|
|
3
|
+
//#region src/components/registry/niko-table/components/data-table-search-filter.d.ts
|
|
4
|
+
type DataTableSearchFilterProps<TData extends RowData> = Omit<TableSearchFilterProps<TData>, "table">;
|
|
5
|
+
/**
|
|
6
|
+
* A search filter component for DataTable.
|
|
7
|
+
* Can be used in controlled or uncontrolled mode.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Uncontrolled (manages its own state)
|
|
11
|
+
* <DataTableSearchFilter placeholder="Search products..." />
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Controlled (you manage the state)
|
|
15
|
+
* const [search, setSearch] = useState("")
|
|
16
|
+
* <DataTableSearchFilter
|
|
17
|
+
* value={search}
|
|
18
|
+
* onChange={setSearch}
|
|
19
|
+
* placeholder="Search..."
|
|
20
|
+
* />
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // With nuqs for URL state
|
|
24
|
+
* const [search, setSearch] = useQueryState('search')
|
|
25
|
+
* <DataTableSearchFilter
|
|
26
|
+
* value={search ?? ""}
|
|
27
|
+
* onChange={setSearch}
|
|
28
|
+
* />
|
|
29
|
+
*/
|
|
30
|
+
export declare function DataTableSearchFilter<TData extends RowData>(props: DataTableSearchFilterProps<TData>): import("react").JSX.Element;
|
|
31
|
+
export declare namespace DataTableSearchFilter {
|
|
32
|
+
var displayName: string;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useDataTable } from "../core/data-table-context.js";
|
|
3
|
+
import { TableSearchFilter } from "../filters/table-search-filter.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/registry/niko-table/components/data-table-search-filter.tsx
|
|
6
|
+
/**
|
|
7
|
+
* A search filter component for DataTable.
|
|
8
|
+
* Can be used in controlled or uncontrolled mode.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Uncontrolled (manages its own state)
|
|
12
|
+
* <DataTableSearchFilter placeholder="Search products..." />
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Controlled (you manage the state)
|
|
16
|
+
* const [search, setSearch] = useState("")
|
|
17
|
+
* <DataTableSearchFilter
|
|
18
|
+
* value={search}
|
|
19
|
+
* onChange={setSearch}
|
|
20
|
+
* placeholder="Search..."
|
|
21
|
+
* />
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // With nuqs for URL state
|
|
25
|
+
* const [search, setSearch] = useQueryState('search')
|
|
26
|
+
* <DataTableSearchFilter
|
|
27
|
+
* value={search ?? ""}
|
|
28
|
+
* onChange={setSearch}
|
|
29
|
+
* />
|
|
30
|
+
*/
|
|
31
|
+
function DataTableSearchFilter(props) {
|
|
32
|
+
const { table } = useDataTable();
|
|
33
|
+
return /* @__PURE__ */ jsx(TableSearchFilter, {
|
|
34
|
+
table,
|
|
35
|
+
...props
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @required displayName is required for auto feature detection
|
|
40
|
+
* @see "feature-detection.ts"
|
|
41
|
+
*/
|
|
42
|
+
DataTableSearchFilter.displayName = "DataTableSearchFilter";
|
|
43
|
+
//#endregion
|
|
44
|
+
export { DataTableSearchFilter };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { DataTableInstance } from "../types/index.js";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
import { RowData } from "@tanstack/react-table";
|
|
4
|
+
//#region src/components/registry/niko-table/filters/table-search-filter.d.ts
|
|
5
|
+
export interface TableSearchFilterProps<TData extends RowData> {
|
|
6
|
+
table: DataTableInstance<TData>;
|
|
7
|
+
className?: string;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
/** The field's accessible name, such as "Filter holdings". */
|
|
10
|
+
label?: string;
|
|
11
|
+
showClearButton?: boolean;
|
|
12
|
+
onChange?: (value: string) => void;
|
|
13
|
+
value?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Debounce ms before pushing the typed value into table state. The
|
|
16
|
+
* input reflects keystrokes immediately; only the call to
|
|
17
|
+
* `table.setGlobalFilter` (and the supplied `onChange`) is delayed.
|
|
18
|
+
* Useful for client-side filtering of larger fully-loaded datasets
|
|
19
|
+
* (e.g. 1k+ rows) where each keystroke would otherwise re-walk the
|
|
20
|
+
* row model synchronously.
|
|
21
|
+
*
|
|
22
|
+
* Server-driven search (small `data` array, infinite-query-backed)
|
|
23
|
+
* usually wants this OFF because the network request is already
|
|
24
|
+
* the natural rate limiter — keep at the default.
|
|
25
|
+
*
|
|
26
|
+
* Only applies in uncontrolled mode (when neither `value` nor
|
|
27
|
+
* `onChange` is supplied). In controlled mode, debounce in the
|
|
28
|
+
* consumer's `onChange` instead.
|
|
29
|
+
*
|
|
30
|
+
* @default 0
|
|
31
|
+
*/
|
|
32
|
+
debounceMs?: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function TableSearchFilter<TData extends RowData>({ table, className, placeholder, label, showClearButton, onChange, value, debounceMs }: TableSearchFilterProps<TData>): React$1.JSX.Element;
|
|
35
|
+
export declare namespace TableSearchFilter {
|
|
36
|
+
var displayName: string;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Button } from "../../../ui/button.js";
|
|
2
|
+
import { Input } from "../../../ui/input.js";
|
|
3
|
+
import { cn } from "../../../../lib/utils.js";
|
|
4
|
+
import { Search, X } from "lucide-react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import * as React$1 from "react";
|
|
7
|
+
//#region src/components/registry/niko-table/filters/table-search-filter.tsx
|
|
8
|
+
function TableSearchFilter({ table, className, placeholder = "Search...", label = "Search table", showClearButton = true, onChange, value, debounceMs = 0 }) {
|
|
9
|
+
const isControlled = value !== void 0;
|
|
10
|
+
const tableGlobalFilter = table.state.globalFilter;
|
|
11
|
+
const globalFilterValue = typeof tableGlobalFilter === "string" ? tableGlobalFilter : "";
|
|
12
|
+
const debounceEnabled = !isControlled && debounceMs > 0;
|
|
13
|
+
const [pendingValue, setPendingValue] = React$1.useState(globalFilterValue);
|
|
14
|
+
const debounceTimerRef = React$1.useRef(null);
|
|
15
|
+
React$1.useEffect(() => {
|
|
16
|
+
if (debounceTimerRef.current) {
|
|
17
|
+
clearTimeout(debounceTimerRef.current);
|
|
18
|
+
debounceTimerRef.current = null;
|
|
19
|
+
}
|
|
20
|
+
if (!debounceEnabled) return;
|
|
21
|
+
setPendingValue(globalFilterValue);
|
|
22
|
+
}, [globalFilterValue, debounceEnabled]);
|
|
23
|
+
React$1.useEffect(() => {
|
|
24
|
+
return () => {
|
|
25
|
+
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
const currentValue = isControlled ? value : debounceEnabled ? pendingValue : globalFilterValue;
|
|
29
|
+
const handleClear = React$1.useCallback(() => {
|
|
30
|
+
const emptyValue = "";
|
|
31
|
+
if (debounceTimerRef.current) {
|
|
32
|
+
clearTimeout(debounceTimerRef.current);
|
|
33
|
+
debounceTimerRef.current = null;
|
|
34
|
+
}
|
|
35
|
+
if (debounceEnabled) setPendingValue(emptyValue);
|
|
36
|
+
table.setGlobalFilter(emptyValue);
|
|
37
|
+
onChange?.(emptyValue);
|
|
38
|
+
}, [
|
|
39
|
+
table,
|
|
40
|
+
onChange,
|
|
41
|
+
debounceEnabled
|
|
42
|
+
]);
|
|
43
|
+
const handleChange = React$1.useCallback((event) => {
|
|
44
|
+
const newValue = event.target.value;
|
|
45
|
+
if (!debounceEnabled) {
|
|
46
|
+
table.setGlobalFilter(newValue);
|
|
47
|
+
onChange?.(newValue);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
setPendingValue(newValue);
|
|
51
|
+
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
|
52
|
+
debounceTimerRef.current = setTimeout(() => {
|
|
53
|
+
debounceTimerRef.current = null;
|
|
54
|
+
table.setGlobalFilter(newValue);
|
|
55
|
+
onChange?.(newValue);
|
|
56
|
+
}, debounceMs);
|
|
57
|
+
}, [
|
|
58
|
+
table,
|
|
59
|
+
onChange,
|
|
60
|
+
debounceEnabled,
|
|
61
|
+
debounceMs
|
|
62
|
+
]);
|
|
63
|
+
const hasValue = currentValue.length > 0;
|
|
64
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
65
|
+
className: cn("relative flex flex-1 items-center", className),
|
|
66
|
+
role: "search",
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ jsx(Search, {
|
|
69
|
+
className: "absolute left-3 h-4 w-4 text-muted-foreground",
|
|
70
|
+
"aria-hidden": "true"
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ jsx(Input, {
|
|
73
|
+
placeholder,
|
|
74
|
+
value: currentValue,
|
|
75
|
+
onChange: handleChange,
|
|
76
|
+
className: "pr-9 pl-9",
|
|
77
|
+
"aria-label": label
|
|
78
|
+
}),
|
|
79
|
+
hasValue && showClearButton && /* @__PURE__ */ jsxs(Button, {
|
|
80
|
+
variant: "ghost",
|
|
81
|
+
size: "sm",
|
|
82
|
+
onClick: handleClear,
|
|
83
|
+
className: "absolute right-1 h-7 w-7 p-0 hover:bg-muted",
|
|
84
|
+
type: "button",
|
|
85
|
+
"aria-label": "Clear search",
|
|
86
|
+
children: [/* @__PURE__ */ jsx(X, {
|
|
87
|
+
className: "h-3 w-3",
|
|
88
|
+
"aria-hidden": "true"
|
|
89
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
90
|
+
className: "sr-only",
|
|
91
|
+
children: "Clear search"
|
|
92
|
+
})]
|
|
93
|
+
})
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @required displayName is required for auto feature detection
|
|
99
|
+
* @see src/components/niko-table/config/feature-detection.ts
|
|
100
|
+
*/
|
|
101
|
+
TableSearchFilter.displayName = "TableSearchFilter";
|
|
102
|
+
//#endregion
|
|
103
|
+
export { TableSearchFilter };
|
|
@@ -52,14 +52,14 @@ function TableRow({ className, ...props }) {
|
|
|
52
52
|
function TableHead({ className, ...props }) {
|
|
53
53
|
return /* @__PURE__ */ jsx("th", {
|
|
54
54
|
"data-slot": "table-head",
|
|
55
|
-
className: cn("h-
|
|
55
|
+
className: cn("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
|
|
56
56
|
...props
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
function TableCell({ className, ...props }) {
|
|
60
60
|
return /* @__PURE__ */ jsx("td", {
|
|
61
61
|
"data-slot": "table-cell",
|
|
62
|
-
className: cn("p-
|
|
62
|
+
className: cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
|
|
63
63
|
...props
|
|
64
64
|
});
|
|
65
65
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/components/siyahi/data-bar.d.ts
|
|
3
|
+
export interface DataBarProps {
|
|
4
|
+
value: number;
|
|
5
|
+
/** The value a full bar stands for, usually the column's largest: Math.max(...rows.map((row) => row.value)). */
|
|
6
|
+
max: number;
|
|
7
|
+
/** "sign" draws a gain in --positive and a loss in --negative; by default every bar is --chart-1. */
|
|
8
|
+
tone?: "chart" | "sign";
|
|
9
|
+
/** The figure, as the page formats it. */
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A table cell's figure behind a short bar whose length is the figure's share of max, so a column reads as a chart.
|
|
15
|
+
* The bar has a fixed width, so every row draws to the same scale, and sits on the figure's line, so the row
|
|
16
|
+
* keeps its height.
|
|
17
|
+
*/
|
|
18
|
+
export declare function DataBar({ value, max, tone, children, className }: DataBarProps): import("react").JSX.Element;
|
|
19
|
+
//#endregion
|