@siyahi/ui 0.1.9-next.2 → 0.1.9-next.20
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 +82 -176
- package/THIRD_PARTY_NOTICES.md +11 -11
- package/agents/examples/accordion.tsx +39 -0
- package/agents/examples/alert.tsx +25 -0
- package/agents/examples/app-sidebar.tsx +38 -0
- package/agents/examples/avatar.tsx +14 -0
- package/agents/examples/badge.tsx +18 -0
- package/agents/examples/bar-list-labels.tsx +22 -0
- package/agents/examples/bar-list-sides.tsx +30 -0
- package/agents/examples/bar-list.tsx +24 -0
- package/agents/examples/breadcrumb.tsx +28 -0
- package/agents/examples/button.tsx +21 -0
- package/agents/examples/calendar.tsx +15 -0
- package/agents/examples/card.tsx +34 -0
- package/agents/examples/chart-area.tsx +38 -0
- package/agents/examples/chart-bar-flows.tsx +31 -0
- package/agents/examples/chart-bar-hourly.tsx +28 -0
- package/agents/examples/chart-bar-losses.tsx +29 -0
- package/agents/examples/chart-bar-sectors.tsx +70 -0
- package/agents/examples/chart-bar-years.tsx +40 -0
- package/agents/examples/chart-bar.tsx +56 -0
- package/agents/examples/chart-category-ticks.tsx +50 -0
- package/agents/examples/chart-line-events.tsx +47 -0
- package/agents/examples/chart-line-timeline.tsx +41 -0
- package/agents/examples/chart-line.tsx +57 -0
- package/agents/examples/code-block.tsx +38 -0
- package/agents/examples/collapsible.tsx +16 -0
- package/agents/examples/command.tsx +27 -0
- package/agents/examples/context-menu.tsx +15 -0
- package/agents/examples/copy-button.tsx +8 -0
- package/agents/examples/data-bar.tsx +121 -0
- package/agents/examples/data-table-filters.tsx +103 -0
- package/agents/examples/data-table.tsx +98 -0
- package/agents/examples/delta.tsx +13 -0
- package/agents/examples/dialog.tsx +28 -0
- package/agents/examples/distribution-bar-budget.tsx +30 -0
- package/agents/examples/distribution-bar-tug.tsx +72 -0
- package/agents/examples/dropdown-menu.tsx +31 -0
- package/agents/examples/formatters.tsx +42 -0
- package/agents/examples/heatmap-calendar.tsx +50 -0
- package/agents/examples/heatmap-status.tsx +51 -0
- package/agents/examples/heatmap-weekday.tsx +53 -0
- package/agents/examples/input.tsx +17 -0
- package/agents/examples/local-time.tsx +14 -0
- package/agents/examples/metric-strip.tsx +40 -0
- package/agents/examples/page-section.tsx +52 -0
- package/agents/examples/popover.tsx +23 -0
- package/agents/examples/price-chart.tsx +32 -0
- package/agents/examples/progress-tone.tsx +28 -0
- package/agents/examples/progress.tsx +10 -0
- package/agents/examples/scroll-area.tsx +20 -0
- package/agents/examples/section-cards-select.tsx +27 -0
- package/agents/examples/section-cards-trend.tsx +30 -0
- package/agents/examples/section-cards.tsx +48 -0
- package/agents/examples/select.tsx +27 -0
- package/agents/examples/separator.tsx +13 -0
- package/agents/examples/sheet.tsx +15 -0
- package/agents/examples/skeleton.tsx +11 -0
- package/agents/examples/slider.tsx +5 -0
- package/agents/examples/surfaces.tsx +115 -0
- package/agents/examples/table.tsx +50 -0
- package/agents/examples/tabs.tsx +16 -0
- package/agents/examples/toggle-group.tsx +12 -0
- package/agents/examples/toggle.tsx +11 -0
- package/agents/examples/tooltip.tsx +13 -0
- package/agents/examples/treemap.tsx +87 -0
- package/agents/manifest.json +931 -0
- package/agents/report-rules.json +7 -0
- package/dist/components/registry/charts/bar-chart-card.js +33 -26
- package/dist/components/registry/dashboard-01/section-cards.d.ts +2 -0
- package/dist/components/registry/dashboard-01/section-cards.js +42 -35
- package/dist/components/registry/dashboardcn/bar-list.js +9 -8
- package/dist/components/registry/dashboardcn/distribution-bar.js +1 -1
- 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/heatmap/calendar-heatmap.js +8 -1
- package/dist/components/registry/heatmap/status-heatmap.js +8 -1
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +6 -2
- package/dist/components/registry/heatmap/weekday-heatmap.js +56 -37
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +2 -0
- package/dist/components/registry/niko-table/core/data-table-context.js +21 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
- package/dist/components/registry/niko-table/core/data-table-structure.js +15 -12
- package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
- package/dist/components/registry/niko-table/core/data-table.js +5 -1
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +4 -4
- package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
- package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/styles.js +2 -2
- package/dist/components/registry/niko-table/ui/table.js +2 -2
- package/dist/components/registry/sidebar-07/app-sidebar.d.ts +1 -1
- package/dist/components/registry/sidebar-07/app-sidebar.js +3 -1
- package/dist/components/registry/sidebar-07/nav-main.d.ts +1 -1
- package/dist/components/registry/sidebar-07/nav-main.js +7 -1
- package/dist/components/siyahi/data-bar.d.ts +19 -0
- package/dist/components/siyahi/data-bar.js +29 -0
- package/dist/components/siyahi/page-section.d.ts +4 -1
- package/dist/components/siyahi/page-section.js +6 -3
- 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/treemap.js +46 -4
- 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.js +13 -12
- 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 +7 -4
- package/dist/index.js +7 -4
- package/dist/lib/chart-frame.d.ts +13 -5
- package/dist/lib/chart-frame.js +207 -127
- package/dist/lib/chart-series.d.ts +7 -4
- package/dist/lib/format.js +32 -13
- package/dist/lib/heatmap-navigation.d.ts +8 -0
- package/dist/lib/heatmap-navigation.js +49 -0
- package/package.json +3 -1
- package/registry.lock.json +16 -0
- package/report.css +11 -11
- package/styles/siyahi.css +77 -11
- package/dist/lib/recharts.d.ts +0 -2
- package/dist/lib/recharts.js +0 -1
|
@@ -1,39 +1,46 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { ChartFrame } from "../../../lib/chart-frame.js";
|
|
2
|
+
import { ChartFrame, seriesFill } from "../../../lib/chart-frame.js";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { Bar, BarChart, Cell } from "recharts";
|
|
5
5
|
//#region src/components/registry/charts/bar-chart-card.tsx
|
|
6
|
+
const DOWN_FROM_ZERO = ["auto", 0];
|
|
6
7
|
/** Values over an ordered axis as bars: one series, or several grouped or stacked. */
|
|
7
8
|
function BarChartCard({ stacked = false, selected = null, onSelect, ...props }) {
|
|
8
9
|
return /* @__PURE__ */ jsx(ChartFrame, {
|
|
9
10
|
chart: BarChart,
|
|
10
11
|
...props,
|
|
12
|
+
yDomain: props.yDomain ?? (props.data.some((row) => props.series.some(({ key }) => Number(row[key]) > 0)) ? void 0 : DOWN_FROM_ZERO),
|
|
11
13
|
stackOffset: stacked ? "sign" : void 0,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
0,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
14
|
+
onClick: onSelect ? ({ activeTooltipIndex, isTooltipActive }) => {
|
|
15
|
+
if (!isTooltipActive || activeTooltipIndex == null) return;
|
|
16
|
+
const row = props.data[Number(activeTooltipIndex)];
|
|
17
|
+
if (row) onSelect(row[props.xKey] === selected ? null : row[props.xKey]);
|
|
18
|
+
} : void 0,
|
|
19
|
+
children: props.series.map((item, index) => {
|
|
20
|
+
const { key, label, color, negativeColor } = item;
|
|
21
|
+
return /* @__PURE__ */ jsx(Bar, {
|
|
22
|
+
dataKey: key,
|
|
23
|
+
name: label,
|
|
24
|
+
fill: seriesFill(item, props.data),
|
|
25
|
+
maxBarSize: props.horizontal ? 24 : void 0,
|
|
26
|
+
stackId: stacked ? "stack" : void 0,
|
|
27
|
+
radius: !stacked ? 2 : index === props.series.length - 1 ? props.horizontal ? [
|
|
28
|
+
0,
|
|
29
|
+
2,
|
|
30
|
+
2,
|
|
31
|
+
0
|
|
32
|
+
] : [
|
|
33
|
+
2,
|
|
34
|
+
2,
|
|
35
|
+
0,
|
|
36
|
+
0
|
|
37
|
+
] : 0,
|
|
38
|
+
children: (negativeColor || selected !== null) && props.data.map((row, i) => /* @__PURE__ */ jsx(Cell, {
|
|
39
|
+
fill: negativeColor && Number(row[key]) < 0 ? negativeColor : color,
|
|
40
|
+
fillOpacity: selected === null || row[props.xKey] === selected ? 1 : .35
|
|
41
|
+
}, i))
|
|
42
|
+
}, key);
|
|
43
|
+
})
|
|
37
44
|
});
|
|
38
45
|
}
|
|
39
46
|
//#endregion
|
|
@@ -6,6 +6,8 @@ export interface SectionCard {
|
|
|
6
6
|
value: React$1.ReactNode;
|
|
7
7
|
/** Outline badge, top right; usually a <Delta />. */
|
|
8
8
|
badge?: React$1.ReactNode;
|
|
9
|
+
/** A small chart under the value, usually a <Sparkline /> of the recent run. */
|
|
10
|
+
trend?: React$1.ReactNode;
|
|
9
11
|
/** Bold first footer line. */
|
|
10
12
|
headline?: React$1.ReactNode;
|
|
11
13
|
/** Muted second footer line. */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Badge } from "../../ui/badge.js";
|
|
3
|
-
import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from "../../ui/card.js";
|
|
3
|
+
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../../ui/card.js";
|
|
4
4
|
import { cn } from "../../../lib/utils.js";
|
|
5
|
+
import { useSurface } from "../../siyahi/siyahi-provider.js";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
import "react";
|
|
7
8
|
//#region src/components/registry/dashboard-01/section-cards.tsx
|
|
@@ -13,43 +14,49 @@ const COLUMNS = {
|
|
|
13
14
|
6: "@3xl/main:grid-cols-3"
|
|
14
15
|
};
|
|
15
16
|
function SectionCards({ items, selected, onSelect }) {
|
|
17
|
+
const ruled = useSurface() === "ruled";
|
|
16
18
|
return /* @__PURE__ */ jsx("div", {
|
|
17
|
-
className: "@container/main",
|
|
19
|
+
className: cn("@container/main", ruled && "overflow-hidden border-y"),
|
|
18
20
|
children: /* @__PURE__ */ jsx("div", {
|
|
19
|
-
className: cn("grid grid-cols-1 gap-4", COLUMNS[items.length] ?? COLUMNS[4]),
|
|
20
|
-
children: items.map((item) => /* @__PURE__ */
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/* @__PURE__ */ jsx(CardTitle, {
|
|
34
|
-
className: "text-2xl font-semibold tabular-nums @[250px]/card:text-3xl",
|
|
35
|
-
children: item.value
|
|
21
|
+
className: cn("grid grid-cols-1", ruled ? "-mt-px -ml-px" : "gap-4", COLUMNS[items.length] ?? COLUMNS[4]),
|
|
22
|
+
children: items.map((item) => /* @__PURE__ */ jsx("div", {
|
|
23
|
+
"data-slot": "section-card",
|
|
24
|
+
className: cn("min-w-0", ruled && "border-t border-l"),
|
|
25
|
+
children: /* @__PURE__ */ jsxs(Card, {
|
|
26
|
+
className: cn("@container/card h-full [--card-spacing:--spacing(3)]", onSelect && "relative", item.label === selected && "ring-2 ring-primary!", item.label === selected && ruled && "ring-inset", onSelect && item.disabled && "opacity-60"),
|
|
27
|
+
children: [
|
|
28
|
+
onSelect && /* @__PURE__ */ jsx("button", {
|
|
29
|
+
type: "button",
|
|
30
|
+
"aria-label": item.label,
|
|
31
|
+
"aria-pressed": item.label === selected,
|
|
32
|
+
disabled: item.disabled,
|
|
33
|
+
onClick: () => onSelect(item.label),
|
|
34
|
+
className: "absolute inset-0 z-10 rounded-[inherit] outline-none focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-inset"
|
|
36
35
|
}),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
36
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
37
|
+
/* @__PURE__ */ jsx(CardDescription, { children: item.label }),
|
|
38
|
+
/* @__PURE__ */ jsx(CardTitle, {
|
|
39
|
+
className: "text-2xl font-semibold tabular-nums",
|
|
40
|
+
children: item.value
|
|
41
|
+
}),
|
|
42
|
+
item.badge && /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Badge, {
|
|
43
|
+
variant: "outline",
|
|
44
|
+
children: item.badge
|
|
45
|
+
}) })
|
|
46
|
+
] }),
|
|
47
|
+
item.trend && /* @__PURE__ */ jsx(CardContent, { children: item.trend }),
|
|
48
|
+
(item.headline || item.detail) && /* @__PURE__ */ jsxs(CardFooter, {
|
|
49
|
+
className: "flex-col items-start gap-1 text-sm",
|
|
50
|
+
children: [item.headline && /* @__PURE__ */ jsx("div", {
|
|
51
|
+
className: "flex flex-wrap gap-2 font-medium",
|
|
52
|
+
children: item.headline
|
|
53
|
+
}), item.detail && /* @__PURE__ */ jsx("div", {
|
|
54
|
+
className: "text-muted-foreground",
|
|
55
|
+
children: item.detail
|
|
56
|
+
})]
|
|
57
|
+
})
|
|
58
|
+
]
|
|
59
|
+
})
|
|
53
60
|
}, item.label))
|
|
54
61
|
})
|
|
55
62
|
});
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { useLocale } from "../../siyahi/siyahi-provider.js";
|
|
2
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useLocale } from "../../siyahi/siyahi-provider.js";
|
|
3
3
|
import { pct } from "../../../lib/format.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import * as React$1 from "react";
|
|
6
6
|
//#region src/components/registry/dashboardcn/bar-list.tsx
|
|
7
7
|
function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(--chart-1)", showPercentage = false, max: scale, onItemClick, className, ...props }) {
|
|
8
8
|
const locale = useLocale();
|
|
9
|
-
const
|
|
9
|
+
const defaultFormat = React$1.useMemo(() => new Intl.NumberFormat(locale.numberLocale, {
|
|
10
10
|
notation: "compact",
|
|
11
11
|
maximumFractionDigits: 1
|
|
12
|
-
}).format
|
|
12
|
+
}).format, [locale.numberLocale]);
|
|
13
|
+
const format = valueFormatter ?? defaultFormat;
|
|
13
14
|
const items = React$1.useMemo(() => {
|
|
14
15
|
if (sortOrder === "none") return data;
|
|
15
16
|
const sorted = [...data].sort((a, b) => a.value - b.value);
|
|
@@ -19,7 +20,7 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
|
|
|
19
20
|
const total = items.reduce((sum, item) => sum + item.value, 0);
|
|
20
21
|
return /* @__PURE__ */ jsx("div", {
|
|
21
22
|
"data-slot": "bar-list",
|
|
22
|
-
className: cn("flex flex-col gap-1.5", className),
|
|
23
|
+
className: cn("@container/bar-list flex flex-col gap-1.5", className),
|
|
23
24
|
...props,
|
|
24
25
|
children: items.map((item) => {
|
|
25
26
|
const width = max > 0 ? item.value / max * 100 : 0;
|
|
@@ -28,13 +29,13 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
|
|
|
28
29
|
const Label = item.href ? "a" : onItemClick ? "button" : "div";
|
|
29
30
|
return /* @__PURE__ */ jsxs("div", {
|
|
30
31
|
"data-slot": "bar-list-row",
|
|
31
|
-
className: "flex items-center gap-3 text-sm",
|
|
32
|
+
className: "flex items-center gap-3 text-sm @max-sm/bar-list:items-start",
|
|
32
33
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
33
|
-
className: "relative flex h-8 min-w-0 flex-1 items-center",
|
|
34
|
+
className: "relative flex h-8 min-w-0 flex-1 items-center @max-sm/bar-list:h-auto @max-sm/bar-list:min-h-8 @max-sm/bar-list:items-start @max-sm/bar-list:pb-3",
|
|
34
35
|
children: [
|
|
35
36
|
/* @__PURE__ */ jsx("div", {
|
|
36
37
|
"aria-hidden": "true",
|
|
37
|
-
className: "absolute inset-y-0 left-0 rounded-md transition-[width] duration-300",
|
|
38
|
+
className: "absolute inset-y-0 left-0 rounded-md transition-[width] duration-300 @max-sm/bar-list:inset-y-auto @max-sm/bar-list:bottom-0 @max-sm/bar-list:h-1.5",
|
|
38
39
|
style: {
|
|
39
40
|
width: `${width}%`,
|
|
40
41
|
backgroundColor: `color-mix(in oklab, ${color} 20%, transparent)`
|
|
@@ -51,7 +52,7 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
|
|
|
51
52
|
className: "text-muted-foreground shrink-0 [&>svg]:size-4",
|
|
52
53
|
children: item.icon
|
|
53
54
|
}) : null, /* @__PURE__ */ jsx("span", {
|
|
54
|
-
className: "truncate",
|
|
55
|
+
className: "truncate @max-sm/bar-list:whitespace-normal",
|
|
55
56
|
children: item.name
|
|
56
57
|
})]
|
|
57
58
|
}),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useLocale } from "../../siyahi/siyahi-provider.js";
|
|
2
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useLocale } from "../../siyahi/siyahi-provider.js";
|
|
3
3
|
import { pct } from "../../../lib/format.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import "react";
|
|
@@ -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 };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
2
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
import { Fragment as Fragment$1, createContext, use, useMemo } from "react";
|
|
4
5
|
import { differenceInCalendarDays, eachDayOfInterval, format, formatISO, getDay, getMonth, getYear, nextDay, parseISO, subWeeks } from "date-fns";
|
|
@@ -290,7 +291,7 @@ const CalendarHeatmap = ({ data, weekStart = 0, continuousMonths = true, hasEmpt
|
|
|
290
291
|
value: contextValue,
|
|
291
292
|
children: /* @__PURE__ */ jsx("div", {
|
|
292
293
|
"data-slot": "calendar-heatmap",
|
|
293
|
-
className: cn("flex w-max max-w-full flex-col gap-2
|
|
294
|
+
className: cn("flex w-max max-w-full flex-col gap-2", className),
|
|
294
295
|
style: {
|
|
295
296
|
fontSize,
|
|
296
297
|
...style
|
|
@@ -349,6 +350,7 @@ const CalendarHeatmapBlock = ({ ref, activity, dayIndex, weekIndex, highlighted
|
|
|
349
350
|
};
|
|
350
351
|
CalendarHeatmapBlock.displayName = "CalendarHeatmapBlock";
|
|
351
352
|
const CalendarHeatmapBody = ({ hideMonthLabels = false, hideWeekdayLabels = false, hideYearLabels = false, className, labelClassName, yearClassName, children, renderYearFooter, ...props }) => {
|
|
353
|
+
const navigation = useHeatmapNavigation();
|
|
352
354
|
const { yearRows, blockSize, blockWidth, blockMargin, labels, labelHeight, fontSize, weekStart, data } = useCalendarHeatmap();
|
|
353
355
|
const weekdayLabelWidth = hideWeekdayLabels ? 0 : 40;
|
|
354
356
|
const strokePadding = 3;
|
|
@@ -374,8 +376,13 @@ const CalendarHeatmapBody = ({ hideMonthLabels = false, hideWeekdayLabels = fals
|
|
|
374
376
|
const totalWidth = weekdayLabelWidth + Math.max(...rowData.map((r) => r.width)) + 6;
|
|
375
377
|
return /* @__PURE__ */ jsx("div", {
|
|
376
378
|
"data-slot": "calendar-heatmap-body",
|
|
379
|
+
ref: navigation.ref,
|
|
377
380
|
className: cn("flex max-w-full flex-col gap-6 overflow-x-auto overflow-y-hidden py-4", className),
|
|
378
381
|
...props,
|
|
382
|
+
onKeyDown: (event) => {
|
|
383
|
+
props.onKeyDown?.(event);
|
|
384
|
+
if (!event.defaultPrevented) navigation.onKeyDown(event);
|
|
385
|
+
},
|
|
379
386
|
children: rowData.map(({ yearRow, height, monthLabels, yearTotalCount }) => /* @__PURE__ */ jsxs("div", { children: [
|
|
380
387
|
!hideYearLabels && /* @__PURE__ */ jsx("div", {
|
|
381
388
|
className: cn("mb-2 text-muted-foreground", yearClassName),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
2
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
import { Fragment as Fragment$1, createContext, use, useMemo } from "react";
|
|
4
5
|
import { format } from "date-fns";
|
|
@@ -155,7 +156,7 @@ const StatusHeatmap = ({ data, dateFormat = "MMM d", blockSize = 40, blockMargin
|
|
|
155
156
|
value: contextValue,
|
|
156
157
|
children: /* @__PURE__ */ jsx("div", {
|
|
157
158
|
"data-slot": "status-heatmap",
|
|
158
|
-
className: cn("flex w-max max-w-full flex-col gap-2
|
|
159
|
+
className: cn("flex w-max max-w-full flex-col gap-2", className),
|
|
159
160
|
style: {
|
|
160
161
|
fontSize,
|
|
161
162
|
...style
|
|
@@ -214,6 +215,7 @@ const StatusHeatmapBlock = ({ ref, activity, dayIndex, highlighted = false, onCe
|
|
|
214
215
|
};
|
|
215
216
|
StatusHeatmapBlock.displayName = "StatusHeatmapBlock";
|
|
216
217
|
const StatusHeatmapBody = ({ hideDateLabels = false, labelInterval = 30, className, labelClassName, children, ...props }) => {
|
|
218
|
+
const navigation = useHeatmapNavigation();
|
|
217
219
|
const { data, dates, width, height, blockWidth, blockMargin, fontSize, dateFormat, locale, labels } = useStatusHeatmap();
|
|
218
220
|
const activityMap = useMemo(() => {
|
|
219
221
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -234,8 +236,13 @@ const StatusHeatmapBody = ({ hideDateLabels = false, labelInterval = 30, classNa
|
|
|
234
236
|
const labelHeight = hideDateLabels ? 0 : labelFontSize * 2;
|
|
235
237
|
return /* @__PURE__ */ jsx("div", {
|
|
236
238
|
"data-slot": "status-heatmap-body",
|
|
239
|
+
ref: navigation.ref,
|
|
237
240
|
className: cn("max-w-full overflow-x-auto overflow-y-hidden", className),
|
|
238
241
|
...props,
|
|
242
|
+
onKeyDown: (event) => {
|
|
243
|
+
props.onKeyDown?.(event);
|
|
244
|
+
if (!event.defaultPrevented) navigation.onKeyDown(event);
|
|
245
|
+
},
|
|
239
246
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
240
247
|
role: "img",
|
|
241
248
|
"aria-label": labels.heatmapLabel ?? "Status heatmap",
|
|
@@ -36,6 +36,10 @@ export type ColorConfig = {
|
|
|
36
36
|
export type WeekdayHeatmapProps = HTMLAttributes<HTMLDivElement> & {
|
|
37
37
|
data: WeekdayHourlyActivity[];
|
|
38
38
|
weekStart?: Day;
|
|
39
|
+
/** Displayed hours, in order; defaults to the full day. */
|
|
40
|
+
hours?: readonly WeekdayHour[];
|
|
41
|
+
/** Displayed days, in order; defaults to the full week from weekStart. */
|
|
42
|
+
weekdays?: readonly WeekdayIndex[];
|
|
39
43
|
use12Hour?: boolean;
|
|
40
44
|
blockSize?: number;
|
|
41
45
|
blockMargin?: number;
|
|
@@ -86,7 +90,7 @@ export type WeekdayHeatmapProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
86
90
|
* @param extraRow - Appends an aggregated row below the grid. `compute` receives all activities and must return 24 values (one per hour). Rendered through `renderExtraRow` on the body, or falls back to `children`.
|
|
87
91
|
* @param extraColumn - Appends an aggregated column to the right of the grid. `compute` receives all activities and must return 7 values indexed by weekday 0–6. Rendered through `renderExtraColumn` on the body, or falls back to `children`.
|
|
88
92
|
*/
|
|
89
|
-
export declare const WeekdayHeatmap: ({ data, weekStart, use12Hour, blockSize, blockMargin, blockRadius, blockAspectRatio, levels: levelsProp, isNormalized, colors, locale, labels: labelsProp, fontSize, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style, className, children, ...props }: WeekdayHeatmapProps) => string | number | bigint | true | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
|
|
93
|
+
export declare const WeekdayHeatmap: ({ data, weekStart, hours, weekdays: displayedWeekdays, use12Hour, blockSize, blockMargin, blockRadius, blockAspectRatio, levels: levelsProp, isNormalized, colors, locale, labels: labelsProp, fontSize, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style, className, children, ...props }: WeekdayHeatmapProps) => string | number | bigint | true | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
|
|
90
94
|
export type WeekdayHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
|
|
91
95
|
activity: WeekdayHourlyActivityWithLevel;
|
|
92
96
|
extra?: "row" | "column";
|
|
@@ -97,7 +101,7 @@ export type WeekdayHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
|
|
|
97
101
|
};
|
|
98
102
|
export declare function WeekdayHeatmapBlock({ ref, activity, extra, highlighted, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }: WeekdayHeatmapBlockProps & {
|
|
99
103
|
ref?: React.RefObject<SVGRectElement | null>;
|
|
100
|
-
}): import("react").JSX.Element;
|
|
104
|
+
}): import("react").JSX.Element | null;
|
|
101
105
|
export declare namespace WeekdayHeatmapBlock {
|
|
102
106
|
var displayName: string;
|
|
103
107
|
}
|