@vertebr/react 0.18.0 → 0.19.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.
Files changed (60) hide show
  1. package/dist/components/admin/AdminContent.d.ts +5 -1
  2. package/dist/components/admin/AdminContent.d.ts.map +1 -1
  3. package/dist/components/admin/AdminContent.js +75 -50
  4. package/dist/components/admin/AuditBoard.d.ts +6 -2
  5. package/dist/components/admin/AuditBoard.d.ts.map +1 -1
  6. package/dist/components/admin/AuditBoard.js +145 -3
  7. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  8. package/dist/components/admin/VertebrAdmin.js +14 -3
  9. package/dist/components/config/EnumInput.d.ts.map +1 -1
  10. package/dist/components/config/EnumInput.js +9 -2
  11. package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
  12. package/dist/components/config/form/ConfigForm.js +14 -2
  13. package/dist/components/emails/EmailsActivityPanel.d.ts.map +1 -1
  14. package/dist/components/emails/EmailsActivityPanel.js +80 -2
  15. package/dist/components/metrics/MetricsBoard.d.ts.map +1 -1
  16. package/dist/components/metrics/MetricsBoard.js +7 -1
  17. package/dist/components/resources/ColumnVisibilityMenu.d.ts +4 -0
  18. package/dist/components/resources/ColumnVisibilityMenu.d.ts.map +1 -1
  19. package/dist/components/resources/ColumnVisibilityMenu.js +14 -7
  20. package/dist/components/resources/ResourceTable.d.ts +7 -1
  21. package/dist/components/resources/ResourceTable.d.ts.map +1 -1
  22. package/dist/components/resources/ResourceTable.js +84 -64
  23. package/dist/components/resources/SortableHeader.d.ts +9 -0
  24. package/dist/components/resources/SortableHeader.d.ts.map +1 -0
  25. package/dist/components/resources/SortableHeader.js +9 -0
  26. package/dist/components/table/TableFilters.d.ts +20 -0
  27. package/dist/components/table/TableFilters.d.ts.map +1 -0
  28. package/dist/components/table/TableFilters.js +55 -0
  29. package/dist/components/table/TableSearch.d.ts +11 -0
  30. package/dist/components/table/TableSearch.d.ts.map +1 -0
  31. package/dist/components/table/TableSearch.js +23 -0
  32. package/dist/components/table/TableSkeleton.d.ts +16 -0
  33. package/dist/components/table/TableSkeleton.d.ts.map +1 -0
  34. package/dist/components/table/TableSkeleton.js +29 -0
  35. package/dist/components/table/TableWrap.d.ts +23 -0
  36. package/dist/components/table/TableWrap.d.ts.map +1 -0
  37. package/dist/components/table/TableWrap.js +35 -0
  38. package/dist/components/table/filterModel.d.ts +35 -0
  39. package/dist/components/table/filterModel.d.ts.map +1 -0
  40. package/dist/components/table/filterModel.js +150 -0
  41. package/dist/components/table/index.d.ts +13 -0
  42. package/dist/components/table/index.d.ts.map +1 -0
  43. package/dist/components/table/index.js +6 -0
  44. package/dist/components/table/useTableOverflow.d.ts +17 -0
  45. package/dist/components/table/useTableOverflow.d.ts.map +1 -0
  46. package/dist/components/table/useTableOverflow.js +54 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +2 -0
  50. package/dist/lib/sort.d.ts +10 -0
  51. package/dist/lib/sort.d.ts.map +1 -0
  52. package/dist/lib/sort.js +59 -0
  53. package/dist/styles/audit.css +11 -1
  54. package/dist/styles/columns.css +1 -84
  55. package/dist/styles/metrics.css +27 -4
  56. package/dist/styles/panel.css +143 -0
  57. package/dist/styles/select.css +16 -0
  58. package/dist/styles/sync.css +4 -1
  59. package/dist/styles/table.css +115 -2
  60. package/package.json +1 -1
