@godxjp/ui 13.11.2 → 13.13.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/dist/components/data-display/card.d.ts +16 -0
- package/dist/components/data-display/card.js +8 -0
- package/dist/components/data-display/data-table.d.ts +9 -3
- package/dist/components/data-display/data-table.js +157 -147
- package/dist/components/data-display/index.d.ts +2 -2
- package/dist/components/data-display/index.js +2 -0
- package/dist/props/registry.d.ts +5 -0
- package/dist/props/registry.js +5 -0
- package/dist/styles/card-layout.css +37 -0
- package/dist/styles/table-layout.css +6 -0
- package/package.json +1 -1
|
@@ -65,6 +65,22 @@ export declare const CardFooter: React.ForwardRefExoticComponent<React.HTMLAttri
|
|
|
65
65
|
/** Full-bleed footer (Ant Design `actions` bar). */
|
|
66
66
|
flush?: boolean;
|
|
67
67
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
68
|
+
export type CardBarProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
69
|
+
/** Right-aligned actions slot (settings/save), Ant `tabBarExtraContent`-style. */
|
|
70
|
+
extra?: React.ReactNode;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* CardBar — a horizontal bar (view tabs, toolbar, filter chips) that can sit at
|
|
74
|
+
* ANY position inside a Card. It draws its own separators FROM its position: a
|
|
75
|
+
* top bar gets a bottom border, a bottom bar gets a top border, a middle bar
|
|
76
|
+
* gets both, and a sole child gets none (the card border is enough). The main
|
|
77
|
+
* area scrolls horizontally; the `extra` slot is pinned to the inline-end edge
|
|
78
|
+
* for actions (column settings, save view, …).
|
|
79
|
+
*/
|
|
80
|
+
export declare const CardBar: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|
|
81
|
+
/** Right-aligned actions slot (settings/save), Ant `tabBarExtraContent`-style. */
|
|
82
|
+
extra?: React.ReactNode;
|
|
83
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
68
84
|
export type StatCardProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof cardVariants> & {
|
|
69
85
|
label: React.ReactNode;
|
|
70
86
|
value: React.ReactNode;
|
|
@@ -77,6 +77,13 @@ const CardFooter = React.forwardRef(
|
|
|
77
77
|
)
|
|
78
78
|
);
|
|
79
79
|
CardFooter.displayName = "CardFooter";
|
|
80
|
+
const CardBar = React.forwardRef(
|
|
81
|
+
({ className, children, extra, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, "data-slot": "card-bar", className: cn("ui-card-bar", className), ...props, children: [
|
|
82
|
+
/* @__PURE__ */ jsx("div", { "data-slot": "card-bar-main", className: "ui-card-bar-main", children }),
|
|
83
|
+
extra != null ? /* @__PURE__ */ jsx("div", { "data-slot": "card-bar-extra", className: "ui-card-bar-extra", children: extra }) : null
|
|
84
|
+
] })
|
|
85
|
+
);
|
|
86
|
+
CardBar.displayName = "CardBar";
|
|
80
87
|
function getDeltaTone(delta, inverse) {
|
|
81
88
|
const text = typeof delta === "string" || typeof delta === "number" ? String(delta).trim() : "";
|
|
82
89
|
const sign = text.match(/^[+\-−]/)?.[0];
|
|
@@ -143,6 +150,7 @@ CardAction.displayName = "CardAction";
|
|
|
143
150
|
export {
|
|
144
151
|
Card,
|
|
145
152
|
CardAction,
|
|
153
|
+
CardBar,
|
|
146
154
|
CardContent,
|
|
147
155
|
CardCover,
|
|
148
156
|
CardDescription,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type { ColumnDefProp,
|
|
3
|
-
export type Density =
|
|
2
|
+
import type { ColumnDefProp, DensityProp, SortStateProp } from "../../props/vocabulary";
|
|
3
|
+
export type Density = DensityProp;
|
|
4
4
|
export type ColumnDef<T> = ColumnDefProp<T>;
|
|
5
5
|
interface DataTableProps<T> {
|
|
6
6
|
data: T[];
|
|
@@ -19,10 +19,16 @@ interface DataTableProps<T> {
|
|
|
19
19
|
loading?: boolean;
|
|
20
20
|
/** Custom empty content when `data` is empty; defaults to a built-in EmptyState. */
|
|
21
21
|
empty?: React.ReactNode;
|
|
22
|
+
/** Zebra-stripe the body rows (even rows get a subtle fill). */
|
|
23
|
+
striped?: boolean;
|
|
24
|
+
/** Highlight a row on hover even when it is not clickable (no `onRowClick`). */
|
|
25
|
+
hoverable?: boolean;
|
|
26
|
+
/** Pin the header to the top while the body scrolls. Default true. */
|
|
27
|
+
stickyHeader?: boolean;
|
|
22
28
|
className?: string;
|
|
23
29
|
children?: React.ReactNode;
|
|
24
30
|
}
|
|
25
|
-
export declare function DataTable<T>({ data, columns, getRowId, selectable, selected: controlledSelected, onSelectChange, onRowClick, density: controlledDensity, onDensityChange, sort, onSortChange, loading, empty, className, children, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
export declare function DataTable<T>({ data, columns, getRowId, selectable, selected: controlledSelected, onSelectChange, onRowClick, density: controlledDensity, onDensityChange, sort, onSortChange, loading, empty, striped, hoverable, stickyHeader, className, children, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
26
32
|
export declare namespace DataTable {
|
|
27
33
|
var Toolbar: ({ children, className, }: {
|
|
28
34
|
children?: React.ReactNode;
|
|
@@ -50,6 +50,9 @@ function DataTable({
|
|
|
50
50
|
onSortChange,
|
|
51
51
|
loading = false,
|
|
52
52
|
empty,
|
|
53
|
+
striped = false,
|
|
54
|
+
hoverable = false,
|
|
55
|
+
stickyHeader = true,
|
|
53
56
|
className,
|
|
54
57
|
children
|
|
55
58
|
}) {
|
|
@@ -95,25 +98,18 @@ function DataTable({
|
|
|
95
98
|
onSortChange,
|
|
96
99
|
loading,
|
|
97
100
|
empty,
|
|
98
|
-
emptyColSpan
|
|
101
|
+
emptyColSpan,
|
|
102
|
+
striped,
|
|
103
|
+
hoverable,
|
|
104
|
+
stickyHeader
|
|
99
105
|
};
|
|
100
106
|
const hasContent = React.Children.toArray(children).some(
|
|
101
107
|
(c) => React.isValidElement(c) && c.type.displayName === "DataTable.Content"
|
|
102
108
|
);
|
|
103
|
-
return /* @__PURE__ */ jsx(DataTableContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
"ui-data-table-root",
|
|
108
|
-
densityClass[density === "compact" ? "compact" : "comfortable"],
|
|
109
|
-
className
|
|
110
|
-
),
|
|
111
|
-
children: [
|
|
112
|
-
children,
|
|
113
|
-
!hasContent && /* @__PURE__ */ jsx(DataTable.Content, {})
|
|
114
|
-
]
|
|
115
|
-
}
|
|
116
|
-
) });
|
|
109
|
+
return /* @__PURE__ */ jsx(DataTableContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cn("ui-data-table-root", densityClass[density], className), children: [
|
|
110
|
+
children,
|
|
111
|
+
!hasContent && /* @__PURE__ */ jsx(DataTable.Content, {})
|
|
112
|
+
] }) });
|
|
117
113
|
}
|
|
118
114
|
DataTable.Toolbar = function DataTableToolbar({
|
|
119
115
|
children,
|
|
@@ -196,7 +192,10 @@ DataTable.Content = function DataTableContent() {
|
|
|
196
192
|
onSortChange,
|
|
197
193
|
loading,
|
|
198
194
|
empty,
|
|
199
|
-
emptyColSpan
|
|
195
|
+
emptyColSpan,
|
|
196
|
+
striped,
|
|
197
|
+
hoverable,
|
|
198
|
+
stickyHeader
|
|
200
199
|
} = useDataTableContext();
|
|
201
200
|
const { t } = useTranslation();
|
|
202
201
|
const rowPadding = tableRowHeightClass;
|
|
@@ -217,127 +216,60 @@ DataTable.Content = function DataTableContent() {
|
|
|
217
216
|
{
|
|
218
217
|
className: cn("ui-data-table-scroll", hasPinEnd && "ui-data-table-has-pin-end"),
|
|
219
218
|
"aria-busy": loading,
|
|
220
|
-
children: /* @__PURE__ */ jsx(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
col.align === "center" && "text-center",
|
|
240
|
-
col.hiddenOnMobile && "hidden md:table-cell",
|
|
241
|
-
isSortable && "select-none",
|
|
242
|
-
col.pin === "end" && "ui-data-table-pin-end"
|
|
243
|
-
),
|
|
244
|
-
children: isSortable ? /* @__PURE__ */ jsx(
|
|
245
|
-
"button",
|
|
246
|
-
{
|
|
247
|
-
type: "button",
|
|
248
|
-
className: "ui-data-table-sort-button focus-visible:ring-ring rounded-sm focus-visible:ring-2",
|
|
249
|
-
onClick: () => {
|
|
250
|
-
onHeaderClick(col);
|
|
251
|
-
},
|
|
252
|
-
children: label
|
|
253
|
-
}
|
|
254
|
-
) : label
|
|
255
|
-
},
|
|
256
|
-
col.key
|
|
257
|
-
);
|
|
258
|
-
})
|
|
259
|
-
] }) }),
|
|
260
|
-
/* @__PURE__ */ jsx(TableBody, { children: loading ? (
|
|
261
|
-
// Shaped skeleton rows rendered INSIDE the real table grid so they
|
|
262
|
-
// share its borders + column widths — no second framed container
|
|
263
|
-
// (which double-borders when the table sits in a Card). Count is
|
|
264
|
-
// bounded to the previous page so the height barely shifts.
|
|
265
|
-
Array.from({ length: Math.min(Math.max(data.length, 6), 10) }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { className: cn(rowPadding, "hover:bg-transparent"), children: [
|
|
266
|
-
selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx("div", { className: "ui-skeleton-block size-4 rounded-sm" }) }),
|
|
267
|
-
columns.map((col, j) => /* @__PURE__ */ jsx(
|
|
268
|
-
TableCell,
|
|
269
|
-
{
|
|
270
|
-
className: cn(
|
|
271
|
-
cellPadding,
|
|
272
|
-
col.width,
|
|
273
|
-
col.align === "right" && "text-end",
|
|
274
|
-
col.align === "center" && "text-center",
|
|
275
|
-
col.hiddenOnMobile && "hidden md:table-cell",
|
|
276
|
-
col.pin === "end" && "ui-data-table-pin-end"
|
|
277
|
-
),
|
|
278
|
-
children: /* @__PURE__ */ jsx(
|
|
279
|
-
"div",
|
|
219
|
+
children: /* @__PURE__ */ jsx(
|
|
220
|
+
"div",
|
|
221
|
+
{
|
|
222
|
+
className: "ui-data-table-surface min-w-[640px] sm:min-w-0",
|
|
223
|
+
"data-striped": striped ? "" : void 0,
|
|
224
|
+
"data-hoverable": hoverable ? "" : void 0,
|
|
225
|
+
children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
226
|
+
/* @__PURE__ */ jsx(TableHeader, { className: cn("bg-secondary", stickyHeader && "sticky top-0 z-10"), children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
227
|
+
selectable && /* @__PURE__ */ jsx(TableHead, { className: "w-10", children: /* @__PURE__ */ jsx(DataTable.SelectAll, {}) }),
|
|
228
|
+
columns.map((col) => {
|
|
229
|
+
const isSortable = !!col.sortable && !!onSortChange;
|
|
230
|
+
const isActiveSort = isSortable && sort?.key === col.key;
|
|
231
|
+
const sortIndicator = isSortable ? isActiveSort ? sort?.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-muted-foreground size-3", "aria-hidden": "true" }) : null;
|
|
232
|
+
const label = /* @__PURE__ */ jsxs("span", { className: "ui-data-table-sort-label", children: [
|
|
233
|
+
col.header,
|
|
234
|
+
sortIndicator
|
|
235
|
+
] });
|
|
236
|
+
return /* @__PURE__ */ jsx(
|
|
237
|
+
TableHead,
|
|
280
238
|
{
|
|
239
|
+
"data-empty": !col.header || void 0,
|
|
240
|
+
"aria-sort": isSortable ? isActiveSort ? sort?.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
|
|
281
241
|
className: cn(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
col.align === "
|
|
285
|
-
col.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
onRowClick?.(row);
|
|
314
|
-
},
|
|
315
|
-
onKeyDown: onRowClick ? (e) => {
|
|
316
|
-
if (e.key !== "Enter" && e.key !== " ") return;
|
|
317
|
-
if (e.target !== e.currentTarget) return;
|
|
318
|
-
e.preventDefault();
|
|
319
|
-
onRowClick?.(row);
|
|
320
|
-
} : void 0,
|
|
321
|
-
className: cn(
|
|
322
|
-
rowPadding,
|
|
323
|
-
onRowClick && "hover:bg-muted/50 focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset",
|
|
324
|
-
isSelected && "bg-muted/30"
|
|
325
|
-
),
|
|
326
|
-
children: [
|
|
327
|
-
selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx(
|
|
328
|
-
Checkbox,
|
|
329
|
-
{
|
|
330
|
-
checked: isSelected,
|
|
331
|
-
onCheckedChange: () => {
|
|
332
|
-
toggleSelect(id);
|
|
333
|
-
},
|
|
334
|
-
"aria-label": t("dataTable.selectRow", { id }),
|
|
335
|
-
onClick: (e) => {
|
|
336
|
-
e.stopPropagation();
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
) }),
|
|
340
|
-
columns.map((col) => /* @__PURE__ */ jsx(
|
|
242
|
+
col.width,
|
|
243
|
+
col.align === "right" && "text-end",
|
|
244
|
+
col.align === "center" && "text-center",
|
|
245
|
+
col.hiddenOnMobile && "hidden md:table-cell",
|
|
246
|
+
isSortable && "select-none",
|
|
247
|
+
col.pin === "end" && "ui-data-table-pin-end"
|
|
248
|
+
),
|
|
249
|
+
children: isSortable ? /* @__PURE__ */ jsx(
|
|
250
|
+
"button",
|
|
251
|
+
{
|
|
252
|
+
type: "button",
|
|
253
|
+
className: "ui-data-table-sort-button focus-visible:ring-ring rounded-sm focus-visible:ring-2",
|
|
254
|
+
onClick: () => {
|
|
255
|
+
onHeaderClick(col);
|
|
256
|
+
},
|
|
257
|
+
children: label
|
|
258
|
+
}
|
|
259
|
+
) : label
|
|
260
|
+
},
|
|
261
|
+
col.key
|
|
262
|
+
);
|
|
263
|
+
})
|
|
264
|
+
] }) }),
|
|
265
|
+
/* @__PURE__ */ jsx(TableBody, { children: loading ? (
|
|
266
|
+
// Shaped skeleton rows rendered INSIDE the real table grid so they
|
|
267
|
+
// share its borders + column widths — no second framed container
|
|
268
|
+
// (which double-borders when the table sits in a Card). Count is
|
|
269
|
+
// bounded to the previous page so the height barely shifts.
|
|
270
|
+
Array.from({ length: Math.min(Math.max(data.length, 6), 10) }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { className: cn(rowPadding, "hover:bg-transparent"), children: [
|
|
271
|
+
selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx("div", { className: "ui-skeleton-block size-4 rounded-sm" }) }),
|
|
272
|
+
columns.map((col, j) => /* @__PURE__ */ jsx(
|
|
341
273
|
TableCell,
|
|
342
274
|
{
|
|
343
275
|
className: cn(
|
|
@@ -348,21 +280,99 @@ DataTable.Content = function DataTableContent() {
|
|
|
348
280
|
col.hiddenOnMobile && "hidden md:table-cell",
|
|
349
281
|
col.pin === "end" && "ui-data-table-pin-end"
|
|
350
282
|
),
|
|
351
|
-
children:
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
283
|
+
children: /* @__PURE__ */ jsx(
|
|
284
|
+
"div",
|
|
285
|
+
{
|
|
286
|
+
className: cn(
|
|
287
|
+
"ui-skeleton-block h-4",
|
|
288
|
+
j === 0 ? "w-1/2" : "w-3/4",
|
|
289
|
+
col.align === "right" && "ms-auto",
|
|
290
|
+
col.align === "center" && "mx-auto"
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
)
|
|
357
294
|
},
|
|
358
295
|
col.key
|
|
359
296
|
))
|
|
360
|
-
]
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
297
|
+
] }, `skeleton-${i}`))
|
|
298
|
+
) : data.length === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
|
|
299
|
+
TableCell,
|
|
300
|
+
{
|
|
301
|
+
colSpan: emptyColSpan,
|
|
302
|
+
className: "ui-data-table-empty",
|
|
303
|
+
"aria-live": "polite",
|
|
304
|
+
children: empty ?? /* @__PURE__ */ jsx(EmptyState, { title: t("dataTable.empty") })
|
|
305
|
+
}
|
|
306
|
+
) }) : data.map((row) => {
|
|
307
|
+
const id = getRowId(row);
|
|
308
|
+
const isSelected = selected.has(id);
|
|
309
|
+
const isInteractiveTarget = (target) => !!target.closest("button, a, input, select, textarea, [role=menuitem]");
|
|
310
|
+
return /* @__PURE__ */ jsxs(
|
|
311
|
+
TableRow,
|
|
312
|
+
{
|
|
313
|
+
"data-state": isSelected ? "selected" : void 0,
|
|
314
|
+
tabIndex: onRowClick ? 0 : void 0,
|
|
315
|
+
onClick: (e) => {
|
|
316
|
+
const target = e.target;
|
|
317
|
+
if (isInteractiveTarget(target)) return;
|
|
318
|
+
onRowClick?.(row);
|
|
319
|
+
},
|
|
320
|
+
onKeyDown: onRowClick ? (e) => {
|
|
321
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
322
|
+
if (e.target !== e.currentTarget) return;
|
|
323
|
+
e.preventDefault();
|
|
324
|
+
onRowClick?.(row);
|
|
325
|
+
} : void 0,
|
|
326
|
+
className: cn(
|
|
327
|
+
rowPadding,
|
|
328
|
+
// Hover highlight when rows are clickable OR explicitly hoverable…
|
|
329
|
+
(onRowClick || hoverable) && "hover:bg-muted/50",
|
|
330
|
+
// …but the affordance (cursor + focus ring) only when clickable.
|
|
331
|
+
onRowClick && "focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset",
|
|
332
|
+
isSelected && "bg-muted/30"
|
|
333
|
+
),
|
|
334
|
+
children: [
|
|
335
|
+
selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx(
|
|
336
|
+
Checkbox,
|
|
337
|
+
{
|
|
338
|
+
checked: isSelected,
|
|
339
|
+
onCheckedChange: () => {
|
|
340
|
+
toggleSelect(id);
|
|
341
|
+
},
|
|
342
|
+
"aria-label": t("dataTable.selectRow", { id }),
|
|
343
|
+
onClick: (e) => {
|
|
344
|
+
e.stopPropagation();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
) }),
|
|
348
|
+
columns.map((col) => /* @__PURE__ */ jsx(
|
|
349
|
+
TableCell,
|
|
350
|
+
{
|
|
351
|
+
className: cn(
|
|
352
|
+
cellPadding,
|
|
353
|
+
col.width,
|
|
354
|
+
col.align === "right" && "text-end",
|
|
355
|
+
col.align === "center" && "text-center",
|
|
356
|
+
col.hiddenOnMobile && "hidden md:table-cell",
|
|
357
|
+
col.pin === "end" && "ui-data-table-pin-end"
|
|
358
|
+
),
|
|
359
|
+
children: col.render ? col.render(row) : (() => {
|
|
360
|
+
const v = row[col.key];
|
|
361
|
+
if (v == null) return "\u2014";
|
|
362
|
+
if (typeof v === "string" || typeof v === "number") return String(v);
|
|
363
|
+
return "\u2014";
|
|
364
|
+
})()
|
|
365
|
+
},
|
|
366
|
+
col.key
|
|
367
|
+
))
|
|
368
|
+
]
|
|
369
|
+
},
|
|
370
|
+
id
|
|
371
|
+
);
|
|
372
|
+
}) })
|
|
373
|
+
] })
|
|
374
|
+
}
|
|
375
|
+
)
|
|
366
376
|
}
|
|
367
377
|
);
|
|
368
378
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { Badge } from "./badge";
|
|
2
2
|
export type { BadgeProps } from "./badge";
|
|
3
3
|
export { Avatar, AvatarImage, AvatarFallback } from "./avatar";
|
|
4
|
-
export { Card, CardContent, CardCover, CardDescription, CardFooter, CardHeader, CardTitle, CardAction, StatCard, } from "./card";
|
|
5
|
-
export type { StatCardProps } from "./card";
|
|
4
|
+
export { Card, CardBar, CardContent, CardCover, CardDescription, CardFooter, CardHeader, CardTitle, CardAction, StatCard, } from "./card";
|
|
5
|
+
export type { StatCardProps, CardBarProps } from "./card";
|
|
6
6
|
export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table";
|
|
7
7
|
export { Descriptions } from "./descriptions";
|
|
8
8
|
export { DataTable } from "./data-table";
|
|
@@ -2,6 +2,7 @@ import { Badge } from "./badge";
|
|
|
2
2
|
import { Avatar, AvatarImage, AvatarFallback } from "./avatar";
|
|
3
3
|
import {
|
|
4
4
|
Card,
|
|
5
|
+
CardBar,
|
|
5
6
|
CardContent,
|
|
6
7
|
CardCover,
|
|
7
8
|
CardDescription,
|
|
@@ -51,6 +52,7 @@ export {
|
|
|
51
52
|
Badge,
|
|
52
53
|
Card,
|
|
53
54
|
CardAction,
|
|
55
|
+
CardBar,
|
|
54
56
|
CardContent,
|
|
55
57
|
CardCover,
|
|
56
58
|
CardDescription,
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -914,6 +914,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
914
914
|
readonly file: "components/data-display/card.tsx";
|
|
915
915
|
readonly vocabulary: readonly ["ClassNameProp", "ChildrenProp"];
|
|
916
916
|
};
|
|
917
|
+
readonly CardBarProp: {
|
|
918
|
+
readonly group: "data-display";
|
|
919
|
+
readonly file: "components/data-display/card.tsx";
|
|
920
|
+
readonly vocabulary: readonly ["ClassNameProp", "ChildrenProp"];
|
|
921
|
+
};
|
|
917
922
|
readonly StatCardProp: {
|
|
918
923
|
readonly group: "data-display";
|
|
919
924
|
readonly file: "components/data-display/card.tsx";
|
package/dist/props/registry.js
CHANGED
|
@@ -966,6 +966,11 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
966
966
|
file: "components/data-display/card.tsx",
|
|
967
967
|
vocabulary: ["ClassNameProp", "ChildrenProp"]
|
|
968
968
|
},
|
|
969
|
+
CardBarProp: {
|
|
970
|
+
group: "data-display",
|
|
971
|
+
file: "components/data-display/card.tsx",
|
|
972
|
+
vocabulary: ["ClassNameProp", "ChildrenProp"]
|
|
973
|
+
},
|
|
969
974
|
StatCardProp: {
|
|
970
975
|
group: "data-display",
|
|
971
976
|
file: "components/data-display/card.tsx",
|
|
@@ -103,6 +103,43 @@
|
|
|
103
103
|
padding-bottom: 0;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/* ── CardBar — positionable tab/toolbar strip (CardBar in card.tsx) ───────
|
|
107
|
+
* Separators come from POSITION: a top bar gets a bottom border, a bottom
|
|
108
|
+
* bar a top border, a middle bar both, a sole child none. `border-block` is
|
|
109
|
+
* the block (top/bottom) axis — RTL-neutral. The card's own overflow:hidden
|
|
110
|
+
* clips the bar to the rounded corners, so it needs no radius of its own. */
|
|
111
|
+
[data-slot="card-bar"] {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: var(--space-inline-md);
|
|
115
|
+
padding: var(--card-space-header-y) var(--card-space-inset);
|
|
116
|
+
border-block: 1px solid hsl(var(--card-border));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[data-slot="card-bar"]:first-child {
|
|
120
|
+
border-block-start-width: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
[data-slot="card-bar"]:last-child {
|
|
124
|
+
border-block-end-width: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
[data-slot="card-bar-main"] {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: var(--space-inline-sm);
|
|
131
|
+
min-width: 0;
|
|
132
|
+
flex: 1 1 auto;
|
|
133
|
+
overflow-x: auto;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
[data-slot="card-bar-extra"] {
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: var(--space-inline-xs);
|
|
140
|
+
flex-shrink: 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
106
143
|
.ui-card-header--banded {
|
|
107
144
|
border-bottom: var(--card-header-border-bottom);
|
|
108
145
|
background-color: hsl(var(--card-header-background) / var(--card-header-background-alpha));
|
|
@@ -94,6 +94,12 @@
|
|
|
94
94
|
border-radius: var(--radius-md);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/* Zebra striping (opt-in via the `striped` prop). Selected rows keep their
|
|
98
|
+
* own emphasis, so they are excluded from the stripe. */
|
|
99
|
+
.ui-data-table-surface[data-striped] tbody tr:nth-child(even):not([data-state="selected"]) {
|
|
100
|
+
background-color: hsl(var(--muted) / 0.4);
|
|
101
|
+
}
|
|
102
|
+
|
|
97
103
|
/* Empty-state cell hosts the built-in EmptyState (which brings its own padding). */
|
|
98
104
|
.ui-data-table-empty {
|
|
99
105
|
padding: 0;
|