@undevy-org/tablewright 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/es/components/dashboard/ReportSummaryCard.js +67 -0
- package/dist/es/components/data-table/AppliedStateChips.js +113 -0
- package/dist/es/components/data-table/ColumnFilterPopover.js +548 -0
- package/dist/es/components/data-table/ColumnHeaderMenu.js +144 -0
- package/dist/es/components/data-table/CompoundFilterPopover.js +508 -0
- package/dist/es/components/data-table/CopyButton.js +35 -0
- package/dist/es/components/data-table/CopyableText.js +26 -0
- package/dist/es/components/data-table/DataTableShell.js +145 -0
- package/dist/es/components/data-table/DrawerActionGroup.js +59 -0
- package/dist/es/components/data-table/DrawerEntityCard.js +21 -0
- package/dist/es/components/data-table/DrawerField.js +10 -0
- package/dist/es/components/data-table/DrawerFlagItem.js +20 -0
- package/dist/es/components/data-table/DrawerSection.js +26 -0
- package/dist/es/components/data-table/DrawerShell.js +141 -0
- package/dist/es/components/data-table/DrawerSummaryCard.js +11 -0
- package/dist/es/components/data-table/EmptyState.js +14 -0
- package/dist/es/components/data-table/ExpiryTimestampCell.js +43 -0
- package/dist/es/components/data-table/FilterChip.js +47 -0
- package/dist/es/components/data-table/FilterFormPanel.js +357 -0
- package/dist/es/components/data-table/FilterFormToggle.js +77 -0
- package/dist/es/components/data-table/FilterPopoverFooter.js +19 -0
- package/dist/es/components/data-table/FilterToolbar.js +131 -0
- package/dist/es/components/data-table/FlatColumnToggle.js +46 -0
- package/dist/es/components/data-table/GapIndicator.js +29 -0
- package/dist/es/components/data-table/GroupedColumnToggle.js +112 -0
- package/dist/es/components/data-table/RowActionsMenu.js +74 -0
- package/dist/es/components/data-table/RowControlCell.js +104 -0
- package/dist/es/components/data-table/SortableHeader.js +102 -0
- package/dist/es/components/data-table/TableFooter.js +132 -0
- package/dist/es/components/data-table/TableLayout.js +14 -0
- package/dist/es/components/data-table/TimestampCell.js +16 -0
- package/dist/es/components/data-table/TwoPanelMenu.js +64 -0
- package/dist/es/components/data-table/ViewChip.js +25 -0
- package/dist/es/components/data-table/cells/AmountCell.js +127 -0
- package/dist/es/components/data-table/cells/ConfirmedAmountCell.js +115 -0
- package/dist/es/components/data-table/cells/CurrencyAmountCell.js +26 -0
- package/dist/es/components/data-table/cells/DualLineCell.js +59 -0
- package/dist/es/components/data-table/cells/EnumBadgeCell.js +9 -0
- package/dist/es/components/data-table/cells/MonoIdCell.js +31 -0
- package/dist/es/components/data-table/hooks/use-column-resize.js +39 -0
- package/dist/es/components/data-table/hooks/use-row-drag.js +40 -0
- package/dist/es/components/data-table/hooks/use-table-pagination.js +14 -0
- package/dist/es/components/data-table/hooks/use-table-sort.js +35 -0
- package/dist/es/components/data-table/managed-filters.js +34 -0
- package/dist/es/components/data-table/sticky-utils.js +25 -0
- package/dist/es/components/layout/AppShell.js +113 -0
- package/dist/es/components/layout/Sidebar.js +128 -0
- package/dist/es/components/layout/SidebarAccountMenu.js +129 -0
- package/dist/es/components/layout/SidebarUser.js +38 -0
- package/dist/es/components/layout/TopBar.js +25 -0
- package/dist/es/components/layout/app-shell-context.js +9 -0
- package/dist/es/components/ui/badge.js +31 -0
- package/dist/es/components/ui/button-footer.js +25 -0
- package/dist/es/components/ui/button.js +44 -0
- package/dist/es/components/ui/checkbox.js +24 -0
- package/dist/es/components/ui/combobox.js +179 -0
- package/dist/es/components/ui/dialog.js +80 -0
- package/dist/es/components/ui/dropdown-menu.js +66 -0
- package/dist/es/components/ui/input.js +24 -0
- package/dist/es/components/ui/popover.js +33 -0
- package/dist/es/components/ui/segmented-control.js +60 -0
- package/dist/es/components/ui/select.js +66 -0
- package/dist/es/components/ui/tabs.js +41 -0
- package/dist/es/components/ui/theme-switch.js +47 -0
- package/dist/es/constants/drawer.js +4 -0
- package/dist/es/context/drawer-expand-context.js +10 -0
- package/dist/es/context/portal-container-context.js +10 -0
- package/dist/es/hooks/table-orchestration-store.js +165 -0
- package/dist/es/hooks/use-scroll-activity.js +33 -0
- package/dist/es/hooks/useRowDragOverride.js +18 -0
- package/dist/es/hooks/useTableOrchestration.js +452 -0
- package/dist/es/index.js +199 -0
- package/dist/es/lib/column-gaps.js +23 -0
- package/dist/es/lib/filter-stats.js +67 -0
- package/dist/es/lib/format-enum.js +6 -0
- package/dist/es/lib/format-timestamp.js +37 -0
- package/dist/es/lib/style-scope.js +4 -0
- package/dist/es/lib/utils.js +8 -0
- package/dist/fonts.css +5 -0
- package/dist/preflight.css +502 -0
- package/dist/styles.css +1731 -0
- package/dist/tablewright.cjs +5473 -0
- package/dist/tablewright.css +1579 -0
- package/dist/tokens.css +150 -0
- package/dist/types/components/dashboard/ReportSummaryCard.d.ts +15 -0
- package/dist/types/components/data-table/AppliedStateChips.d.ts +28 -0
- package/dist/types/components/data-table/ColumnFilterPopover.d.ts +14 -0
- package/dist/types/components/data-table/ColumnHeaderMenu.d.ts +27 -0
- package/dist/types/components/data-table/CompoundFilterPopover.d.ts +14 -0
- package/dist/types/components/data-table/CopyButton.d.ts +8 -0
- package/dist/types/components/data-table/CopyableText.d.ts +10 -0
- package/dist/types/components/data-table/DataTableShell.d.ts +30 -0
- package/dist/types/components/data-table/DrawerActionGroup.d.ts +8 -0
- package/dist/types/components/data-table/DrawerEntityCard.d.ts +13 -0
- package/dist/types/components/data-table/DrawerField.d.ts +7 -0
- package/dist/types/components/data-table/DrawerFlagItem.d.ts +10 -0
- package/dist/types/components/data-table/DrawerSection.d.ts +8 -0
- package/dist/types/components/data-table/DrawerShell.d.ts +33 -0
- package/dist/types/components/data-table/DrawerSummaryCard.d.ts +7 -0
- package/dist/types/components/data-table/EmptyState.d.ts +6 -0
- package/dist/types/components/data-table/ExpiryTimestampCell.d.ts +6 -0
- package/dist/types/components/data-table/FilterChip.d.ts +8 -0
- package/dist/types/components/data-table/FilterFormPanel.d.ts +34 -0
- package/dist/types/components/data-table/FilterFormToggle.d.ts +18 -0
- package/dist/types/components/data-table/FilterPopoverFooter.d.ts +9 -0
- package/dist/types/components/data-table/FilterToolbar.d.ts +29 -0
- package/dist/types/components/data-table/FlatColumnToggle.d.ts +10 -0
- package/dist/types/components/data-table/GapIndicator.d.ts +11 -0
- package/dist/types/components/data-table/GroupedColumnToggle.d.ts +12 -0
- package/dist/types/components/data-table/RowActionsMenu.d.ts +10 -0
- package/dist/types/components/data-table/RowControlCell.d.ts +16 -0
- package/dist/types/components/data-table/SortableHeader.d.ts +16 -0
- package/dist/types/components/data-table/TableFooter.d.ts +31 -0
- package/dist/types/components/data-table/TableLayout.d.ts +12 -0
- package/dist/types/components/data-table/TimestampCell.d.ts +3 -0
- package/dist/types/components/data-table/TwoPanelMenu.d.ts +22 -0
- package/dist/types/components/data-table/ViewChip.d.ts +6 -0
- package/dist/types/components/data-table/cells/AmountCell.d.ts +11 -0
- package/dist/types/components/data-table/cells/ConfirmedAmountCell.d.ts +10 -0
- package/dist/types/components/data-table/cells/CurrencyAmountCell.d.ts +8 -0
- package/dist/types/components/data-table/cells/DualLineCell.d.ts +44 -0
- package/dist/types/components/data-table/cells/EnumBadgeCell.d.ts +9 -0
- package/dist/types/components/data-table/cells/MonoIdCell.d.ts +19 -0
- package/dist/types/components/data-table/filter-types.d.ts +79 -0
- package/dist/types/components/data-table/hooks/use-column-resize.d.ts +7 -0
- package/dist/types/components/data-table/hooks/use-row-drag.d.ts +14 -0
- package/dist/types/components/data-table/hooks/use-table-pagination.d.ts +10 -0
- package/dist/types/components/data-table/hooks/use-table-sort.d.ts +6 -0
- package/dist/types/components/data-table/managed-filters.d.ts +18 -0
- package/dist/types/components/data-table/sticky-utils.d.ts +4 -0
- package/dist/types/components/data-table/types.d.ts +35 -0
- package/dist/types/components/layout/AppShell.d.ts +23 -0
- package/dist/types/components/layout/Sidebar.d.ts +16 -0
- package/dist/types/components/layout/SidebarAccountMenu.d.ts +20 -0
- package/dist/types/components/layout/SidebarUser.d.ts +8 -0
- package/dist/types/components/layout/TopBar.d.ts +7 -0
- package/dist/types/components/layout/app-shell-context.d.ts +8 -0
- package/dist/types/components/layout/index.d.ts +10 -0
- package/dist/types/components/layout/types.d.ts +11 -0
- package/dist/types/components/ui/badge.d.ts +9 -0
- package/dist/types/components/ui/button-footer.d.ts +7 -0
- package/dist/types/components/ui/button.d.ts +11 -0
- package/dist/types/components/ui/checkbox.d.ts +4 -0
- package/dist/types/components/ui/combobox.d.ts +92 -0
- package/dist/types/components/ui/dialog.d.ts +15 -0
- package/dist/types/components/ui/dropdown-menu.d.ts +10 -0
- package/dist/types/components/ui/input.d.ts +6 -0
- package/dist/types/components/ui/popover.d.ts +7 -0
- package/dist/types/components/ui/segmented-control.d.ts +17 -0
- package/dist/types/components/ui/select.d.ts +8 -0
- package/dist/types/components/ui/tabs.d.ts +10 -0
- package/dist/types/components/ui/theme-switch.d.ts +12 -0
- package/dist/types/constants/drawer.d.ts +1 -0
- package/dist/types/context/drawer-expand-context.d.ts +7 -0
- package/dist/types/context/portal-container-context.d.ts +14 -0
- package/dist/types/hooks/table-orchestration-store.d.ts +75 -0
- package/dist/types/hooks/use-scroll-activity.d.ts +1 -0
- package/dist/types/hooks/useDemoTheme.d.ts +8 -0
- package/dist/types/hooks/useRowDragOverride.d.ts +6 -0
- package/dist/types/hooks/useTableOrchestration.d.ts +115 -0
- package/dist/types/index.d.ts +107 -0
- package/dist/types/lib/column-gaps.d.ts +3 -0
- package/dist/types/lib/filter-stats.d.ts +2 -0
- package/dist/types/lib/format-enum.d.ts +1 -0
- package/dist/types/lib/format-timestamp.d.ts +4 -0
- package/dist/types/lib/style-scope.d.ts +19 -0
- package/dist/types/lib/utils.d.ts +2 -0
- package/package.json +114 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CopyButton } from "./CopyButton.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
function CopyableText({
|
|
5
|
+
value,
|
|
6
|
+
children,
|
|
7
|
+
className,
|
|
8
|
+
textClassName,
|
|
9
|
+
buttonClassName,
|
|
10
|
+
title = "Copy value"
|
|
11
|
+
}) {
|
|
12
|
+
return /* @__PURE__ */ jsxs("span", { className: cn("group/copy inline-flex min-w-0 items-center gap-1", className), children: [
|
|
13
|
+
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 truncate", textClassName), children: children ?? value }),
|
|
14
|
+
/* @__PURE__ */ jsx(
|
|
15
|
+
CopyButton,
|
|
16
|
+
{
|
|
17
|
+
text: value,
|
|
18
|
+
title,
|
|
19
|
+
className: cn("group-hover/copy:opacity-100 group-focus-within/copy:opacity-100", buttonClassName)
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
] });
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
CopyableText
|
|
26
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { flexRender } from "@tanstack/react-table";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { DrawerShell } from "./DrawerShell.js";
|
|
5
|
+
import { EmptyState } from "./EmptyState.js";
|
|
6
|
+
import { TableLayout } from "./TableLayout.js";
|
|
7
|
+
import { stickyStyle, stickyBorderClass } from "./sticky-utils.js";
|
|
8
|
+
function DataTableShell({
|
|
9
|
+
table,
|
|
10
|
+
dense,
|
|
11
|
+
emptyMessage,
|
|
12
|
+
columnMeta,
|
|
13
|
+
columnWidths,
|
|
14
|
+
onColumnResizeStart,
|
|
15
|
+
onRowClick,
|
|
16
|
+
getRowIsActive,
|
|
17
|
+
rowDrag,
|
|
18
|
+
footer,
|
|
19
|
+
drawer,
|
|
20
|
+
className
|
|
21
|
+
}) {
|
|
22
|
+
const visibleTableWidth = table.getVisibleLeafColumns().reduce(
|
|
23
|
+
(sum, column) => sum + (columnWidths[column.id] ?? columnMeta[column.id]?.minW ?? 120),
|
|
24
|
+
0
|
|
25
|
+
);
|
|
26
|
+
return /* @__PURE__ */ jsxs(TableLayout, { className: cn("min-h-0", className), children: [
|
|
27
|
+
/* @__PURE__ */ jsx(TableLayout.ScrollArea, { className: "[scrollbar-gutter:stable]", children: /* @__PURE__ */ jsxs(
|
|
28
|
+
"table",
|
|
29
|
+
{
|
|
30
|
+
className: "w-full border-collapse",
|
|
31
|
+
style: { tableLayout: "fixed", minWidth: `${visibleTableWidth}px` },
|
|
32
|
+
children: [
|
|
33
|
+
/* @__PURE__ */ jsx("thead", { className: "sticky top-0 z-10 bg-[var(--bg-surface)]", children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx("tr", { children: headerGroup.headers.map((header) => {
|
|
34
|
+
const columnId = header.column.id;
|
|
35
|
+
const meta = columnMeta[columnId];
|
|
36
|
+
const isActionsColumn = meta?.sticky === "right";
|
|
37
|
+
const isControlColumn = meta?.variant === "control";
|
|
38
|
+
return /* @__PURE__ */ jsxs(
|
|
39
|
+
"th",
|
|
40
|
+
{
|
|
41
|
+
className: cn(
|
|
42
|
+
"group/th relative border-b border-[var(--border-subtle)] bg-[var(--bg-surface)] px-4 text-left text-[12px] font-medium text-[var(--text-secondary)]",
|
|
43
|
+
dense ? "py-1.5" : "py-3",
|
|
44
|
+
isActionsColumn && "px-3 text-right",
|
|
45
|
+
isControlColumn && "px-1 text-center",
|
|
46
|
+
stickyBorderClass(meta)
|
|
47
|
+
),
|
|
48
|
+
style: {
|
|
49
|
+
width: columnWidths[columnId],
|
|
50
|
+
minWidth: meta?.minW ?? 36,
|
|
51
|
+
...stickyStyle(meta, true)
|
|
52
|
+
},
|
|
53
|
+
children: [
|
|
54
|
+
header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()),
|
|
55
|
+
header.column.columnDef.enableResizing !== false ? /* @__PURE__ */ jsx(
|
|
56
|
+
"div",
|
|
57
|
+
{
|
|
58
|
+
className: "absolute bottom-0 right-0 top-0 w-px cursor-col-resize bg-[var(--border-subtle)] opacity-30 transition-[opacity,width,background-color] duration-150 group-hover/th:w-[4px] group-hover/th:bg-[var(--border-focus)] group-hover/th:opacity-50 hover:!opacity-100",
|
|
59
|
+
onPointerDown: (event) => onColumnResizeStart(columnId, event)
|
|
60
|
+
}
|
|
61
|
+
) : null
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
header.id
|
|
65
|
+
);
|
|
66
|
+
}) }, headerGroup.id)) }),
|
|
67
|
+
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
68
|
+
table.getRowModel().rows.map((row) => {
|
|
69
|
+
const isActive = getRowIsActive?.(row.original) ?? false;
|
|
70
|
+
const dragProps = rowDrag?.getDragProps(row.index);
|
|
71
|
+
const isDragged = rowDrag?.dragRowIdx === row.index;
|
|
72
|
+
const isDropTarget = rowDrag != null && rowDrag.dropTargetIdx === row.index && rowDrag.dragRowIdx !== null && rowDrag.dragRowIdx !== row.index;
|
|
73
|
+
return /* @__PURE__ */ jsx(
|
|
74
|
+
"tr",
|
|
75
|
+
{
|
|
76
|
+
...dragProps,
|
|
77
|
+
onClick: onRowClick ? () => onRowClick(row.original) : void 0,
|
|
78
|
+
className: cn(
|
|
79
|
+
"group border-b border-[var(--border-subtle)] transition-colors",
|
|
80
|
+
onRowClick && "cursor-pointer hover:bg-[var(--bg-hover)]",
|
|
81
|
+
isActive && "bg-[var(--bg-hover)]",
|
|
82
|
+
rowDrag && "cursor-grab active:cursor-grabbing",
|
|
83
|
+
isDragged && "opacity-40",
|
|
84
|
+
isDropTarget && "border-t-2 border-t-[var(--border-focus)]"
|
|
85
|
+
),
|
|
86
|
+
children: row.getVisibleCells().map((cell, cellIndex) => {
|
|
87
|
+
const meta = columnMeta[cell.column.id];
|
|
88
|
+
const isActionsColumn = meta?.sticky === "right";
|
|
89
|
+
const isControlColumn = meta?.variant === "control";
|
|
90
|
+
return /* @__PURE__ */ jsx(
|
|
91
|
+
"td",
|
|
92
|
+
{
|
|
93
|
+
className: cn(
|
|
94
|
+
"overflow-hidden px-4 text-[var(--text-primary)]",
|
|
95
|
+
dense ? "py-1 text-xs leading-tight" : "py-4 text-sm",
|
|
96
|
+
meta?.sticky && (isActive ? "bg-[var(--bg-hover)]" : "bg-[var(--bg-surface)] group-hover:bg-[var(--bg-hover)]"),
|
|
97
|
+
isActionsColumn && "px-3 align-middle text-right",
|
|
98
|
+
isControlColumn && "px-1 text-center",
|
|
99
|
+
cellIndex === 0 && isActive && "relative before:absolute before:bottom-0 before:left-0 before:top-0 before:w-[3px] before:bg-[var(--accent-primary)] before:content-['']",
|
|
100
|
+
stickyBorderClass(meta)
|
|
101
|
+
),
|
|
102
|
+
style: {
|
|
103
|
+
width: columnWidths[cell.column.id],
|
|
104
|
+
minWidth: meta?.minW ?? 36,
|
|
105
|
+
maxWidth: columnWidths[cell.column.id],
|
|
106
|
+
...stickyStyle(meta, false)
|
|
107
|
+
},
|
|
108
|
+
children: flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
109
|
+
},
|
|
110
|
+
cell.id
|
|
111
|
+
);
|
|
112
|
+
})
|
|
113
|
+
},
|
|
114
|
+
row.id
|
|
115
|
+
);
|
|
116
|
+
}),
|
|
117
|
+
table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx(
|
|
118
|
+
EmptyState,
|
|
119
|
+
{
|
|
120
|
+
message: emptyMessage,
|
|
121
|
+
colSpan: table.getVisibleLeafColumns().length
|
|
122
|
+
}
|
|
123
|
+
) : null
|
|
124
|
+
] })
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
) }),
|
|
128
|
+
footer,
|
|
129
|
+
drawer ? /* @__PURE__ */ jsx(
|
|
130
|
+
DrawerShell,
|
|
131
|
+
{
|
|
132
|
+
open: drawer.open,
|
|
133
|
+
onClose: drawer.onClose,
|
|
134
|
+
title: drawer.title,
|
|
135
|
+
subtitle: drawer.subtitle,
|
|
136
|
+
headerExtra: drawer.headerExtra,
|
|
137
|
+
mode: drawer.mode,
|
|
138
|
+
children: drawer.content
|
|
139
|
+
}
|
|
140
|
+
) : null
|
|
141
|
+
] });
|
|
142
|
+
}
|
|
143
|
+
export {
|
|
144
|
+
DataTableShell
|
|
145
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
function DrawerActionGroup({ sections, variant, columns }) {
|
|
4
|
+
if (variant === "flat") {
|
|
5
|
+
const actions = sections.flatMap((s) => s.actions);
|
|
6
|
+
return /* @__PURE__ */ jsx("div", { className: cn("grid gap-2", (columns ?? 2) === 4 ? "grid-cols-4" : "grid-cols-2"), children: actions.map((action) => /* @__PURE__ */ jsxs(
|
|
7
|
+
"button",
|
|
8
|
+
{
|
|
9
|
+
type: "button",
|
|
10
|
+
onClick: (e) => {
|
|
11
|
+
e.stopPropagation();
|
|
12
|
+
action.onClick(e);
|
|
13
|
+
},
|
|
14
|
+
disabled: action.disabled,
|
|
15
|
+
className: "flex items-center gap-2 rounded-lg border border-[var(--border-subtle)] bg-[var(--bg-surface)] px-3 py-2 text-[13px] font-medium text-[var(--text-primary)] transition-colors hover:bg-[var(--bg-hover)] disabled:cursor-not-allowed disabled:opacity-40",
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ jsx(action.icon, { className: "h-4 w-4 shrink-0 text-[var(--text-secondary)]" }),
|
|
18
|
+
action.label
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
action.label
|
|
22
|
+
)) });
|
|
23
|
+
}
|
|
24
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: sections.map((section) => /* @__PURE__ */ jsxs(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
className: "rounded-xl border border-[var(--border-subtle)] bg-[var(--bg-secondary)] p-3",
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 text-[10px] font-semibold uppercase tracking-[0.12em] text-[var(--text-tertiary)]", children: section.label }),
|
|
30
|
+
/* @__PURE__ */ jsx("div", { className: cn("grid gap-2", (columns ?? 2) === 4 ? "grid-cols-4" : "grid-cols-2"), children: section.actions.map((action) => /* @__PURE__ */ jsxs(
|
|
31
|
+
"button",
|
|
32
|
+
{
|
|
33
|
+
type: "button",
|
|
34
|
+
onClick: (e) => {
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
action.onClick(e);
|
|
37
|
+
},
|
|
38
|
+
disabled: action.disabled,
|
|
39
|
+
className: "flex items-center gap-2 rounded-lg border border-[var(--border-subtle)] bg-[var(--bg-surface)] px-3 py-2 text-[13px] font-medium text-[var(--text-primary)] transition-colors hover:bg-[var(--bg-hover)] disabled:cursor-not-allowed disabled:opacity-40",
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsx(
|
|
42
|
+
action.icon,
|
|
43
|
+
{
|
|
44
|
+
className: action.variant === "danger" ? "h-4 w-4 shrink-0 text-[var(--tag-pink-text)]" : action.variant === "accent" ? "h-4 w-4 shrink-0 text-[var(--text-primary)]" : "h-4 w-4 shrink-0 text-[var(--text-secondary)]"
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
/* @__PURE__ */ jsx("span", { children: action.label })
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
action.label
|
|
51
|
+
)) })
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
section.label
|
|
55
|
+
)) });
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
DrawerActionGroup
|
|
59
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CopyableText } from "./CopyableText.js";
|
|
3
|
+
function DrawerEntityCard({ name, id, children }) {
|
|
4
|
+
const headerLayout = id ? "grid grid-cols-2 gap-x-4 items-center" : "flex";
|
|
5
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-[var(--bg-hover)] p-3.5", children: [
|
|
6
|
+
/* @__PURE__ */ jsxs("div", { className: `mb-2.5 border-b border-[var(--border-subtle)] pb-2 ${headerLayout}`, children: [
|
|
7
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[var(--text-primary)]", children: name }),
|
|
8
|
+
id ? /* @__PURE__ */ jsx(
|
|
9
|
+
CopyableText,
|
|
10
|
+
{
|
|
11
|
+
value: id,
|
|
12
|
+
textClassName: "font-mono text-[11px] text-[var(--text-secondary)]"
|
|
13
|
+
}
|
|
14
|
+
) : null
|
|
15
|
+
] }),
|
|
16
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-x-4", children })
|
|
17
|
+
] });
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
DrawerEntityCard
|
|
21
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
function DrawerField({ label, value, children }) {
|
|
3
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[120px_1fr] items-center text-[13px]", children: [
|
|
4
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-secondary)]", children: label }),
|
|
5
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-hidden text-[var(--text-primary)]", children: children ?? value })
|
|
6
|
+
] });
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
DrawerField
|
|
10
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
function DrawerFlagGrid({ children }) {
|
|
3
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-x-4 gap-y-3", children });
|
|
4
|
+
}
|
|
5
|
+
function DrawerFlagItem({ label, value }) {
|
|
6
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
7
|
+
/* @__PURE__ */ jsx("div", { className: "mb-0.5 text-[11px] text-[var(--text-secondary)]", children: label }),
|
|
8
|
+
/* @__PURE__ */ jsx(
|
|
9
|
+
"div",
|
|
10
|
+
{
|
|
11
|
+
className: value ? "text-[13px] font-semibold text-[var(--tag-green-text)]" : "text-[13px] font-semibold text-[var(--text-primary)]",
|
|
12
|
+
children: value ? "Yes" : "No"
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
] });
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
DrawerFlagGrid,
|
|
19
|
+
DrawerFlagItem
|
|
20
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
function DrawerSection({ title, children, className, card = false }) {
|
|
4
|
+
if (card) {
|
|
5
|
+
return /* @__PURE__ */ jsxs(
|
|
6
|
+
"div",
|
|
7
|
+
{
|
|
8
|
+
className: cn(
|
|
9
|
+
"rounded-lg border border-[var(--border-subtle)] bg-[var(--bg-surface)] p-4",
|
|
10
|
+
className
|
|
11
|
+
),
|
|
12
|
+
children: [
|
|
13
|
+
/* @__PURE__ */ jsx("p", { className: "mb-3 text-[10px] font-semibold uppercase tracking-wider text-[var(--text-tertiary)]", children: title }),
|
|
14
|
+
children
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return /* @__PURE__ */ jsxs("section", { className: className ?? "mb-8", children: [
|
|
20
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-4 text-[11px] font-semibold uppercase tracking-[0.05em] text-[var(--text-secondary)]", children: title }),
|
|
21
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-4", children })
|
|
22
|
+
] });
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
DrawerSection
|
|
26
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
|
+
import { Minimize2, Maximize2, X } from "lucide-react";
|
|
4
|
+
import { Button } from "../ui/button.js";
|
|
5
|
+
import { cn } from "../../lib/utils.js";
|
|
6
|
+
import { useScrollActivity } from "../../hooks/use-scroll-activity.js";
|
|
7
|
+
import { DrawerExpandContext } from "../../context/drawer-expand-context.js";
|
|
8
|
+
function DrawerShell({
|
|
9
|
+
open,
|
|
10
|
+
onClose,
|
|
11
|
+
title,
|
|
12
|
+
subtitle,
|
|
13
|
+
headerExtra,
|
|
14
|
+
children,
|
|
15
|
+
footer,
|
|
16
|
+
dismissible = false,
|
|
17
|
+
mode = "side"
|
|
18
|
+
}) {
|
|
19
|
+
const [expandedState, setExpandedState] = useState(false);
|
|
20
|
+
const bodyScrollRef = useScrollActivity();
|
|
21
|
+
const isExpanded = mode === "modal" ? true : mode === "side" ? false : expandedState;
|
|
22
|
+
const canToggle = mode === "side+modal";
|
|
23
|
+
const handleClose = () => {
|
|
24
|
+
setExpandedState(false);
|
|
25
|
+
onClose();
|
|
26
|
+
};
|
|
27
|
+
const closeRef = useRef(handleClose);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
closeRef.current = handleClose;
|
|
30
|
+
});
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!open || !dismissible) return;
|
|
33
|
+
const onKeyDown = (event) => {
|
|
34
|
+
if (event.key !== "Escape" || event.defaultPrevented) return;
|
|
35
|
+
const target = event.target;
|
|
36
|
+
if (target?.closest?.("[role=dialog],[role=menu]")) return;
|
|
37
|
+
closeRef.current();
|
|
38
|
+
};
|
|
39
|
+
window.addEventListener("keydown", onKeyDown);
|
|
40
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
41
|
+
}, [open, dismissible]);
|
|
42
|
+
const header = /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-start justify-between border-b border-[var(--border-subtle)] px-6 py-6", children: [
|
|
43
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
44
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
45
|
+
title ? /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--text-primary)]", children: title }) : null,
|
|
46
|
+
headerExtra
|
|
47
|
+
] }),
|
|
48
|
+
subtitle && /* @__PURE__ */ jsx("div", { className: "mt-1 text-[11px] text-[var(--text-secondary)]", children: subtitle })
|
|
49
|
+
] }),
|
|
50
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
51
|
+
canToggle && (isExpanded ? /* @__PURE__ */ jsx(
|
|
52
|
+
Button,
|
|
53
|
+
{
|
|
54
|
+
variant: "ghost",
|
|
55
|
+
size: "icon",
|
|
56
|
+
className: "h-8 w-8 rounded-sm",
|
|
57
|
+
onClick: () => setExpandedState(false),
|
|
58
|
+
title: "Collapse to panel",
|
|
59
|
+
children: /* @__PURE__ */ jsx(Minimize2, { className: "h-4 w-4" })
|
|
60
|
+
}
|
|
61
|
+
) : /* @__PURE__ */ jsx(
|
|
62
|
+
Button,
|
|
63
|
+
{
|
|
64
|
+
variant: "ghost",
|
|
65
|
+
size: "icon",
|
|
66
|
+
className: "h-8 w-8 rounded-sm",
|
|
67
|
+
onClick: () => setExpandedState(true),
|
|
68
|
+
title: "Expand to full view",
|
|
69
|
+
children: /* @__PURE__ */ jsx(Maximize2, { className: "h-4 w-4" })
|
|
70
|
+
}
|
|
71
|
+
)),
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
Button,
|
|
74
|
+
{
|
|
75
|
+
variant: "ghost",
|
|
76
|
+
size: "icon",
|
|
77
|
+
"aria-label": "Close",
|
|
78
|
+
className: "h-8 w-8 rounded-sm",
|
|
79
|
+
onClick: handleClose,
|
|
80
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
] })
|
|
84
|
+
] });
|
|
85
|
+
const body = /* @__PURE__ */ jsx(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
ref: bodyScrollRef,
|
|
89
|
+
className: "scrollbar-auto-hide min-h-0 flex-1 overflow-y-auto px-6 py-6",
|
|
90
|
+
children
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
const pinnedFooter = footer ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-[var(--border-subtle)] px-6 py-4", children: footer }) : null;
|
|
94
|
+
return /* @__PURE__ */ jsxs(DrawerExpandContext.Provider, { value: isExpanded, children: [
|
|
95
|
+
!isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
96
|
+
dismissible && /* @__PURE__ */ jsx(
|
|
97
|
+
"div",
|
|
98
|
+
{
|
|
99
|
+
className: cn(
|
|
100
|
+
"absolute inset-0 z-30 bg-[var(--overlay-scrim,rgba(15,23,42,0.28))] transition-opacity duration-300",
|
|
101
|
+
open ? "opacity-100" : "pointer-events-none opacity-0"
|
|
102
|
+
),
|
|
103
|
+
onClick: handleClose,
|
|
104
|
+
"aria-hidden": true
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
/* @__PURE__ */ jsxs(
|
|
108
|
+
"aside",
|
|
109
|
+
{
|
|
110
|
+
"aria-label": "Details panel",
|
|
111
|
+
className: cn(
|
|
112
|
+
"absolute inset-y-0 right-0 z-40 flex w-[var(--size-drawer)] flex-col border-l border-[var(--border-subtle)] bg-[var(--bg-surface)] transition-transform duration-300 ease-[cubic-bezier(0.16,1,0.3,1)]",
|
|
113
|
+
open ? "translate-x-0 [box-shadow:var(--shadow-drawer)]" : "translate-x-full shadow-none"
|
|
114
|
+
),
|
|
115
|
+
children: [
|
|
116
|
+
header,
|
|
117
|
+
body,
|
|
118
|
+
pinnedFooter
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
] }),
|
|
123
|
+
isExpanded && open && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
"div",
|
|
126
|
+
{
|
|
127
|
+
className: "fixed inset-0 z-50 bg-black/50 animate-in fade-in-0 duration-200",
|
|
128
|
+
onClick: handleClose
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
/* @__PURE__ */ jsxs("div", { className: "fixed left-1/2 top-[10vh] z-50 flex h-[80vh] w-[min(75vw,1400px)] -translate-x-1/2 flex-col overflow-hidden rounded-xl border border-[var(--border-subtle)] bg-[var(--bg-surface)] shadow-2xl animate-in fade-in-0 zoom-in-95 duration-200", children: [
|
|
132
|
+
header,
|
|
133
|
+
body,
|
|
134
|
+
pinnedFooter
|
|
135
|
+
] })
|
|
136
|
+
] })
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
DrawerShell
|
|
141
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
function DrawerSummaryCard({ label, value, sub }) {
|
|
3
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-[var(--bg-hover)] rounded-lg p-3", children: [
|
|
4
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--text-secondary)] mb-1", children: label }),
|
|
5
|
+
/* @__PURE__ */ jsx("div", { className: "text-lg font-semibold text-[var(--text-primary)]", children: value }),
|
|
6
|
+
sub !== void 0 && /* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--text-secondary)] mt-0.5", children: sub })
|
|
7
|
+
] });
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
DrawerSummaryCard
|
|
11
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
function EmptyState({ message, colSpan }) {
|
|
3
|
+
return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
4
|
+
"td",
|
|
5
|
+
{
|
|
6
|
+
colSpan,
|
|
7
|
+
className: "px-6 py-16 text-center text-[13px] text-[var(--text-secondary)]",
|
|
8
|
+
children: message
|
|
9
|
+
}
|
|
10
|
+
) });
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
EmptyState
|
|
14
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
function splitTimestamp(value) {
|
|
3
|
+
if (!value) return { date: "—", time: "" };
|
|
4
|
+
const [date, time] = value.split(" ");
|
|
5
|
+
return { date: date ?? "—", time: time ?? "" };
|
|
6
|
+
}
|
|
7
|
+
function getUrgency(timestampMs) {
|
|
8
|
+
const diff = timestampMs - Date.now();
|
|
9
|
+
if (diff <= 0) return "expired";
|
|
10
|
+
if (diff < 5 * 60 * 1e3) return "danger";
|
|
11
|
+
if (diff < 30 * 60 * 1e3) return "warning";
|
|
12
|
+
return "ok";
|
|
13
|
+
}
|
|
14
|
+
function formatRelative(timestampMs) {
|
|
15
|
+
const diff = timestampMs - Date.now();
|
|
16
|
+
if (diff <= -864e5) return null;
|
|
17
|
+
if (diff <= 0) return "Expired";
|
|
18
|
+
const totalMinutes = Math.ceil(diff / 6e4);
|
|
19
|
+
if (totalMinutes < 60) return `in ${totalMinutes} min`;
|
|
20
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
21
|
+
return `in ${hours} h`;
|
|
22
|
+
}
|
|
23
|
+
const urgencyClass = {
|
|
24
|
+
expired: "text-red-600",
|
|
25
|
+
danger: "text-red-600",
|
|
26
|
+
warning: "text-[var(--tag-orange-text)]",
|
|
27
|
+
ok: "text-[var(--text-secondary)]"
|
|
28
|
+
};
|
|
29
|
+
function ExpiryTimestampCell({ value, timestampMs }) {
|
|
30
|
+
const { date, time } = splitTimestamp(value);
|
|
31
|
+
const showUrgency = timestampMs !== void 0 && timestampMs > 0;
|
|
32
|
+
const ts = timestampMs ?? 0;
|
|
33
|
+
const urgency = showUrgency ? getUrgency(ts) : "ok";
|
|
34
|
+
const relativeLabel = showUrgency ? formatRelative(ts) : null;
|
|
35
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-left font-mono", children: [
|
|
36
|
+
/* @__PURE__ */ jsx("div", { className: "text-[13px] font-medium text-[var(--text-primary)]", children: date }),
|
|
37
|
+
time ? /* @__PURE__ */ jsx("div", { className: "text-[12px] text-[var(--text-secondary)]", children: time }) : null,
|
|
38
|
+
relativeLabel && /* @__PURE__ */ jsx("div", { className: `text-[11px] ${urgencyClass[urgency]}`, children: relativeLabel })
|
|
39
|
+
] });
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
ExpiryTimestampCell
|
|
43
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { X } from "lucide-react";
|
|
4
|
+
import { cn } from "../../lib/utils.js";
|
|
5
|
+
const FilterChip = React.forwardRef(
|
|
6
|
+
({ label, value, onRemove, active, children, className, ...rest }, ref) => {
|
|
7
|
+
return /* @__PURE__ */ jsxs(
|
|
8
|
+
"div",
|
|
9
|
+
{
|
|
10
|
+
ref,
|
|
11
|
+
...rest,
|
|
12
|
+
className: cn(
|
|
13
|
+
"flex h-[var(--size-control-md)] items-center rounded-md border border-[var(--border-subtle)] text-[13px] transition-colors",
|
|
14
|
+
active ? "bg-[var(--bg-secondary)]" : "bg-[var(--bg-surface)]",
|
|
15
|
+
className
|
|
16
|
+
),
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-full items-center pl-2.5 pr-1.5 cursor-pointer", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
19
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-secondary)]", children: label }),
|
|
20
|
+
value && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-tertiary)]", children: ":" }),
|
|
22
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-primary)] font-medium", children: value })
|
|
23
|
+
] }),
|
|
24
|
+
children
|
|
25
|
+
] }) }),
|
|
26
|
+
/* @__PURE__ */ jsx("div", { className: "h-full flex items-center pr-1.5", children: /* @__PURE__ */ jsx(
|
|
27
|
+
"button",
|
|
28
|
+
{
|
|
29
|
+
type: "button",
|
|
30
|
+
onClick: (e) => {
|
|
31
|
+
e.stopPropagation();
|
|
32
|
+
onRemove();
|
|
33
|
+
},
|
|
34
|
+
className: "flex h-5 w-5 items-center justify-center rounded-sm text-[var(--text-tertiary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)] focus:outline-none focus:ring-1 focus:ring-[var(--border-focus)]",
|
|
35
|
+
"aria-label": `Remove ${label} filter`,
|
|
36
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
37
|
+
}
|
|
38
|
+
) })
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
FilterChip.displayName = "FilterChip";
|
|
45
|
+
export {
|
|
46
|
+
FilterChip
|
|
47
|
+
};
|