@@ -1,5 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from "react";
3
+ import { SortableHeader } from "../resources/SortableHeader";
4
+ import { TableScrollControls, TableSearch, TableWrap, matchesTableQuery, useTableOverflow, } from "../table";
5
+ import { compareFieldValues, nextTableSort, } from "../../lib/sort";
3
6
  function formatWhen(iso) {
4
7
  const ms = Date.parse(iso);
5
8
  if (!Number.isFinite(ms))
@@ -76,8 +79,47 @@ function statusBadge(status) {
76
79
  function CellEmpty() {
77
80
  return _jsx("span", { className: "vertebr-cell-empty", children: "\u2014" });
78
81
  }
82
+ function activitySortValue(row, key) {
83
+ if (row.kind === "scheduled") {
84
+ const campaign = row.campaign;
85
+ switch (key) {
86
+ case "when":
87
+ return campaign.sendAt;
88
+ case "status":
89
+ return campaign.status;
90
+ case "subject":
91
+ return campaign.subject ?? "";
92
+ case "to":
93
+ return String(campaign.recipientCount ?? "");
94
+ case "audience":
95
+ return campaign.resourceLabel ?? campaign.resourceId;
96
+ case "template":
97
+ return campaign.templateLabel ?? campaign.templateId;
98
+ }
99
+ }
100
+ const entry = row.entry;
101
+ switch (key) {
102
+ case "when":
103
+ return entry.at;
104
+ case "status":
105
+ return entry.status;
106
+ case "subject":
107
+ return subjectOf(entry);
108
+ case "to":
109
+ return recipientOf(entry);
110
+ case "audience":
111
+ return entry.resourceLabel ?? entry.resourceId;
112
+ case "template":
113
+ return entry.templateLabel ?? entry.templateId;
114
+ }
115
+ }
79
116
  export function EmailsActivityPanel({ campaigns, history, canSchedule, canHistory, busy, onCancel, }) {
80
117
  const [filter, setFilter] = useState("all");
118
+ const [query, setQuery] = useState("");
119
+ const [sort, setSort] = useState({
120
+ field: "when",
121
+ order: "desc",
122
+ });
81
123
  const upcoming = useMemo(() => campaigns
82
124
  .filter((campaign) => campaign.status === "scheduled")
83
125
  .slice()
@@ -99,6 +141,39 @@ export function EmailsActivityPanel({ campaigns, history, canSchedule, canHistor
99
141
  return sent;
100
142
  return [...scheduled, ...sent];
101
143
  }, [filter, upcoming, history]);
144
+ const visibleRows = useMemo(() => {
145
+ let next = rows;
146
+ if (query.trim()) {
147
+ next = rows.filter((row) => {
148
+ if (row.kind === "scheduled") {
149
+ const campaign = row.campaign;
150
+ return matchesTableQuery({
151
+ subject: campaign.subject,
152
+ to: campaign.resourceLabel ?? campaign.resourceId,
153
+ status: campaign.status,
154
+ template: campaign.templateId,
155
+ }, query);
156
+ }
157
+ const entry = row.entry;
158
+ return matchesTableQuery({
159
+ subject: subjectOf(entry),
160
+ confirmation: confirmationOf(entry),
161
+ to: recipientOf(entry),
162
+ audience: entry.resourceLabel ?? entry.resourceId,
163
+ template: entry.templateLabel ?? entry.templateId,
164
+ status: entry.status,
165
+ via: sourceLabel(entry.source),
166
+ delivery: deliveryOf(entry),
167
+ }, query);
168
+ });
169
+ }
170
+ if (!sort)
171
+ return next;
172
+ const key = sort.field;
173
+ const type = key === "when" ? "datetime" : "string";
174
+ return next.slice().sort((left, right) => compareFieldValues(activitySortValue(left, key), activitySortValue(right, key), type, sort.order));
175
+ }, [rows, query, sort]);
176
+ const tableOverflow = useTableOverflow(visibleRows.length);
102
177
  const showActions = Boolean(onCancel) && filter !== "history";
103
178
  const cancel = async (campaign) => {
104
179
  if (!onCancel)
@@ -124,7 +199,10 @@ export function EmailsActivityPanel({ campaigns, history, canSchedule, canHistor
124
199
  }
125
200
  return "No activity yet.";
126
201
  })();
127
- return (_jsxs("div", { className: "vertebr-emails-activity", children: [_jsxs("div", { className: "vertebr-table-toolbar", children: [_jsx("div", { className: "vertebr-table-toolbar-start", children: _jsxs("div", { className: "vertebr-emails-segment", role: "group", "aria-label": "Activity", children: [_jsx("button", { type: "button", "data-active": filter === "all" ? "true" : undefined, onClick: () => setFilter("all"), children: "All" }), _jsx("button", { type: "button", "data-active": filter === "scheduled" ? "true" : undefined, onClick: () => setFilter("scheduled"), children: "Scheduled" }), _jsx("button", { type: "button", "data-active": filter === "history" ? "true" : undefined, onClick: () => setFilter("history"), children: "History" })] }) }), _jsx("span", { className: "vertebr-count", children: rows.length })] }), rows.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: emptyCopy })) : (_jsx("div", { className: "vertebr-table-wrap", children: _jsxs("table", { className: "vertebr-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "When" }), _jsx("th", { children: "Status" }), _jsx("th", { children: "Subject" }), _jsx("th", { children: "Confirmation" }), _jsx("th", { children: "To" }), _jsx("th", { children: "Audience" }), _jsx("th", { children: "Template" }), _jsx("th", { children: "Via" }), _jsx("th", { children: "Delivery" }), showActions ? _jsx("th", { className: "vertebr-col-actions", children: "Actions" }) : null] }) }), _jsx("tbody", { children: rows.map((row) => {
202
+ const header = (label, field) => (_jsx("th", { children: _jsx(SortableHeader, { label: label, active: sort?.field === field, order: sort?.field === field ? sort.order : undefined, onSort: () => setSort(nextTableSort(sort, field, field === "when" ? "desc" : "asc")) }) }));
203
+ return (_jsxs("div", { className: "vertebr-emails-activity", children: [_jsxs("div", { className: "vertebr-table-toolbar", children: [_jsx("div", { className: "vertebr-table-toolbar-start", children: _jsxs("div", { className: "vertebr-emails-segment", role: "group", "aria-label": "Activity", children: [_jsx("button", { type: "button", "data-active": filter === "all" ? "true" : undefined, onClick: () => setFilter("all"), children: "All" }), _jsx("button", { type: "button", "data-active": filter === "scheduled" ? "true" : undefined, onClick: () => setFilter("scheduled"), children: "Scheduled" }), _jsx("button", { type: "button", "data-active": filter === "history" ? "true" : undefined, onClick: () => setFilter("history"), children: "History" })] }) }), _jsx(TableSearch, { value: query, onChange: setQuery, "aria-label": "Search activity" }), _jsx(TableScrollControls, { canLeft: tableOverflow.canLeft, canRight: tableOverflow.canRight, onScroll: tableOverflow.scrollBy }), _jsx("span", { className: "vertebr-count", children: query.trim()
204
+ ? `${visibleRows.length} of ${rows.length}`
205
+ : rows.length })] }), rows.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: emptyCopy })) : visibleRows.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: "No matching activity." })) : (_jsx(TableWrap, { overflow: tableOverflow, children: _jsxs("table", { className: "vertebr-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [header("When", "when"), header("Status", "status"), header("Subject", "subject"), _jsx("th", { children: "Confirmation" }), header("To", "to"), header("Audience", "audience"), header("Template", "template"), _jsx("th", { children: "Via" }), _jsx("th", { children: "Delivery" }), showActions ? _jsx("th", { className: "vertebr-col-actions", children: "Actions" }) : null] }) }), _jsx("tbody", { children: visibleRows.map((row) => {
128
206
  if (row.kind === "scheduled") {
129
207
  const campaign = row.campaign;
130
208
  const badge = statusBadge("scheduled");
@@ -134,6 +212,6 @@ export function EmailsActivityPanel({ campaigns, history, canSchedule, canHistor
134
212
  const badge = statusBadge(entry.status);
135
213
  const confirmation = confirmationOf(entry);
136
214
  const recipient = recipientOf(entry);
137
- return (_jsxs("tr", { children: [_jsx("td", { children: _jsx("span", { className: "vertebr-cell-datetime", children: formatWhen(entry.at) }) }), _jsx("td", { children: _jsx("span", { className: "vertebr-badge", "data-tone": badge.tone, children: badge.label }) }), _jsx("td", { children: subjectOf(entry) }), _jsx("td", { children: confirmation || _jsx(CellEmpty, {}) }), _jsx("td", { children: recipient || _jsx(CellEmpty, {}) }), _jsx("td", { children: entry.resourceLabel ?? entry.resourceId }), _jsx("td", { children: entry.templateLabel ?? humanize(entry.templateId) }), _jsx("td", { children: sourceLabel(entry.source) }), _jsx("td", { children: deliveryOf(entry) }), showActions ? (_jsx("td", { className: "vertebr-col-actions" })) : null] }, `history:${row.id}`));
215
+ return (_jsxs("tr", { children: [_jsx("td", { children: _jsx("span", { className: "vertebr-cell-datetime", children: formatWhen(entry.at) }) }), _jsx("td", { children: _jsx("span", { className: "vertebr-badge", "data-tone": badge.tone, children: badge.label }) }), _jsx("td", { children: subjectOf(entry) || _jsx(CellEmpty, {}) }), _jsx("td", { children: confirmation || _jsx(CellEmpty, {}) }), _jsx("td", { children: recipient || _jsx(CellEmpty, {}) }), _jsx("td", { children: (entry.resourceLabel ?? entry.resourceId) || (_jsx(CellEmpty, {})) }), _jsx("td", { children: (entry.templateLabel ?? humanize(entry.templateId)) || (_jsx(CellEmpty, {})) }), _jsx("td", { children: sourceLabel(entry.source) }), _jsx("td", { children: deliveryOf(entry) }), showActions ? (_jsx("td", { className: "vertebr-col-actions", children: _jsx(CellEmpty, {}) })) : null] }, `history:${row.id}`));
138
216
  }) })] }) }))] }));
139
217
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MetricsBoard.d.ts","sourceRoot":"","sources":["../../../src/components/metrics/MetricsBoard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAGjC,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA6GF,wBAAgB,YAAY,CAAC,EAC3B,KAAkB,EAClB,WAAW,EACX,YAAY,EACZ,OAAO,EACP,MAAW,EACX,KAAK,EACL,aAAa,EACb,SAAS,EACT,OAAO,GACR,EAAE,iBAAiB,+BAsKnB"}
1
+ {"version":3,"file":"MetricsBoard.d.ts","sourceRoot":"","sources":["../../../src/components/metrics/MetricsBoard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAQjC,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA6GF,wBAAgB,YAAY,CAAC,EAC3B,KAAkB,EAClB,WAAW,EACX,YAAY,EACZ,OAAO,EACP,MAAW,EACX,KAAK,EACL,aAAa,EACb,SAAS,EACT,OAAO,GACR,EAAE,iBAAiB,+BAmLnB"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as Select from "@radix-ui/react-select";
3
3
  import { useMemo, useState } from "react";
4
4
  import { METRICS_RANGES, } from "../../lib/metricsRanges";
5
+ import { TableScrollControls, TableWrap, useTableOverflow, } from "../table";
5
6
  import { MetricLineChart, MetricSparkline } from "./charts";
6
7
  function formatDelta(delta) {
7
8
  const abs = Math.abs(delta);
@@ -40,6 +41,7 @@ export function MetricsBoard({ label = "Overview", windowLabel, compareLabel, me
40
41
  const kpiIds = new Set(kpis.map((card) => card.id));
41
42
  const rest = metrics.filter((card) => !kpiIds.has(card.id));
42
43
  const [tableOpen, setTableOpen] = useState(true);
44
+ const tableOverflow = useTableOverflow(rest.length);
43
45
  const chartIds = useMemo(() => new Set(charts.map((chart) => chart.id)), [charts]);
44
46
  const [chartId, setChartId] = useState(() => charts[0]?.id ?? "");
45
47
  const activeChartId = chartIds.has(chartId) ? chartId : (charts[0]?.id ?? "");
@@ -53,5 +55,9 @@ export function MetricsBoard({ label = "Overview", windowLabel, compareLabel, me
53
55
  }
54
56
  }
55
57
  : undefined, children: [_jsxs("div", { className: "vertebr-kpi-top", children: [_jsx("p", { className: "vertebr-metric-label", children: card.label }), card.series && card.series.length > 1 ? (_jsx(MetricSparkline, { series: card.series, tone: card.tone })) : null] }), _jsx("p", { className: "vertebr-kpi-value", children: card.value }), _jsxs("div", { className: "vertebr-kpi-foot", children: [card.delta != null ? _jsx(Delta, { value: card.delta }) : null, card.hint ? (_jsx("span", { className: "vertebr-metric-hint", children: card.hint })) : null] })] }, card.id));
56
- }) }) })) : null, charts.length > 0 && activeChartId ? (_jsx("section", { className: "vertebr-metrics-block", children: _jsx(MetricLineChart, { charts: charts, chartId: activeChartId, onChartIdChange: setChartId, compareLabel: compareLabel }) })) : null, rest.length > 0 ? (_jsx("section", { className: "vertebr-metrics-block", children: _jsxs("div", { className: "vertebr-metrics-table-wrap", "data-collapsed": tableOpen ? undefined : "true", children: [_jsxs("button", { type: "button", className: "vertebr-metrics-table-head", "aria-expanded": tableOpen, onClick: () => setTableOpen((open) => !open), children: [_jsx("p", { className: "vertebr-chart-kicker", children: "Breakdown" }), _jsx("span", { className: "vertebr-metrics-chevron", "aria-hidden": "true" })] }), tableOpen ? (_jsxs("table", { className: "vertebr-table vertebr-metrics-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Metric" }), _jsx("th", { children: "Value" }), _jsx("th", { children: "Change" }), _jsx("th", { children: "Notes" })] }) }), _jsx("tbody", { children: rest.map((card) => (_jsxs("tr", { "data-tone": card.tone ?? "neutral", children: [_jsxs("td", { children: [_jsx("span", { className: "vertebr-metrics-table-label", children: card.label }), card.section ? (_jsx("span", { className: "vertebr-metrics-table-section", children: card.section })) : null] }), _jsx("td", { className: "vertebr-metrics-table-value", children: card.value }), _jsx("td", { children: _jsx(Delta, { value: card.delta }) }), _jsx("td", { className: "vertebr-metrics-table-notes", children: card.hint ?? "—" })] }, card.id))) })] })) : null] }) })) : null] }))] }));
58
+ }) }) })) : null, charts.length > 0 && activeChartId ? (_jsx("section", { className: "vertebr-metrics-block", children: _jsx(MetricLineChart, { charts: charts, chartId: activeChartId, onChartIdChange: setChartId, compareLabel: compareLabel }) })) : null, rest.length > 0 ? (_jsx("section", { className: "vertebr-metrics-block", children: _jsxs("div", { className: "vertebr-metrics-table-wrap", "data-collapsed": tableOpen ? undefined : "true", children: [_jsxs("div", { className: "vertebr-metrics-table-head", children: [_jsxs("button", { type: "button", className: "vertebr-metrics-table-toggle", "aria-expanded": tableOpen, onClick: () => setTableOpen((open) => !open), children: [_jsx("p", { className: "vertebr-chart-kicker", children: "Breakdown" }), _jsx("span", { className: "vertebr-metrics-chevron", "aria-hidden": "true" })] }), _jsx(TableScrollControls, { canLeft: tableOverflow.canLeft, canRight: tableOverflow.canRight, onScroll: (direction) => {
59
+ if (!tableOpen)
60
+ setTableOpen(true);
61
+ tableOverflow.scrollBy(direction);
62
+ } })] }), tableOpen ? (_jsx(TableWrap, { overflow: tableOverflow, children: _jsxs("table", { className: "vertebr-table vertebr-metrics-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Metric" }), _jsx("th", { children: "Value" }), _jsx("th", { children: "Change" }), _jsx("th", { children: "Notes" })] }) }), _jsx("tbody", { children: rest.map((card) => (_jsxs("tr", { "data-tone": card.tone ?? "neutral", children: [_jsxs("td", { children: [_jsx("span", { className: "vertebr-metrics-table-label", children: card.label }), card.section ? (_jsx("span", { className: "vertebr-metrics-table-section", children: card.section })) : null] }), _jsx("td", { className: "vertebr-metrics-table-value", children: card.value }), _jsx("td", { children: _jsx(Delta, { value: card.delta }) }), _jsx("td", { className: "vertebr-metrics-table-notes", children: card.hint ?? "—" })] }, card.id))) })] }) })) : null] }) })) : null] }))] }));
57
63
  }
