@usetheo/ui 0.10.0-next.0 → 0.11.0-next.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/CHANGELOG.md +119 -3
- package/README.md +22 -21
- package/dist/chunk-BX7A5GUV.js +78 -0
- package/dist/chunk-BX7A5GUV.js.map +1 -0
- package/dist/{chunk-H3ANHVEL.js → chunk-DKQAHZG2.js} +4 -4
- package/dist/{chunk-H3ANHVEL.js.map → chunk-DKQAHZG2.js.map} +1 -1
- package/dist/{chunk-DAKIL5PC.js → chunk-IPEYGWA7.js} +3 -3
- package/dist/{chunk-DAKIL5PC.js.map → chunk-IPEYGWA7.js.map} +1 -1
- package/dist/chunk-IWSLOBYG.js +199 -0
- package/dist/chunk-IWSLOBYG.js.map +1 -0
- package/dist/chunk-MI5CXMZU.js +171 -0
- package/dist/chunk-MI5CXMZU.js.map +1 -0
- package/dist/chunk-QJGGTIUN.js +110 -0
- package/dist/chunk-QJGGTIUN.js.map +1 -0
- package/dist/chunk-R2PAGRDP.js +152 -0
- package/dist/chunk-R2PAGRDP.js.map +1 -0
- package/dist/{chunk-QU6RLHYH.js → chunk-TNBJ36XJ.js} +3 -3
- package/dist/{chunk-QU6RLHYH.js.map → chunk-TNBJ36XJ.js.map} +1 -1
- package/dist/components.css +1 -1
- package/dist/composites/agent-stream/index.js +3 -3
- package/dist/composites/data-table/index.js +10 -0
- package/dist/composites/data-table/index.js.map +1 -0
- package/dist/composites/page-shell/index.js +7 -0
- package/dist/composites/page-shell/index.js.map +1 -0
- package/dist/composites/rule-editor/index.js +2 -2
- package/dist/composites/skill-editor/index.js +2 -2
- package/dist/index.d.ts +281 -12
- package/dist/index.js +47 -42
- package/dist/index.js.map +1 -1
- package/dist/primitives/action-bar/index.js +4 -0
- package/dist/primitives/action-bar/index.js.map +1 -0
- package/dist/primitives/dropdown-menu/index.js +4 -0
- package/dist/primitives/dropdown-menu/index.js.map +1 -0
- package/dist/primitives/pin-input/index.js +4 -0
- package/dist/primitives/pin-input/index.js.map +1 -0
- package/llms.txt +4 -3
- package/package.json +63 -43
- package/registry/index.json +30 -0
- package/registry/r/action-bar.json +22 -0
- package/registry/r/data-table.json +27 -0
- package/registry/r/dropdown-menu.json +23 -0
- package/registry/r/page-shell.json +25 -0
- package/registry/r/pin-input.json +20 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Table } from './chunk-YRSKXEOD.js';
|
|
2
|
+
import { Skeleton } from './chunk-K5ARID4S.js';
|
|
3
|
+
import { Pagination } from './chunk-YOGHS4UU.js';
|
|
4
|
+
import { DropdownMenu } from './chunk-MI5CXMZU.js';
|
|
5
|
+
import { EmptyState } from './chunk-SWJ4EUOI.js';
|
|
6
|
+
import { cn } from './chunk-ZSRJCIWF.js';
|
|
7
|
+
import { ChevronDown, ChevronRight, MoreHorizontal } from 'lucide-react';
|
|
8
|
+
import { useState, useMemo, Fragment } from 'react';
|
|
9
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
function compareValues(a, b) {
|
|
12
|
+
if (a === b) return 0;
|
|
13
|
+
if (a === null || a === void 0) return -1;
|
|
14
|
+
if (b === null || b === void 0) return 1;
|
|
15
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
16
|
+
return String(a).localeCompare(String(b));
|
|
17
|
+
}
|
|
18
|
+
function DataTable(props) {
|
|
19
|
+
const {
|
|
20
|
+
data,
|
|
21
|
+
columns,
|
|
22
|
+
rowKey,
|
|
23
|
+
stickyHeader = true,
|
|
24
|
+
expandable,
|
|
25
|
+
expandMode = "multiple",
|
|
26
|
+
rowActions,
|
|
27
|
+
pagination,
|
|
28
|
+
defaultSort,
|
|
29
|
+
sort: controlledSort,
|
|
30
|
+
onSortChange,
|
|
31
|
+
loading = false,
|
|
32
|
+
emptyState,
|
|
33
|
+
className
|
|
34
|
+
} = props;
|
|
35
|
+
const isControlledSort = onSortChange !== void 0;
|
|
36
|
+
const [uncontrolledSort, setUncontrolledSort] = useState(
|
|
37
|
+
defaultSort ?? null
|
|
38
|
+
);
|
|
39
|
+
const sort = isControlledSort ? controlledSort ?? null : uncontrolledSort;
|
|
40
|
+
const isControlledPage = pagination?.controlledPage !== void 0;
|
|
41
|
+
const [uncontrolledPage, setUncontrolledPage] = useState(0);
|
|
42
|
+
const currentPage = isControlledPage ? pagination?.controlledPage ?? 0 : uncontrolledPage;
|
|
43
|
+
const effectivePageSize = Math.max(1, pagination?.pageSize ?? 10);
|
|
44
|
+
const [expanded, setExpanded] = useState(/* @__PURE__ */ new Set());
|
|
45
|
+
function handleSort(columnKey) {
|
|
46
|
+
let nextSort;
|
|
47
|
+
if (sort?.key !== columnKey) {
|
|
48
|
+
nextSort = { key: columnKey, direction: "asc" };
|
|
49
|
+
} else if (sort.direction === "asc") {
|
|
50
|
+
nextSort = { key: columnKey, direction: "desc" };
|
|
51
|
+
} else {
|
|
52
|
+
nextSort = null;
|
|
53
|
+
}
|
|
54
|
+
if (isControlledSort) {
|
|
55
|
+
onSortChange?.(nextSort);
|
|
56
|
+
} else {
|
|
57
|
+
setUncontrolledSort(nextSort);
|
|
58
|
+
if (!isControlledPage) setUncontrolledPage(0);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function handlePageChange(page) {
|
|
62
|
+
const zeroIdx = page - 1;
|
|
63
|
+
if (isControlledPage) {
|
|
64
|
+
pagination?.onPageChange?.(zeroIdx);
|
|
65
|
+
} else {
|
|
66
|
+
setUncontrolledPage(zeroIdx);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function toggleExpand(key) {
|
|
70
|
+
if (expandMode === "single") {
|
|
71
|
+
setExpanded((prev) => prev.has(key) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([key]));
|
|
72
|
+
} else {
|
|
73
|
+
setExpanded((prev) => {
|
|
74
|
+
const next = new Set(prev);
|
|
75
|
+
if (next.has(key)) {
|
|
76
|
+
next.delete(key);
|
|
77
|
+
} else {
|
|
78
|
+
next.add(key);
|
|
79
|
+
}
|
|
80
|
+
return next;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const sortedData = useMemo(() => {
|
|
85
|
+
if (isControlledSort || sort === null) return data;
|
|
86
|
+
const col = columns.find((c) => c.key === sort.key);
|
|
87
|
+
if (!col) return data;
|
|
88
|
+
const sorted = [...data].sort((a, b) => {
|
|
89
|
+
const aVal = col.render ? null : a[sort.key];
|
|
90
|
+
const bVal = col.render ? null : b[sort.key];
|
|
91
|
+
const cmp = compareValues(aVal, bVal);
|
|
92
|
+
return sort.direction === "asc" ? cmp : -cmp;
|
|
93
|
+
});
|
|
94
|
+
return sorted;
|
|
95
|
+
}, [data, sort, isControlledSort, columns]);
|
|
96
|
+
const visibleData = useMemo(() => {
|
|
97
|
+
if (!pagination) return sortedData;
|
|
98
|
+
if (isControlledPage) return sortedData;
|
|
99
|
+
const start = currentPage * effectivePageSize;
|
|
100
|
+
return sortedData.slice(start, start + effectivePageSize);
|
|
101
|
+
}, [sortedData, pagination, isControlledPage, currentPage, effectivePageSize]);
|
|
102
|
+
const extraCols = (expandable ? 1 : 0) + (rowActions ? 1 : 0);
|
|
103
|
+
const expandedColSpan = columns.length + extraCols;
|
|
104
|
+
const totalCols = columns.length + extraCols;
|
|
105
|
+
if (loading) {
|
|
106
|
+
return /* @__PURE__ */ jsx("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
107
|
+
/* @__PURE__ */ jsx(Table.Header, { className: stickyHeader ? "sticky top-0 bg-card" : void 0, children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
108
|
+
expandable ? /* @__PURE__ */ jsx(Table.HeaderCell, { children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Expand" }) }) : null,
|
|
109
|
+
columns.map((col) => /* @__PURE__ */ jsx(Table.HeaderCell, { align: col.align, children: col.label }, col.key)),
|
|
110
|
+
rowActions ? /* @__PURE__ */ jsx(Table.HeaderCell, { children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Actions" }) }) : null
|
|
111
|
+
] }) }),
|
|
112
|
+
/* @__PURE__ */ jsx(Table.Body, { children: Array.from({ length: 5 }, (_, i) => (
|
|
113
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows are positional placeholders
|
|
114
|
+
/* @__PURE__ */ jsx(Table.Row, { children: Array.from({ length: totalCols }, (_2, j) => (
|
|
115
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton cells are positional placeholders
|
|
116
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }) }, `s-${i}-${j}`)
|
|
117
|
+
)) }, `skeleton-${i}`)
|
|
118
|
+
)) })
|
|
119
|
+
] }) });
|
|
120
|
+
}
|
|
121
|
+
if (sortedData.length === 0) {
|
|
122
|
+
return /* @__PURE__ */ jsx("div", { className: cn("w-full", className), children: emptyState ?? /* @__PURE__ */ jsx(EmptyState, { title: "No data", description: "There's nothing here yet." }) });
|
|
123
|
+
}
|
|
124
|
+
const totalPages = pagination ? Math.ceil(sortedData.length / effectivePageSize) : 1;
|
|
125
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
|
|
126
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
127
|
+
/* @__PURE__ */ jsx(Table.Header, { className: stickyHeader ? "sticky top-0 z-10 bg-card" : void 0, children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
128
|
+
expandable ? /* @__PURE__ */ jsx(Table.HeaderCell, { children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Expand" }) }) : null,
|
|
129
|
+
columns.map((col) => {
|
|
130
|
+
const isSortable = col.sortable === true;
|
|
131
|
+
const isActive = sort?.key === col.key;
|
|
132
|
+
return /* @__PURE__ */ jsx(
|
|
133
|
+
Table.HeaderCell,
|
|
134
|
+
{
|
|
135
|
+
align: col.align,
|
|
136
|
+
onSort: isSortable ? () => handleSort(col.key) : void 0,
|
|
137
|
+
sortDirection: isSortable ? isActive ? sort?.direction : "none" : void 0,
|
|
138
|
+
style: col.width ? { width: col.width } : void 0,
|
|
139
|
+
children: col.label
|
|
140
|
+
},
|
|
141
|
+
col.key
|
|
142
|
+
);
|
|
143
|
+
}),
|
|
144
|
+
rowActions ? /* @__PURE__ */ jsx(Table.HeaderCell, { "aria-label": "Actions" }) : null
|
|
145
|
+
] }) }),
|
|
146
|
+
/* @__PURE__ */ jsx(Table.Body, { children: visibleData.map((row) => {
|
|
147
|
+
const key = rowKey(row);
|
|
148
|
+
const expandedContent = expandable ? expandable(row) : null;
|
|
149
|
+
const isExpandable = expandedContent !== null && expandedContent !== void 0;
|
|
150
|
+
const isExpanded = expanded.has(key);
|
|
151
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
152
|
+
/* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
153
|
+
expandable ? /* @__PURE__ */ jsx(Table.Cell, { children: isExpandable ? /* @__PURE__ */ jsx(
|
|
154
|
+
"button",
|
|
155
|
+
{
|
|
156
|
+
type: "button",
|
|
157
|
+
onClick: () => toggleExpand(key),
|
|
158
|
+
"aria-expanded": isExpanded,
|
|
159
|
+
"aria-controls": `expanded-${key}`,
|
|
160
|
+
"aria-label": isExpanded ? "Collapse row" : "Expand row",
|
|
161
|
+
className: "inline-flex items-center justify-center rounded-md p-0.5 hover:bg-muted",
|
|
162
|
+
children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true", className: "size-4" }) : /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "size-4" })
|
|
163
|
+
}
|
|
164
|
+
) : null }) : null,
|
|
165
|
+
columns.map((col) => /* @__PURE__ */ jsx(Table.Cell, { align: col.align, className: col.className, children: col.render ? col.render(row) : String(row[col.key] ?? "") }, col.key)),
|
|
166
|
+
rowActions ? /* @__PURE__ */ jsx(Table.Cell, { align: "right", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
167
|
+
/* @__PURE__ */ jsx(
|
|
168
|
+
DropdownMenu.Trigger,
|
|
169
|
+
{
|
|
170
|
+
"aria-label": "Row actions",
|
|
171
|
+
className: cn(
|
|
172
|
+
"inline-flex size-7 items-center justify-center rounded-md",
|
|
173
|
+
"text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
174
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
175
|
+
),
|
|
176
|
+
children: /* @__PURE__ */ jsx(MoreHorizontal, { "aria-hidden": "true", className: "size-4" })
|
|
177
|
+
}
|
|
178
|
+
),
|
|
179
|
+
/* @__PURE__ */ jsx(DropdownMenu.Content, { align: "end", children: rowActions(row) })
|
|
180
|
+
] }) }) : null
|
|
181
|
+
] }),
|
|
182
|
+
isExpanded && isExpandable ? /* @__PURE__ */ jsx("tr", { id: `expanded-${key}`, children: /* @__PURE__ */ jsx("td", { colSpan: expandedColSpan, className: "bg-muted/30 p-4", children: expandedContent }) }) : null
|
|
183
|
+
] }, key);
|
|
184
|
+
}) })
|
|
185
|
+
] }),
|
|
186
|
+
pagination && totalPages > 1 ? /* @__PURE__ */ jsx("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsx(
|
|
187
|
+
Pagination,
|
|
188
|
+
{
|
|
189
|
+
currentPage: currentPage + 1,
|
|
190
|
+
totalPages,
|
|
191
|
+
onPageChange: handlePageChange
|
|
192
|
+
}
|
|
193
|
+
) }) : null
|
|
194
|
+
] });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export { DataTable };
|
|
198
|
+
//# sourceMappingURL=chunk-IWSLOBYG.js.map
|
|
199
|
+
//# sourceMappingURL=chunk-IWSLOBYG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/composites/data-table/data-table.tsx"],"names":["_"],"mappings":"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","file":"chunk-IWSLOBYG.js","sourcesContent":["import { ChevronDown, ChevronRight, MoreHorizontal } from \"lucide-react\";\nimport { Fragment, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\nimport { DropdownMenu } from \"../../primitives/dropdown-menu/index.js\";\nimport { EmptyState } from \"../../primitives/empty-state/index.js\";\nimport { Pagination } from \"../../primitives/pagination/index.js\";\nimport { Skeleton } from \"../../primitives/skeleton/index.js\";\nimport { Table } from \"../../primitives/table/index.js\";\n\n/**\n * DataTable — generic, sortable, expandable composite over `<Table>`.\n *\n * Adds operator-grade entity-list patterns on top of the plain Table\n * primitive: sortable headers, sticky header, expandable rows\n * (multi-row by default), row action menus (Dropdown), client-side\n * pagination, loading skeleton rows, empty state. Both sort and\n * pagination support controlled OR uncontrolled mode (consumer\n * passes onSortChange / onPageChange to take over state).\n *\n * @example\n * <DataTable\n * columns={[\n * { key: \"name\", label: \"Name\", sortable: true },\n * { key: \"status\", label: \"Status\" },\n * ]}\n * data={domains}\n * rowKey={(d) => d.id}\n * expandable={(d) => d.status === \"pending\" ? <DnsRecords domain={d} /> : null}\n * rowActions={(d) => (\n * <>\n * <DropdownMenu.Item onSelect={() => editDomain(d)}>Edit</DropdownMenu.Item>\n * <DropdownMenu.Item onSelect={() => deleteDomain(d)}>Delete</DropdownMenu.Item>\n * </>\n * )}\n * />\n */\nexport interface DataTableColumn<T> {\n key: string;\n label: ReactNode;\n align?: \"left\" | \"center\" | \"right\";\n sortable?: boolean;\n width?: string;\n render?: (row: T) => ReactNode;\n className?: string;\n}\n\nexport interface DataTableSort {\n key: string;\n direction: \"asc\" | \"desc\";\n}\n\nexport interface DataTableProps<T> {\n data: T[];\n columns: DataTableColumn<T>[];\n rowKey: (row: T) => string;\n stickyHeader?: boolean;\n expandable?: (row: T) => ReactNode | null;\n expandMode?: \"single\" | \"multiple\";\n rowActions?: (row: T) => ReactNode;\n pagination?: {\n pageSize: number;\n controlledPage?: number;\n onPageChange?: (page: number) => void;\n } | null;\n defaultSort?: DataTableSort;\n sort?: DataTableSort | null;\n onSortChange?: (sort: DataTableSort | null) => void;\n loading?: boolean;\n emptyState?: ReactNode;\n className?: string;\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === null || a === undefined) return -1;\n if (b === null || b === undefined) return 1;\n if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n return String(a).localeCompare(String(b));\n}\n\nfunction DataTable<T>(props: DataTableProps<T>): ReactNode {\n const {\n data,\n columns,\n rowKey,\n stickyHeader = true,\n expandable,\n expandMode = \"multiple\",\n rowActions,\n pagination,\n defaultSort,\n sort: controlledSort,\n onSortChange,\n loading = false,\n emptyState,\n className,\n } = props;\n\n const isControlledSort = onSortChange !== undefined;\n const [uncontrolledSort, setUncontrolledSort] = useState<DataTableSort | null>(\n defaultSort ?? null,\n );\n const sort = isControlledSort ? (controlledSort ?? null) : uncontrolledSort;\n\n const isControlledPage = pagination?.controlledPage !== undefined;\n const [uncontrolledPage, setUncontrolledPage] = useState(0);\n const currentPage = isControlledPage ? (pagination?.controlledPage ?? 0) : uncontrolledPage;\n\n // EC-9: clamp pageSize to >= 1 to avoid divide-by-zero / infinite render\n const effectivePageSize = Math.max(1, pagination?.pageSize ?? 10);\n\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n\n function handleSort(columnKey: string) {\n // Cycle: none → asc → desc → none\n let nextSort: DataTableSort | null;\n if (sort?.key !== columnKey) {\n nextSort = { key: columnKey, direction: \"asc\" };\n } else if (sort.direction === \"asc\") {\n nextSort = { key: columnKey, direction: \"desc\" };\n } else {\n nextSort = null;\n }\n if (isControlledSort) {\n onSortChange?.(nextSort);\n } else {\n setUncontrolledSort(nextSort);\n // EC-8: sort change resets pagination to page 0\n if (!isControlledPage) setUncontrolledPage(0);\n }\n }\n\n function handlePageChange(page: number) {\n // Pagination uses 1-indexed; internal state 0-indexed\n const zeroIdx = page - 1;\n if (isControlledPage) {\n pagination?.onPageChange?.(zeroIdx);\n } else {\n setUncontrolledPage(zeroIdx);\n }\n }\n\n function toggleExpand(key: string) {\n if (expandMode === \"single\") {\n setExpanded((prev) => (prev.has(key) ? new Set() : new Set([key])));\n } else {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n }\n }\n\n // Apply client-side sort in uncontrolled mode\n const sortedData = useMemo(() => {\n if (isControlledSort || sort === null) return data;\n const col = columns.find((c) => c.key === sort.key);\n if (!col) return data;\n const sorted = [...data].sort((a, b) => {\n const aVal = col.render\n ? null\n : (a as Record<string, unknown>)[sort.key as keyof T as string];\n const bVal = col.render\n ? null\n : (b as Record<string, unknown>)[sort.key as keyof T as string];\n const cmp = compareValues(aVal, bVal);\n return sort.direction === \"asc\" ? cmp : -cmp;\n });\n return sorted;\n }, [data, sort, isControlledSort, columns]);\n\n // Apply client-side pagination in uncontrolled mode\n const visibleData = useMemo(() => {\n if (!pagination) return sortedData;\n if (isControlledPage) return sortedData; // consumer pre-sliced\n const start = currentPage * effectivePageSize;\n return sortedData.slice(start, start + effectivePageSize);\n }, [sortedData, pagination, isControlledPage, currentPage, effectivePageSize]);\n\n // EC-1 fix: compute colSpan accounting for chevron + actions columns\n const extraCols = (expandable ? 1 : 0) + (rowActions ? 1 : 0);\n const expandedColSpan = columns.length + extraCols;\n const totalCols = columns.length + extraCols;\n\n // Loading state (EC-7: loading > empty)\n if (loading) {\n return (\n <div className={cn(\"w-full\", className)}>\n <Table>\n <Table.Header className={stickyHeader ? \"sticky top-0 bg-card\" : undefined}>\n <Table.Row>\n {expandable ? (\n <Table.HeaderCell>\n <span className=\"sr-only\">Expand</span>\n </Table.HeaderCell>\n ) : null}\n {columns.map((col) => (\n <Table.HeaderCell key={col.key} align={col.align}>\n {col.label}\n </Table.HeaderCell>\n ))}\n {rowActions ? (\n <Table.HeaderCell>\n <span className=\"sr-only\">Actions</span>\n </Table.HeaderCell>\n ) : null}\n </Table.Row>\n </Table.Header>\n <Table.Body>\n {Array.from({ length: 5 }, (_, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows are positional placeholders\n <Table.Row key={`skeleton-${i}`}>\n {Array.from({ length: totalCols }, (_, j) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: skeleton cells are positional placeholders\n <Table.Cell key={`s-${i}-${j}`}>\n <Skeleton className=\"h-4 w-full\" />\n </Table.Cell>\n ))}\n </Table.Row>\n ))}\n </Table.Body>\n </Table>\n </div>\n );\n }\n\n // Empty state (after loading check)\n if (sortedData.length === 0) {\n return (\n <div className={cn(\"w-full\", className)}>\n {emptyState ?? <EmptyState title=\"No data\" description=\"There's nothing here yet.\" />}\n </div>\n );\n }\n\n const totalPages = pagination ? Math.ceil(sortedData.length / effectivePageSize) : 1;\n\n return (\n <div className={cn(\"w-full\", className)}>\n <Table>\n <Table.Header className={stickyHeader ? \"sticky top-0 z-10 bg-card\" : undefined}>\n <Table.Row>\n {expandable ? (\n <Table.HeaderCell>\n <span className=\"sr-only\">Expand</span>\n </Table.HeaderCell>\n ) : null}\n {columns.map((col) => {\n const isSortable = col.sortable === true;\n const isActive = sort?.key === col.key;\n return (\n <Table.HeaderCell\n key={col.key}\n align={col.align}\n onSort={isSortable ? () => handleSort(col.key) : undefined}\n sortDirection={isSortable ? (isActive ? sort?.direction : \"none\") : undefined}\n style={col.width ? { width: col.width } : undefined}\n >\n {col.label}\n </Table.HeaderCell>\n );\n })}\n {rowActions ? <Table.HeaderCell aria-label=\"Actions\" /> : null}\n </Table.Row>\n </Table.Header>\n <Table.Body>\n {visibleData.map((row) => {\n const key = rowKey(row);\n const expandedContent = expandable ? expandable(row) : null;\n const isExpandable = expandedContent !== null && expandedContent !== undefined;\n const isExpanded = expanded.has(key);\n return (\n <Fragment key={key}>\n <Table.Row>\n {expandable ? (\n <Table.Cell>\n {isExpandable ? (\n <button\n type=\"button\"\n onClick={() => toggleExpand(key)}\n aria-expanded={isExpanded}\n aria-controls={`expanded-${key}`}\n aria-label={isExpanded ? \"Collapse row\" : \"Expand row\"}\n className=\"inline-flex items-center justify-center rounded-md p-0.5 hover:bg-muted\"\n >\n {isExpanded ? (\n <ChevronDown aria-hidden=\"true\" className=\"size-4\" />\n ) : (\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n )}\n </button>\n ) : null}\n </Table.Cell>\n ) : null}\n {columns.map((col) => (\n <Table.Cell key={col.key} align={col.align} className={col.className}>\n {col.render\n ? col.render(row)\n : String((row as Record<string, unknown>)[col.key] ?? \"\")}\n </Table.Cell>\n ))}\n {rowActions ? (\n <Table.Cell align=\"right\">\n <DropdownMenu>\n <DropdownMenu.Trigger\n aria-label=\"Row actions\"\n className={cn(\n \"inline-flex size-7 items-center justify-center rounded-md\",\n \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n >\n <MoreHorizontal aria-hidden=\"true\" className=\"size-4\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\">{rowActions(row)}</DropdownMenu.Content>\n </DropdownMenu>\n </Table.Cell>\n ) : null}\n </Table.Row>\n {isExpanded && isExpandable ? (\n <tr id={`expanded-${key}`}>\n <td colSpan={expandedColSpan} className=\"bg-muted/30 p-4\">\n {expandedContent}\n </td>\n </tr>\n ) : null}\n </Fragment>\n );\n })}\n </Table.Body>\n </Table>\n {pagination && totalPages > 1 ? (\n <div className=\"mt-4 flex items-center justify-end\">\n <Pagination\n currentPage={currentPage + 1}\n totalPages={totalPages}\n onPageChange={handlePageChange}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nexport { DataTable };\n"]}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { cn } from './chunk-ZSRJCIWF.js';
|
|
2
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
|
+
import { Check, Circle, ChevronRight } from 'lucide-react';
|
|
4
|
+
import { forwardRef } from 'react';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var Trigger2 = DropdownMenuPrimitive.Trigger;
|
|
8
|
+
var Portal2 = DropdownMenuPrimitive.Portal;
|
|
9
|
+
var Group2 = DropdownMenuPrimitive.Group;
|
|
10
|
+
var Sub2 = DropdownMenuPrimitive.Sub;
|
|
11
|
+
var RadioGroup2 = DropdownMenuPrimitive.RadioGroup;
|
|
12
|
+
var Content2 = forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
13
|
+
DropdownMenuPrimitive.Content,
|
|
14
|
+
{
|
|
15
|
+
ref,
|
|
16
|
+
sideOffset,
|
|
17
|
+
className: cn(
|
|
18
|
+
"z-50 min-w-32 overflow-hidden rounded-lg border border-border/40 bg-card p-1",
|
|
19
|
+
"text-card-foreground shadow-md",
|
|
20
|
+
"data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:animate-in",
|
|
21
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out",
|
|
22
|
+
className
|
|
23
|
+
),
|
|
24
|
+
...props
|
|
25
|
+
}
|
|
26
|
+
) }));
|
|
27
|
+
Content2.displayName = "DropdownMenu.Content";
|
|
28
|
+
var Item2 = forwardRef(
|
|
29
|
+
({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
30
|
+
DropdownMenuPrimitive.Item,
|
|
31
|
+
{
|
|
32
|
+
ref,
|
|
33
|
+
className: cn(
|
|
34
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-md px-2 py-1.5",
|
|
35
|
+
"font-sans text-body-sm text-foreground outline-none",
|
|
36
|
+
"transition-colors",
|
|
37
|
+
"focus:bg-muted focus:text-foreground",
|
|
38
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
39
|
+
inset && "pl-8",
|
|
40
|
+
className
|
|
41
|
+
),
|
|
42
|
+
...props
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
Item2.displayName = "DropdownMenu.Item";
|
|
47
|
+
var CheckboxItem2 = forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
48
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
49
|
+
{
|
|
50
|
+
ref,
|
|
51
|
+
checked,
|
|
52
|
+
className: cn(
|
|
53
|
+
"relative flex cursor-default select-none items-center rounded-md py-1.5 pr-2 pl-8",
|
|
54
|
+
"font-sans text-body-sm text-foreground outline-none",
|
|
55
|
+
"transition-colors focus:bg-muted focus:text-foreground",
|
|
56
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
57
|
+
className
|
|
58
|
+
),
|
|
59
|
+
...props,
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { "aria-hidden": "true", className: "size-4" }) }) }),
|
|
62
|
+
children
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
));
|
|
66
|
+
CheckboxItem2.displayName = "DropdownMenu.CheckboxItem";
|
|
67
|
+
var RadioItem2 = forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
68
|
+
DropdownMenuPrimitive.RadioItem,
|
|
69
|
+
{
|
|
70
|
+
ref,
|
|
71
|
+
className: cn(
|
|
72
|
+
"relative flex cursor-default select-none items-center rounded-md py-1.5 pr-2 pl-8",
|
|
73
|
+
"font-sans text-body-sm text-foreground outline-none",
|
|
74
|
+
"transition-colors focus:bg-muted focus:text-foreground",
|
|
75
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
76
|
+
className
|
|
77
|
+
),
|
|
78
|
+
...props,
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Circle, { "aria-hidden": "true", className: "size-2 fill-current" }) }) }),
|
|
81
|
+
children
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
));
|
|
85
|
+
RadioItem2.displayName = "DropdownMenu.RadioItem";
|
|
86
|
+
var Label2 = forwardRef(
|
|
87
|
+
({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
88
|
+
DropdownMenuPrimitive.Label,
|
|
89
|
+
{
|
|
90
|
+
ref,
|
|
91
|
+
className: cn(
|
|
92
|
+
"px-2 py-1.5 font-medium font-sans text-label-caps text-muted-foreground uppercase tracking-wider",
|
|
93
|
+
inset && "pl-8",
|
|
94
|
+
className
|
|
95
|
+
),
|
|
96
|
+
...props
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
Label2.displayName = "DropdownMenu.Label";
|
|
101
|
+
var Separator2 = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
102
|
+
DropdownMenuPrimitive.Separator,
|
|
103
|
+
{
|
|
104
|
+
ref,
|
|
105
|
+
className: cn("-mx-1 my-1 h-px bg-border/40", className),
|
|
106
|
+
...props
|
|
107
|
+
}
|
|
108
|
+
));
|
|
109
|
+
Separator2.displayName = "DropdownMenu.Separator";
|
|
110
|
+
var Shortcut = ({ className, ...props }) => /* @__PURE__ */ jsx(
|
|
111
|
+
"span",
|
|
112
|
+
{
|
|
113
|
+
className: cn("ml-auto font-mono text-label text-muted-foreground", className),
|
|
114
|
+
...props
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
Shortcut.displayName = "DropdownMenu.Shortcut";
|
|
118
|
+
var SubTrigger2 = forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
119
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
120
|
+
{
|
|
121
|
+
ref,
|
|
122
|
+
className: cn(
|
|
123
|
+
"flex cursor-default select-none items-center gap-2 rounded-md px-2 py-1.5",
|
|
124
|
+
"font-sans text-body-sm text-foreground outline-none",
|
|
125
|
+
"focus:bg-muted data-[state=open]:bg-muted",
|
|
126
|
+
inset && "pl-8",
|
|
127
|
+
className
|
|
128
|
+
),
|
|
129
|
+
...props,
|
|
130
|
+
children: [
|
|
131
|
+
children,
|
|
132
|
+
/* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "ml-auto size-3.5" })
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
));
|
|
136
|
+
SubTrigger2.displayName = "DropdownMenu.SubTrigger";
|
|
137
|
+
var SubContent2 = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
138
|
+
DropdownMenuPrimitive.SubContent,
|
|
139
|
+
{
|
|
140
|
+
ref,
|
|
141
|
+
className: cn(
|
|
142
|
+
"z-50 min-w-32 overflow-hidden rounded-lg border border-border/40 bg-card p-1",
|
|
143
|
+
"text-card-foreground shadow-md",
|
|
144
|
+
"data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:animate-in",
|
|
145
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out",
|
|
146
|
+
className
|
|
147
|
+
),
|
|
148
|
+
...props
|
|
149
|
+
}
|
|
150
|
+
));
|
|
151
|
+
SubContent2.displayName = "DropdownMenu.SubContent";
|
|
152
|
+
var DropdownMenu = Object.assign(DropdownMenuPrimitive.Root, {
|
|
153
|
+
Trigger: Trigger2,
|
|
154
|
+
Portal: Portal2,
|
|
155
|
+
Content: Content2,
|
|
156
|
+
Item: Item2,
|
|
157
|
+
CheckboxItem: CheckboxItem2,
|
|
158
|
+
RadioItem: RadioItem2,
|
|
159
|
+
Label: Label2,
|
|
160
|
+
Separator: Separator2,
|
|
161
|
+
Shortcut,
|
|
162
|
+
Group: Group2,
|
|
163
|
+
Sub: Sub2,
|
|
164
|
+
SubTrigger: SubTrigger2,
|
|
165
|
+
SubContent: SubContent2,
|
|
166
|
+
RadioGroup: RadioGroup2
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
export { DropdownMenu };
|
|
170
|
+
//# sourceMappingURL=chunk-MI5CXMZU.js.map
|
|
171
|
+
//# sourceMappingURL=chunk-MI5CXMZU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/primitives/dropdown-menu/dropdown-menu.tsx"],"names":["Trigger","Portal","Group","Sub","RadioGroup","Content","Item","CheckboxItem","RadioItem","Label","Separator","SubTrigger","SubContent"],"mappings":"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","file":"chunk-MI5CXMZU.js","sourcesContent":["import * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { Check, ChevronRight, Circle } from \"lucide-react\";\nimport { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ElementRef, HTMLAttributes } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\n\n/**\n * DropdownMenu — accessible menu primitive built on Radix.\n *\n * Composition (single import surface):\n * <DropdownMenu>\n * <DropdownMenu.Trigger>…</DropdownMenu.Trigger>\n * <DropdownMenu.Content>\n * <DropdownMenu.Label>Section</DropdownMenu.Label>\n * <DropdownMenu.Item onSelect={…}>Edit</DropdownMenu.Item>\n * <DropdownMenu.Separator />\n * <DropdownMenu.Item disabled>Delete</DropdownMenu.Item>\n * </DropdownMenu.Content>\n * </DropdownMenu>\n *\n * The primitive consolidates 5 prior direct-Radix usages\n * (`model-selector`, `intent-selector`, `agent-profile`,\n * `theme-switcher`, `theo-code-shell`) under a single styled\n * wrapper so consumers get consistent visuals + the design tokens.\n *\n * a11y note for tests: Radix's focus-guard spans intentionally\n * violate `aria-hidden-focus`. Tests should pass\n * `{ rules: { \"aria-hidden-focus\": { enabled: false } } }` to axe.\n */\n\nconst Trigger = DropdownMenuPrimitive.Trigger;\nconst Portal = DropdownMenuPrimitive.Portal;\nconst Group = DropdownMenuPrimitive.Group;\nconst Sub = DropdownMenuPrimitive.Sub;\nconst RadioGroup = DropdownMenuPrimitive.RadioGroup;\n\nconst Content = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Content>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 min-w-32 overflow-hidden rounded-lg border border-border/40 bg-card p-1\",\n \"text-card-foreground shadow-md\",\n \"data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out\",\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nContent.displayName = \"DropdownMenu.Content\";\n\ninterface ItemProps extends ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> {\n inset?: boolean;\n}\n\nconst Item = forwardRef<ElementRef<typeof DropdownMenuPrimitive.Item>, ItemProps>(\n ({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center gap-2 rounded-md px-2 py-1.5\",\n \"font-sans text-body-sm text-foreground outline-none\",\n \"transition-colors\",\n \"focus:bg-muted focus:text-foreground\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n ),\n);\nItem.displayName = \"DropdownMenu.Item\";\n\nconst CheckboxItem = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n checked={checked}\n className={cn(\n \"relative flex cursor-default select-none items-center rounded-md py-1.5 pr-2 pl-8\",\n \"font-sans text-body-sm text-foreground outline-none\",\n \"transition-colors focus:bg-muted focus:text-foreground\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Check aria-hidden=\"true\" className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n));\nCheckboxItem.displayName = \"DropdownMenu.CheckboxItem\";\n\nconst RadioItem = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(\n \"relative flex cursor-default select-none items-center rounded-md py-1.5 pr-2 pl-8\",\n \"font-sans text-body-sm text-foreground outline-none\",\n \"transition-colors focus:bg-muted focus:text-foreground\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Circle aria-hidden=\"true\" className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n));\nRadioItem.displayName = \"DropdownMenu.RadioItem\";\n\ninterface LabelProps extends ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> {\n inset?: boolean;\n}\n\nconst Label = forwardRef<ElementRef<typeof DropdownMenuPrimitive.Label>, LabelProps>(\n ({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\n \"px-2 py-1.5 font-medium font-sans text-label-caps text-muted-foreground uppercase tracking-wider\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n ),\n);\nLabel.displayName = \"DropdownMenu.Label\";\n\nconst Separator = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.Separator>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-border/40\", className)}\n {...props}\n />\n));\nSeparator.displayName = \"DropdownMenu.Separator\";\n\nconst Shortcut = ({ className, ...props }: HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn(\"ml-auto font-mono text-label text-muted-foreground\", className)}\n {...props}\n />\n);\nShortcut.displayName = \"DropdownMenu.Shortcut\";\n\nconst SubTrigger = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n \"flex cursor-default select-none items-center gap-2 rounded-md px-2 py-1.5\",\n \"font-sans text-body-sm text-foreground outline-none\",\n \"focus:bg-muted data-[state=open]:bg-muted\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRight aria-hidden=\"true\" className=\"ml-auto size-3.5\" />\n </DropdownMenuPrimitive.SubTrigger>\n));\nSubTrigger.displayName = \"DropdownMenu.SubTrigger\";\n\nconst SubContent = forwardRef<\n ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n className={cn(\n \"z-50 min-w-32 overflow-hidden rounded-lg border border-border/40 bg-card p-1\",\n \"text-card-foreground shadow-md\",\n \"data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out\",\n className,\n )}\n {...props}\n />\n));\nSubContent.displayName = \"DropdownMenu.SubContent\";\n\ntype DropdownMenuRoot = typeof DropdownMenuPrimitive.Root & {\n Trigger: typeof Trigger;\n Portal: typeof Portal;\n Content: typeof Content;\n Item: typeof Item;\n CheckboxItem: typeof CheckboxItem;\n RadioItem: typeof RadioItem;\n Label: typeof Label;\n Separator: typeof Separator;\n Shortcut: typeof Shortcut;\n Group: typeof Group;\n Sub: typeof Sub;\n SubTrigger: typeof SubTrigger;\n SubContent: typeof SubContent;\n RadioGroup: typeof RadioGroup;\n};\n\nconst DropdownMenu: DropdownMenuRoot = Object.assign(DropdownMenuPrimitive.Root, {\n Trigger,\n Portal,\n Content,\n Item,\n CheckboxItem,\n RadioItem,\n Label,\n Separator,\n Shortcut,\n Group,\n Sub,\n SubTrigger,\n SubContent,\n RadioGroup,\n});\n\nexport { DropdownMenu };\n"]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { EmptyState } from './chunk-SWJ4EUOI.js';
|
|
2
|
+
import { Card } from './chunk-ZALLCR7X.js';
|
|
3
|
+
import { ActionBar } from './chunk-BX7A5GUV.js';
|
|
4
|
+
import { cn } from './chunk-ZSRJCIWF.js';
|
|
5
|
+
import { AlertCircle, Loader2 } from 'lucide-react';
|
|
6
|
+
import { forwardRef, useEffect } from 'react';
|
|
7
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
var PageShell = forwardRef(
|
|
10
|
+
({
|
|
11
|
+
title,
|
|
12
|
+
description,
|
|
13
|
+
onTitleChange,
|
|
14
|
+
primaryAction,
|
|
15
|
+
search,
|
|
16
|
+
onFilterClick,
|
|
17
|
+
loading = false,
|
|
18
|
+
loadingNode,
|
|
19
|
+
error,
|
|
20
|
+
empty,
|
|
21
|
+
children,
|
|
22
|
+
className
|
|
23
|
+
}, ref) => {
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
onTitleChange?.(title);
|
|
26
|
+
}, [title, onTitleChange]);
|
|
27
|
+
const hasActionBar = search !== void 0 || primaryAction !== void 0 || onFilterClick !== void 0;
|
|
28
|
+
let content;
|
|
29
|
+
if (loading) {
|
|
30
|
+
content = loadingNode ?? /* @__PURE__ */ jsxs(Card, { className: "flex items-center justify-center gap-3 p-12 text-muted-foreground", children: [
|
|
31
|
+
/* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: "size-5 animate-spin" }),
|
|
32
|
+
/* @__PURE__ */ jsx("span", { className: "font-sans text-body-sm", children: "Loading\u2026" })
|
|
33
|
+
] });
|
|
34
|
+
} else if (error) {
|
|
35
|
+
content = /* @__PURE__ */ jsxs(Card, { className: "flex flex-col items-center gap-3 p-8 text-center", children: [
|
|
36
|
+
/* @__PURE__ */ jsx(AlertCircle, { "aria-hidden": "true", className: "size-8 text-destructive" }),
|
|
37
|
+
/* @__PURE__ */ jsx("p", { className: "font-sans text-body-sm text-foreground", children: error.message }),
|
|
38
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
39
|
+
error.onRetry ? /* @__PURE__ */ jsx(
|
|
40
|
+
"button",
|
|
41
|
+
{
|
|
42
|
+
type: "button",
|
|
43
|
+
onClick: error.onRetry,
|
|
44
|
+
className: cn(
|
|
45
|
+
"inline-flex items-center rounded-md border border-border/40 px-3 py-1.5",
|
|
46
|
+
"font-sans text-body-sm text-foreground",
|
|
47
|
+
"transition-colors hover:bg-muted"
|
|
48
|
+
),
|
|
49
|
+
children: "Retry"
|
|
50
|
+
}
|
|
51
|
+
) : null,
|
|
52
|
+
error.docsHref ? /* @__PURE__ */ jsx(
|
|
53
|
+
"a",
|
|
54
|
+
{
|
|
55
|
+
href: error.docsHref,
|
|
56
|
+
className: "font-sans text-body-sm text-primary hover:underline",
|
|
57
|
+
children: "View docs"
|
|
58
|
+
}
|
|
59
|
+
) : null
|
|
60
|
+
] })
|
|
61
|
+
] });
|
|
62
|
+
} else if (empty) {
|
|
63
|
+
const emptyAction = empty.action;
|
|
64
|
+
content = /* @__PURE__ */ jsx(
|
|
65
|
+
EmptyState,
|
|
66
|
+
{
|
|
67
|
+
icon: empty.icon,
|
|
68
|
+
title: empty.title,
|
|
69
|
+
description: empty.description,
|
|
70
|
+
action: emptyAction ? /* @__PURE__ */ jsx(
|
|
71
|
+
"button",
|
|
72
|
+
{
|
|
73
|
+
type: "button",
|
|
74
|
+
onClick: emptyAction.onClick,
|
|
75
|
+
className: cn(
|
|
76
|
+
"inline-flex items-center rounded-md bg-primary px-3 py-1.5",
|
|
77
|
+
"font-medium font-sans text-body-sm text-primary-foreground",
|
|
78
|
+
"transition-colors hover:bg-primary-deep"
|
|
79
|
+
),
|
|
80
|
+
children: emptyAction.label
|
|
81
|
+
}
|
|
82
|
+
) : void 0
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
} else {
|
|
86
|
+
content = children;
|
|
87
|
+
}
|
|
88
|
+
return /* @__PURE__ */ jsxs(
|
|
89
|
+
"main",
|
|
90
|
+
{
|
|
91
|
+
ref,
|
|
92
|
+
"aria-busy": loading || void 0,
|
|
93
|
+
className: cn("flex flex-col gap-6", className),
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
|
|
96
|
+
/* @__PURE__ */ jsx("h1", { className: "font-display font-semibold text-display-sm text-foreground tracking-tight", children: title }),
|
|
97
|
+
description ? /* @__PURE__ */ jsx("p", { className: "font-sans text-body-md text-muted-foreground", children: description }) : null
|
|
98
|
+
] }),
|
|
99
|
+
hasActionBar ? /* @__PURE__ */ jsx(ActionBar, { search, primaryAction, onFilterClick }) : null,
|
|
100
|
+
/* @__PURE__ */ jsx("div", { children: content })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
PageShell.displayName = "PageShell";
|
|
107
|
+
|
|
108
|
+
export { PageShell };
|
|
109
|
+
//# sourceMappingURL=chunk-QJGGTIUN.js.map
|
|
110
|
+
//# sourceMappingURL=chunk-QJGGTIUN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/composites/page-shell/page-shell.tsx"],"names":[],"mappings":"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","file":"chunk-QJGGTIUN.js","sourcesContent":["import { AlertCircle, Loader2 } from \"lucide-react\";\nimport { forwardRef, useEffect } from \"react\";\nimport type { ElementType, ReactNode } from \"react\";\nimport { cn } from \"../../../lib/cn.js\";\nimport { ActionBar } from \"../../primitives/action-bar/index.js\";\nimport { Card } from \"../../primitives/card/index.js\";\nimport { EmptyState } from \"../../primitives/empty-state/index.js\";\n\n/**\n * PageShell — page-level scaffold composite.\n *\n * Renders title + optional description + optional ActionBar, then\n * one of four mutually-exclusive content states:\n * 1. loading (highest precedence)\n * 2. error\n * 3. empty\n * 4. children (default)\n *\n * Scope-narrowed per Brief #5 D3: PageShell does NOT manage\n * `document.title`. Use the optional `onTitleChange` callback to\n * wire your own hook (e.g. useSetPageTitle, react-helmet,\n * next/head).\n *\n * @example\n * <PageShell\n * title=\"Domains\"\n * description=\"Custom domains and DNS verification.\"\n * search={{ placeholder: \"Search…\", value: q, onChange: setQ }}\n * primaryAction={{ label: \"Add domain\", icon: Plus, onClick: openModal }}\n * loading={isLoading}\n * error={error ? { message: error.message, onRetry: refetch } : undefined}\n * empty={data?.length === 0 ? { title: \"No domains yet\" } : undefined}\n * >\n * <DataTable columns={…} data={data} />\n * </PageShell>\n */\nexport interface PageShellProps {\n title: string;\n description?: ReactNode;\n /** Optional callback invoked when `title` changes — wire to your own document.title hook. */\n onTitleChange?: (title: string) => void;\n primaryAction?: {\n label: ReactNode;\n icon?: ElementType;\n onClick: () => void;\n loading?: boolean;\n };\n search?: {\n placeholder: string;\n value: string;\n onChange: (v: string) => void;\n };\n onFilterClick?: () => void;\n loading?: boolean;\n /** Custom loading UI. Defaults to a centered spinner card. */\n loadingNode?: ReactNode;\n error?: {\n message: string;\n onRetry?: () => void;\n docsHref?: string;\n };\n empty?: {\n icon?: ElementType;\n title: string;\n description?: ReactNode;\n action?: { label: string; onClick: () => void };\n };\n children?: ReactNode;\n className?: string;\n}\n\nconst PageShell = forwardRef<HTMLElement, PageShellProps>(\n (\n {\n title,\n description,\n onTitleChange,\n primaryAction,\n search,\n onFilterClick,\n loading = false,\n loadingNode,\n error,\n empty,\n children,\n className,\n },\n ref,\n ) => {\n useEffect(() => {\n onTitleChange?.(title);\n }, [title, onTitleChange]);\n\n const hasActionBar =\n search !== undefined || primaryAction !== undefined || onFilterClick !== undefined;\n\n // State precedence: loading > error > empty > children\n let content: ReactNode;\n if (loading) {\n content = loadingNode ?? (\n <Card className=\"flex items-center justify-center gap-3 p-12 text-muted-foreground\">\n <Loader2 aria-hidden=\"true\" className=\"size-5 animate-spin\" />\n <span className=\"font-sans text-body-sm\">Loading…</span>\n </Card>\n );\n } else if (error) {\n content = (\n <Card className=\"flex flex-col items-center gap-3 p-8 text-center\">\n <AlertCircle aria-hidden=\"true\" className=\"size-8 text-destructive\" />\n <p className=\"font-sans text-body-sm text-foreground\">{error.message}</p>\n <div className=\"flex items-center gap-3\">\n {error.onRetry ? (\n <button\n type=\"button\"\n onClick={error.onRetry}\n className={cn(\n \"inline-flex items-center rounded-md border border-border/40 px-3 py-1.5\",\n \"font-sans text-body-sm text-foreground\",\n \"transition-colors hover:bg-muted\",\n )}\n >\n Retry\n </button>\n ) : null}\n {error.docsHref ? (\n <a\n href={error.docsHref}\n className=\"font-sans text-body-sm text-primary hover:underline\"\n >\n View docs\n </a>\n ) : null}\n </div>\n </Card>\n );\n } else if (empty) {\n const emptyAction = empty.action;\n content = (\n <EmptyState\n icon={empty.icon as Parameters<typeof EmptyState>[0][\"icon\"]}\n title={empty.title}\n description={empty.description}\n action={\n emptyAction ? (\n <button\n type=\"button\"\n onClick={emptyAction.onClick}\n className={cn(\n \"inline-flex items-center rounded-md bg-primary px-3 py-1.5\",\n \"font-medium font-sans text-body-sm text-primary-foreground\",\n \"transition-colors hover:bg-primary-deep\",\n )}\n >\n {emptyAction.label}\n </button>\n ) : undefined\n }\n />\n );\n } else {\n content = children;\n }\n\n return (\n <main\n ref={ref}\n aria-busy={loading || undefined}\n className={cn(\"flex flex-col gap-6\", className)}\n >\n <header className=\"flex flex-col gap-1\">\n <h1 className=\"font-display font-semibold text-display-sm text-foreground tracking-tight\">\n {title}\n </h1>\n {description ? (\n <p className=\"font-sans text-body-md text-muted-foreground\">{description}</p>\n ) : null}\n </header>\n {hasActionBar ? (\n <ActionBar search={search} primaryAction={primaryAction} onFilterClick={onFilterClick} />\n ) : null}\n <div>{content}</div>\n </main>\n );\n },\n);\nPageShell.displayName = \"PageShell\";\n\nexport { PageShell };\n"]}
|