harebear-ui 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +63 -0
- package/LICENSE +21 -0
- package/README.md +92 -2
- package/bin/harebear-ui.mjs +125 -0
- package/dist/components/admin/activity-feed.d.ts +15 -0
- package/dist/components/admin/activity-feed.js +50 -0
- package/dist/components/admin/app-shell.d.ts +47 -0
- package/dist/components/admin/app-shell.js +108 -0
- package/dist/components/admin/callout.d.ts +7 -0
- package/dist/components/admin/callout.js +18 -0
- package/dist/components/admin/key-value-grid.d.ts +11 -0
- package/dist/components/admin/key-value-grid.js +23 -0
- package/dist/components/admin/link-tile.d.ts +17 -0
- package/dist/components/admin/link-tile.js +36 -0
- package/dist/components/admin/page-header.d.ts +14 -0
- package/dist/components/admin/page-header.js +41 -0
- package/dist/components/admin/search-input.d.ts +12 -0
- package/dist/components/admin/search-input.js +32 -0
- package/dist/components/admin/stat-card.d.ts +20 -0
- package/dist/components/admin/stat-card.js +53 -0
- package/dist/components/admin/status.d.ts +12 -0
- package/dist/components/admin/status.js +32 -0
- package/dist/components/admin/timeline.d.ts +13 -0
- package/dist/components/admin/timeline.js +36 -0
- package/dist/components/admin/usage-meter.d.ts +17 -0
- package/dist/components/admin/usage-meter.js +43 -0
- package/dist/components/admin/user-chip.d.ts +14 -0
- package/dist/components/admin/user-chip.js +38 -0
- package/dist/components/ai/chat.d.ts +44 -0
- package/dist/components/ai/chat.js +147 -0
- package/dist/components/ai/code-block.d.ts +21 -0
- package/dist/components/ai/code-block.js +53 -0
- package/dist/components/ai/conversation-list.d.ts +18 -0
- package/dist/components/ai/conversation-list.js +30 -0
- package/dist/components/ai/model-picker.d.ts +21 -0
- package/dist/components/ai/model-picker.js +55 -0
- package/dist/components/ai/prompt-input.d.ts +27 -0
- package/dist/components/ai/prompt-input.js +99 -0
- package/dist/components/data/bar-list.d.ts +14 -0
- package/dist/components/data/bar-list.js +27 -0
- package/dist/components/data/data-table.d.ts +70 -0
- package/dist/components/data/data-table.js +208 -0
- package/dist/components/data/sparkline.d.ts +16 -0
- package/dist/components/data/sparkline.js +72 -0
- package/dist/components/data/status-bars.d.ts +17 -0
- package/dist/components/data/status-bars.js +37 -0
- package/dist/components/data/trend-chart.d.ts +31 -0
- package/dist/components/data/trend-chart.js +136 -0
- package/dist/components/theme/theme-provider.d.ts +42 -0
- package/dist/components/theme/theme-provider.js +110 -0
- package/dist/components/theme/theme-toggle.d.ts +5 -0
- package/dist/components/theme/theme-toggle.js +20 -0
- package/dist/components/ui/accordion.d.ts +7 -0
- package/dist/components/ui/accordion.js +53 -0
- package/dist/components/ui/alert-dialog.d.ts +18 -0
- package/dist/components/ui/alert-dialog.js +99 -0
- package/dist/components/ui/alert.d.ts +10 -0
- package/dist/components/ui/alert.js +42 -0
- package/dist/components/ui/aspect-ratio.d.ts +3 -0
- package/dist/components/ui/aspect-ratio.js +11 -0
- package/dist/components/ui/avatar.d.ts +11 -0
- package/dist/components/ui/avatar.js +49 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +30 -0
- package/dist/components/ui/breadcrumb.d.ts +11 -0
- package/dist/components/ui/breadcrumb.js +70 -0
- package/dist/components/ui/button-group.d.ts +11 -0
- package/dist/components/ui/button-group.js +39 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/button.js +55 -0
- package/dist/components/ui/calendar.d.ts +10 -0
- package/dist/components/ui/calendar.js +108 -0
- package/dist/components/ui/card.d.ts +11 -0
- package/dist/components/ui/card.js +55 -0
- package/dist/components/ui/carousel.d.ts +28 -0
- package/dist/components/ui/carousel.js +131 -0
- package/dist/components/ui/chart.d.ts +44 -0
- package/dist/components/ui/chart.js +148 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/checkbox.js +19 -0
- package/dist/components/ui/collapsible.d.ts +5 -0
- package/dist/components/ui/collapsible.js +23 -0
- package/dist/components/ui/command.d.ts +18 -0
- package/dist/components/ui/command.js +86 -0
- package/dist/components/ui/context-menu.d.ts +31 -0
- package/dist/components/ui/context-menu.js +123 -0
- package/dist/components/ui/dialog.d.ts +17 -0
- package/dist/components/ui/dialog.js +94 -0
- package/dist/components/ui/drawer.d.ts +13 -0
- package/dist/components/ui/drawer.js +76 -0
- package/dist/components/ui/dropdown-menu.d.ts +29 -0
- package/dist/components/ui/dropdown-menu.js +126 -0
- package/dist/components/ui/empty.d.ts +11 -0
- package/dist/components/ui/empty.js +59 -0
- package/dist/components/ui/field.d.ts +24 -0
- package/dist/components/ui/field.js +109 -0
- package/dist/components/ui/hover-card.d.ts +6 -0
- package/dist/components/ui/hover-card.js +30 -0
- package/dist/components/ui/input-group.d.ts +16 -0
- package/dist/components/ui/input-group.js +77 -0
- package/dist/components/ui/input-otp.d.ts +11 -0
- package/dist/components/ui/input-otp.js +46 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/input.js +13 -0
- package/dist/components/ui/item.d.ts +23 -0
- package/dist/components/ui/item.js +117 -0
- package/dist/components/ui/kbd.d.ts +3 -0
- package/dist/components/ui/kbd.js +19 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.js +13 -0
- package/dist/components/ui/menubar.d.ts +30 -0
- package/dist/components/ui/menubar.js +133 -0
- package/dist/components/ui/navigation-menu.d.ts +14 -0
- package/dist/components/ui/navigation-menu.js +79 -0
- package/dist/components/ui/pagination.d.ts +17 -0
- package/dist/components/ui/pagination.js +79 -0
- package/dist/components/ui/popover.d.ts +10 -0
- package/dist/components/ui/popover.js +54 -0
- package/dist/components/ui/progress.d.ts +4 -0
- package/dist/components/ui/progress.js +18 -0
- package/dist/components/ui/radio-group.d.ts +5 -0
- package/dist/components/ui/radio-group.js +25 -0
- package/dist/components/ui/resizable.d.ts +7 -0
- package/dist/components/ui/resizable.js +27 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/scroll-area.js +35 -0
- package/dist/components/ui/select.d.ts +15 -0
- package/dist/components/ui/select.js +98 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/separator.js +15 -0
- package/dist/components/ui/sheet.d.ts +14 -0
- package/dist/components/ui/sheet.js +88 -0
- package/dist/components/ui/sidebar.d.ts +69 -0
- package/dist/components/ui/sidebar.js +377 -0
- package/dist/components/ui/skeleton.d.ts +2 -0
- package/dist/components/ui/skeleton.js +12 -0
- package/dist/components/ui/slider.d.ts +4 -0
- package/dist/components/ui/slider.js +35 -0
- package/dist/components/ui/sonner.d.ts +3 -0
- package/dist/components/ui/sonner.js +29 -0
- package/dist/components/ui/spinner.d.ts +2 -0
- package/dist/components/ui/spinner.js +15 -0
- package/dist/components/ui/switch.d.ts +6 -0
- package/dist/components/ui/switch.js +18 -0
- package/dist/components/ui/table.d.ts +10 -0
- package/dist/components/ui/table.js +65 -0
- package/dist/components/ui/tabs.d.ts +11 -0
- package/dist/components/ui/tabs.js +45 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/textarea.js +12 -0
- package/dist/components/ui/toggle-group.d.ts +10 -0
- package/dist/components/ui/toggle-group.js +50 -0
- package/dist/components/ui/toggle.d.ts +9 -0
- package/dist/components/ui/toggle.js +35 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tooltip.js +34 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts +5 -0
- package/dist/hooks/use-copy-to-clipboard.js +24 -0
- package/dist/hooks/use-mobile.d.ts +1 -0
- package/dist/hooks/use-mobile.js +17 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +80 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.js +8 -0
- package/dist/shadcn-base.css +638 -0
- package/dist/styles.css +188 -0
- package/docs/COMPONENTS.md +352 -0
- package/llms.txt +34 -0
- package/package.json +116 -4
- package/skills/harebear-ui/SKILL.md +33 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { StatusTone } from "../admin/status.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
export type StatusBarDatum = number | {
|
|
4
|
+
value: number;
|
|
5
|
+
tone?: StatusTone;
|
|
6
|
+
label?: string;
|
|
7
|
+
};
|
|
8
|
+
export interface StatusBarsProps extends Omit<React.ComponentProps<"div">, "children"> {
|
|
9
|
+
data: StatusBarDatum[];
|
|
10
|
+
/** Value mapped to full height. Defaults to the max in data. */
|
|
11
|
+
max?: number;
|
|
12
|
+
/** Tone for plain-number data. Default "info" (primary). */
|
|
13
|
+
tone?: StatusTone;
|
|
14
|
+
}
|
|
15
|
+
/** Mini bar history, e.g. last 20 pings or uptime checks. Size it with className (default 96x26). */
|
|
16
|
+
declare function StatusBars({ data, max, tone, className, ...props }: StatusBarsProps): React.JSX.Element;
|
|
17
|
+
export { StatusBars };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
// src/components/data/status-bars.tsx
|
|
5
|
+
const toneBg = {
|
|
6
|
+
success: "var(--success)",
|
|
7
|
+
warning: "var(--warning)",
|
|
8
|
+
danger: "var(--destructive)",
|
|
9
|
+
info: "var(--primary)",
|
|
10
|
+
neutral: "var(--muted-foreground)"
|
|
11
|
+
};
|
|
12
|
+
/** Mini bar history, e.g. last 20 pings or uptime checks. Size it with className (default 96x26). */
|
|
13
|
+
function StatusBars({ data, max, tone = "info", className, ...props }) {
|
|
14
|
+
const items = data.map((d) => typeof d === "number" ? {
|
|
15
|
+
value: d,
|
|
16
|
+
tone
|
|
17
|
+
} : {
|
|
18
|
+
tone,
|
|
19
|
+
...d
|
|
20
|
+
});
|
|
21
|
+
const top = max ?? Math.max(1, ...items.map((d) => d.value));
|
|
22
|
+
return /* @__PURE__ */ jsx("div", {
|
|
23
|
+
"data-slot": "status-bars",
|
|
24
|
+
"aria-hidden": "true",
|
|
25
|
+
className: cn("flex h-6.5 w-24 shrink-0 items-end gap-0.5", className),
|
|
26
|
+
...props,
|
|
27
|
+
children: items.map((d, i) => /* @__PURE__ */ jsx("span", {
|
|
28
|
+
title: d.label,
|
|
29
|
+
className: "min-h-[3px] flex-1 rounded-[2px]",
|
|
30
|
+
style: {
|
|
31
|
+
height: `${Math.min(100, d.value / top * 100)}%`,
|
|
32
|
+
background: toneBg[d.tone ?? tone]
|
|
33
|
+
}
|
|
34
|
+
}, i))
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export { StatusBars };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface TrendSeries {
|
|
3
|
+
/** Key in each data row. */
|
|
4
|
+
key: string;
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
/** CSS color. Defaults to chart-1..5 in order. */
|
|
7
|
+
color?: string;
|
|
8
|
+
/** Dashed line, no fill. Use for "previous period". */
|
|
9
|
+
dashed?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface TrendChartProps extends Omit<React.ComponentProps<"div">, "children"> {
|
|
12
|
+
data: Array<Record<string, unknown>>;
|
|
13
|
+
/** Key of the x-axis value in each row. */
|
|
14
|
+
xKey: string;
|
|
15
|
+
series: TrendSeries[];
|
|
16
|
+
/** Plot height in px. Default 240. */
|
|
17
|
+
height?: number;
|
|
18
|
+
/** Format y-axis ticks and tooltip values. */
|
|
19
|
+
valueFormatter?: (value: number) => string;
|
|
20
|
+
/** Format x-axis ticks. */
|
|
21
|
+
xFormatter?: (value: unknown) => string;
|
|
22
|
+
/** Hide the y-axis labels. */
|
|
23
|
+
hideYAxis?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Smoothed area/line chart with grid, axis labels and hover tooltip. Recharts under the hood. */
|
|
26
|
+
declare function TrendChart({ data, xKey, series, height, valueFormatter, xFormatter, hideYAxis, className, style, ...props }: TrendChartProps): React.JSX.Element;
|
|
27
|
+
/** Inline legend matching TrendChart series styling (solid vs dashed swatches). */
|
|
28
|
+
declare function TrendLegend({ series, className, ...props }: Omit<React.ComponentProps<"div">, "children"> & {
|
|
29
|
+
series: TrendSeries[];
|
|
30
|
+
}): React.JSX.Element;
|
|
31
|
+
export { TrendChart, TrendLegend };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../ui/chart.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
|
|
7
|
+
// src/components/data/trend-chart.tsx
|
|
8
|
+
const palette = [
|
|
9
|
+
"var(--chart-1)",
|
|
10
|
+
"var(--chart-2)",
|
|
11
|
+
"var(--chart-3)",
|
|
12
|
+
"var(--chart-4)",
|
|
13
|
+
"var(--chart-5)"
|
|
14
|
+
];
|
|
15
|
+
/** Smoothed area/line chart with grid, axis labels and hover tooltip. Recharts under the hood. */
|
|
16
|
+
function TrendChart({ data, xKey, series, height = 240, valueFormatter, xFormatter, hideYAxis, className, style, ...props }) {
|
|
17
|
+
const config = React.useMemo(() => Object.fromEntries(series.map((s, i) => [s.key, {
|
|
18
|
+
label: s.label,
|
|
19
|
+
color: s.color ?? palette[i % palette.length]
|
|
20
|
+
}])), [series]);
|
|
21
|
+
const ordered = React.useMemo(() => [...series].sort((a, b) => Number(!!b.dashed) - Number(!!a.dashed)), [series]);
|
|
22
|
+
return /* @__PURE__ */ jsx(ChartContainer, {
|
|
23
|
+
"data-slot": "trend-chart",
|
|
24
|
+
config,
|
|
25
|
+
className: cn("aspect-auto w-full", className),
|
|
26
|
+
style: {
|
|
27
|
+
height,
|
|
28
|
+
...style
|
|
29
|
+
},
|
|
30
|
+
...props,
|
|
31
|
+
children: /* @__PURE__ */ jsxs(AreaChart, {
|
|
32
|
+
data,
|
|
33
|
+
margin: {
|
|
34
|
+
top: 8,
|
|
35
|
+
right: 4,
|
|
36
|
+
bottom: 0,
|
|
37
|
+
left: 0
|
|
38
|
+
},
|
|
39
|
+
children: [
|
|
40
|
+
/* @__PURE__ */ jsx("defs", { children: series.map((s) => /* @__PURE__ */ jsxs("linearGradient", {
|
|
41
|
+
id: `hb-fill-${s.key}`,
|
|
42
|
+
x1: "0",
|
|
43
|
+
y1: "0",
|
|
44
|
+
x2: "0",
|
|
45
|
+
y2: "1",
|
|
46
|
+
children: [/* @__PURE__ */ jsx("stop", {
|
|
47
|
+
offset: "0%",
|
|
48
|
+
stopColor: `var(--color-${s.key})`,
|
|
49
|
+
stopOpacity: .14
|
|
50
|
+
}), /* @__PURE__ */ jsx("stop", {
|
|
51
|
+
offset: "100%",
|
|
52
|
+
stopColor: `var(--color-${s.key})`,
|
|
53
|
+
stopOpacity: .02
|
|
54
|
+
})]
|
|
55
|
+
}, s.key)) }),
|
|
56
|
+
/* @__PURE__ */ jsx(CartesianGrid, {
|
|
57
|
+
vertical: false,
|
|
58
|
+
stroke: "var(--chart-grid)"
|
|
59
|
+
}),
|
|
60
|
+
/* @__PURE__ */ jsx(XAxis, {
|
|
61
|
+
dataKey: xKey,
|
|
62
|
+
tickLine: false,
|
|
63
|
+
axisLine: { stroke: "var(--border)" },
|
|
64
|
+
tickMargin: 10,
|
|
65
|
+
minTickGap: 32,
|
|
66
|
+
tickFormatter: xFormatter,
|
|
67
|
+
fontSize: 11
|
|
68
|
+
}),
|
|
69
|
+
/* @__PURE__ */ jsx(YAxis, {
|
|
70
|
+
hide: hideYAxis,
|
|
71
|
+
tickLine: false,
|
|
72
|
+
axisLine: false,
|
|
73
|
+
width: 40,
|
|
74
|
+
tickMargin: 6,
|
|
75
|
+
tickFormatter: valueFormatter,
|
|
76
|
+
fontSize: 11
|
|
77
|
+
}),
|
|
78
|
+
/* @__PURE__ */ jsx(ChartTooltip, {
|
|
79
|
+
cursor: { stroke: "var(--border)" },
|
|
80
|
+
content: /* @__PURE__ */ jsx(ChartTooltipContent, {
|
|
81
|
+
indicator: "dot",
|
|
82
|
+
formatter: valueFormatter ? (value, name) => /* @__PURE__ */ jsxs("div", {
|
|
83
|
+
className: "flex w-full items-center justify-between gap-4",
|
|
84
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
85
|
+
className: "text-muted-foreground",
|
|
86
|
+
children: config[String(name)]?.label ?? name
|
|
87
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
88
|
+
className: "font-semibold tabular-nums",
|
|
89
|
+
children: valueFormatter(Number(value))
|
|
90
|
+
})]
|
|
91
|
+
}) : void 0
|
|
92
|
+
})
|
|
93
|
+
}),
|
|
94
|
+
ordered.map((s) => /* @__PURE__ */ jsx(Area, {
|
|
95
|
+
dataKey: s.key,
|
|
96
|
+
type: "monotone",
|
|
97
|
+
stroke: `var(--color-${s.key})`,
|
|
98
|
+
strokeWidth: 2,
|
|
99
|
+
strokeDasharray: s.dashed ? "5 5" : void 0,
|
|
100
|
+
fill: s.dashed ? "none" : `url(#hb-fill-${s.key})`,
|
|
101
|
+
dot: false,
|
|
102
|
+
activeDot: {
|
|
103
|
+
r: 5,
|
|
104
|
+
strokeWidth: 2,
|
|
105
|
+
stroke: "var(--card)"
|
|
106
|
+
},
|
|
107
|
+
isAnimationActive: false
|
|
108
|
+
}, s.key))
|
|
109
|
+
]
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** Inline legend matching TrendChart series styling (solid vs dashed swatches). */
|
|
114
|
+
function TrendLegend({ series, className, ...props }) {
|
|
115
|
+
return /* @__PURE__ */ jsx("div", {
|
|
116
|
+
"data-slot": "trend-legend",
|
|
117
|
+
className: cn("flex flex-wrap items-center gap-4.5 text-[13px]", className),
|
|
118
|
+
...props,
|
|
119
|
+
children: series.map((s, i) => {
|
|
120
|
+
const color = s.color ?? palette[i % palette.length];
|
|
121
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
122
|
+
className: "inline-flex items-center gap-2",
|
|
123
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
124
|
+
"aria-hidden": "true",
|
|
125
|
+
className: "w-4",
|
|
126
|
+
style: s.dashed ? { borderTop: `2px dashed ${color}` } : {
|
|
127
|
+
height: 3,
|
|
128
|
+
borderRadius: 3,
|
|
129
|
+
background: color
|
|
130
|
+
}
|
|
131
|
+
}), s.label]
|
|
132
|
+
}, s.key);
|
|
133
|
+
})
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
export { TrendChart, TrendLegend };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type ThemeMode = "light" | "dark" | "system";
|
|
3
|
+
export type ResolvedThemeMode = "light" | "dark";
|
|
4
|
+
export interface ThemeProviderProps {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
/** Initial mode when nothing is stored. Default "system". */
|
|
7
|
+
defaultMode?: ThemeMode;
|
|
8
|
+
/** Brand/accent color (any CSS color). Drives --brand, which primary derives from. */
|
|
9
|
+
accent?: string;
|
|
10
|
+
/** Base corner radius in px. Default 14. */
|
|
11
|
+
radius?: number;
|
|
12
|
+
/** Font family name for body text, e.g. "Inter". Default Geist (bundled). */
|
|
13
|
+
font?: string;
|
|
14
|
+
/** localStorage key for the chosen mode. Pass null to disable persistence. */
|
|
15
|
+
storageKey?: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface ThemeContextValue {
|
|
18
|
+
mode: ThemeMode;
|
|
19
|
+
resolvedMode: ResolvedThemeMode;
|
|
20
|
+
setMode: (mode: ThemeMode) => void;
|
|
21
|
+
toggleMode: () => void;
|
|
22
|
+
accent: string | undefined;
|
|
23
|
+
setAccent: (accent: string | undefined) => void;
|
|
24
|
+
radius: number | undefined;
|
|
25
|
+
setRadius: (radius: number | undefined) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Applies light/dark mode (the `.dark` class on <html>), accent, radius and
|
|
29
|
+
* font to the document. Wrap your app once, near the root.
|
|
30
|
+
*/
|
|
31
|
+
declare function ThemeProvider({ children, defaultMode, accent: accentProp, radius: radiusProp, font, storageKey }: ThemeProviderProps): React.JSX.Element;
|
|
32
|
+
/** Read and change the current theme. Safe to call outside a provider (returns light). */
|
|
33
|
+
declare function useTheme(): ThemeContextValue;
|
|
34
|
+
/**
|
|
35
|
+
* Inline script for SSR apps that sets `.dark` before first paint, avoiding a
|
|
36
|
+
* flash of the wrong theme. Render it inside <head>.
|
|
37
|
+
*/
|
|
38
|
+
declare function ThemeScript({ storageKey, defaultMode }: {
|
|
39
|
+
storageKey?: string;
|
|
40
|
+
defaultMode?: ThemeMode;
|
|
41
|
+
}): React.JSX.Element;
|
|
42
|
+
export { ThemeProvider, ThemeScript, useTheme };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
// src/components/theme/theme-provider.tsx
|
|
5
|
+
const ThemeContext = React.createContext({
|
|
6
|
+
mode: "light",
|
|
7
|
+
resolvedMode: "light",
|
|
8
|
+
setMode: () => {},
|
|
9
|
+
toggleMode: () => {},
|
|
10
|
+
accent: void 0,
|
|
11
|
+
setAccent: () => {},
|
|
12
|
+
radius: void 0,
|
|
13
|
+
setRadius: () => {}
|
|
14
|
+
});
|
|
15
|
+
const DEFAULT_STORAGE_KEY = "harebear-theme";
|
|
16
|
+
function readStored(key) {
|
|
17
|
+
if (!key || typeof window === "undefined") return null;
|
|
18
|
+
try {
|
|
19
|
+
const v = window.localStorage.getItem(key);
|
|
20
|
+
return v === "light" || v === "dark" || v === "system" ? v : null;
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function systemPrefersDark() {
|
|
26
|
+
return typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Applies light/dark mode (the `.dark` class on <html>), accent, radius and
|
|
30
|
+
* font to the document. Wrap your app once, near the root.
|
|
31
|
+
*/
|
|
32
|
+
function ThemeProvider({ children, defaultMode = "system", accent: accentProp, radius: radiusProp, font, storageKey = DEFAULT_STORAGE_KEY }) {
|
|
33
|
+
const [mode, setModeState] = React.useState(() => readStored(storageKey) ?? defaultMode);
|
|
34
|
+
const [systemDark, setSystemDark] = React.useState(systemPrefersDark);
|
|
35
|
+
const [accent, setAccent] = React.useState(accentProp);
|
|
36
|
+
const [radius, setRadius] = React.useState(radiusProp);
|
|
37
|
+
React.useEffect(() => setAccent(accentProp), [accentProp]);
|
|
38
|
+
React.useEffect(() => setRadius(radiusProp), [radiusProp]);
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
|
|
41
|
+
if (!mq) return;
|
|
42
|
+
const onChange = () => setSystemDark(mq.matches);
|
|
43
|
+
mq.addEventListener("change", onChange);
|
|
44
|
+
return () => mq.removeEventListener("change", onChange);
|
|
45
|
+
}, []);
|
|
46
|
+
const resolvedMode = mode === "system" ? systemDark ? "dark" : "light" : mode;
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
const root = document.documentElement;
|
|
49
|
+
if (root.classList.contains("dark") === (resolvedMode === "dark")) return;
|
|
50
|
+
const style = document.createElement("style");
|
|
51
|
+
style.textContent = "*,*::before,*::after{transition:none!important}";
|
|
52
|
+
document.head.appendChild(style);
|
|
53
|
+
root.classList.toggle("dark", resolvedMode === "dark");
|
|
54
|
+
window.getComputedStyle(document.body).opacity;
|
|
55
|
+
requestAnimationFrame(() => style.remove());
|
|
56
|
+
}, [resolvedMode]);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
const s = document.documentElement.style;
|
|
59
|
+
if (accent) s.setProperty("--brand", accent);
|
|
60
|
+
else s.removeProperty("--brand");
|
|
61
|
+
if (radius !== void 0) s.setProperty("--radius", `${radius / 16}rem`);
|
|
62
|
+
else s.removeProperty("--radius");
|
|
63
|
+
if (font) s.setProperty("--hb-font-sans", `"${font}", ui-sans-serif, system-ui, sans-serif`);
|
|
64
|
+
else s.removeProperty("--hb-font-sans");
|
|
65
|
+
}, [
|
|
66
|
+
accent,
|
|
67
|
+
radius,
|
|
68
|
+
font
|
|
69
|
+
]);
|
|
70
|
+
const setMode = React.useCallback((next) => {
|
|
71
|
+
setModeState(next);
|
|
72
|
+
if (!storageKey) return;
|
|
73
|
+
try {
|
|
74
|
+
window.localStorage.setItem(storageKey, next);
|
|
75
|
+
} catch {}
|
|
76
|
+
}, [storageKey]);
|
|
77
|
+
const value = React.useMemo(() => ({
|
|
78
|
+
mode,
|
|
79
|
+
resolvedMode,
|
|
80
|
+
setMode,
|
|
81
|
+
toggleMode: () => setMode(resolvedMode === "dark" ? "light" : "dark"),
|
|
82
|
+
accent,
|
|
83
|
+
setAccent,
|
|
84
|
+
radius,
|
|
85
|
+
setRadius
|
|
86
|
+
}), [
|
|
87
|
+
mode,
|
|
88
|
+
resolvedMode,
|
|
89
|
+
setMode,
|
|
90
|
+
accent,
|
|
91
|
+
radius
|
|
92
|
+
]);
|
|
93
|
+
return /* @__PURE__ */ jsx(ThemeContext, {
|
|
94
|
+
value,
|
|
95
|
+
children
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/** Read and change the current theme. Safe to call outside a provider (returns light). */
|
|
99
|
+
function useTheme() {
|
|
100
|
+
return React.useContext(ThemeContext);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Inline script for SSR apps that sets `.dark` before first paint, avoiding a
|
|
104
|
+
* flash of the wrong theme. Render it inside <head>.
|
|
105
|
+
*/
|
|
106
|
+
function ThemeScript({ storageKey = DEFAULT_STORAGE_KEY, defaultMode = "system" }) {
|
|
107
|
+
const code = `(function(){try{var m=localStorage.getItem(${JSON.stringify(storageKey)})||${JSON.stringify(defaultMode)};var d=m==="dark"||(m==="system"&&matchMedia("(prefers-color-scheme: dark)").matches);document.documentElement.classList.toggle("dark",d)}catch(e){}})()`;
|
|
108
|
+
return /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: code } });
|
|
109
|
+
}
|
|
110
|
+
export { ThemeProvider, ThemeScript, useTheme };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Button } from "../ui/button.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/** Icon button that flips between light and dark mode. */
|
|
4
|
+
declare function ThemeToggle(props: Omit<React.ComponentProps<typeof Button>, "onClick">): React.JSX.Element;
|
|
5
|
+
export { ThemeToggle };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button } from "../ui/button.js";
|
|
3
|
+
import { useTheme } from "./theme-provider.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { MoonIcon, SunIcon } from "lucide-react";
|
|
6
|
+
// src/components/theme/theme-toggle.tsx
|
|
7
|
+
/** Icon button that flips between light and dark mode. */
|
|
8
|
+
function ThemeToggle(props) {
|
|
9
|
+
const { resolvedMode, toggleMode } = useTheme();
|
|
10
|
+
const dark = resolvedMode === "dark";
|
|
11
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
12
|
+
variant: "ghost",
|
|
13
|
+
size: "icon",
|
|
14
|
+
"aria-label": dark ? "Switch to light mode" : "Switch to dark mode",
|
|
15
|
+
onClick: toggleMode,
|
|
16
|
+
...props,
|
|
17
|
+
children: dark ? /* @__PURE__ */ jsx(SunIcon, {}) : /* @__PURE__ */ jsx(MoonIcon, {})
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export { ThemeToggle };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Accordion as Accordion$1 } from "radix-ui";
|
|
3
|
+
declare function Accordion({ className, ...props }: React.ComponentProps<typeof Accordion$1.Root>): React.JSX.Element;
|
|
4
|
+
declare function AccordionItem({ className, ...props }: React.ComponentProps<typeof Accordion$1.Item>): React.JSX.Element;
|
|
5
|
+
declare function AccordionTrigger({ className, children, ...props }: React.ComponentProps<typeof Accordion$1.Trigger>): React.JSX.Element;
|
|
6
|
+
declare function AccordionContent({ className, children, ...props }: React.ComponentProps<typeof Accordion$1.Content>): React.JSX.Element;
|
|
7
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
5
|
+
import { Accordion as Accordion$1 } from "radix-ui";
|
|
6
|
+
// src/components/ui/accordion.tsx
|
|
7
|
+
function Accordion({ className, ...props }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(Accordion$1.Root, {
|
|
9
|
+
"data-slot": "accordion",
|
|
10
|
+
className: cn("flex w-full flex-col", className),
|
|
11
|
+
...props
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function AccordionItem({ className, ...props }) {
|
|
15
|
+
return /* @__PURE__ */ jsx(Accordion$1.Item, {
|
|
16
|
+
"data-slot": "accordion-item",
|
|
17
|
+
className: cn("not-last:border-b", className),
|
|
18
|
+
...props
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function AccordionTrigger({ className, children, ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsx(Accordion$1.Header, {
|
|
23
|
+
className: "flex",
|
|
24
|
+
children: /* @__PURE__ */ jsxs(Accordion$1.Trigger, {
|
|
25
|
+
"data-slot": "accordion-trigger",
|
|
26
|
+
className: cn("group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground", className),
|
|
27
|
+
...props,
|
|
28
|
+
children: [
|
|
29
|
+
children,
|
|
30
|
+
/* @__PURE__ */ jsx(ChevronDownIcon, {
|
|
31
|
+
"data-slot": "accordion-trigger-icon",
|
|
32
|
+
className: "pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
|
|
33
|
+
}),
|
|
34
|
+
/* @__PURE__ */ jsx(ChevronUpIcon, {
|
|
35
|
+
"data-slot": "accordion-trigger-icon",
|
|
36
|
+
className: "pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function AccordionContent({ className, children, ...props }) {
|
|
43
|
+
return /* @__PURE__ */ jsx(Accordion$1.Content, {
|
|
44
|
+
"data-slot": "accordion-content",
|
|
45
|
+
className: "overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up",
|
|
46
|
+
...props,
|
|
47
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
48
|
+
className: cn("h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4", className),
|
|
49
|
+
children
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Button } from "./button.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { AlertDialog as AlertDialog$1 } from "radix-ui";
|
|
4
|
+
declare function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialog$1.Root>): React.JSX.Element;
|
|
5
|
+
declare function AlertDialogTrigger({ ...props }: React.ComponentProps<typeof AlertDialog$1.Trigger>): React.JSX.Element;
|
|
6
|
+
declare function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialog$1.Portal>): React.JSX.Element;
|
|
7
|
+
declare function AlertDialogOverlay({ className, ...props }: React.ComponentProps<typeof AlertDialog$1.Overlay>): React.JSX.Element;
|
|
8
|
+
declare function AlertDialogContent({ className, size, ...props }: React.ComponentProps<typeof AlertDialog$1.Content> & {
|
|
9
|
+
size?: "default" | "sm";
|
|
10
|
+
}): React.JSX.Element;
|
|
11
|
+
declare function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
12
|
+
declare function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
13
|
+
declare function AlertDialogMedia({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
14
|
+
declare function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialog$1.Title>): React.JSX.Element;
|
|
15
|
+
declare function AlertDialogDescription({ className, ...props }: React.ComponentProps<typeof AlertDialog$1.Description>): React.JSX.Element;
|
|
16
|
+
declare function AlertDialogAction({ className, variant, size, ...props }: React.ComponentProps<typeof AlertDialog$1.Action> & Pick<React.ComponentProps<typeof Button>, "variant" | "size">): React.JSX.Element;
|
|
17
|
+
declare function AlertDialogCancel({ className, variant, size, ...props }: React.ComponentProps<typeof AlertDialog$1.Cancel> & Pick<React.ComponentProps<typeof Button>, "variant" | "size">): React.JSX.Element;
|
|
18
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
import { Button } from "./button.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { AlertDialog as AlertDialog$1 } from "radix-ui";
|
|
6
|
+
// src/components/ui/alert-dialog.tsx
|
|
7
|
+
function AlertDialog({ ...props }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(AlertDialog$1.Root, {
|
|
9
|
+
"data-slot": "alert-dialog",
|
|
10
|
+
...props
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function AlertDialogTrigger({ ...props }) {
|
|
14
|
+
return /* @__PURE__ */ jsx(AlertDialog$1.Trigger, {
|
|
15
|
+
"data-slot": "alert-dialog-trigger",
|
|
16
|
+
...props
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function AlertDialogPortal({ ...props }) {
|
|
20
|
+
return /* @__PURE__ */ jsx(AlertDialog$1.Portal, {
|
|
21
|
+
"data-slot": "alert-dialog-portal",
|
|
22
|
+
...props
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function AlertDialogOverlay({ className, ...props }) {
|
|
26
|
+
return /* @__PURE__ */ jsx(AlertDialog$1.Overlay, {
|
|
27
|
+
"data-slot": "alert-dialog-overlay",
|
|
28
|
+
className: cn("fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className),
|
|
29
|
+
...props
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function AlertDialogContent({ className, size = "default", ...props }) {
|
|
33
|
+
return /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [/* @__PURE__ */ jsx(AlertDialogOverlay, {}), /* @__PURE__ */ jsx(AlertDialog$1.Content, {
|
|
34
|
+
"data-slot": "alert-dialog-content",
|
|
35
|
+
"data-size": size,
|
|
36
|
+
className: cn("group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
37
|
+
...props
|
|
38
|
+
})] });
|
|
39
|
+
}
|
|
40
|
+
function AlertDialogHeader({ className, ...props }) {
|
|
41
|
+
return /* @__PURE__ */ jsx("div", {
|
|
42
|
+
"data-slot": "alert-dialog-header",
|
|
43
|
+
className: cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className),
|
|
44
|
+
...props
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function AlertDialogFooter({ className, ...props }) {
|
|
48
|
+
return /* @__PURE__ */ jsx("div", {
|
|
49
|
+
"data-slot": "alert-dialog-footer",
|
|
50
|
+
className: cn("-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", className),
|
|
51
|
+
...props
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function AlertDialogMedia({ className, ...props }) {
|
|
55
|
+
return /* @__PURE__ */ jsx("div", {
|
|
56
|
+
"data-slot": "alert-dialog-media",
|
|
57
|
+
className: cn("mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", className),
|
|
58
|
+
...props
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function AlertDialogTitle({ className, ...props }) {
|
|
62
|
+
return /* @__PURE__ */ jsx(AlertDialog$1.Title, {
|
|
63
|
+
"data-slot": "alert-dialog-title",
|
|
64
|
+
className: cn("text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className),
|
|
65
|
+
...props
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function AlertDialogDescription({ className, ...props }) {
|
|
69
|
+
return /* @__PURE__ */ jsx(AlertDialog$1.Description, {
|
|
70
|
+
"data-slot": "alert-dialog-description",
|
|
71
|
+
className: cn("text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground", className),
|
|
72
|
+
...props
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function AlertDialogAction({ className, variant = "default", size = "default", ...props }) {
|
|
76
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
77
|
+
variant,
|
|
78
|
+
size,
|
|
79
|
+
asChild: true,
|
|
80
|
+
children: /* @__PURE__ */ jsx(AlertDialog$1.Action, {
|
|
81
|
+
"data-slot": "alert-dialog-action",
|
|
82
|
+
className: cn(className),
|
|
83
|
+
...props
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function AlertDialogCancel({ className, variant = "outline", size = "default", ...props }) {
|
|
88
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
89
|
+
variant,
|
|
90
|
+
size,
|
|
91
|
+
asChild: true,
|
|
92
|
+
children: /* @__PURE__ */ jsx(AlertDialog$1.Cancel, {
|
|
93
|
+
"data-slot": "alert-dialog-cancel",
|
|
94
|
+
className: cn(className),
|
|
95
|
+
...props
|
|
96
|
+
})
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { VariantProps } from "class-variance-authority";
|
|
3
|
+
declare const alertVariants: (props?: ({
|
|
4
|
+
variant?: "default" | "destructive" | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
+
declare function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>): React.JSX.Element;
|
|
7
|
+
declare function AlertTitle({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
8
|
+
declare function AlertDescription({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
9
|
+
declare function AlertAction({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
10
|
+
export { Alert, AlertAction, AlertDescription, AlertTitle };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
// src/components/ui/alert.tsx
|
|
6
|
+
const alertVariants = cva("group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", {
|
|
7
|
+
variants: { variant: {
|
|
8
|
+
default: "bg-card text-card-foreground",
|
|
9
|
+
destructive: "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current"
|
|
10
|
+
} },
|
|
11
|
+
defaultVariants: { variant: "default" }
|
|
12
|
+
});
|
|
13
|
+
function Alert({ className, variant, ...props }) {
|
|
14
|
+
return /* @__PURE__ */ jsx("div", {
|
|
15
|
+
"data-slot": "alert",
|
|
16
|
+
role: "alert",
|
|
17
|
+
className: cn(alertVariants({ variant }), className),
|
|
18
|
+
...props
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function AlertTitle({ className, ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsx("div", {
|
|
23
|
+
"data-slot": "alert-title",
|
|
24
|
+
className: cn("font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground", className),
|
|
25
|
+
...props
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function AlertDescription({ className, ...props }) {
|
|
29
|
+
return /* @__PURE__ */ jsx("div", {
|
|
30
|
+
"data-slot": "alert-description",
|
|
31
|
+
className: cn("text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4", className),
|
|
32
|
+
...props
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function AlertAction({ className, ...props }) {
|
|
36
|
+
return /* @__PURE__ */ jsx("div", {
|
|
37
|
+
"data-slot": "alert-action",
|
|
38
|
+
className: cn("absolute top-2 right-2", className),
|
|
39
|
+
...props
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export { Alert, AlertAction, AlertDescription, AlertTitle };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { AspectRatio as AspectRatio$1 } from "radix-ui";
|
|
4
|
+
// src/components/ui/aspect-ratio.tsx
|
|
5
|
+
function AspectRatio({ ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(AspectRatio$1.Root, {
|
|
7
|
+
"data-slot": "aspect-ratio",
|
|
8
|
+
...props
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export { AspectRatio };
|