@@ -7,5 +7,9 @@ export type ColumnVisibilityMenuProps = {
7
7
  visibility: Record<string, boolean>;
8
8
  onChange: (next: Record<string, boolean>) => void;
9
9
  };
10
+ /**
11
+ * Column visibility — same filter-chip pattern as table field filters
12
+ * (quiet label + count pill + checklist menu).
13
+ */
10
14
  export declare function ColumnVisibilityMenu({ columns, visibility, onChange, }: ColumnVisibilityMenuProps): import("react").JSX.Element;
11
15
  //# sourceMappingURL=ColumnVisibilityMenu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ColumnVisibilityMenu.d.ts","sourceRoot":"","sources":["../../../src/components/resources/ColumnVisibilityMenu.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,UAAU,EACV,QAAQ,GACT,EAAE,yBAAyB,+BAkF3B"}
1
+ {"version":3,"file":"ColumnVisibilityMenu.d.ts","sourceRoot":"","sources":["../../../src/components/resources/ColumnVisibilityMenu.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,UAAU,EACV,QAAQ,GACT,EAAE,yBAAyB,+BAmG3B"}
@@ -1,10 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useId, useRef, useState } from "react";
3
+ /**
4
+ * Column visibility — same filter-chip pattern as table field filters
5
+ * (quiet label + count pill + checklist menu).
6
+ */
3
7
  export function ColumnVisibilityMenu({ columns, visibility, onChange, }) {
4
8
  const [open, setOpen] = useState(false);
5
9
  const rootRef = useRef(null);
6
10
  const menuId = useId();
7
- const hiddenCount = columns.filter((col) => visibility[col.id] === false).length;
11
+ const hiddenCount = columns.filter((col) => visibility[col.id] === false)
12
+ .length;
13
+ const visibleCount = columns.length - hiddenCount;
14
+ const filtering = hiddenCount > 0;
8
15
  useEffect(() => {
9
16
  if (!open)
10
17
  return;
@@ -24,17 +31,17 @@ export function ColumnVisibilityMenu({ columns, visibility, onChange, }) {
24
31
  window.removeEventListener("keydown", onKeyDown);
25
32
  };
26
33
  }, [open]);
27
- const visibleCount = columns.length - hiddenCount;
28
- return (_jsxs("div", { className: "vertebr-col-menu", ref: rootRef, children: [_jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", "aria-expanded": open, "aria-controls": menuId, onClick: () => setOpen((value) => !value), children: ["Columns", hiddenCount > 0 ? (_jsx("span", { className: "vertebr-col-menu-count", children: visibleCount })) : null] }), open ? (_jsxs("div", { className: "vertebr-col-menu-panel", id: menuId, role: "menu", children: [_jsxs("div", { className: "vertebr-col-menu-head", children: [_jsx("span", { children: "Visible fields" }), _jsx("button", { type: "button", className: "vertebr-link-btn", onClick: () => {
34
+ return (_jsxs("div", { className: "vertebr-table-filter-control", ref: rootRef, "data-open": open ? "true" : undefined, "data-active": filtering ? "true" : undefined, children: [_jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-table-filter-trigger", "aria-expanded": open, "aria-controls": menuId, "aria-label": filtering
35
+ ? `Columns, ${visibleCount} of ${columns.length} visible`
36
+ : "Columns", onClick: () => setOpen((value) => !value), children: ["Columns", filtering ? (_jsx("span", { className: "vertebr-table-filter-count", children: visibleCount })) : null] }), open ? (_jsxs("div", { className: "vertebr-table-filter-menu", id: menuId, role: "listbox", "aria-multiselectable": true, children: [_jsxs("div", { className: "vertebr-table-filter-menu-head", children: [_jsx("span", { children: "Columns" }), filtering ? (_jsx("button", { type: "button", className: "vertebr-link-btn", onClick: () => {
29
37
  const next = {};
30
38
  for (const col of columns)
31
39
  next[col.id] = true;
32
40
  onChange(next);
33
- }, children: "Show all" })] }), _jsx("ul", { className: "vertebr-col-menu-list", children: columns.map((col) => {
41
+ }, children: "Show all" })) : null] }), _jsx("ul", { className: "vertebr-table-filter-list", children: columns.map((col) => {
34
42
  const checked = visibility[col.id] !== false;
35
- return (_jsx("li", { children: _jsxs("label", { className: "vertebr-col-menu-item", children: [_jsx("input", { type: "checkbox", checked: checked, disabled: checked && visibleCount <= 1, onChange: () => {
36
- const next = { ...visibility, [col.id]: !checked };
37
- onChange(next);
43
+ return (_jsx("li", { children: _jsxs("label", { className: "vertebr-table-filter-item", children: [_jsx("input", { type: "checkbox", checked: checked, disabled: checked && visibleCount <= 1, onChange: () => {
44
+ onChange({ ...visibility, [col.id]: !checked });
38
45
  } }), _jsx("span", { children: col.label })] }) }, col.id));
39
46
  }) })] })) : null] }));
40
47
  }
@@ -1,4 +1,5 @@
1
1
  import type { FieldSchema, GalleryItem, MailPreviewResponse, ResourceActionMeta, ResourceUploadMeta } from "@vertebr/client";
2
+ import { type TableSortState } from "../../lib/sort";
2
3
  type ResourcePanelProps = {
3
4
  schema: Record<string, FieldSchema>;
4
5
  items: Record<string, unknown>[];
@@ -26,8 +27,13 @@ type ResourcePanelProps = {
26
27
  onBulkAction?: (actionId: string, rowIds: string[], input?: Record<string, unknown>) => Promise<unknown>;
27
28
  listQuery?: boolean | Record<string, FieldSchema>;
28
29
  total?: number;
30
+ /** When true, show table skeleton instead of rows / empty. */
31
+ loading?: boolean;
29
32
  searchQuery?: string;
30
33
  onSearchChange?: (q: string) => void;
34
+ /** Active column sort (`schema[field].sortable`). */
35
+ sort?: TableSortState | null;
36
+ onSortChange?: (sort: TableSortState) => void;
31
37
  onUpload?: (field: string, files: File[]) => Promise<Array<{
32
38
  url: string;
33
39
  name?: string;
@@ -38,7 +44,7 @@ type ResourcePanelProps = {
38
44
  initialEditRowId?: string;
39
45
  onEditRowIdChange?: (rowId: string | null) => void;
40
46
  };
41
- export declare function ResourcePanel({ schema, items, permissions, actions, label, uploads, rowIdKeys, storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions, onCollectionAction, bulkActions, onBulkAction, listQuery, total, searchQuery, onSearchChange, onUpload, galleryUpload, onListGallery, onUploadGallery, initialEditRowId, onEditRowIdChange, }: ResourcePanelProps): import("react").JSX.Element;
47
+ export declare function ResourcePanel({ schema, items, permissions, actions, label, uploads, rowIdKeys, storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions, onCollectionAction, bulkActions, onBulkAction, listQuery, total, loading, searchQuery, onSearchChange, sort: sortProp, onSortChange, onUpload, galleryUpload, onListGallery, onUploadGallery, initialEditRowId, onEditRowIdChange, }: ResourcePanelProps): import("react").JSX.Element;
42
48
  /** @deprecated Prefer ResourcePanel */
43
49
  export declare function ResourceTable(props: {
44
50
  schema: Record<string, FieldSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceTable.d.ts","sourceRoot":"","sources":["../../../src/components/resources/ResourceTable.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ResourceTable.d.ts","sourceRoot":"","sources":["../../../src/components/resources/ResourceTable.tsx"],"names":[],"mappings":"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"}
@@ -1,15 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
3
  import { flexRender, getCoreRowModel, useReactTable, } from "@tanstack/react-table";
4
4
  import { ConfigForm } from "../config/ConfigForm";
5
5
  import { BulkImageUploadModal } from "./BulkImageUploadModal";
6
- import { ColumnVisibilityMenu } from "./ColumnVisibilityMenu";
7
6
  import { ResourceCell } from "./ResourceCell";
8
7
  import { ResourceCreateModal } from "./ResourceCreateModal";
9
8
  import { actionInputSchema, ResourceActionModal, } from "./ResourceActionModal";
10
9
  import { ResourceMailModal } from "./ResourceMailModal";
11
10
  import { ResourceViewModal } from "./ResourceViewModal";
12
11
  import { VisibilityEyeToggle } from "../config/VisibilityEye";
12
+ import { SortableHeader } from "./SortableHeader";
13
+ import { TableFilters, TableScrollControls, TableSearch, TableSkeleton, TableWrap, matchesTableFilters, matchesTableQuery, resolveFieldFilter, useTableOverflow, } from "../table";
14
+ import { nextTableSort, sortRows, } from "../../lib/sort";
13
15
  function actionHasInput(meta) {
14
16
  return Boolean(meta?.input && Object.keys(meta.input).length > 0);
15
17
  }
@@ -130,7 +132,7 @@ function titleFromFilename(name) {
130
132
  .replace(/\s+/g, " ")
131
133
  .trim();
132
134
  }
133
- export function ResourcePanel({ schema, items, permissions, actions = {}, label, uploads, rowIdKeys = ["id", "slug", "uid", "email"], storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions = {}, onCollectionAction, bulkActions = {}, onBulkAction, listQuery, total, searchQuery = "", onSearchChange, onUpload, galleryUpload, onListGallery, onUploadGallery, initialEditRowId, onEditRowIdChange, }) {
135
+ export function ResourcePanel({ schema, items, permissions, actions = {}, label, uploads, rowIdKeys = ["id", "slug", "uid", "email"], storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions = {}, onCollectionAction, bulkActions = {}, onBulkAction, listQuery, total, loading = false, searchQuery = "", onSearchChange, sort: sortProp = null, onSortChange, onUpload, galleryUpload, onListGallery, onUploadGallery, initialEditRowId, onEditRowIdChange, }) {
134
136
  const [editing, setEditing] = useState(null);
135
137
  const [creating, setCreating] = useState(false);
136
138
  const [viewing, setViewing] = useState(null);
@@ -138,16 +140,17 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
138
140
  const [busyAction, setBusyAction] = useState(null);
139
141
  const [selected, setSelected] = useState(new Set());
140
142
  const [searchDraft, setSearchDraft] = useState(searchQuery);
143
+ const [filters, setFilters] = useState({});
144
+ const [localSort, setLocalSort] = useState(null);
145
+ const sort = onSortChange ? sortProp : localSort;
146
+ const setSort = onSortChange ?? setLocalSort;
141
147
  const [pendingAction, setPendingAction] = useState(null);
142
148
  const [pendingCollectionAction, setPendingCollectionAction] = useState(null);
143
149
  const [pendingBulkAction, setPendingBulkAction] = useState(null);
144
- const tableWrapRef = useRef(null);
145
- const [tableOverflow, setTableOverflow] = useState({
146
- overflow: false,
147
- canLeft: false,
148
- canRight: false,
149
- });
150
150
  const [columnVisibility, setColumnVisibility] = useState(() => readVisibility(storageKey, schema));
151
+ const tableOverflow = useTableOverflow(`${items.length}:${Object.keys(columnVisibility)
152
+ .filter((key) => columnVisibility[key] !== false)
153
+ .join(",")}`);
151
154
  /** Pending list visibility toggles keyed by row id → field → value. */
152
155
  const [visibilityDrafts, setVisibilityDrafts] = useState({});
153
156
  const [savingVisibility, setSavingVisibility] = useState(false);
@@ -163,19 +166,40 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
163
166
  const visibilityFields = useMemo(() => fieldKeys.filter((key) => isListVisibilityField(key, schema[key])), [fieldKeys, schema]);
164
167
  const selectable = Boolean((onAction && actionEntries.length > 0) ||
165
168
  (onBulkAction && bulkActionEntries.length > 0));
166
- const showSearch = Boolean(listQuery && onSearchChange);
169
+ /** Server `q` when listQuery is advertised; otherwise filter loaded rows. */
170
+ const serverSearch = Boolean(listQuery && onSearchChange);
167
171
  useEffect(() => {
168
172
  setSearchDraft(searchQuery);
169
173
  }, [searchQuery]);
170
174
  useEffect(() => {
171
- if (!onSearchChange)
175
+ if (!serverSearch || !onSearchChange)
172
176
  return;
173
177
  const handle = window.setTimeout(() => {
174
178
  if (searchDraft !== searchQuery)
175
179
  onSearchChange(searchDraft);
176
180
  }, 250);
177
181
  return () => window.clearTimeout(handle);
178
- }, [searchDraft, searchQuery, onSearchChange]);
182
+ }, [searchDraft, searchQuery, onSearchChange, serverSearch]);
183
+ const filterColumns = useMemo(() => {
184
+ return fieldKeys
185
+ .map((key) => {
186
+ const field = schema[key];
187
+ if (!field)
188
+ return null;
189
+ return resolveFieldFilter(key, field.label ?? humanizeKey(key), field);
190
+ })
191
+ .filter((column) => Boolean(column));
192
+ }, [fieldKeys, schema]);
193
+ const visibleItems = useMemo(() => {
194
+ const searched = serverSearch
195
+ ? items
196
+ : items.filter((row) => matchesTableQuery(row, searchDraft));
197
+ const filterDefs = Object.fromEntries(filterColumns.map((column) => [
198
+ column.id,
199
+ (row) => row[column.id],
200
+ ]));
201
+ return searched.filter((row) => matchesTableFilters(row, filters, filterDefs));
202
+ }, [items, searchDraft, serverSearch, filters, filterColumns]);
179
203
  const openEdit = useCallback((row) => {
180
204
  setEditing(row);
181
205
  onEditRowIdChange?.(resolveRowId(row, rowIdKeys));
@@ -384,49 +408,12 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
384
408
  useEffect(() => {
385
409
  setColumnVisibility(readVisibility(storageKey, schema));
386
410
  }, [storageKey]);
411
+ useEffect(() => {
412
+ setFilters({});
413
+ }, [storageKey]);
387
414
  useEffect(() => {
388
415
  writeVisibility(storageKey, columnVisibility);
389
416
  }, [storageKey, columnVisibility]);
390
- const updateTableOverflow = useCallback(() => {
391
- const el = tableWrapRef.current;
392
- if (!el) {
393
- setTableOverflow({ overflow: false, canLeft: false, canRight: false });
394
- return;
395
- }
396
- const overflow = el.scrollWidth > el.clientWidth + 2;
397
- setTableOverflow({
398
- overflow,
399
- canLeft: overflow && el.scrollLeft > 2,
400
- canRight: overflow && el.scrollLeft + el.clientWidth < el.scrollWidth - 2,
401
- });
402
- }, []);
403
- useEffect(() => {
404
- const el = tableWrapRef.current;
405
- if (!el)
406
- return;
407
- updateTableOverflow();
408
- const onScroll = () => updateTableOverflow();
409
- el.addEventListener("scroll", onScroll, { passive: true });
410
- const observer = new ResizeObserver(updateTableOverflow);
411
- observer.observe(el);
412
- const table = el.querySelector("table");
413
- if (table)
414
- observer.observe(table);
415
- return () => {
416
- el.removeEventListener("scroll", onScroll);
417
- observer.disconnect();
418
- };
419
- }, [updateTableOverflow, items.length, columnVisibility]);
420
- const scrollTable = (direction) => {
421
- const el = tableWrapRef.current;
422
- if (!el)
423
- return;
424
- const amount = Math.max(220, Math.round(el.clientWidth * 0.55));
425
- el.scrollBy({
426
- left: direction === "left" ? -amount : amount,
427
- behavior: "smooth",
428
- });
429
- };
430
417
  const columnOptions = useMemo(() => fieldKeys.map((key) => ({
431
418
  id: key,
432
419
  label: schema[key]?.label ?? humanizeKey(key),
@@ -474,7 +461,16 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
474
461
  return {
475
462
  id: key,
476
463
  accessorKey: key,
477
- header: field?.label ?? humanizeKey(key),
464
+ header: () => {
465
+ const label = field?.label ?? humanizeKey(key);
466
+ if (!field?.sortable)
467
+ return label;
468
+ const active = sort?.field === key;
469
+ const preferredFirst = field.type === "datetime" || field.type === "number"
470
+ ? "desc"
471
+ : "asc";
472
+ return (_jsx(SortableHeader, { label: label, active: Boolean(active), order: active ? sort?.order : undefined, onSort: () => setSort(nextTableSort(sort, key, preferredFirst)) }));
473
+ },
478
474
  meta: { fieldKey: key, field },
479
475
  cell: ({ row, getValue, }) => {
480
476
  if (listVisibility) {
@@ -525,9 +521,12 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
525
521
  rowIdKeys,
526
522
  visibilityDrafts,
527
523
  setVisibilityDraft,
524
+ sort,
525
+ setSort,
528
526
  ]);
527
+ const sortedItems = useMemo(() => sortRows(visibleItems, sort, schema), [visibleItems, sort, schema]);
529
528
  const table = useReactTable({
530
- data: items,
529
+ data: sortedItems,
531
530
  columns,
532
531
  state: { columnVisibility },
533
532
  onColumnVisibilityChange: setColumnVisibility,
@@ -559,7 +558,13 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
559
558
  closeEdit();
560
559
  } })] }));
561
560
  }
562
- const toolbar = (_jsxs("div", { className: "vertebr-table-toolbar", children: [_jsxs("div", { className: "vertebr-table-toolbar-start", children: [columnOptions.length > 0 ? (_jsx(ColumnVisibilityMenu, { columns: columnOptions, visibility: columnVisibility, onChange: setColumnVisibility })) : null, canUpload ? (_jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon", onClick: () => setBulkOpen(true), children: [_jsx(ImageUploadIcon, {}), "Bulk upload"] })) : null, visibilityFields.length > 0 && dirtyVisibilityCount > 0 ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "vertebr-count", children: [dirtyVisibilityCount, " visibility change", dirtyVisibilityCount === 1 ? "" : "s"] }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", disabled: savingVisibility, onClick: () => void saveVisibility(), children: savingVisibility ? "Saving…" : "Save visibility" }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: savingVisibility, onClick: () => setVisibilityDrafts({}), children: "Discard" })] })) : null, collectionActionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueCollectionAction(actionId), children: meta.label }, actionId))), selectable && selected.size > 0 ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "vertebr-count", children: [selected.size, " selected"] }), actionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueAction(actionId, [...selected]), children: meta.label }, actionId))), bulkActionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueBulkAction(actionId, [...selected]), children: meta.label }, `bulk:${actionId}`))), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => setSelected(new Set()), children: "Clear" })] })) : null] }), tableOverflow.overflow ? (_jsxs("div", { className: "vertebr-table-scroll-controls", role: "group", "aria-label": "Scroll columns", children: [_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon vertebr-table-scroll-btn", disabled: !tableOverflow.canLeft, onClick: () => scrollTable("left"), "aria-label": "Scroll columns left", children: _jsx(ChevronLeftIcon, {}) }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon vertebr-table-scroll-btn", disabled: !tableOverflow.canRight, onClick: () => scrollTable("right"), "aria-label": "Scroll columns right", children: _jsx(ChevronRightIcon, {}) })] })) : null] }));
561
+ const toolbar = (_jsxs("div", { className: "vertebr-table-toolbar", children: [_jsxs("div", { className: "vertebr-table-toolbar-start", children: [filterColumns.length > 0 || columnOptions.length > 0 ? (_jsx(TableFilters, { columns: filterColumns, selected: filters, onChange: setFilters, columnVisibility: columnOptions.length > 0
562
+ ? {
563
+ columns: columnOptions,
564
+ visibility: columnVisibility,
565
+ onChange: setColumnVisibility,
566
+ }
567
+ : undefined })) : null, canUpload ? (_jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon", onClick: () => setBulkOpen(true), children: [_jsx(ImageUploadIcon, {}), "Bulk upload"] })) : null, visibilityFields.length > 0 && dirtyVisibilityCount > 0 ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "vertebr-count", children: [dirtyVisibilityCount, " visibility change", dirtyVisibilityCount === 1 ? "" : "s"] }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", disabled: savingVisibility, onClick: () => void saveVisibility(), children: savingVisibility ? "Saving…" : "Save visibility" }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: savingVisibility, onClick: () => setVisibilityDrafts({}), children: "Discard" })] })) : null, collectionActionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueCollectionAction(actionId), children: meta.label }, actionId))), selectable && selected.size > 0 ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "vertebr-count", children: [selected.size, " selected"] }), actionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueAction(actionId, [...selected]), children: meta.label }, actionId))), bulkActionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueBulkAction(actionId, [...selected]), children: meta.label }, `bulk:${actionId}`))), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => setSelected(new Set()), children: "Clear" })] })) : null] }), _jsx(TableScrollControls, { canLeft: tableOverflow.canLeft, canRight: tableOverflow.canRight, onScroll: tableOverflow.scrollBy })] }));
563
568
  const bulkModal = bulkOpen && uploads && onUpload ? (_jsx(BulkImageUploadModal, { uploads: uploads, canCreate: canCreate, fieldLabels: Object.fromEntries(Object.keys(uploads).map((key) => [
564
569
  key,
565
570
  schema[key]?.label ?? humanizeKey(key),
@@ -588,15 +593,36 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
588
593
  const pendingCollectionModal = pendingCollectionAction &&
589
594
  collectionActions[pendingCollectionAction]?.input ? (_jsx(ResourceActionModal, { title: collectionActions[pendingCollectionAction]?.label ?? "Run action", eyebrow: "This resource", schema: actionInputSchema(collectionActions[pendingCollectionAction].input), submitLabel: busyAction ? "Working…" : "Run", onClose: () => setPendingCollectionAction(null), onSubmit: (value) => runCollectionAction(pendingCollectionAction, value) })) : null;
590
595
  const pendingBulkModal = pendingBulkAction && bulkActions[pendingBulkAction]?.input ? (_jsx(ResourceActionModal, { title: bulkActions[pendingBulkAction]?.label ?? "Bulk action", eyebrow: `${selected.size} selected`, schema: actionInputSchema(bulkActions[pendingBulkAction].input), submitLabel: busyAction ? "Working…" : "Run", onClose: () => setPendingBulkAction(null), onSubmit: (value) => runBulkAction(pendingBulkAction, [...selected], value) })) : null;
591
- const recordsHead = (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: "Records" }), _jsxs("div", { className: "vertebr-panel-head-actions", children: [showSearch ? (_jsx("input", { className: "vertebr-resource-search", type: "search", placeholder: "Search\u2026", value: searchDraft, onChange: (event) => setSearchDraft(event.target.value), "aria-label": "Search records" })) : null, canCreate ? (_jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon", onClick: () => setCreating(true), children: [_jsx(PlusIcon, {}), createLabel] })) : null, _jsx("span", { className: "vertebr-count", children: total != null ? `${items.length} of ${total}` : items.length })] })] }));
596
+ const recordsHead = (_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: "Records" }), _jsxs("div", { className: "vertebr-panel-head-actions", children: [_jsx(TableSearch, { value: searchDraft, onChange: setSearchDraft, "aria-label": "Search records" }), canCreate ? (_jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon", onClick: () => setCreating(true), children: [_jsx(PlusIcon, {}), createLabel] })) : null, _jsx("span", { className: "vertebr-count", children: loading
597
+ ? "…"
598
+ : total != null
599
+ ? `${visibleItems.length} of ${total}`
600
+ : visibleItems.length !== items.length
601
+ ? `${visibleItems.length} of ${items.length}`
602
+ : visibleItems.length })] })] }));
603
+ if (loading) {
604
+ return (_jsxs("div", { className: "vertebr-resource-body", children: [recordsHead, _jsx(TableSkeleton, { columns: fieldKeys.map((key) => ({
605
+ id: key,
606
+ label: schema[key]?.label ?? humanizeKey(key),
607
+ })) })] }));
608
+ }
592
609
  if (items.length === 0) {
593
- return (_jsxs("div", { className: "vertebr-resource-body", children: [recordsHead, canUpload ||
610
+ return (_jsxs("div", { className: "vertebr-resource-body", children: [recordsHead, filterColumns.length > 0 ||
594
611
  columnOptions.length > 0 ||
612
+ canUpload ||
595
613
  collectionActionEntries.length > 0
596
614
  ? toolbar
597
615
  : null, _jsx("div", { className: "vertebr-empty", children: "No rows yet." }), bulkModal, createModal, pendingActionModal, pendingCollectionModal, pendingBulkModal] }));
598
616
  }
599
- return (_jsxs("div", { className: "vertebr-resource-body", children: [recordsHead, toolbar, _jsx("div", { className: "vertebr-table-wrap", ref: tableWrapRef, "data-overflow": tableOverflow.overflow ? "true" : undefined, "data-overflow-left": tableOverflow.canLeft ? "true" : undefined, "data-overflow-right": tableOverflow.canRight ? "true" : undefined, children: _jsxs("table", { className: "vertebr-table", children: [_jsx("thead", { children: table.getHeaderGroups().map((headerGroup) => (_jsx("tr", { children: headerGroup.headers.map((header) => (_jsx("th", { className: header.column.id === "_actions"
617
+ if (visibleItems.length === 0) {
618
+ return (_jsxs("div", { className: "vertebr-resource-body", children: [recordsHead, filterColumns.length > 0 ||
619
+ columnOptions.length > 0 ||
620
+ canUpload ||
621
+ collectionActionEntries.length > 0
622
+ ? toolbar
623
+ : null, _jsx("div", { className: "vertebr-empty", children: "No matching rows." }), bulkModal, createModal, pendingActionModal, pendingCollectionModal, pendingBulkModal] }));
624
+ }
625
+ return (_jsxs("div", { className: "vertebr-resource-body", children: [recordsHead, toolbar, _jsx(TableWrap, { overflow: tableOverflow, transitionKey: JSON.stringify(filters), children: _jsxs("table", { className: "vertebr-table", children: [_jsx("thead", { children: table.getHeaderGroups().map((headerGroup) => (_jsx("tr", { children: headerGroup.headers.map((header) => (_jsx("th", { className: header.column.id === "_actions"
600
626
  ? "vertebr-col-actions"
601
627
  : header.column.id === "_select"
602
628
  ? "vertebr-col-select"
@@ -627,12 +653,6 @@ export function ResourceTable(props) {
627
653
  function PlusIcon() {
628
654
  return (_jsxs("svg", { className: "vertebr-btn-glyph", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M12 5v14" }), _jsx("path", { d: "M5 12h14" })] }));
629
655
  }
630
- function ChevronLeftIcon() {
631
- return (_jsx("svg", { className: "vertebr-btn-glyph", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m15 18-6-6 6-6" }) }));
632
- }
633
- function ChevronRightIcon() {
634
- return (_jsx("svg", { className: "vertebr-btn-glyph", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m9 18 6-6-6-6" }) }));
635
- }
636
656
  function ImageUploadIcon() {
637
657
  return (_jsxs("svg", { className: "vertebr-btn-glyph", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M16 5h6" }), _jsx("path", { d: "M19 2v6" }), _jsx("rect", { x: "3", y: "7", width: "14", height: "14", rx: "2" }), _jsx("circle", { cx: "8.5", cy: "12.5", r: "1.5" }), _jsx("path", { d: "m17 21-4.5-4.5L7 21" })] }));
638
658
  }
@@ -0,0 +1,9 @@
1
+ import type { SortOrder } from "../../lib/sort";
2
+ export type SortableHeaderProps = {
3
+ label: string;
4
+ active: boolean;
5
+ order?: SortOrder;
6
+ onSort: () => void;
7
+ };
8
+ export declare function SortableHeader({ label, active, order, onSort, }: SortableHeaderProps): import("react").JSX.Element;
9
+ //# sourceMappingURL=SortableHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SortableHeader.d.ts","sourceRoot":"","sources":["../../../src/components/resources/SortableHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,mBAAmB,+BAuBrB"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function SortableHeader({ label, active, order, onSort, }) {
3
+ return (_jsxs("button", { type: "button", className: "vertebr-th-sort", "data-active": active ? "true" : undefined, "data-order": active ? order : undefined, "aria-label": active
4
+ ? `Sorted by ${label}, ${order === "desc" ? "descending" : "ascending"}. Click to reverse.`
5
+ : `Sort by ${label}`, onClick: (event) => {
6
+ event.stopPropagation();
7
+ onSort();
8
+ }, children: [_jsx("span", { children: label }), _jsx("span", { className: "vertebr-th-sort-icon", "aria-hidden": "true", children: active ? (order === "desc" ? "↓" : "↑") : "↕" })] }));
9
+ }
@@ -0,0 +1,20 @@
1
+ import type { TableFilterColumn, TableFilterState } from "./filterModel";
2
+ import { type ColumnOption } from "../resources/ColumnVisibilityMenu";
3
+ export type TableColumnVisibilityProps = {
4
+ columns: ColumnOption[];
5
+ visibility: Record<string, boolean>;
6
+ onChange: (next: Record<string, boolean>) => void;
7
+ };
8
+ export type TableFiltersProps = {
9
+ columns: TableFilterColumn[];
10
+ selected: TableFilterState;
11
+ onChange: (next: TableFilterState) => void;
12
+ /** Optional Columns chip — same filter-chip UI as field filters. */
13
+ columnVisibility?: TableColumnVisibilityProps;
14
+ };
15
+ /**
16
+ * Host-declared filter chips (+ optional Columns chip).
17
+ * Selected values stay checked in each dropdown.
18
+ */
19
+ export declare function TableFilters({ columns, selected, onChange, columnVisibility, }: TableFiltersProps): import("react").JSX.Element | null;
20
+ //# sourceMappingURL=TableFilters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableFilters.d.ts","sourceRoot":"","sources":["../../../src/components/table/TableFilters.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMzE,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,mCAAmC,CAAC;AAE3C,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3C,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,gBAAgB,GACjB,EAAE,iBAAiB,sCA4CnB"}