@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.
Files changed (90) hide show
  1. package/README.md +75 -23
  2. package/THIRD_PARTY_NOTICES.md +11 -10
  3. package/dist/components/registry/charts/area-chart-card.d.ts +4 -3
  4. package/dist/components/registry/charts/area-chart-card.js +2 -2
  5. package/dist/components/registry/charts/bar-chart-card.d.ts +7 -1
  6. package/dist/components/registry/charts/bar-chart-card.js +35 -14
  7. package/dist/components/registry/charts/line-chart-card.d.ts +5 -2
  8. package/dist/components/registry/charts/line-chart-card.js +2 -2
  9. package/dist/components/registry/dashboard-01/section-cards.d.ts +11 -2
  10. package/dist/components/registry/dashboard-01/section-cards.js +44 -26
  11. package/dist/components/registry/dashboardcn/bar-list.d.ts +3 -1
  12. package/dist/components/registry/dashboardcn/bar-list.js +3 -3
  13. package/dist/components/registry/dashboardcn/distribution-bar.d.ts +38 -0
  14. package/dist/components/registry/dashboardcn/distribution-bar.js +118 -0
  15. package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
  16. package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
  17. package/dist/components/registry/dashboardcn/sparkline.d.ts +18 -0
  18. package/dist/components/registry/dashboardcn/sparkline.js +95 -0
  19. package/dist/components/registry/minidev/copy-button.d.ts +11 -0
  20. package/dist/components/registry/minidev/copy-button.js +34 -0
  21. package/dist/components/registry/niko-table/components/data-table-search-filter.d.ts +35 -0
  22. package/dist/components/registry/niko-table/components/data-table-search-filter.js +44 -0
  23. package/dist/components/registry/niko-table/filters/table-search-filter.d.ts +38 -0
  24. package/dist/components/registry/niko-table/filters/table-search-filter.js +103 -0
  25. package/dist/components/registry/niko-table/ui/table.js +2 -2
  26. package/dist/components/registry/sidebar-07/nav-main.d.ts +1 -1
  27. package/dist/components/siyahi/data-bar.d.ts +19 -0
  28. package/dist/components/siyahi/data-bar.js +29 -0
  29. package/dist/components/siyahi/data-list.d.ts +14 -0
  30. package/dist/components/siyahi/data-list.js +25 -0
  31. package/dist/components/siyahi/data-table-row-count.d.ts +11 -0
  32. package/dist/components/siyahi/data-table-row-count.js +18 -0
  33. package/dist/components/siyahi/data-table-sort-header.d.ts +15 -0
  34. package/dist/components/siyahi/data-table-sort-header.js +36 -0
  35. package/dist/components/siyahi/inline-code.d.ts +7 -0
  36. package/dist/components/siyahi/inline-code.js +11 -0
  37. package/dist/components/siyahi/page-section.d.ts +21 -0
  38. package/dist/components/siyahi/page-section.js +39 -0
  39. package/dist/components/siyahi/panel-grid.d.ts +22 -0
  40. package/dist/components/siyahi/panel-grid.js +34 -0
  41. package/dist/components/siyahi/siyahi-provider.d.ts +13 -2
  42. package/dist/components/siyahi/siyahi-provider.js +17 -4
  43. package/dist/components/siyahi/tone-progress.d.ts +15 -0
  44. package/dist/components/siyahi/tone-progress.js +18 -0
  45. package/dist/components/siyahi/treemap.d.ts +7 -0
  46. package/dist/components/siyahi/treemap.js +69 -2
  47. package/dist/components/ui/accordion.js +5 -5
  48. package/dist/components/ui/alert.js +2 -2
  49. package/dist/components/ui/badge.js +1 -1
  50. package/dist/components/ui/breadcrumb.js +2 -2
  51. package/dist/components/ui/button.js +10 -10
  52. package/dist/components/ui/calendar.js +1 -1
  53. package/dist/components/ui/card.js +4 -4
  54. package/dist/components/ui/chart.js +1 -1
  55. package/dist/components/ui/code-block.d.ts +28 -0
  56. package/dist/components/ui/code-block.js +123 -0
  57. package/dist/components/ui/command.js +7 -6
  58. package/dist/components/ui/context-menu.js +7 -7
  59. package/dist/components/ui/dialog.js +5 -5
  60. package/dist/components/ui/dropdown-menu.js +8 -8
  61. package/dist/components/ui/input-group.js +11 -11
  62. package/dist/components/ui/input.js +1 -1
  63. package/dist/components/ui/popover.js +3 -3
  64. package/dist/components/ui/progress.js +1 -1
  65. package/dist/components/ui/select.js +6 -6
  66. package/dist/components/ui/sheet.js +5 -5
  67. package/dist/components/ui/sidebar.js +17 -18
  68. package/dist/components/ui/skeleton.js +1 -1
  69. package/dist/components/ui/slider.js +2 -2
  70. package/dist/components/ui/table.js +2 -2
  71. package/dist/components/ui/tabs.js +2 -2
  72. package/dist/components/ui/textarea.js +1 -1
  73. package/dist/components/ui/toggle-group.js +2 -2
  74. package/dist/components/ui/toggle.js +4 -4
  75. package/dist/components/ui/tooltip.js +2 -2
  76. package/dist/index.d.ts +25 -11
  77. package/dist/index.js +20 -6
  78. package/dist/lib/chart-frame.d.ts +18 -4
  79. package/dist/lib/chart-frame.js +183 -56
  80. package/dist/lib/chart-series.d.ts +58 -3
  81. package/dist/lib/format.d.ts +3 -0
  82. package/dist/lib/format.js +6 -1
  83. package/dist/lib/treemap.d.ts +4 -0
  84. package/dist/lib/treemap.js +9 -4
  85. package/package.json +1 -1
  86. package/registry.lock.json +48 -0
  87. package/report.css +11 -11
  88. package/styles/siyahi.css +53 -11
  89. package/dist/lib/recharts.d.ts +0 -2
  90. 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-12 px-3 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
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-3 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
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
  }
@@ -4,7 +4,7 @@ export declare function NavMain({ items, label }: {
4
4
  items: {
5
5
  title: string;
6
6
  url: string;
7
- icon?: React.ReactNode;
7
+ icon: React.ReactNode;
8
8
  isActive?: boolean;
9
9
  linkProps?: Omit<React.ComponentProps<"a">, "href">;
10
10
  items?: {
@@ -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