cortena-ui 1.3.0 → 1.4.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/README.md +74 -3
- package/dist/a2ui/catalogue-doc.d.ts +44 -0
- package/dist/a2ui/catalogue-doc.js +169 -0
- package/dist/a2ui/catalogue-doc.js.map +1 -0
- package/dist/a2ui/catalogue.d.ts +44 -0
- package/dist/a2ui/catalogue.js +942 -0
- package/dist/a2ui/catalogue.js.map +1 -0
- package/dist/a2ui/index.d.ts +7 -0
- package/dist/a2ui/message.d.ts +232 -0
- package/dist/a2ui/message.js +365 -0
- package/dist/a2ui/message.js.map +1 -0
- package/dist/a2ui/renderer.d.ts +46 -0
- package/dist/a2ui/renderer.js +252 -0
- package/dist/a2ui/renderer.js.map +1 -0
- package/dist/a2ui/views.d.ts +80 -0
- package/dist/a2ui/views.js +596 -0
- package/dist/a2ui/views.js.map +1 -0
- package/dist/a2ui.d.ts +8 -0
- package/dist/a2ui.js +7 -0
- package/dist/chart.d.ts +9 -0
- package/dist/chart.js +9 -0
- package/dist/components/accordion.d.ts +22 -0
- package/dist/components/accordion.js +63 -0
- package/dist/components/accordion.js.map +1 -0
- package/dist/components/alert.d.ts +15 -0
- package/dist/components/alert.js +64 -0
- package/dist/components/alert.js.map +1 -0
- package/dist/components/avatar.d.ts +18 -0
- package/dist/components/avatar.js +40 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +12 -0
- package/dist/components/badge.js +47 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/breadcrumb.d.ts +71 -0
- package/dist/components/breadcrumb.js +123 -0
- package/dist/components/breadcrumb.js.map +1 -0
- package/dist/components/button-link.d.ts +28 -0
- package/dist/components/button-link.js +24 -0
- package/dist/components/button-link.js.map +1 -0
- package/dist/components/button.d.ts +24 -0
- package/dist/components/button.js +65 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/calendar.d.ts +28 -0
- package/dist/components/calendar.js +123 -0
- package/dist/components/calendar.js.map +1 -0
- package/dist/components/card.d.ts +32 -0
- package/dist/components/card.js +57 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/chart/chart.d.ts +71 -0
- package/dist/components/chart/chart.js +167 -0
- package/dist/components/chart/chart.js.map +1 -0
- package/dist/components/chart/container.d.ts +88 -0
- package/dist/components/chart/container.js +189 -0
- package/dist/components/chart/container.js.map +1 -0
- package/dist/components/chart/data.js +128 -0
- package/dist/components/chart/data.js.map +1 -0
- package/dist/components/chart/index.d.ts +9 -0
- package/dist/components/chart/index.js +10 -0
- package/dist/components/chart/nivo-charts.js +225 -0
- package/dist/components/chart/nivo-charts.js.map +1 -0
- package/dist/components/chart/nivo-lazy.js +50 -0
- package/dist/components/chart/nivo-lazy.js.map +1 -0
- package/dist/components/chart/nivo-theme.d.ts +156 -0
- package/dist/components/chart/nivo-theme.js +161 -0
- package/dist/components/chart/nivo-theme.js.map +1 -0
- package/dist/components/chart/recharts-charts.js +276 -0
- package/dist/components/chart/recharts-charts.js.map +1 -0
- package/dist/components/chart/recharts-lazy.js +58 -0
- package/dist/components/chart/recharts-lazy.js.map +1 -0
- package/dist/components/chart/recharts-parts.d.ts +10 -0
- package/dist/components/chart/recharts-parts.js +25 -0
- package/dist/components/chart/recharts-parts.js.map +1 -0
- package/dist/components/chart/tokens.d.ts +62 -0
- package/dist/components/chart/tokens.js +192 -0
- package/dist/components/chart/tokens.js.map +1 -0
- package/dist/components/chart/types.d.ts +114 -0
- package/dist/components/chart/types.js +27 -0
- package/dist/components/chart/types.js.map +1 -0
- package/dist/components/checkbox.d.ts +20 -0
- package/dist/components/checkbox.js +24 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +17 -0
- package/dist/components/chip.js +47 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/combobox.d.ts +76 -0
- package/dist/components/combobox.js +201 -0
- package/dist/components/combobox.js.map +1 -0
- package/dist/components/command.d.ts +50 -0
- package/dist/components/command.js +117 -0
- package/dist/components/command.js.map +1 -0
- package/dist/components/data-table/data-table.d.ts +65 -0
- package/dist/components/data-table/data-table.js +588 -0
- package/dist/components/data-table/data-table.js.map +1 -0
- package/dist/components/data-table/export.d.ts +23 -0
- package/dist/components/data-table/export.js +95 -0
- package/dist/components/data-table/export.js.map +1 -0
- package/dist/components/data-table/features.d.ts +66 -0
- package/dist/components/data-table/features.js +103 -0
- package/dist/components/data-table/features.js.map +1 -0
- package/dist/components/data-table/index.d.ts +8 -0
- package/dist/components/data-table/parts.d.ts +79 -0
- package/dist/components/data-table/parts.js +400 -0
- package/dist/components/data-table/parts.js.map +1 -0
- package/dist/components/data-table/system-columns.js +84 -0
- package/dist/components/data-table/system-columns.js.map +1 -0
- package/dist/components/data-table/types.d.ts +172 -0
- package/dist/components/data-table/use-data-table.d.ts +19 -0
- package/dist/components/data-table/use-data-table.js +171 -0
- package/dist/components/data-table/use-data-table.js.map +1 -0
- package/dist/components/data-table/use-server-source.js +200 -0
- package/dist/components/data-table/use-server-source.js.map +1 -0
- package/dist/components/date-field.d.ts +54 -0
- package/dist/components/date-field.js +235 -0
- package/dist/components/date-field.js.map +1 -0
- package/dist/components/date-picker.d.ts +42 -0
- package/dist/components/date-picker.js +125 -0
- package/dist/components/date-picker.js.map +1 -0
- package/dist/components/dialog.d.ts +36 -0
- package/dist/components/dialog.js +97 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +54 -0
- package/dist/components/dropdown-menu.js +191 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/dropzone.d.ts +48 -0
- package/dist/components/dropzone.js +185 -0
- package/dist/components/dropzone.js.map +1 -0
- package/dist/components/empty-state.d.ts +22 -0
- package/dist/components/empty-state.js +46 -0
- package/dist/components/empty-state.js.map +1 -0
- package/dist/components/error-banner.d.ts +17 -0
- package/dist/components/error-banner.js +52 -0
- package/dist/components/error-banner.js.map +1 -0
- package/dist/components/field.d.ts +64 -0
- package/dist/components/field.js +133 -0
- package/dist/components/field.js.map +1 -0
- package/dist/components/form.d.ts +37 -0
- package/dist/components/form.js +103 -0
- package/dist/components/form.js.map +1 -0
- package/dist/components/input.d.ts +17 -0
- package/dist/components/input.js +26 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/kbd.d.ts +11 -0
- package/dist/components/kbd.js +20 -0
- package/dist/components/kbd.js.map +1 -0
- package/dist/components/label.d.ts +14 -0
- package/dist/components/label.js +28 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/loading-skeleton.d.ts +22 -0
- package/dist/components/loading-skeleton.js +123 -0
- package/dist/components/loading-skeleton.js.map +1 -0
- package/dist/components/markdown.d.ts +44 -0
- package/dist/components/markdown.js +231 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/page-header.d.ts +26 -0
- package/dist/components/page-header.js +53 -0
- package/dist/components/page-header.js.map +1 -0
- package/dist/components/popover.d.ts +29 -0
- package/dist/components/popover.js +74 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/progress.d.ts +22 -0
- package/dist/components/progress.js +49 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/radio-group.d.ts +20 -0
- package/dist/components/radio-group.js +29 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/scroll-area.d.ts +25 -0
- package/dist/components/scroll-area.js +39 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/section-card.d.ts +29 -0
- package/dist/components/section-card.js +55 -0
- package/dist/components/section-card.js.map +1 -0
- package/dist/components/segmented.d.ts +27 -0
- package/dist/components/segmented.js +32 -0
- package/dist/components/segmented.js.map +1 -0
- package/dist/components/select.d.ts +62 -0
- package/dist/components/select.js +97 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +19 -0
- package/dist/components/separator.js +23 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.d.ts +35 -0
- package/dist/components/sheet.js +131 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/skeleton.d.ts +12 -0
- package/dist/components/skeleton.js +22 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/sortable-list.d.ts +67 -0
- package/dist/components/sortable-list.js +206 -0
- package/dist/components/sortable-list.js.map +1 -0
- package/dist/components/spinner.d.ts +29 -0
- package/dist/components/spinner.js +34 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/components/status-dot.d.ts +26 -0
- package/dist/components/status-dot.js +47 -0
- package/dist/components/status-dot.js.map +1 -0
- package/dist/components/switch.d.ts +21 -0
- package/dist/components/switch.js +21 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/tabs.d.ts +24 -0
- package/dist/components/tabs.js +60 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +19 -0
- package/dist/components/textarea.js +47 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/toast.d.ts +47 -0
- package/dist/components/toast.js +106 -0
- package/dist/components/toast.js.map +1 -0
- package/dist/components/toolbar.d.ts +19 -0
- package/dist/components/toolbar.js +45 -0
- package/dist/components/toolbar.js.map +1 -0
- package/dist/components/tooltip.d.ts +28 -0
- package/dist/components/tooltip.js +59 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/core.d.ts +44 -0
- package/dist/core.js +44 -0
- package/dist/data-table.d.ts +9 -0
- package/dist/data-table.js +7 -0
- package/dist/form.d.ts +7 -0
- package/dist/form.js +7 -0
- package/dist/hooks/use-cortena-theme.d.ts +98 -0
- package/dist/hooks/use-cortena-theme.js +145 -0
- package/dist/hooks/use-cortena-theme.js.map +1 -0
- package/dist/index.d.ts +75 -2678
- package/dist/index.js +66 -8598
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.js +15 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/render.d.ts +23 -0
- package/dist/lib/render.js +20 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/shakeable.js +39 -0
- package/dist/lib/shakeable.js.map +1 -0
- package/dist/markdown.d.ts +3 -0
- package/dist/markdown.js +3 -0
- package/dist/sortable-list.d.ts +3 -0
- package/dist/sortable-list.js +3 -0
- package/package.json +37 -14
- package/src/a2ui/catalogue.ts +140 -103
- package/src/a2ui/views.tsx +5 -2
- package/src/components/alert.tsx +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/chart/chart.tsx +72 -25
- package/src/components/chart/container.tsx +24 -39
- package/src/components/chart/index.tsx +4 -2
- package/src/components/chart/nivo-charts.tsx +41 -13
- package/src/components/chart/nivo-lazy.tsx +52 -0
- package/src/components/chart/recharts-charts.tsx +98 -3
- package/src/components/chart/recharts-lazy.tsx +72 -0
- package/src/components/chart/recharts-parts.tsx +34 -0
- package/src/components/chart/tokens.ts +1 -1
- package/src/components/chart/types.ts +14 -0
- package/src/components/chip.tsx +1 -1
- package/src/components/combobox.tsx +2 -2
- package/src/components/data-table/export.ts +18 -2
- package/src/components/data-table/features.ts +77 -45
- package/src/components/data-table/parts.tsx +15 -0
- package/src/components/data-table/use-data-table.ts +7 -2
- package/src/components/data-table/use-server-source.ts +17 -4
- package/src/components/date-field.tsx +1 -1
- package/src/components/error-banner.tsx +1 -1
- package/src/components/field.tsx +2 -2
- package/src/components/form.tsx +2 -2
- package/src/components/input.tsx +1 -1
- package/src/components/label.tsx +1 -1
- package/src/components/markdown.tsx +5 -4
- package/src/components/sheet.tsx +2 -2
- package/src/components/sortable-list.tsx +2 -2
- package/src/components/textarea.tsx +1 -1
- package/src/components/toast.tsx +1 -1
- package/src/entries/a2ui.ts +11 -0
- package/src/entries/chart.ts +10 -0
- package/src/entries/core.ts +91 -0
- package/src/entries/data-table.ts +9 -0
- package/src/entries/form.ts +24 -0
- package/src/entries/markdown.ts +9 -0
- package/src/entries/sortable-list.ts +13 -0
- package/src/index.ts +32 -98
- package/src/lib/shakeable.ts +33 -0
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { Skeleton } from "./skeleton.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/loading-skeleton.tsx
|
|
7
|
+
const DEFAULT_ROWS = {
|
|
8
|
+
list: 4,
|
|
9
|
+
table: 5,
|
|
10
|
+
card: 3,
|
|
11
|
+
form: 3
|
|
12
|
+
};
|
|
13
|
+
const DEFAULT_COLUMNS = {
|
|
14
|
+
list: 1,
|
|
15
|
+
table: 4,
|
|
16
|
+
card: 3,
|
|
17
|
+
form: 1
|
|
18
|
+
};
|
|
19
|
+
const LINE_WIDTHS = [
|
|
20
|
+
"w-3/4",
|
|
21
|
+
"w-1/2",
|
|
22
|
+
"w-5/6",
|
|
23
|
+
"w-2/3",
|
|
24
|
+
"w-3/5"
|
|
25
|
+
];
|
|
26
|
+
const width = (i) => LINE_WIDTHS[i % LINE_WIDTHS.length];
|
|
27
|
+
function ListSkeleton({ rows }) {
|
|
28
|
+
return /* @__PURE__ */ jsx("div", {
|
|
29
|
+
"data-slot": "loading-skeleton-list",
|
|
30
|
+
className: "flex flex-col gap-4",
|
|
31
|
+
children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsxs("div", {
|
|
32
|
+
"data-slot": "loading-skeleton-row",
|
|
33
|
+
className: "flex items-center gap-3",
|
|
34
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: "size-9 shrink-0 rounded-[var(--ds-radius-full)]" }), /* @__PURE__ */ jsxs("div", {
|
|
35
|
+
className: "flex min-w-0 flex-1 flex-col gap-2",
|
|
36
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: cn("h-3.5", width(i)) }), /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3", width(i + 2)) })]
|
|
37
|
+
})]
|
|
38
|
+
}, i))
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function TableSkeleton({ rows, columns }) {
|
|
42
|
+
const grid = { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` };
|
|
43
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
44
|
+
"data-slot": "loading-skeleton-table",
|
|
45
|
+
className: "flex flex-col",
|
|
46
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
47
|
+
"data-slot": "loading-skeleton-row",
|
|
48
|
+
className: "grid gap-4 border-b border-[var(--ds-border-subtle)] px-3 py-3",
|
|
49
|
+
style: grid,
|
|
50
|
+
children: Array.from({ length: columns }, (_, c) => /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-1/2" }, c))
|
|
51
|
+
}), Array.from({ length: rows }, (_, r) => /* @__PURE__ */ jsx("div", {
|
|
52
|
+
"data-slot": "loading-skeleton-row",
|
|
53
|
+
className: "grid gap-4 border-b border-[var(--ds-border-subtle)] px-3 py-3.5 last:border-b-0",
|
|
54
|
+
style: grid,
|
|
55
|
+
children: Array.from({ length: columns }, (_, c) => /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3.5", width(r + c)) }, c))
|
|
56
|
+
}, r))]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function CardSkeleton({ rows, columns }) {
|
|
60
|
+
return /* @__PURE__ */ jsx("div", {
|
|
61
|
+
"data-slot": "loading-skeleton-cards",
|
|
62
|
+
className: "grid gap-4",
|
|
63
|
+
style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` },
|
|
64
|
+
children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsxs("div", {
|
|
65
|
+
"data-slot": "loading-skeleton-card",
|
|
66
|
+
className: "flex flex-col gap-3 rounded-[var(--ds-radius-lg)] border border-[var(--ds-border-subtle)] bg-[var(--ds-card)] p-5",
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/2" }),
|
|
69
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-3/4" }),
|
|
70
|
+
/* @__PURE__ */ jsxs("div", {
|
|
71
|
+
className: "mt-2 flex flex-col gap-2",
|
|
72
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-full" }), /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3", width(i)) })]
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
}, i))
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function FormSkeleton({ rows }) {
|
|
79
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
80
|
+
"data-slot": "loading-skeleton-form",
|
|
81
|
+
className: "flex max-w-xl flex-col gap-5",
|
|
82
|
+
children: [Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsxs("div", {
|
|
83
|
+
"data-slot": "loading-skeleton-field",
|
|
84
|
+
className: "flex flex-col gap-2",
|
|
85
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full rounded-[var(--ds-radius-md)]" })]
|
|
86
|
+
}, i)), /* @__PURE__ */ jsx(Skeleton, { className: "mt-1 h-9 w-28 rounded-[var(--ds-radius-md)]" })]
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* LoadingSkeleton — page-level placeholders assembled from Skeleton, one per
|
|
91
|
+
* shape of content: a list of rows with an avatar, a table with a header row,
|
|
92
|
+
* a grid of cards, or a stacked form. Announces itself as busy so assistive
|
|
93
|
+
* technology knows the region is loading; the blocks themselves are hidden.
|
|
94
|
+
*/
|
|
95
|
+
function LoadingSkeleton({ className, preset = "list", rows, columns, ...props }) {
|
|
96
|
+
const count = Math.max(1, rows ?? DEFAULT_ROWS[preset]);
|
|
97
|
+
const cols = Math.max(1, columns ?? DEFAULT_COLUMNS[preset]);
|
|
98
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
99
|
+
"data-slot": "loading-skeleton",
|
|
100
|
+
"data-preset": preset,
|
|
101
|
+
role: "status",
|
|
102
|
+
"aria-busy": "true",
|
|
103
|
+
"aria-label": "Loading",
|
|
104
|
+
className: cn("w-full", className),
|
|
105
|
+
...props,
|
|
106
|
+
children: [
|
|
107
|
+
preset === "list" ? /* @__PURE__ */ jsx(ListSkeleton, { rows: count }) : null,
|
|
108
|
+
preset === "table" ? /* @__PURE__ */ jsx(TableSkeleton, {
|
|
109
|
+
rows: count,
|
|
110
|
+
columns: cols
|
|
111
|
+
}) : null,
|
|
112
|
+
preset === "card" ? /* @__PURE__ */ jsx(CardSkeleton, {
|
|
113
|
+
rows: count,
|
|
114
|
+
columns: cols
|
|
115
|
+
}) : null,
|
|
116
|
+
preset === "form" ? /* @__PURE__ */ jsx(FormSkeleton, { rows: count }) : null
|
|
117
|
+
]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { LoadingSkeleton };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=loading-skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loading-skeleton.js","names":[],"sources":["../../src/components/loading-skeleton.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { Skeleton } from \"@/components/skeleton\";\nimport { cn } from \"@/lib/cn\";\n\nexport type LoadingSkeletonPreset = \"list\" | \"table\" | \"card\" | \"form\";\n\nexport interface LoadingSkeletonProps extends React.ComponentProps<\"div\"> {\n /** Which shape of content is loading. */\n preset?: LoadingSkeletonPreset;\n /** Rows (list, table, form) or cards. Each preset has a sensible default. */\n rows?: number;\n /** Columns for `table` and `card`. */\n columns?: number;\n}\n\nconst DEFAULT_ROWS: Record<LoadingSkeletonPreset, number> = {\n list: 4,\n table: 5,\n card: 3,\n form: 3,\n};\n\nconst DEFAULT_COLUMNS: Record<LoadingSkeletonPreset, number> = {\n list: 1,\n table: 4,\n card: 3,\n form: 1,\n};\n\n/* Widths cycle so rows do not line up into a solid block. */\nconst LINE_WIDTHS = [\"w-3/4\", \"w-1/2\", \"w-5/6\", \"w-2/3\", \"w-3/5\"] as const;\nconst width = (i: number) => LINE_WIDTHS[i % LINE_WIDTHS.length];\n\nfunction ListSkeleton({ rows }: { rows: number }) {\n return (\n <div data-slot=\"loading-skeleton-list\" className=\"flex flex-col gap-4\">\n {Array.from({ length: rows }, (_, i) => (\n <div key={i} data-slot=\"loading-skeleton-row\" className=\"flex items-center gap-3\">\n <Skeleton className=\"size-9 shrink-0 rounded-[var(--ds-radius-full)]\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Skeleton className={cn(\"h-3.5\", width(i))} />\n <Skeleton className={cn(\"h-3\", width(i + 2))} />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction TableSkeleton({ rows, columns }: { rows: number; columns: number }) {\n const grid = { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` };\n return (\n <div data-slot=\"loading-skeleton-table\" className=\"flex flex-col\">\n <div\n data-slot=\"loading-skeleton-row\"\n className=\"grid gap-4 border-b border-[var(--ds-border-subtle)] px-3 py-3\"\n style={grid}\n >\n {Array.from({ length: columns }, (_, c) => (\n <Skeleton key={c} className=\"h-3 w-1/2\" />\n ))}\n </div>\n {Array.from({ length: rows }, (_, r) => (\n <div\n key={r}\n data-slot=\"loading-skeleton-row\"\n className=\"grid gap-4 border-b border-[var(--ds-border-subtle)] px-3 py-3.5 last:border-b-0\"\n style={grid}\n >\n {Array.from({ length: columns }, (_, c) => (\n <Skeleton key={c} className={cn(\"h-3.5\", width(r + c))} />\n ))}\n </div>\n ))}\n </div>\n );\n}\n\nfunction CardSkeleton({ rows, columns }: { rows: number; columns: number }) {\n return (\n <div\n data-slot=\"loading-skeleton-cards\"\n className=\"grid gap-4\"\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}\n >\n {Array.from({ length: rows }, (_, i) => (\n <div\n key={i}\n data-slot=\"loading-skeleton-card\"\n className=\"flex flex-col gap-3 rounded-[var(--ds-radius-lg)] border border-[var(--ds-border-subtle)] bg-[var(--ds-card)] p-5\"\n >\n <Skeleton className=\"h-4 w-1/2\" />\n <Skeleton className=\"h-3 w-3/4\" />\n <div className=\"mt-2 flex flex-col gap-2\">\n <Skeleton className=\"h-3 w-full\" />\n <Skeleton className={cn(\"h-3\", width(i))} />\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction FormSkeleton({ rows }: { rows: number }) {\n return (\n <div data-slot=\"loading-skeleton-form\" className=\"flex max-w-xl flex-col gap-5\">\n {Array.from({ length: rows }, (_, i) => (\n <div key={i} data-slot=\"loading-skeleton-field\" className=\"flex flex-col gap-2\">\n <Skeleton className=\"h-3 w-24\" />\n <Skeleton className=\"h-9 w-full rounded-[var(--ds-radius-md)]\" />\n </div>\n ))}\n <Skeleton className=\"mt-1 h-9 w-28 rounded-[var(--ds-radius-md)]\" />\n </div>\n );\n}\n\n/**\n * LoadingSkeleton — page-level placeholders assembled from Skeleton, one per\n * shape of content: a list of rows with an avatar, a table with a header row,\n * a grid of cards, or a stacked form. Announces itself as busy so assistive\n * technology knows the region is loading; the blocks themselves are hidden.\n */\nfunction LoadingSkeleton({\n className,\n preset = \"list\",\n rows,\n columns,\n ...props\n}: LoadingSkeletonProps) {\n const count = Math.max(1, rows ?? DEFAULT_ROWS[preset]);\n const cols = Math.max(1, columns ?? DEFAULT_COLUMNS[preset]);\n return (\n <div\n data-slot=\"loading-skeleton\"\n data-preset={preset}\n role=\"status\"\n aria-busy=\"true\"\n aria-label=\"Loading\"\n className={cn(\"w-full\", className)}\n {...props}\n >\n {preset === \"list\" ? <ListSkeleton rows={count} /> : null}\n {preset === \"table\" ? <TableSkeleton rows={count} columns={cols} /> : null}\n {preset === \"card\" ? <CardSkeleton rows={count} columns={cols} /> : null}\n {preset === \"form\" ? <FormSkeleton rows={count} /> : null}\n </div>\n );\n}\n\nexport { LoadingSkeleton };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Components, Options } from "react-markdown";
|
|
4
|
+
import { Options as Options$1 } from "rehype-sanitize";
|
|
5
|
+
//#region src/components/markdown.d.ts
|
|
6
|
+
/** The GitHub schema, plus `class="language-*"` on code and `checked` on task boxes. */
|
|
7
|
+
declare const markdownSanitizeSchema: Options$1;
|
|
8
|
+
export interface CodeRendererProps {
|
|
9
|
+
/** The fenced block's text, without a trailing newline. */
|
|
10
|
+
code: string;
|
|
11
|
+
/** From the fence's info string, e.g. `ts` for ```ts. */
|
|
12
|
+
language: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface MarkdownProps extends Omit<React.ComponentProps<"div">, "children"> {
|
|
15
|
+
/** The markdown source. */
|
|
16
|
+
children: string | null | undefined;
|
|
17
|
+
/** Tighter rhythm and smaller headings, for chat bubbles. */
|
|
18
|
+
compact?: boolean;
|
|
19
|
+
/** Renders fenced code blocks; the copy button is still added. Use for syntax highlighting. */
|
|
20
|
+
codeRenderer?: (props: CodeRendererProps) => React.ReactNode;
|
|
21
|
+
/** Override or extend the element map. */
|
|
22
|
+
components?: Components;
|
|
23
|
+
/** Extra remark plugins, appended after GFM. */
|
|
24
|
+
remarkPlugins?: NonNullable<Options["remarkPlugins"]>;
|
|
25
|
+
/** Extra rehype plugins, appended after sanitize. */
|
|
26
|
+
rehypePlugins?: NonNullable<Options["rehypePlugins"]>;
|
|
27
|
+
/**
|
|
28
|
+
* Rehype plugins that must run BEFORE sanitize. The one legitimate use is
|
|
29
|
+
* `rehype-raw` in a host that embeds trusted custom tags in markdown (the
|
|
30
|
+
* webchat's `<cortena-*>` blocks): raw HTML has to become nodes before the
|
|
31
|
+
* sanitizer sees it, and the sanitize schema then decides what survives.
|
|
32
|
+
* Anything placed here widens what reaches the sanitizer, so keep the
|
|
33
|
+
* schema strict when you use it.
|
|
34
|
+
*/
|
|
35
|
+
rehypePluginsBeforeSanitize?: NonNullable<Options["rehypePlugins"]>;
|
|
36
|
+
/** Replace the sanitize schema; defaults to `markdownSanitizeSchema`. */
|
|
37
|
+
sanitizeSchema?: Options$1;
|
|
38
|
+
/** Rewrite URLs; defaults to react-markdown's, which drops non-http(s)/mailto/tel schemes. */
|
|
39
|
+
urlTransform?: Options["urlTransform"];
|
|
40
|
+
}
|
|
41
|
+
declare function Markdown({ children, compact, codeRenderer, components, remarkPlugins, rehypePlugins, rehypePluginsBeforeSanitize, sanitizeSchema, urlTransform, className, ...props }: MarkdownProps): React.JSX.Element;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { Markdown, type Components as MarkdownComponents, markdownSanitizeSchema };
|
|
44
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { shakeable } from "../lib/shakeable.js";
|
|
5
|
+
import { Check, Copy } from "lucide-react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import ReactMarkdown from "react-markdown";
|
|
9
|
+
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
|
|
10
|
+
import remarkGfm from "remark-gfm";
|
|
11
|
+
//#region src/components/markdown.tsx
|
|
12
|
+
function hastText(node) {
|
|
13
|
+
if (node.type === "text") return node.value;
|
|
14
|
+
if ("children" in node) return node.children.map(hastText).join("");
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
/** The GitHub schema, plus `class="language-*"` on code and `checked` on task boxes. */
|
|
18
|
+
const markdownSanitizeSchema = /* @__PURE__ */ shakeable(() => ({
|
|
19
|
+
...defaultSchema,
|
|
20
|
+
attributes: {
|
|
21
|
+
...defaultSchema.attributes,
|
|
22
|
+
code: [...defaultSchema.attributes?.code ?? [], ["className", /^language-./]],
|
|
23
|
+
input: [...defaultSchema.attributes?.input ?? [], "checked"]
|
|
24
|
+
}
|
|
25
|
+
}));
|
|
26
|
+
/** Strip react-markdown's `node` so the rest can be spread onto a DOM element. */
|
|
27
|
+
function dom(props) {
|
|
28
|
+
const { node: _node, ...rest } = props;
|
|
29
|
+
return rest;
|
|
30
|
+
}
|
|
31
|
+
const block = "my-3 first:mt-0 last:mb-0 group-data-[compact]/markdown:my-1.5";
|
|
32
|
+
const body = /* @__PURE__ */ cn("text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)] text-[color:var(--ds-foreground)]");
|
|
33
|
+
const heading = /* @__PURE__ */ cn("font-[family-name:var(--ds-font-display)] text-[color:var(--ds-foreground)]", "mt-6 mb-2 first:mt-0 group-data-[compact]/markdown:mt-3 group-data-[compact]/markdown:mb-1");
|
|
34
|
+
const cellClassName = "border border-[var(--ds-border)] px-3 py-1.5 align-top";
|
|
35
|
+
function CopyButton({ text }) {
|
|
36
|
+
const [copied, setCopied] = React.useState(false);
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
if (!copied) return;
|
|
39
|
+
const id = window.setTimeout(() => setCopied(false), 1500);
|
|
40
|
+
return () => window.clearTimeout(id);
|
|
41
|
+
}, [copied]);
|
|
42
|
+
return /* @__PURE__ */ jsx("button", {
|
|
43
|
+
type: "button",
|
|
44
|
+
"data-slot": "markdown-copy",
|
|
45
|
+
"data-copied": copied || void 0,
|
|
46
|
+
"aria-label": copied ? "Copied" : "Copy code",
|
|
47
|
+
onClick: () => {
|
|
48
|
+
navigator.clipboard?.writeText(text).then(() => setCopied(true));
|
|
49
|
+
},
|
|
50
|
+
className: cn("absolute top-1.5 right-1.5 inline-flex size-7 items-center justify-center", "rounded-[var(--ds-radius-sm)] text-[color:var(--ds-muted-foreground)]", "opacity-0 transition-opacity duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "group-hover/pre:opacity-100 focus-visible:opacity-100 data-[copied]:opacity-100", "hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]", "outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[copied]:text-[color:var(--ds-success)]", "[&_svg]:size-3.5"),
|
|
51
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": true }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": true })
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function buildComponents(codeRenderer) {
|
|
55
|
+
return {
|
|
56
|
+
h1: (p) => /* @__PURE__ */ jsx("h1", {
|
|
57
|
+
...dom(p),
|
|
58
|
+
className: cn(heading, "text-[length:var(--ds-text-h1)] leading-[var(--ds-text-h1--line-height)] tracking-[var(--ds-text-h1--letter-spacing)] font-semibold", "group-data-[compact]/markdown:text-[length:var(--ds-text-h3)] group-data-[compact]/markdown:leading-[var(--ds-text-h3--line-height)]")
|
|
59
|
+
}),
|
|
60
|
+
h2: (p) => /* @__PURE__ */ jsx("h2", {
|
|
61
|
+
...dom(p),
|
|
62
|
+
className: cn(heading, "text-[length:var(--ds-text-h2)] leading-[var(--ds-text-h2--line-height)] tracking-[var(--ds-text-h2--letter-spacing)] font-bold", "group-data-[compact]/markdown:text-[length:var(--ds-text-body-lg)] group-data-[compact]/markdown:leading-[var(--ds-text-body-lg--line-height)]")
|
|
63
|
+
}),
|
|
64
|
+
h3: (p) => /* @__PURE__ */ jsx("h3", {
|
|
65
|
+
...dom(p),
|
|
66
|
+
className: cn(heading, "text-[length:var(--ds-text-h3)] leading-[var(--ds-text-h3--line-height)] font-semibold", "group-data-[compact]/markdown:text-[length:var(--ds-text-body-md)] group-data-[compact]/markdown:leading-[var(--ds-text-body-md--line-height)]")
|
|
67
|
+
}),
|
|
68
|
+
h4: (p) => /* @__PURE__ */ jsx("h4", {
|
|
69
|
+
...dom(p),
|
|
70
|
+
className: cn(heading, "font-[family-name:var(--ds-font-body)] text-[length:var(--ds-text-body-md)] leading-[var(--ds-text-body-md--line-height)] font-semibold")
|
|
71
|
+
}),
|
|
72
|
+
h5: (p) => /* @__PURE__ */ jsx("h5", {
|
|
73
|
+
...dom(p),
|
|
74
|
+
className: cn(heading, "font-[family-name:var(--ds-font-body)] text-[length:var(--ds-text-body)] leading-[var(--ds-text-body--line-height)] font-semibold")
|
|
75
|
+
}),
|
|
76
|
+
h6: (p) => /* @__PURE__ */ jsx("h6", {
|
|
77
|
+
...dom(p),
|
|
78
|
+
className: cn(heading, "font-[family-name:var(--ds-font-body)] text-[length:var(--ds-text-eyebrow)] leading-[var(--ds-text-eyebrow--line-height)] tracking-[var(--ds-text-eyebrow--letter-spacing)] font-semibold uppercase text-[color:var(--ds-muted-foreground)]")
|
|
79
|
+
}),
|
|
80
|
+
p: (p) => /* @__PURE__ */ jsx("p", {
|
|
81
|
+
...dom(p),
|
|
82
|
+
className: cn(block, body)
|
|
83
|
+
}),
|
|
84
|
+
a: (p) => {
|
|
85
|
+
const { href, ...rest } = dom(p);
|
|
86
|
+
const external = typeof href === "string" && /^https?:\/\//.test(href);
|
|
87
|
+
return /* @__PURE__ */ jsx("a", {
|
|
88
|
+
...rest,
|
|
89
|
+
href,
|
|
90
|
+
target: external ? "_blank" : void 0,
|
|
91
|
+
rel: external ? "noopener noreferrer" : void 0,
|
|
92
|
+
className: cn("text-[color:var(--ds-primary)] underline underline-offset-4 decoration-[color:var(--ds-primary)]/40", "hover:text-[color:var(--ds-primary-hover)] hover:decoration-[color:var(--ds-primary-hover)]", "outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40 rounded-[var(--ds-radius-sm)]")
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
strong: (p) => /* @__PURE__ */ jsx("strong", {
|
|
96
|
+
...dom(p),
|
|
97
|
+
className: "font-semibold text-[color:var(--ds-foreground)]"
|
|
98
|
+
}),
|
|
99
|
+
em: (p) => /* @__PURE__ */ jsx("em", {
|
|
100
|
+
...dom(p),
|
|
101
|
+
className: "italic"
|
|
102
|
+
}),
|
|
103
|
+
del: (p) => /* @__PURE__ */ jsx("del", {
|
|
104
|
+
...dom(p),
|
|
105
|
+
className: "line-through text-[color:var(--ds-muted-foreground)]"
|
|
106
|
+
}),
|
|
107
|
+
code: (p) => /* @__PURE__ */ jsx("code", {
|
|
108
|
+
...dom(p),
|
|
109
|
+
className: cn("rounded-[var(--ds-radius-sm)] bg-[var(--ds-muted)] px-1 py-0.5", "font-[family-name:var(--ds-font-mono)] text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-foreground)]", "border border-[var(--ds-border-subtle)]")
|
|
110
|
+
}),
|
|
111
|
+
pre: (p) => {
|
|
112
|
+
const codeNode = p.node?.children.find((c) => c.type === "element" && c.tagName === "code");
|
|
113
|
+
const code = (codeNode ? hastText(codeNode) : hastText(p.node ?? {
|
|
114
|
+
type: "text",
|
|
115
|
+
value: ""
|
|
116
|
+
})).replace(/\n$/, "");
|
|
117
|
+
const classes = codeNode?.properties.className;
|
|
118
|
+
const language = (Array.isArray(classes) ? classes : [classes]).map(String).find((c) => c.startsWith("language-"))?.slice(9);
|
|
119
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
120
|
+
"data-slot": "markdown-pre",
|
|
121
|
+
"data-language": language,
|
|
122
|
+
className: cn(block, "group/pre relative overflow-hidden", "rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)] bg-[var(--ds-muted)]"),
|
|
123
|
+
children: [codeRenderer ? codeRenderer({
|
|
124
|
+
code,
|
|
125
|
+
language
|
|
126
|
+
}) : /* @__PURE__ */ jsx("pre", {
|
|
127
|
+
className: cn("overflow-x-auto p-3 pr-10", "font-[family-name:var(--ds-font-mono)] text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-foreground)]"),
|
|
128
|
+
children: /* @__PURE__ */ jsx("code", {
|
|
129
|
+
"data-language": language,
|
|
130
|
+
children: code
|
|
131
|
+
})
|
|
132
|
+
}), /* @__PURE__ */ jsx(CopyButton, { text: code })]
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
blockquote: (p) => /* @__PURE__ */ jsx("blockquote", {
|
|
136
|
+
...dom(p),
|
|
137
|
+
className: cn(block, "border-l-2 border-[var(--ds-border-strong)] pl-4 text-[color:var(--ds-muted-foreground)]", "[&>p]:text-[color:inherit]")
|
|
138
|
+
}),
|
|
139
|
+
ul: (p) => /* @__PURE__ */ jsx("ul", {
|
|
140
|
+
...dom(p),
|
|
141
|
+
className: cn(block, body, "list-disc pl-6 marker:text-[color:var(--ds-text-tertiary)]", "[&_ul]:my-1 [&_ol]:my-1")
|
|
142
|
+
}),
|
|
143
|
+
ol: (p) => /* @__PURE__ */ jsx("ol", {
|
|
144
|
+
...dom(p),
|
|
145
|
+
className: cn(block, body, "list-decimal pl-6 marker:text-[color:var(--ds-text-tertiary)]", "[&_ul]:my-1 [&_ol]:my-1")
|
|
146
|
+
}),
|
|
147
|
+
li: (p) => {
|
|
148
|
+
const rest = dom(p);
|
|
149
|
+
const task = typeof rest.className === "string" && rest.className.includes("task-list-item");
|
|
150
|
+
return /* @__PURE__ */ jsx("li", {
|
|
151
|
+
...rest,
|
|
152
|
+
"data-task": task || void 0,
|
|
153
|
+
className: cn("my-1 group-data-[compact]/markdown:my-0.5", task && "-ml-6 list-none")
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
input: (p) => {
|
|
157
|
+
const rest = dom(p);
|
|
158
|
+
return /* @__PURE__ */ jsx("input", {
|
|
159
|
+
...rest,
|
|
160
|
+
readOnly: true,
|
|
161
|
+
className: cn("mr-2 size-3.5 translate-y-px accent-[var(--ds-primary)] align-baseline", "rounded-[var(--ds-radius-sm)]")
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
table: (p) => /* @__PURE__ */ jsx("div", {
|
|
165
|
+
"data-slot": "markdown-table",
|
|
166
|
+
className: cn(block, "w-full overflow-x-auto"),
|
|
167
|
+
children: /* @__PURE__ */ jsx("table", {
|
|
168
|
+
...dom(p),
|
|
169
|
+
className: cn("w-full border-collapse text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-foreground)]")
|
|
170
|
+
})
|
|
171
|
+
}),
|
|
172
|
+
thead: (p) => /* @__PURE__ */ jsx("thead", {
|
|
173
|
+
...dom(p),
|
|
174
|
+
className: "bg-[var(--ds-muted)]"
|
|
175
|
+
}),
|
|
176
|
+
tbody: (p) => /* @__PURE__ */ jsx("tbody", {
|
|
177
|
+
...dom(p),
|
|
178
|
+
className: "[&>tr:nth-child(even)]:bg-[var(--ds-muted)]"
|
|
179
|
+
}),
|
|
180
|
+
tr: (p) => /* @__PURE__ */ jsx("tr", { ...dom(p) }),
|
|
181
|
+
th: (p) => /* @__PURE__ */ jsx("th", {
|
|
182
|
+
...dom(p),
|
|
183
|
+
className: cn(cellClassName, "text-left font-semibold text-[color:var(--ds-foreground)]")
|
|
184
|
+
}),
|
|
185
|
+
td: (p) => /* @__PURE__ */ jsx("td", {
|
|
186
|
+
...dom(p),
|
|
187
|
+
className: cellClassName
|
|
188
|
+
}),
|
|
189
|
+
hr: (p) => /* @__PURE__ */ jsx("hr", {
|
|
190
|
+
...dom(p),
|
|
191
|
+
className: "my-5 border-0 border-t border-[var(--ds-border)] group-data-[compact]/markdown:my-3"
|
|
192
|
+
}),
|
|
193
|
+
img: (p) => /* @__PURE__ */ jsx("img", {
|
|
194
|
+
...dom(p),
|
|
195
|
+
className: "my-3 max-w-full rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)]"
|
|
196
|
+
})
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function Markdown({ children, compact = false, codeRenderer, components, remarkPlugins, rehypePlugins, rehypePluginsBeforeSanitize, sanitizeSchema = markdownSanitizeSchema, urlTransform, className, ...props }) {
|
|
200
|
+
const map = React.useMemo(() => ({
|
|
201
|
+
...buildComponents(codeRenderer),
|
|
202
|
+
...components
|
|
203
|
+
}), [codeRenderer, components]);
|
|
204
|
+
const remark = React.useMemo(() => [remarkGfm, ...remarkPlugins ?? []], [remarkPlugins]);
|
|
205
|
+
const rehype = React.useMemo(() => [
|
|
206
|
+
...rehypePluginsBeforeSanitize ?? [],
|
|
207
|
+
[rehypeSanitize, sanitizeSchema],
|
|
208
|
+
...rehypePlugins ?? []
|
|
209
|
+
], [
|
|
210
|
+
rehypePluginsBeforeSanitize,
|
|
211
|
+
rehypePlugins,
|
|
212
|
+
sanitizeSchema
|
|
213
|
+
]);
|
|
214
|
+
return /* @__PURE__ */ jsx("div", {
|
|
215
|
+
"data-slot": "markdown",
|
|
216
|
+
"data-compact": compact || void 0,
|
|
217
|
+
className: cn("group/markdown min-w-0 break-words text-[color:var(--ds-foreground)]", className),
|
|
218
|
+
...props,
|
|
219
|
+
children: /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
220
|
+
remarkPlugins: remark,
|
|
221
|
+
rehypePlugins: rehype,
|
|
222
|
+
components: map,
|
|
223
|
+
urlTransform,
|
|
224
|
+
children: children ?? ""
|
|
225
|
+
})
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
export { Markdown, markdownSanitizeSchema };
|
|
230
|
+
|
|
231
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","names":[],"sources":["../../src/components/markdown.tsx"],"sourcesContent":["\"use client\";\n\nimport { Check, Copy } from \"lucide-react\";\nimport * as React from \"react\";\nimport ReactMarkdown, { type Components, type ExtraProps, type Options } from \"react-markdown\";\nimport rehypeSanitize, { defaultSchema, type Options as SanitizeSchema } from \"rehype-sanitize\";\nimport remarkGfm from \"remark-gfm\";\nimport { cn } from \"@/lib/cn\";\nimport { shakeable } from \"@/lib/shakeable\";\n\n/**\n * Markdown.\n *\n * react-markdown with GFM (tables, task lists, strikethrough, autolinks) and\n * rehype-sanitize's GitHub schema, so pasted or model-written HTML cannot run\n * scripts or leak styles. Every element is mapped through `components` onto\n * tokens: headings take the type scale with the display face, body copy is\n * `body-sm`, code is mono on the sunken surface, tables stripe with the\n * border colour and fenced blocks carry a copy button.\n *\n * `compact` halves the vertical rhythm and steps headings down for chat\n * bubbles. There is no syntax highlighting; pass `codeRenderer` to plug one in\n * (shiki, prism) for fenced blocks — it receives the raw text and language.\n */\n\ntype HastNode = NonNullable<ExtraProps[\"node\"]>;\ntype HastChild = HastNode[\"children\"][number];\n\nfunction hastText(node: HastChild | HastNode): string {\n if (node.type === \"text\") return node.value;\n if (\"children\" in node) return node.children.map(hastText).join(\"\");\n return \"\";\n}\n\n/** The GitHub schema, plus `class=\"language-*\"` on code and `checked` on task boxes. */\nconst markdownSanitizeSchema: SanitizeSchema = /* @__PURE__ */ shakeable(() => ({\n ...defaultSchema,\n attributes: {\n ...defaultSchema.attributes,\n code: [...(defaultSchema.attributes?.code ?? []), [\"className\", /^language-./]],\n input: [...(defaultSchema.attributes?.input ?? []), \"checked\"],\n },\n}));\n\nexport interface CodeRendererProps {\n /** The fenced block's text, without a trailing newline. */\n code: string;\n /** From the fence's info string, e.g. `ts` for ```ts. */\n language: string | undefined;\n}\n\n/** Strip react-markdown's `node` so the rest can be spread onto a DOM element. */\nfunction dom<T extends object>(props: T & ExtraProps): T {\n const { node: _node, ...rest } = props;\n return rest as T;\n}\n\nconst block = \"my-3 first:mt-0 last:mb-0 group-data-[compact]/markdown:my-1.5\";\n\nconst body = /* @__PURE__ */ cn(\n \"text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)] text-[color:var(--ds-foreground)]\",\n);\n\nconst heading = /* @__PURE__ */ cn(\n \"font-[family-name:var(--ds-font-display)] text-[color:var(--ds-foreground)]\",\n \"mt-6 mb-2 first:mt-0 group-data-[compact]/markdown:mt-3 group-data-[compact]/markdown:mb-1\",\n);\n\nconst cellClassName = \"border border-[var(--ds-border)] px-3 py-1.5 align-top\";\n\nfunction CopyButton({ text }: { text: string }) {\n const [copied, setCopied] = React.useState(false);\n React.useEffect(() => {\n if (!copied) return;\n const id = window.setTimeout(() => setCopied(false), 1500);\n return () => window.clearTimeout(id);\n }, [copied]);\n return (\n <button\n type=\"button\"\n data-slot=\"markdown-copy\"\n data-copied={copied || undefined}\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n onClick={() => {\n void navigator.clipboard?.writeText(text).then(() => setCopied(true));\n }}\n className={cn(\n \"absolute top-1.5 right-1.5 inline-flex size-7 items-center justify-center\",\n \"rounded-[var(--ds-radius-sm)] text-[color:var(--ds-muted-foreground)]\",\n \"opacity-0 transition-opacity duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n \"group-hover/pre:opacity-100 focus-visible:opacity-100 data-[copied]:opacity-100\",\n \"hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]\",\n \"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"data-[copied]:text-[color:var(--ds-success)]\",\n \"[&_svg]:size-3.5\",\n )}\n >\n {copied ? <Check aria-hidden /> : <Copy aria-hidden />}\n </button>\n );\n}\n\nfunction buildComponents(codeRenderer: MarkdownProps[\"codeRenderer\"]): Components {\n return {\n h1: (p) => (\n <h1\n {...dom(p)}\n className={cn(\n heading,\n \"text-[length:var(--ds-text-h1)] leading-[var(--ds-text-h1--line-height)] tracking-[var(--ds-text-h1--letter-spacing)] font-semibold\",\n \"group-data-[compact]/markdown:text-[length:var(--ds-text-h3)] group-data-[compact]/markdown:leading-[var(--ds-text-h3--line-height)]\",\n )}\n />\n ),\n h2: (p) => (\n <h2\n {...dom(p)}\n className={cn(\n heading,\n \"text-[length:var(--ds-text-h2)] leading-[var(--ds-text-h2--line-height)] tracking-[var(--ds-text-h2--letter-spacing)] font-bold\",\n \"group-data-[compact]/markdown:text-[length:var(--ds-text-body-lg)] group-data-[compact]/markdown:leading-[var(--ds-text-body-lg--line-height)]\",\n )}\n />\n ),\n h3: (p) => (\n <h3\n {...dom(p)}\n className={cn(\n heading,\n \"text-[length:var(--ds-text-h3)] leading-[var(--ds-text-h3--line-height)] font-semibold\",\n \"group-data-[compact]/markdown:text-[length:var(--ds-text-body-md)] group-data-[compact]/markdown:leading-[var(--ds-text-body-md--line-height)]\",\n )}\n />\n ),\n h4: (p) => (\n <h4\n {...dom(p)}\n className={cn(\n heading,\n \"font-[family-name:var(--ds-font-body)] text-[length:var(--ds-text-body-md)] leading-[var(--ds-text-body-md--line-height)] font-semibold\",\n )}\n />\n ),\n h5: (p) => (\n <h5\n {...dom(p)}\n className={cn(\n heading,\n \"font-[family-name:var(--ds-font-body)] text-[length:var(--ds-text-body)] leading-[var(--ds-text-body--line-height)] font-semibold\",\n )}\n />\n ),\n h6: (p) => (\n <h6\n {...dom(p)}\n className={cn(\n heading,\n \"font-[family-name:var(--ds-font-body)] text-[length:var(--ds-text-eyebrow)] leading-[var(--ds-text-eyebrow--line-height)] tracking-[var(--ds-text-eyebrow--letter-spacing)] font-semibold uppercase text-[color:var(--ds-muted-foreground)]\",\n )}\n />\n ),\n p: (p) => <p {...dom(p)} className={cn(block, body)} />,\n a: (p) => {\n const { href, ...rest } = dom(p);\n const external = typeof href === \"string\" && /^https?:\\/\\//.test(href);\n return (\n <a\n {...rest}\n href={href}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n className={cn(\n \"text-[color:var(--ds-primary)] underline underline-offset-4 decoration-[color:var(--ds-primary)]/40\",\n \"hover:text-[color:var(--ds-primary-hover)] hover:decoration-[color:var(--ds-primary-hover)]\",\n \"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40 rounded-[var(--ds-radius-sm)]\",\n )}\n />\n );\n },\n strong: (p) => <strong {...dom(p)} className=\"font-semibold text-[color:var(--ds-foreground)]\" />,\n em: (p) => <em {...dom(p)} className=\"italic\" />,\n del: (p) => <del {...dom(p)} className=\"line-through text-[color:var(--ds-muted-foreground)]\" />,\n code: (p) => (\n <code\n {...dom(p)}\n className={cn(\n \"rounded-[var(--ds-radius-sm)] bg-[var(--ds-muted)] px-1 py-0.5\",\n \"font-[family-name:var(--ds-font-mono)] text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-foreground)]\",\n \"border border-[var(--ds-border-subtle)]\",\n )}\n />\n ),\n pre: (p) => {\n const codeNode = p.node?.children.find(\n (c): c is HastNode => c.type === \"element\" && c.tagName === \"code\",\n );\n const source = codeNode ? hastText(codeNode) : hastText(p.node ?? { type: \"text\", value: \"\" });\n const code = source.replace(/\\n$/, \"\");\n const classes = codeNode?.properties.className;\n const language = (Array.isArray(classes) ? classes : [classes])\n .map(String)\n .find((c) => c.startsWith(\"language-\"))\n ?.slice(\"language-\".length);\n return (\n <div\n data-slot=\"markdown-pre\"\n data-language={language}\n className={cn(\n block,\n \"group/pre relative overflow-hidden\",\n \"rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)] bg-[var(--ds-muted)]\",\n )}\n >\n {codeRenderer ? (\n codeRenderer({ code, language })\n ) : (\n <pre\n className={cn(\n \"overflow-x-auto p-3 pr-10\",\n \"font-[family-name:var(--ds-font-mono)] text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-foreground)]\",\n )}\n >\n <code data-language={language}>{code}</code>\n </pre>\n )}\n <CopyButton text={code} />\n </div>\n );\n },\n blockquote: (p) => (\n <blockquote\n {...dom(p)}\n className={cn(\n block,\n \"border-l-2 border-[var(--ds-border-strong)] pl-4 text-[color:var(--ds-muted-foreground)]\",\n \"[&>p]:text-[color:inherit]\",\n )}\n />\n ),\n ul: (p) => (\n <ul\n {...dom(p)}\n className={cn(\n block,\n body,\n \"list-disc pl-6 marker:text-[color:var(--ds-text-tertiary)]\",\n \"[&_ul]:my-1 [&_ol]:my-1\",\n )}\n />\n ),\n ol: (p) => (\n <ol\n {...dom(p)}\n className={cn(\n block,\n body,\n \"list-decimal pl-6 marker:text-[color:var(--ds-text-tertiary)]\",\n \"[&_ul]:my-1 [&_ol]:my-1\",\n )}\n />\n ),\n li: (p) => {\n const rest = dom(p);\n const task = typeof rest.className === \"string\" && rest.className.includes(\"task-list-item\");\n return (\n <li\n {...rest}\n data-task={task || undefined}\n className={cn(\"my-1 group-data-[compact]/markdown:my-0.5\", task && \"-ml-6 list-none\")}\n />\n );\n },\n input: (p) => {\n const rest = dom(p);\n return (\n <input\n {...rest}\n readOnly\n className={cn(\n \"mr-2 size-3.5 translate-y-px accent-[var(--ds-primary)] align-baseline\",\n \"rounded-[var(--ds-radius-sm)]\",\n )}\n />\n );\n },\n table: (p) => (\n <div data-slot=\"markdown-table\" className={cn(block, \"w-full overflow-x-auto\")}>\n <table\n {...dom(p)}\n className={cn(\n \"w-full border-collapse text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-foreground)]\",\n )}\n />\n </div>\n ),\n thead: (p) => <thead {...dom(p)} className=\"bg-[var(--ds-muted)]\" />,\n tbody: (p) => <tbody {...dom(p)} className=\"[&>tr:nth-child(even)]:bg-[var(--ds-muted)]\" />,\n tr: (p) => <tr {...dom(p)} />,\n th: (p) => (\n <th\n {...dom(p)}\n className={cn(cellClassName, \"text-left font-semibold text-[color:var(--ds-foreground)]\")}\n />\n ),\n td: (p) => <td {...dom(p)} className={cellClassName} />,\n hr: (p) => (\n <hr {...dom(p)} className=\"my-5 border-0 border-t border-[var(--ds-border)] group-data-[compact]/markdown:my-3\" />\n ),\n img: (p) => (\n <img\n {...dom(p)}\n className=\"my-3 max-w-full rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)]\"\n />\n ),\n };\n}\n\nexport interface MarkdownProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** The markdown source. */\n children: string | null | undefined;\n /** Tighter rhythm and smaller headings, for chat bubbles. */\n compact?: boolean;\n /** Renders fenced code blocks; the copy button is still added. Use for syntax highlighting. */\n codeRenderer?: (props: CodeRendererProps) => React.ReactNode;\n /** Override or extend the element map. */\n components?: Components;\n /** Extra remark plugins, appended after GFM. */\n remarkPlugins?: NonNullable<Options[\"remarkPlugins\"]>;\n /** Extra rehype plugins, appended after sanitize. */\n rehypePlugins?: NonNullable<Options[\"rehypePlugins\"]>;\n /**\n * Rehype plugins that must run BEFORE sanitize. The one legitimate use is\n * `rehype-raw` in a host that embeds trusted custom tags in markdown (the\n * webchat's `<cortena-*>` blocks): raw HTML has to become nodes before the\n * sanitizer sees it, and the sanitize schema then decides what survives.\n * Anything placed here widens what reaches the sanitizer, so keep the\n * schema strict when you use it.\n */\n rehypePluginsBeforeSanitize?: NonNullable<Options[\"rehypePlugins\"]>;\n /** Replace the sanitize schema; defaults to `markdownSanitizeSchema`. */\n sanitizeSchema?: SanitizeSchema;\n /** Rewrite URLs; defaults to react-markdown's, which drops non-http(s)/mailto/tel schemes. */\n urlTransform?: Options[\"urlTransform\"];\n}\n\nfunction Markdown({\n children,\n compact = false,\n codeRenderer,\n components,\n remarkPlugins,\n rehypePlugins,\n rehypePluginsBeforeSanitize,\n sanitizeSchema = markdownSanitizeSchema,\n urlTransform,\n className,\n ...props\n}: MarkdownProps) {\n const map = React.useMemo(\n () => ({ ...buildComponents(codeRenderer), ...components }),\n [codeRenderer, components],\n );\n const remark = React.useMemo(() => [remarkGfm, ...(remarkPlugins ?? [])], [remarkPlugins]);\n const rehype = React.useMemo(\n () => [...(rehypePluginsBeforeSanitize ?? []), [rehypeSanitize, sanitizeSchema] as const, ...(rehypePlugins ?? [])],\n [rehypePluginsBeforeSanitize, rehypePlugins, sanitizeSchema],\n );\n return (\n <div\n data-slot=\"markdown\"\n data-compact={compact || undefined}\n className={cn(\"group/markdown min-w-0 break-words text-[color:var(--ds-foreground)]\", className)}\n {...props}\n >\n <ReactMarkdown\n remarkPlugins={remark}\n rehypePlugins={rehype as NonNullable<Options[\"rehypePlugins\"]>}\n components={map}\n urlTransform={urlTransform}\n >\n {children ?? \"\"}\n </ReactMarkdown>\n </div>\n );\n}\n\nexport { Markdown, markdownSanitizeSchema };\nexport type { Components as MarkdownComponents };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
//#region src/components/page-header.d.ts
|
|
4
|
+
export interface PageHeaderProps extends Omit<React.ComponentProps<"header">, "title"> {
|
|
5
|
+
/** The page title. Rendered as the h1; use `titleAs` to demote it. */
|
|
6
|
+
title: React.ReactNode;
|
|
7
|
+
description?: React.ReactNode;
|
|
8
|
+
/** Buttons for the page, right-aligned on wide viewports. */
|
|
9
|
+
actions?: React.ReactNode;
|
|
10
|
+
/** Short uppercase label above the title, e.g. a section or product name. */
|
|
11
|
+
eyebrow?: React.ReactNode;
|
|
12
|
+
/** A Breadcrumb or any navigation rendered above the eyebrow. */
|
|
13
|
+
breadcrumb?: React.ReactNode;
|
|
14
|
+
/** Heading level when the page already has an h1. */
|
|
15
|
+
titleAs?: "h1" | "h2" | "h3";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* PageHeader — the top of every screen: breadcrumb, eyebrow, title,
|
|
19
|
+
* description, and an actions slot. The title is an h1 on the h2 step of the
|
|
20
|
+
* type scale in the display face, so screens read as one product rather than
|
|
21
|
+
* as a stack of card titles.
|
|
22
|
+
*/
|
|
23
|
+
declare function PageHeader({ className, title, description, actions, eyebrow, breadcrumb, titleAs: Title, children, ...props }: PageHeaderProps): React.JSX.Element;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { PageHeader };
|
|
26
|
+
//# sourceMappingURL=page-header.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/page-header.tsx
|
|
6
|
+
/**
|
|
7
|
+
* PageHeader — the top of every screen: breadcrumb, eyebrow, title,
|
|
8
|
+
* description, and an actions slot. The title is an h1 on the h2 step of the
|
|
9
|
+
* type scale in the display face, so screens read as one product rather than
|
|
10
|
+
* as a stack of card titles.
|
|
11
|
+
*/
|
|
12
|
+
function PageHeader({ className, title, description, actions, eyebrow, breadcrumb, titleAs: Title = "h1", children, ...props }) {
|
|
13
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
14
|
+
"data-slot": "page-header",
|
|
15
|
+
className: cn("flex flex-col gap-4 text-[color:var(--ds-foreground)] md:flex-row md:items-end md:justify-between", className),
|
|
16
|
+
...props,
|
|
17
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
18
|
+
"data-slot": "page-header-content",
|
|
19
|
+
className: "flex min-w-0 flex-col gap-1.5",
|
|
20
|
+
children: [
|
|
21
|
+
breadcrumb ? /* @__PURE__ */ jsx("div", {
|
|
22
|
+
"data-slot": "page-header-breadcrumb",
|
|
23
|
+
className: "mb-1",
|
|
24
|
+
children: breadcrumb
|
|
25
|
+
}) : null,
|
|
26
|
+
eyebrow ? /* @__PURE__ */ jsx("span", {
|
|
27
|
+
"data-slot": "page-header-eyebrow",
|
|
28
|
+
className: cn("text-[length:var(--ds-text-eyebrow)] font-extrabold uppercase", "leading-[var(--ds-text-eyebrow--line-height)] tracking-[var(--ds-text-eyebrow--letter-spacing)]", "text-[color:var(--ds-eyebrow)]"),
|
|
29
|
+
children: eyebrow
|
|
30
|
+
}) : null,
|
|
31
|
+
/* @__PURE__ */ jsx(Title, {
|
|
32
|
+
"data-slot": "page-header-title",
|
|
33
|
+
className: cn("font-display text-[length:var(--ds-text-h2)] font-bold text-balance", "leading-[var(--ds-text-h2--line-height)] tracking-[var(--ds-text-h2--letter-spacing)]", "text-[color:var(--ds-foreground)]"),
|
|
34
|
+
children: title
|
|
35
|
+
}),
|
|
36
|
+
description ? /* @__PURE__ */ jsx("p", {
|
|
37
|
+
"data-slot": "page-header-description",
|
|
38
|
+
className: cn("max-w-prose text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)]", "text-[color:var(--ds-muted-foreground)]"),
|
|
39
|
+
children: description
|
|
40
|
+
}) : null,
|
|
41
|
+
children
|
|
42
|
+
]
|
|
43
|
+
}), actions ? /* @__PURE__ */ jsx("div", {
|
|
44
|
+
"data-slot": "page-header-actions",
|
|
45
|
+
className: "flex shrink-0 flex-wrap items-center gap-2 md:justify-end",
|
|
46
|
+
children: actions
|
|
47
|
+
}) : null]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { PageHeader };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=page-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-header.js","names":[],"sources":["../../src/components/page-header.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface PageHeaderProps extends Omit<React.ComponentProps<\"header\">, \"title\"> {\n /** The page title. Rendered as the h1; use `titleAs` to demote it. */\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Buttons for the page, right-aligned on wide viewports. */\n actions?: React.ReactNode;\n /** Short uppercase label above the title, e.g. a section or product name. */\n eyebrow?: React.ReactNode;\n /** A Breadcrumb or any navigation rendered above the eyebrow. */\n breadcrumb?: React.ReactNode;\n /** Heading level when the page already has an h1. */\n titleAs?: \"h1\" | \"h2\" | \"h3\";\n}\n\n/**\n * PageHeader — the top of every screen: breadcrumb, eyebrow, title,\n * description, and an actions slot. The title is an h1 on the h2 step of the\n * type scale in the display face, so screens read as one product rather than\n * as a stack of card titles.\n */\nfunction PageHeader({\n className,\n title,\n description,\n actions,\n eyebrow,\n breadcrumb,\n titleAs: Title = \"h1\",\n children,\n ...props\n}: PageHeaderProps) {\n return (\n <header\n data-slot=\"page-header\"\n className={cn(\n \"flex flex-col gap-4 text-[color:var(--ds-foreground)] md:flex-row md:items-end md:justify-between\",\n className,\n )}\n {...props}\n >\n <div data-slot=\"page-header-content\" className=\"flex min-w-0 flex-col gap-1.5\">\n {breadcrumb ? (\n <div data-slot=\"page-header-breadcrumb\" className=\"mb-1\">\n {breadcrumb}\n </div>\n ) : null}\n {eyebrow ? (\n <span\n data-slot=\"page-header-eyebrow\"\n className={cn(\n \"text-[length:var(--ds-text-eyebrow)] font-extrabold uppercase\",\n \"leading-[var(--ds-text-eyebrow--line-height)] tracking-[var(--ds-text-eyebrow--letter-spacing)]\",\n \"text-[color:var(--ds-eyebrow)]\",\n )}\n >\n {eyebrow}\n </span>\n ) : null}\n <Title\n data-slot=\"page-header-title\"\n className={cn(\n \"font-display text-[length:var(--ds-text-h2)] font-bold text-balance\",\n \"leading-[var(--ds-text-h2--line-height)] tracking-[var(--ds-text-h2--letter-spacing)]\",\n \"text-[color:var(--ds-foreground)]\",\n )}\n >\n {title}\n </Title>\n {description ? (\n <p\n data-slot=\"page-header-description\"\n className={cn(\n \"max-w-prose text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)]\",\n \"text-[color:var(--ds-muted-foreground)]\",\n )}\n >\n {description}\n </p>\n ) : null}\n {children}\n </div>\n {actions ? (\n <div\n data-slot=\"page-header-actions\"\n className=\"flex shrink-0 flex-wrap items-center gap-2 md:justify-end\"\n >\n {actions}\n </div>\n ) : null}\n </header>\n );\n}\n\nexport { PageHeader };\n"],"mappings":";;;;;;;;;;;AAyBA,SAAS,WAAW,EAClB,WACA,OACA,aACA,SACA,SACA,YACA,SAAS,QAAQ,MACjB,UACA,GAAG,SACe;CAClB,OACE,qBAAC,UAAD;EACE,aAAU;EACV,WAAW,GACT,qGACA,SACF;EACA,GAAI;EANN,UAAA,CAQE,qBAAC,OAAD;GAAK,aAAU;GAAsB,WAAU;GAA/C,UAAA;IACG,aACC,oBAAC,OAAD;KAAK,aAAU;KAAyB,WAAU;KAC/C,UAAA;IACE,CAAA,IACH;IACH,UACC,oBAAC,QAAD;KACE,aAAU;KACV,WAAW,GACT,iEACA,mGACA,gCACF;KAEC,UAAA;IACG,CAAA,IACJ;IACJ,oBAAC,OAAD;KACE,aAAU;KACV,WAAW,GACT,uEACA,yFACA,mCACF;KAEC,UAAA;IACI,CAAA;IACN,cACC,oBAAC,KAAD;KACE,aAAU;KACV,WAAW,GACT,kGACA,yCACF;KAEC,UAAA;IACA,CAAA,IACD;IACH;GACE;EACJ,CAAA,GAAA,UACC,oBAAC,OAAD;GACE,aAAU;GACV,WAAU;GAET,UAAA;EACE,CAAA,IACH,IACE;;AAEZ"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Popover } from "@base-ui/react/popover";
|
|
3
|
+
//#region src/components/popover.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Popover.
|
|
6
|
+
*
|
|
7
|
+
* `PopoverContent` composes Base UI's Portal + Positioner + Popup so consumers
|
|
8
|
+
* keep writing `<PopoverContent side="bottom" align="start">`; the positioning
|
|
9
|
+
* props are forwarded to the Positioner, which is where Base UI reads them.
|
|
10
|
+
*
|
|
11
|
+
* Radix users: `asChild` on the trigger becomes `render={<Button />}`;
|
|
12
|
+
* `PopoverAnchor` is gone — pass `anchor={ref}` to `PopoverContent` instead.
|
|
13
|
+
*/
|
|
14
|
+
declare function Popover$1(props: Popover.Root.Props): import("react").JSX.Element;
|
|
15
|
+
declare function PopoverTrigger(props: Popover.Trigger.Props): import("react").JSX.Element;
|
|
16
|
+
declare function PopoverClose(props: Popover.Close.Props): import("react").JSX.Element;
|
|
17
|
+
declare function PopoverTitle({ className, ...props }: Popover.Title.Props): import("react").JSX.Element;
|
|
18
|
+
declare function PopoverDescription({ className, ...props }: Popover.Description.Props): import("react").JSX.Element;
|
|
19
|
+
type PositionerProps = Pick<Popover.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "anchor" | "collisionBoundary" | "collisionPadding" | "collisionAvoidance" | "sticky" | "positionMethod">;
|
|
20
|
+
export interface PopoverContentProps extends Popover.Popup.Props, PositionerProps {
|
|
21
|
+
/** Keep the popup mounted in the DOM while hidden. */
|
|
22
|
+
keepMounted?: boolean;
|
|
23
|
+
/** Where to portal the popup; defaults to `<body>`. */
|
|
24
|
+
container?: Popover.Portal.Props["container"];
|
|
25
|
+
}
|
|
26
|
+
declare function PopoverContent({ className, side, align, sideOffset, alignOffset, anchor, collisionBoundary, collisionPadding, collisionAvoidance, sticky, positionMethod, keepMounted, container, ...props }: PopoverContentProps): import("react").JSX.Element;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Popover$1 as Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger };
|
|
29
|
+
//# sourceMappingURL=popover.d.ts.map
|