@neasg/design-system 0.4.10 → 0.4.12
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 +2 -2
- package/dist/avatar-profile-popover.d.ts +34 -0
- package/dist/avatar-profile-popover.js +27 -0
- package/dist/badge.d.ts +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/card.d.ts +2 -0
- package/dist/card.js +72 -3
- package/dist/command-search.d.ts +47 -2
- package/dist/command-search.js +173 -24
- package/dist/command.d.ts +2 -0
- package/dist/command.js +2 -2
- package/dist/date-input.d.ts +2 -0
- package/dist/date-input.js +44 -4
- package/dist/dialog-primitive.js +1 -1
- package/dist/draggable-tabs.d.ts +3 -0
- package/dist/draggable-tabs.js +4 -0
- package/dist/editable-table.js +2 -2
- package/dist/filter-popover.d.ts +2 -1
- package/dist/filter-popover.js +2 -2
- package/dist/guidance-tip.d.ts +26 -0
- package/dist/guidance-tip.js +162 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.js +5 -1
- package/dist/jump-target-highlight.d.ts +13 -0
- package/dist/jump-target-highlight.js +95 -0
- package/dist/layout.d.ts +27 -5
- package/dist/layout.js +128 -34
- package/dist/message-item.js +9 -10
- package/dist/notification.js +1 -1
- package/dist/page-layout.d.ts +22 -0
- package/dist/page-layout.js +38 -0
- package/dist/page-section.js +1 -1
- package/dist/popover-menu.js +2 -2
- package/dist/routing-timeline.js +1 -1
- package/dist/table-column-visibility.d.ts +3 -1
- package/dist/table-column-visibility.js +74 -27
- package/dist/table-toolbar.d.ts +2 -1
- package/dist/table-toolbar.js +7 -2
- package/dist/table.d.ts +32 -3
- package/dist/table.js +318 -29
- package/dist/tabs.d.ts +2 -1
- package/dist/tabs.js +8 -5
- package/dist/theme-switcher.d.ts +1 -1
- package/dist/theme-switcher.js +4 -6
- package/package.json +2 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "./lib/utils";
|
|
3
|
+
const pageLayoutGapClassNames = {
|
|
4
|
+
page: "space-y-[var(--layout-page-gap)]",
|
|
5
|
+
header: "space-y-[var(--layout-page-header-gap)]",
|
|
6
|
+
section: "space-y-[var(--layout-page-section-gap)]",
|
|
7
|
+
control: "space-y-[var(--layout-page-control-gap)]",
|
|
8
|
+
context: "space-y-[var(--layout-page-context-gap)]",
|
|
9
|
+
};
|
|
10
|
+
const pageLayoutStackGapClassNames = {
|
|
11
|
+
header: pageLayoutGapClassNames.header,
|
|
12
|
+
section: pageLayoutGapClassNames.section,
|
|
13
|
+
control: pageLayoutGapClassNames.control,
|
|
14
|
+
context: pageLayoutGapClassNames.context,
|
|
15
|
+
title: "space-y-[var(--space-1)]",
|
|
16
|
+
};
|
|
17
|
+
function PageLayout({ gap = "page", className, ...props }) {
|
|
18
|
+
return (_jsx("div", { "data-slot": "page-layout", className: cn(pageLayoutGapClassNames[gap], className), ...props }));
|
|
19
|
+
}
|
|
20
|
+
function PageLayoutStack({ gap = "section", titleOffset = false, className, ...props }) {
|
|
21
|
+
return (_jsx("div", { "data-slot": "page-layout-stack", className: cn(pageLayoutStackGapClassNames[gap], titleOffset && "pt-[var(--layout-page-title-offset)]", className), ...props }));
|
|
22
|
+
}
|
|
23
|
+
function PageLayoutTitle(props) {
|
|
24
|
+
return (_jsx(PageLayoutStack, { gap: "title", "data-slot": "page-layout-title", ...props }));
|
|
25
|
+
}
|
|
26
|
+
function PageLayoutHeader(props) {
|
|
27
|
+
return (_jsx(PageLayoutStack, { gap: "header", "data-slot": "page-layout-header", ...props }));
|
|
28
|
+
}
|
|
29
|
+
function PageLayoutSection(props) {
|
|
30
|
+
return (_jsx(PageLayoutStack, { gap: "section", "data-slot": "page-layout-section", ...props }));
|
|
31
|
+
}
|
|
32
|
+
function PageLayoutControls(props) {
|
|
33
|
+
return (_jsx(PageLayoutStack, { gap: "control", "data-slot": "page-layout-controls", ...props }));
|
|
34
|
+
}
|
|
35
|
+
function PageLayoutContext(props) {
|
|
36
|
+
return (_jsx(PageLayoutStack, { gap: "context", "data-slot": "page-layout-context", ...props }));
|
|
37
|
+
}
|
|
38
|
+
export { PageLayout, PageLayoutStack, PageLayoutTitle, PageLayoutHeader, PageLayoutSection, PageLayoutControls, PageLayoutContext, pageLayoutGapClassNames, pageLayoutStackGapClassNames, };
|
package/dist/page-section.js
CHANGED
|
@@ -9,6 +9,6 @@ function PageSectionGroup({ variant = "divided", className, ...props }) {
|
|
|
9
9
|
function PageSection({ title, description, headerAction, titleAdornment, className, headerClassName, contentClassName, children, ...props }) {
|
|
10
10
|
const renderedTitle = React.isValidElement(title) ? (title) : (_jsx(Typography, { as: "h3", variant: "subheading", children: title }));
|
|
11
11
|
const renderedDescription = description == null ? null : React.isValidElement(description) ? (description) : (_jsx(Typography, { as: "p", variant: "caption", children: description }));
|
|
12
|
-
return (_jsxs("section", { "data-slot": "page-section", className: cn("py-6", className), ...props, children: [_jsxs("div", { className: cn("flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between", headerClassName), children: [_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [renderedTitle, titleAdornment] }), renderedDescription] }), headerAction ? (_jsx("div", { className: "flex flex-wrap items-center justify-end gap-
|
|
12
|
+
return (_jsxs("section", { "data-slot": "page-section", className: cn("py-6", className), ...props, children: [_jsxs("div", { className: cn("flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between", headerClassName), children: [_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [renderedTitle, titleAdornment] }), renderedDescription] }), headerAction ? (_jsx("div", { className: "flex flex-wrap items-center justify-end gap-3 sm:shrink-0", children: headerAction })) : null] }), children ? (_jsx("div", { className: cn("pt-4", contentClassName), children: children })) : null] }));
|
|
13
13
|
}
|
|
14
14
|
export { PageSection, PageSectionGroup };
|
package/dist/popover-menu.js
CHANGED
|
@@ -19,7 +19,7 @@ function PopoverMenuSection({ className, children, ...props }) {
|
|
|
19
19
|
return (_jsx("div", { className: cn("space-y-0", className), ...props, children: children }));
|
|
20
20
|
}
|
|
21
21
|
function PopoverMenuItem({ className, children, active, type = "button", ...props }) {
|
|
22
|
-
return (_jsxs(Button, { type: type, variant: "ghost", className: cn("h-auto min-h-8 w-full justify-start gap-2 rounded-sm px-2 py-2 text-left font-normal whitespace-normal", active &&
|
|
22
|
+
return (_jsxs(Button, { type: type, variant: "ghost", className: cn("h-auto min-h-8 w-full justify-start gap-2 rounded-sm px-2 py-2 text-left font-normal whitespace-normal focus-visible:ring-inset focus-visible:ring-1 focus-visible:ring-offset-0", active &&
|
|
23
23
|
"bg-accent text-accent-foreground hover:bg-accent hover:text-accent-foreground", className), ...props, children: [children, active ? _jsx(Check, { "aria-hidden": "true", className: "ml-auto h-4 w-4 shrink-0" }) : null] }));
|
|
24
24
|
}
|
|
25
25
|
function PopoverMenuCheckboxItem({ className, children, checked = false, description, disabled, onChange, readOnly, ...props }) {
|
|
@@ -33,7 +33,7 @@ function PopoverMenuFooter({ className, children, inset = "menu", ...props }) {
|
|
|
33
33
|
return (_jsx("div", { className: cn("flex items-center gap-2 border-t p-1.5", inset === "menu" && "-mx-1 -mb-1", inset === "flush" && "m-0", className), ...props, children: children }));
|
|
34
34
|
}
|
|
35
35
|
function PopoverMenuFooterAction({ className, type = "button", ...props }) {
|
|
36
|
-
return (_jsx(Button, { type: type, variant: "ghost", size: "sm", className: cn("h-auto min-h-8 w-full justify-start px-2 py-1.5 text-left whitespace-normal", className), ...props }));
|
|
36
|
+
return (_jsx(Button, { type: type, variant: "ghost", size: "sm", className: cn("h-auto min-h-8 w-full justify-start px-2 py-1.5 text-left whitespace-normal focus-visible:ring-inset focus-visible:ring-1 focus-visible:ring-offset-0", className), ...props }));
|
|
37
37
|
}
|
|
38
38
|
function PopoverMenuFooterMeta({ className, children, ...props }) {
|
|
39
39
|
return (_jsx(Typography, { as: "span", variant: "bodySm", color: "muted", className: cn("shrink-0 px-2 font-medium", className), ...props, children: children }));
|
package/dist/routing-timeline.js
CHANGED
|
@@ -52,7 +52,7 @@ function RoutingTimeline({ items, emptyMessage = "No routing activity yet.", cla
|
|
|
52
52
|
const hasCompleteRoute = Boolean(item.from && item.to);
|
|
53
53
|
const title = (_b = item.title) !== null && _b !== void 0 ? _b : config.title;
|
|
54
54
|
const remark = (_c = item.remark) !== null && _c !== void 0 ? _c : item.description;
|
|
55
|
-
return (_jsxs("li", { className: "relative grid grid-cols-[var(--space-3)_minmax(0,1fr)] gap-[var(--space-3)] pb-[var(--space-5)] last:pb-0", children: [_jsx("div", { className: "relative flex justify-center", children: _jsx("span", { "aria-hidden": "true", "data-slot": "routing-timeline-marker", className: cn("relative z-10 mt-[var(--space-1)] size-[var(--space-2)] rounded-full border", config.markerClassName) }) }), _jsxs("div", { "data-slot": "routing-timeline-event-card", className: "min-w-0 rounded-surface border border-border/70 bg-card p-[var(--space-3)] text-card-foreground", children: [_jsxs("div", { className: "min-w-0 space-y-[var(--space-1)]", children: [_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-[var(--space-2)]", children: [_jsx(Typography, { as: "p", variant: "label", className: "min-w-0 flex-1 font-semibold", children: title }), _jsx(Badge, { variant: config.badgeVariant, bordered: true, className: "max-w-full shrink-0 self-start", children: (_d = item.badgeLabel) !== null && _d !== void 0 ? _d : config.label })] }), item.timestamp || item.actor ? (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-[var(--space-1)] gap-y-[var(--space-1)]", children: [item.timestamp ? (_jsx(Typography, { as: "time", variant: "caption", className: "shrink-0", children: item.timestamp })) : null, item.actor ? (_jsxs(Typography, { as: "p", variant: "caption", className: "inline-flex min-w-0 items-center gap-x-[var(--space-1)]", children: [item.timestamp ? (_jsx("span", { "aria-hidden": "true", children: "\u00B7" })) : null, "By ", item.actor] })) : null] })) : null] }), remark ? (_jsx(Typography, { as: "div", variant: "bodySm", "data-slot": "routing-timeline-remark", className: "mt-[var(--space-4)] font-normal", children: remark })) : null, hasRouteEndpoints ? (_jsxs("footer", { "data-slot": "routing-timeline-handoff", "data-complete-route": hasCompleteRoute ? "true" : undefined, className: "mt-[var(--space-4)] flex flex-col space-y-[var(--space-2)] text-xs text-muted-foreground", children: [item.from ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.from })) : null, item.from && item.to ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: "to" }), _jsx(ArrowDown, { "aria-hidden": "true", "data-slot": "routing-timeline-handoff-arrow", className: "ml-1 size-4 shrink-0 text-muted-foreground" })] })) : null, item.to ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.to })) : null] })) : null] })] }, item.id));
|
|
55
|
+
return (_jsxs("li", { className: "relative grid grid-cols-[var(--space-3)_minmax(0,1fr)] gap-[var(--space-3)] pb-[var(--space-5)] last:pb-0", children: [_jsx("div", { className: "relative flex justify-center", children: _jsx("span", { "aria-hidden": "true", "data-slot": "routing-timeline-marker", className: cn("relative z-10 mt-[var(--space-1)] size-[var(--space-2)] rounded-full border", config.markerClassName) }) }), _jsxs("div", { "data-slot": "routing-timeline-event-card", className: "min-w-0 rounded-surface border border-border/70 bg-card p-[var(--space-3)] text-card-foreground", children: [_jsxs("div", { className: "min-w-0 space-y-[var(--space-1)]", children: [_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-[var(--space-2)]", children: [_jsx(Typography, { as: "p", variant: "label", className: "min-w-0 flex-1 font-semibold", children: title }), _jsx(Badge, { variant: config.badgeVariant, bordered: true, className: "max-w-full shrink-0 self-start", children: (_d = item.badgeLabel) !== null && _d !== void 0 ? _d : config.label })] }), item.timestamp || item.actor ? (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-[var(--space-1)] gap-y-[var(--space-1)]", children: [item.timestamp ? (_jsx(Typography, { as: "time", variant: "caption", className: "shrink-0 text-muted-foreground/60", children: item.timestamp })) : null, item.actor ? (_jsxs(Typography, { as: "p", variant: "caption", className: "inline-flex min-w-0 items-center gap-x-[var(--space-1)]", children: [item.timestamp ? (_jsx("span", { "aria-hidden": "true", children: "\u00B7" })) : null, "By ", item.actor] })) : null] })) : null] }), remark ? (_jsx(Typography, { as: "div", variant: "bodySm", "data-slot": "routing-timeline-remark", className: "mt-[var(--space-4)] font-normal", children: remark })) : null, hasRouteEndpoints ? (_jsxs("footer", { "data-slot": "routing-timeline-handoff", "data-complete-route": hasCompleteRoute ? "true" : undefined, className: "mt-[var(--space-4)] flex flex-col space-y-[var(--space-2)] text-xs text-muted-foreground", children: [item.from ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.from })) : null, item.from && item.to ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: "to" }), _jsx(ArrowDown, { "aria-hidden": "true", "data-slot": "routing-timeline-handoff-arrow", className: "ml-1 size-4 shrink-0 text-muted-foreground" })] })) : null, item.to ? (_jsx(RoutingTimelineHandoffEndpoint, { value: item.to })) : null] })) : null] })] }, item.id));
|
|
56
56
|
}) }) }));
|
|
57
57
|
}
|
|
58
58
|
export { RoutingTimeline };
|
|
@@ -4,6 +4,7 @@ import type { TableColumn } from "./table";
|
|
|
4
4
|
type PopoverPlacementProps = Pick<PopoverProps, "align" | "side" | "sideOffset">;
|
|
5
5
|
export interface TableColumnVisibilityMenuProps<Row> extends PopoverPlacementProps {
|
|
6
6
|
columns: TableColumn<Row>[];
|
|
7
|
+
/** Ordered visible column keys. Reordering in the menu emits the next order through `onVisibleColumnKeysChange`. */
|
|
7
8
|
visibleColumnKeys: readonly string[];
|
|
8
9
|
onVisibleColumnKeysChange: (visibleColumnKeys: string[]) => void;
|
|
9
10
|
defaultVisibleColumnKeys?: readonly string[];
|
|
@@ -12,10 +13,11 @@ export interface TableColumnVisibilityMenuProps<Row> extends PopoverPlacementPro
|
|
|
12
13
|
triggerClassName?: string;
|
|
13
14
|
contentClassName?: string;
|
|
14
15
|
className?: string;
|
|
16
|
+
title?: React.ReactNode;
|
|
15
17
|
searchPlaceholder?: string;
|
|
16
18
|
restoreDefaultsLabel?: string;
|
|
17
19
|
emptyMessage?: React.ReactNode;
|
|
18
20
|
minVisibleColumns?: number;
|
|
19
21
|
}
|
|
20
|
-
declare function TableColumnVisibilityMenu<Row>({ columns, visibleColumnKeys, onVisibleColumnKeysChange, defaultVisibleColumnKeys, trigger, triggerLabel, triggerClassName, contentClassName, className, searchPlaceholder, restoreDefaultsLabel, emptyMessage, minVisibleColumns, align, sideOffset, side, }: TableColumnVisibilityMenuProps<Row>): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
declare function TableColumnVisibilityMenu<Row>({ columns, visibleColumnKeys, onVisibleColumnKeysChange, defaultVisibleColumnKeys, trigger, triggerLabel, triggerClassName, contentClassName, className, title, searchPlaceholder, restoreDefaultsLabel, emptyMessage, minVisibleColumns, align, sideOffset, side, }: TableColumnVisibilityMenuProps<Row>): import("react/jsx-runtime").JSX.Element;
|
|
21
23
|
export { TableColumnVisibilityMenu };
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors, } from "@dnd-kit/core";
|
|
5
|
+
import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable";
|
|
6
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
7
|
+
import { Columns3, GripVertical } from "lucide-react";
|
|
5
8
|
import { Button } from "./button";
|
|
6
9
|
import { Checkbox } from "./checkbox";
|
|
7
10
|
import { InputControl } from "./input-control";
|
|
8
11
|
import { cn } from "./lib/utils";
|
|
9
12
|
import { Popover } from "./popover";
|
|
10
|
-
import { PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuFooterMeta, } from "./popover-menu";
|
|
13
|
+
import { PopoverMenuFooter, PopoverMenuFooterAction, PopoverMenuFooterMeta, PopoverMenuGroupLabel, } from "./popover-menu";
|
|
11
14
|
import { SearchInputShell } from "./search-input-shell";
|
|
12
15
|
function getColumnVisibilityLabel(column) {
|
|
13
16
|
if (column.visibilityLabel) {
|
|
@@ -19,11 +22,18 @@ function getColumnVisibilityLabel(column) {
|
|
|
19
22
|
}
|
|
20
23
|
return column.key;
|
|
21
24
|
}
|
|
22
|
-
function
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
function orderColumnKeys(columns, columnKeys) {
|
|
26
|
+
const columnKeySet = new Set(columns.map((column) => column.key));
|
|
27
|
+
const seenColumnKeys = new Set();
|
|
28
|
+
const orderedColumnKeys = [];
|
|
29
|
+
Array.from(columnKeys).forEach((columnKey) => {
|
|
30
|
+
if (!columnKeySet.has(columnKey) || seenColumnKeys.has(columnKey)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
orderedColumnKeys.push(columnKey);
|
|
34
|
+
seenColumnKeys.add(columnKey);
|
|
35
|
+
});
|
|
36
|
+
return orderedColumnKeys;
|
|
27
37
|
}
|
|
28
38
|
function arraysMatch(left, right) {
|
|
29
39
|
if (left.length !== right.length) {
|
|
@@ -31,44 +41,81 @@ function arraysMatch(left, right) {
|
|
|
31
41
|
}
|
|
32
42
|
return left.every((value, index) => value === right[index]);
|
|
33
43
|
}
|
|
34
|
-
function
|
|
44
|
+
function SortableColumnOption({ columnKey, label, checked, disabled, onToggle, }) {
|
|
45
|
+
const checkboxId = React.useId();
|
|
46
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: columnKey, disabled: !checked });
|
|
47
|
+
return (_jsxs("div", { ref: setNodeRef, style: {
|
|
48
|
+
transform: CSS.Transform.toString(transform),
|
|
49
|
+
transition,
|
|
50
|
+
zIndex: isDragging ? 50 : undefined,
|
|
51
|
+
}, className: cn("flex h-9 items-center gap-2 rounded-sm px-1.5 pr-2 text-sm transition-colors hover:bg-accent hover:text-accent-foreground", isDragging && "opacity-50 shadow-sm"), children: [_jsx("button", { type: "button", "aria-label": `Reorder ${label}`, disabled: !checked, className: cn("inline-flex size-6 shrink-0 touch-none items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-background hover:text-foreground", checked
|
|
52
|
+
? "cursor-grab active:cursor-grabbing"
|
|
53
|
+
: "cursor-not-allowed opacity-40"), ...(checked ? { ...attributes, ...listeners } : {}), children: _jsx(GripVertical, { className: "h-4 w-4", "aria-hidden": "true" }) }), _jsx(Checkbox, { id: checkboxId, checked: checked, disabled: disabled, onChange: onToggle, "aria-label": label }), _jsx("label", { htmlFor: checkboxId, className: cn("min-w-0 flex-1 cursor-pointer truncate", disabled && "cursor-not-allowed text-muted-foreground"), children: label })] }));
|
|
54
|
+
}
|
|
55
|
+
function TableColumnVisibilityMenu({ columns, visibleColumnKeys, onVisibleColumnKeysChange, defaultVisibleColumnKeys, trigger, triggerLabel = "Customize columns", triggerClassName, contentClassName, className, title = "Customize columns", searchPlaceholder = "Search columns", restoreDefaultsLabel = "Restore defaults", emptyMessage = "No columns found", minVisibleColumns = 1, align = "end", sideOffset = 4, side, }) {
|
|
35
56
|
const [query, setQuery] = React.useState("");
|
|
57
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
58
|
+
activationConstraint: {
|
|
59
|
+
distance: 8,
|
|
60
|
+
},
|
|
61
|
+
}), useSensor(KeyboardSensor, {
|
|
62
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
63
|
+
}));
|
|
36
64
|
const searchInputId = React.useId();
|
|
65
|
+
const titleId = React.useId();
|
|
37
66
|
const requiredVisibleColumns = Math.max(0, minVisibleColumns);
|
|
38
|
-
const visibleColumnKeySet = React.useMemo(() => new Set(visibleColumnKeys), [visibleColumnKeys]);
|
|
39
|
-
const visibleColumnCount = columns.filter((column) => visibleColumnKeySet.has(column.key)).length;
|
|
40
67
|
const selectableColumns = React.useMemo(() => columns.filter((column) => column.hideable !== false), [columns]);
|
|
41
|
-
const defaultKeys = React.useMemo(() =>
|
|
68
|
+
const defaultKeys = React.useMemo(() => orderColumnKeys(columns, defaultVisibleColumnKeys !== null && defaultVisibleColumnKeys !== void 0 ? defaultVisibleColumnKeys : columns
|
|
42
69
|
.filter((column) => column.defaultVisible !== false)
|
|
43
70
|
.map((column) => column.key)), [columns, defaultVisibleColumnKeys]);
|
|
44
|
-
const currentKeys = React.useMemo(() =>
|
|
71
|
+
const currentKeys = React.useMemo(() => orderColumnKeys(columns, visibleColumnKeys), [columns, visibleColumnKeys]);
|
|
72
|
+
const visibleColumnKeySet = React.useMemo(() => new Set(currentKeys), [currentKeys]);
|
|
73
|
+
const visibleColumnCount = currentKeys.length;
|
|
74
|
+
const orderedSelectableColumns = React.useMemo(() => {
|
|
75
|
+
const selectableColumnsByKey = new Map(selectableColumns.map((column) => [column.key, column]));
|
|
76
|
+
const orderedVisibleColumns = currentKeys
|
|
77
|
+
.map((columnKey) => selectableColumnsByKey.get(columnKey))
|
|
78
|
+
.filter((column) => Boolean(column));
|
|
79
|
+
const orderedVisibleColumnKeySet = new Set(orderedVisibleColumns.map((column) => column.key));
|
|
80
|
+
const hiddenColumns = selectableColumns.filter((column) => !orderedVisibleColumnKeySet.has(column.key));
|
|
81
|
+
return [...orderedVisibleColumns, ...hiddenColumns];
|
|
82
|
+
}, [currentKeys, selectableColumns]);
|
|
45
83
|
const canRestoreDefaults = defaultKeys.length >= requiredVisibleColumns &&
|
|
46
84
|
!arraysMatch(currentKeys, defaultKeys);
|
|
47
85
|
const normalizedQuery = query.trim().toLowerCase();
|
|
48
|
-
const filteredColumns =
|
|
86
|
+
const filteredColumns = orderedSelectableColumns.filter((column) => getColumnVisibilityLabel(column).toLowerCase().includes(normalizedQuery));
|
|
49
87
|
const commitVisibleColumnKeys = React.useCallback((nextVisibleColumnKeys) => {
|
|
50
|
-
const orderedKeys =
|
|
88
|
+
const orderedKeys = orderColumnKeys(columns, nextVisibleColumnKeys);
|
|
51
89
|
if (orderedKeys.length < requiredVisibleColumns) {
|
|
52
90
|
return;
|
|
53
91
|
}
|
|
54
92
|
onVisibleColumnKeysChange(orderedKeys);
|
|
55
93
|
}, [columns, onVisibleColumnKeysChange, requiredVisibleColumns]);
|
|
56
94
|
const toggleColumn = React.useCallback((columnKey) => {
|
|
57
|
-
const nextVisibleColumnKeys =
|
|
58
|
-
if (
|
|
59
|
-
nextVisibleColumnKeys.
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
nextVisibleColumnKeys.add(columnKey);
|
|
95
|
+
const nextVisibleColumnKeys = currentKeys.filter((currentColumnKey) => currentColumnKey !== columnKey);
|
|
96
|
+
if (!visibleColumnKeySet.has(columnKey)) {
|
|
97
|
+
nextVisibleColumnKeys.push(columnKey);
|
|
63
98
|
}
|
|
64
99
|
commitVisibleColumnKeys(nextVisibleColumnKeys);
|
|
65
|
-
}, [commitVisibleColumnKeys,
|
|
100
|
+
}, [commitVisibleColumnKeys, currentKeys, visibleColumnKeySet]);
|
|
101
|
+
const handleDragEnd = React.useCallback((event) => {
|
|
102
|
+
const { active, over } = event;
|
|
103
|
+
if (!over || active.id === over.id) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const oldIndex = currentKeys.indexOf(String(active.id));
|
|
107
|
+
const newIndex = currentKeys.indexOf(String(over.id));
|
|
108
|
+
if (oldIndex < 0 || newIndex < 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
commitVisibleColumnKeys(arrayMove(currentKeys, oldIndex, newIndex));
|
|
112
|
+
}, [commitVisibleColumnKeys, currentKeys]);
|
|
66
113
|
const defaultTrigger = (_jsx(Button, { type: "button", variant: "outline", size: "icon", "aria-label": triggerLabel, className: triggerClassName, children: _jsx(Columns3, { "aria-hidden": "true" }) }));
|
|
67
|
-
return (_jsx(Popover, { trigger: trigger !== null && trigger !== void 0 ? trigger : defaultTrigger, align: align, side: side, sideOffset: sideOffset, contentClassName: cn("w-80 p-0", contentClassName), children: _jsxs("div", { className: cn("flex max-h-96 flex-col", className), children: [_jsxs("div", { className: "border-b p-2", children: [_jsx("label", { htmlFor: searchInputId, className: "sr-only", children: searchPlaceholder }), _jsx(SearchInputShell, { children: _jsx(InputControl, { id: searchInputId, value: query, onChange: (event) => setQuery(event.target.value), placeholder: searchPlaceholder, className: "h-8 bg-background pl-8 pr-2" }) })] }),
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
114
|
+
return (_jsx(Popover, { trigger: trigger !== null && trigger !== void 0 ? trigger : defaultTrigger, align: align, side: side, sideOffset: sideOffset, contentClassName: cn("w-80 p-0", contentClassName), "aria-labelledby": title ? titleId : undefined, children: _jsxs("div", { className: cn("flex max-h-96 flex-col", className), children: [_jsxs("div", { className: "border-b p-2", children: [_jsx("label", { htmlFor: searchInputId, className: "sr-only", children: searchPlaceholder }), _jsx(SearchInputShell, { children: _jsx(InputControl, { id: searchInputId, value: query, onChange: (event) => setQuery(event.target.value), placeholder: searchPlaceholder, className: "h-8 bg-background pl-8 pr-2" }) })] }), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto p-1", children: [title ? (_jsx(PopoverMenuGroupLabel, { id: titleId, children: title })) : null, filteredColumns.length ? (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: filteredColumns.map((column) => column.key), strategy: verticalListSortingStrategy, children: filteredColumns.map((column) => {
|
|
115
|
+
const label = getColumnVisibilityLabel(column);
|
|
116
|
+
const checked = visibleColumnKeySet.has(column.key);
|
|
117
|
+
const disabled = checked && visibleColumnCount <= requiredVisibleColumns;
|
|
118
|
+
return (_jsx(SortableColumnOption, { columnKey: column.key, label: label, checked: checked, disabled: disabled, onToggle: () => toggleColumn(column.key) }, column.key));
|
|
119
|
+
}) }) })) : (_jsx("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: emptyMessage }))] }), _jsxs(PopoverMenuFooter, { inset: "flush", className: "justify-between", children: [_jsx(PopoverMenuFooterAction, { onClick: () => commitVisibleColumnKeys(defaultKeys), disabled: !canRestoreDefaults, className: "w-auto min-w-0 flex-1", children: restoreDefaultsLabel }), _jsxs(PopoverMenuFooterMeta, { children: [visibleColumnCount, " of ", columns.length] })] })] }) }));
|
|
73
120
|
}
|
|
74
121
|
export { TableColumnVisibilityMenu };
|
package/dist/table-toolbar.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { type SearchInputProps } from "./search-input";
|
|
3
3
|
export interface TableToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
4
|
search?: SearchInputProps;
|
|
5
|
+
actionsClassName?: string;
|
|
5
6
|
}
|
|
6
|
-
declare function TableToolbar({ search, className, children, ...props }: TableToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
declare function TableToolbar({ search, className, actionsClassName, children, ...props }: TableToolbarProps): import("react/jsx-runtime").JSX.Element;
|
|
7
8
|
export { TableToolbar };
|
package/dist/table-toolbar.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { SearchInput } from "./search-input";
|
|
3
3
|
import { cn } from "./lib/utils";
|
|
4
|
-
function TableToolbar({ search, className, children, ...props }) {
|
|
5
|
-
|
|
4
|
+
function TableToolbar({ search, className, actionsClassName, children, ...props }) {
|
|
5
|
+
const hasSearch = Boolean(search);
|
|
6
|
+
return (_jsxs("div", { className: cn(hasSearch
|
|
7
|
+
? "grid w-full grid-cols-1 items-center gap-[calc(var(--space-1)+0.125rem)] sm:grid-cols-[minmax(calc(var(--space-10)*5),28rem)_minmax(0,1fr)]"
|
|
8
|
+
: "flex w-full flex-wrap items-center justify-end gap-[calc(var(--space-1)+0.125rem)]", className), ...props, children: [search ? (_jsx(SearchInput, { ...search, containerClassName: cn("w-full min-w-[calc(var(--space-10)*5)] max-w-md", search.containerClassName) })) : null, children ? (_jsx("div", { className: cn("flex flex-wrap items-center gap-[calc(var(--space-1)+0.125rem)]", hasSearch
|
|
9
|
+
? "justify-start sm:justify-end sm:justify-self-end"
|
|
10
|
+
: "justify-end", actionsClassName), children: children })) : null] }));
|
|
6
11
|
}
|
|
7
12
|
export { TableToolbar };
|
package/dist/table.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ declare const TableRoot: React.ForwardRefExoticComponent<TableRootProps & React.
|
|
|
7
7
|
interface TableContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
8
|
}
|
|
9
9
|
declare const TableContainer: React.ForwardRefExoticComponent<TableContainerProps & React.RefAttributes<HTMLDivElement>>;
|
|
10
|
+
export type TableColumnWidths = Record<string, number>;
|
|
10
11
|
declare const TableHeader: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableSectionElement> & React.RefAttributes<HTMLTableSectionElement>>;
|
|
11
12
|
declare const TableBody: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableSectionElement> & React.RefAttributes<HTMLTableSectionElement>>;
|
|
12
13
|
declare const TableFooter: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableSectionElement> & React.RefAttributes<HTMLTableSectionElement>>;
|
|
@@ -16,12 +17,16 @@ declare const TableHead: React.ForwardRefExoticComponent<React.ThHTMLAttributes<
|
|
|
16
17
|
minWidth?: string;
|
|
17
18
|
maxWidth?: string;
|
|
18
19
|
wrap?: boolean;
|
|
20
|
+
treatWidthAsMinWidth?: boolean;
|
|
21
|
+
containOverflow?: boolean;
|
|
19
22
|
} & React.RefAttributes<HTMLTableCellElement>>;
|
|
20
23
|
declare const TableCell: React.ForwardRefExoticComponent<React.TdHTMLAttributes<HTMLTableCellElement> & {
|
|
21
24
|
width?: string;
|
|
22
25
|
minWidth?: string;
|
|
23
26
|
maxWidth?: string;
|
|
24
27
|
wrap?: boolean;
|
|
28
|
+
treatWidthAsMinWidth?: boolean;
|
|
29
|
+
containOverflow?: boolean;
|
|
25
30
|
} & React.RefAttributes<HTMLTableCellElement>>;
|
|
26
31
|
declare const TableCaption: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableCaptionElement> & React.RefAttributes<HTMLTableCaptionElement>>;
|
|
27
32
|
type TableAlign = "left" | "center" | "right";
|
|
@@ -76,6 +81,12 @@ export interface TableColumn<Row> {
|
|
|
76
81
|
cellClassName?: string;
|
|
77
82
|
/** Pins the column to the left or right edge while the table scrolls horizontally. */
|
|
78
83
|
sticky?: "left" | "right";
|
|
84
|
+
/** Enables a resize handle for this column. Use `resizableColumns` on Table to opt every column in by default. */
|
|
85
|
+
resizable?: boolean;
|
|
86
|
+
/** Minimum pixel width while resizing. Defaults to `minWidth`, then 64. */
|
|
87
|
+
resizeMinWidth?: number;
|
|
88
|
+
/** Maximum pixel width while resizing. Omit to allow the column to grow. */
|
|
89
|
+
resizeMaxWidth?: number;
|
|
79
90
|
}
|
|
80
91
|
export interface TableSortingProps {
|
|
81
92
|
field: string | null;
|
|
@@ -95,8 +106,16 @@ interface SortableTableHeadProps<T extends string> {
|
|
|
95
106
|
maxWidth?: string;
|
|
96
107
|
align?: TableAlign;
|
|
97
108
|
sortLabel?: string;
|
|
109
|
+
resizeHandle?: React.ReactNode;
|
|
110
|
+
treatWidthAsMinWidth?: boolean;
|
|
111
|
+
containOverflow?: boolean;
|
|
112
|
+
}
|
|
113
|
+
declare function SortableTableHead<T extends string>({ field, children, currentSortField, currentSortDirection, onSort, className, allowWrap, width, minWidth, maxWidth, align, sortLabel, resizeHandle, treatWidthAsMinWidth, containOverflow, }: SortableTableHeadProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
114
|
+
export interface TableColumnResizeHandleRenderProps<Row> {
|
|
115
|
+
column: TableColumn<Row>;
|
|
116
|
+
nextColumn: TableColumn<Row>;
|
|
117
|
+
handle: React.ReactElement;
|
|
98
118
|
}
|
|
99
|
-
declare function SortableTableHead<T extends string>({ field, children, currentSortField, currentSortDirection, onSort, className, allowWrap, width, minWidth, maxWidth, align, sortLabel, }: SortableTableHeadProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
100
119
|
export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
101
120
|
columns: TableColumn<Row>[];
|
|
102
121
|
rows: Row[];
|
|
@@ -115,12 +134,22 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
|
|
|
115
134
|
pagination?: Omit<PaginationProps, "className">;
|
|
116
135
|
loading?: boolean;
|
|
117
136
|
loadingRows?: number;
|
|
118
|
-
/** Optional controlled
|
|
137
|
+
/** Optional controlled ordered list of visible column keys. Omit to render every column in config order. */
|
|
119
138
|
visibleColumnKeys?: readonly string[];
|
|
139
|
+
/** When true, every column is resizable unless its column config sets `resizable: false`. */
|
|
140
|
+
resizableColumns?: boolean;
|
|
141
|
+
/** Controlled pixel widths keyed by column key. */
|
|
142
|
+
columnWidths?: TableColumnWidths;
|
|
143
|
+
/** Initial pixel widths keyed by column key for uncontrolled resizing. */
|
|
144
|
+
defaultColumnWidths?: TableColumnWidths;
|
|
145
|
+
/** Emits whenever a resize handle changes column widths. */
|
|
146
|
+
onColumnWidthsChange?: (columnWidths: TableColumnWidths) => void;
|
|
147
|
+
/** Optional hook for decorating resize handles without replacing table sizing behavior. */
|
|
148
|
+
renderColumnResizeHandle?: (props: TableColumnResizeHandleRenderProps<Row>) => React.ReactNode;
|
|
120
149
|
/** When true, applies alternating row backgrounds for easier scanning. */
|
|
121
150
|
striped?: boolean;
|
|
122
151
|
/** When set, rows become interactive: cursor-pointer, hover highlight, and click handling. */
|
|
123
152
|
onRowClick?: (row: Row, index: number) => void;
|
|
124
153
|
}
|
|
125
|
-
declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, sorting, pagination, loading, loadingRows, visibleColumnKeys, striped, onRowClick, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
|
|
154
|
+
declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, rowTone, maxHeight, sorting, pagination, loading, loadingRows, visibleColumnKeys, resizableColumns, columnWidths, defaultColumnWidths, onColumnWidthsChange, renderColumnResizeHandle, striped, onRowClick, onScroll, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
|
|
126
155
|
export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, SortableTableHead, TableRowSkeleton, };
|