@adapttable/mui 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,1930 +1,1819 @@
1
- 'use strict';
2
-
3
- var core = require('@adapttable/core');
4
- var material = require('@mui/material');
5
- var react = require('react');
6
- var jsxRuntime = require('react/jsx-runtime');
7
-
8
- // src/components/SavedViewsMenu.tsx
9
- function SavedViewsMenu({
10
- options,
11
- labels
12
- }) {
13
- const { views, save, apply, remove } = core.useSavedViews(options);
14
- const [anchor, setAnchor] = react.useState(null);
15
- const [name, setName] = react.useState("");
16
- const trimmed = name.trim();
17
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18
- /* @__PURE__ */ jsxRuntime.jsx(
19
- material.Button,
20
- {
21
- size: "small",
22
- variant: "outlined",
23
- "aria-expanded": anchor !== null,
24
- "aria-haspopup": "true",
25
- onClick: (e) => setAnchor(e.currentTarget),
26
- children: labels.savedViews
27
- }
28
- ),
29
- /* @__PURE__ */ jsxRuntime.jsx(
30
- material.Popover,
31
- {
32
- anchorEl: anchor,
33
- open: anchor !== null,
34
- onClose: () => setAnchor(null),
35
- anchorOrigin: { vertical: "bottom", horizontal: "left" },
36
- children: /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { sx: { p: 0.75, minWidth: 250 }, children: [
37
- views.map((view) => /* @__PURE__ */ jsxRuntime.jsxs(
38
- material.Stack,
39
- {
40
- direction: "row",
41
- alignItems: "center",
42
- spacing: 0.5,
43
- children: [
44
- /* @__PURE__ */ jsxRuntime.jsx(
45
- material.Button,
46
- {
47
- size: "small",
48
- fullWidth: true,
49
- sx: { justifyContent: "flex-start" },
50
- onClick: () => {
51
- apply(view.name);
52
- setAnchor(null);
53
- },
54
- children: view.name
55
- }
56
- ),
57
- /* @__PURE__ */ jsxRuntime.jsx(
58
- material.IconButton,
59
- {
60
- size: "small",
61
- "aria-label": `${labels.deleteView}: ${view.name}`,
62
- onClick: () => remove(view.name),
63
- children: "\xD7"
64
- }
65
- )
66
- ]
67
- },
68
- view.name
69
- )),
70
- /* @__PURE__ */ jsxRuntime.jsx(material.Divider, { sx: { my: 0.5 } }),
71
- /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
72
- /* @__PURE__ */ jsxRuntime.jsx(
73
- material.TextField,
74
- {
75
- size: "small",
76
- value: name,
77
- placeholder: labels.viewName,
78
- slotProps: { htmlInput: { "aria-label": labels.viewName } },
79
- onChange: (e) => setName(e.target.value)
80
- }
81
- ),
82
- /* @__PURE__ */ jsxRuntime.jsx(
83
- material.Button,
84
- {
85
- size: "small",
86
- variant: "contained",
87
- disabled: trimmed === "",
88
- onClick: () => {
89
- save(trimmed);
90
- setName("");
91
- },
92
- children: labels.saveView
93
- }
94
- )
95
- ] })
96
- ] })
97
- }
98
- )
99
- ] });
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _adapttable_core = require("@adapttable/core");
3
+ let _mui_material = require("@mui/material");
4
+ let react = require("react");
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ //#region src/components/SavedViewsMenu.tsx
7
+ /**
8
+ * MUI saved-views menu: a toolbar button opening a popover that lists the
9
+ * saved views — click applies one to the table, the trailing × deletes it —
10
+ * above a save row that captures the table's CURRENT URL state (search,
11
+ * sort, page, filters, column layout) under a typed name.
12
+ */
13
+ function SavedViewsMenu({ options, labels }) {
14
+ const { views, save, apply, remove } = (0, _adapttable_core.useSavedViews)(options);
15
+ const [anchor, setAnchor] = (0, react.useState)(null);
16
+ const [name, setName] = (0, react.useState)("");
17
+ const trimmed = name.trim();
18
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
19
+ size: "small",
20
+ variant: "outlined",
21
+ "aria-expanded": anchor !== null,
22
+ "aria-haspopup": "true",
23
+ onClick: (e) => setAnchor(e.currentTarget),
24
+ children: labels.savedViews
25
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Popover, {
26
+ anchorEl: anchor,
27
+ open: anchor !== null,
28
+ onClose: () => setAnchor(null),
29
+ anchorOrigin: {
30
+ vertical: "bottom",
31
+ horizontal: "left"
32
+ },
33
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Box, {
34
+ sx: {
35
+ p: .75,
36
+ minWidth: 250
37
+ },
38
+ children: [
39
+ views.map((view) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
40
+ direction: "row",
41
+ spacing: .5,
42
+ sx: { alignItems: "center" },
43
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
44
+ size: "small",
45
+ fullWidth: true,
46
+ sx: { justifyContent: "flex-start" },
47
+ onClick: () => {
48
+ apply(view.name);
49
+ setAnchor(null);
50
+ },
51
+ children: view.name
52
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.IconButton, {
53
+ size: "small",
54
+ "aria-label": `${labels.deleteView}: ${view.name}`,
55
+ onClick: () => remove(view.name),
56
+ children: "×"
57
+ })]
58
+ }, view.name)),
59
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Divider, { sx: { my: .5 } }),
60
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
61
+ direction: "row",
62
+ spacing: .5,
63
+ sx: { alignItems: "center" },
64
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TextField, {
65
+ size: "small",
66
+ value: name,
67
+ placeholder: labels.viewName,
68
+ slotProps: { htmlInput: { "aria-label": labels.viewName } },
69
+ onChange: (e) => setName(e.target.value)
70
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
71
+ size: "small",
72
+ variant: "contained",
73
+ disabled: trimmed === "",
74
+ onClick: () => {
75
+ save(trimmed);
76
+ setName("");
77
+ },
78
+ children: labels.saveView
79
+ })]
80
+ })
81
+ ]
82
+ })
83
+ })] });
100
84
  }
85
+ //#endregion
86
+ //#region src/components/AutoFilterForm.tsx
87
+ /** A scalar filter value as input text (arrays/blanks render empty). */
101
88
  function scalarText(value) {
102
- return typeof value === "string" || typeof value === "number" ? String(value) : "";
89
+ return typeof value === "string" || typeof value === "number" ? String(value) : "";
103
90
  }
91
+ /** A multi-select filter value as the checked-value list. */
104
92
  function selectedList(value) {
105
- return Array.isArray(value) ? value : [];
93
+ return Array.isArray(value) ? value : [];
106
94
  }
107
95
  function TextFilter({ def, source }) {
108
- return /* @__PURE__ */ jsxRuntime.jsx(
109
- material.TextField,
110
- {
111
- size: "small",
112
- label: core.filterLabel(def),
113
- placeholder: def.placeholder,
114
- value: scalarText(source.extra[def.key]),
115
- onChange: (e) => source.setExtra(def.key, e.target.value)
116
- }
117
- );
96
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TextField, {
97
+ size: "small",
98
+ label: (0, _adapttable_core.filterLabel)(def),
99
+ placeholder: def.placeholder,
100
+ value: scalarText(source.extra[def.key]),
101
+ onChange: (e) => source.setExtra(def.key, e.target.value)
102
+ });
118
103
  }
119
104
  function SelectFilter({ def, source }) {
120
- const { options, loading } = core.useFilterOptions(def);
121
- return /* @__PURE__ */ jsxRuntime.jsxs(
122
- material.TextField,
123
- {
124
- select: true,
125
- size: "small",
126
- label: core.filterLabel(def),
127
- value: scalarText(source.extra[def.key]),
128
- onChange: (e) => source.setExtra(def.key, e.target.value),
129
- slotProps: {
130
- select: { native: true },
131
- inputLabel: { shrink: true }
132
- },
133
- children: [
134
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "All" }),
135
- loading && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "\u2026" }),
136
- options.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value))
137
- ]
138
- }
139
- );
105
+ const { options, loading } = (0, _adapttable_core.useFilterOptions)(def);
106
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TextField, {
107
+ select: true,
108
+ size: "small",
109
+ label: (0, _adapttable_core.filterLabel)(def),
110
+ value: scalarText(source.extra[def.key]),
111
+ onChange: (e) => source.setExtra(def.key, e.target.value),
112
+ slotProps: {
113
+ select: { native: true },
114
+ inputLabel: { shrink: true }
115
+ },
116
+ children: [
117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
118
+ value: "",
119
+ children: "All"
120
+ }),
121
+ loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
122
+ value: "",
123
+ disabled: true,
124
+ children: "…"
125
+ }),
126
+ options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
127
+ value: option.value,
128
+ children: option.label
129
+ }, option.value))
130
+ ]
131
+ });
140
132
  }
141
133
  function MultiSelectFilter({ def, source }) {
142
- const { options, loading } = core.useFilterOptions(def);
143
- const checked = selectedList(source.extra[def.key]);
144
- const toggle = (value, on) => {
145
- const next = on ? [...checked, value] : checked.filter((v) => v !== value);
146
- source.setExtra(def.key, next);
147
- };
148
- return /* @__PURE__ */ jsxRuntime.jsxs(material.FormControl, { component: "fieldset", variant: "standard", children: [
149
- /* @__PURE__ */ jsxRuntime.jsx(material.FormLabel, { component: "legend", children: core.filterLabel(def) }),
150
- /* @__PURE__ */ jsxRuntime.jsx(material.FormGroup, { row: true, sx: { columnGap: 1.5 }, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 16 }) : options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
151
- material.FormControlLabel,
152
- {
153
- label: option.label,
154
- control: /* @__PURE__ */ jsxRuntime.jsx(
155
- material.Checkbox,
156
- {
157
- size: "small",
158
- checked: checked.includes(option.value),
159
- onChange: (_, on) => toggle(option.value, on)
160
- }
161
- )
162
- },
163
- option.value
164
- )) })
165
- ] });
134
+ const { options, loading } = (0, _adapttable_core.useFilterOptions)(def);
135
+ const checked = selectedList(source.extra[def.key]);
136
+ const toggle = (value, on) => {
137
+ const next = on ? [...checked, value] : checked.filter((v) => v !== value);
138
+ source.setExtra(def.key, next);
139
+ };
140
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.FormControl, {
141
+ component: "fieldset",
142
+ variant: "standard",
143
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.FormLabel, {
144
+ component: "legend",
145
+ children: (0, _adapttable_core.filterLabel)(def)
146
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.FormGroup, {
147
+ row: true,
148
+ sx: { columnGap: 1.5 },
149
+ children: loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.CircularProgress, { size: 16 }) : options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.FormControlLabel, {
150
+ label: option.label,
151
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Checkbox, {
152
+ size: "small",
153
+ checked: checked.includes(option.value),
154
+ onChange: (_, on) => toggle(option.value, on)
155
+ })
156
+ }, option.value))
157
+ })]
158
+ });
166
159
  }
167
- function RangeFilter({
168
- def,
169
- type,
170
- source,
171
- labels
172
- }) {
173
- const suffixes = core.RANGE_SUFFIXES[type];
174
- const lowKey = def.key + suffixes.start;
175
- const highKey = def.key + suffixes.end;
176
- const opLabelKeys = core.RANGE_OP_LABEL_KEYS[type === "dateRange" ? "date" : "number"];
177
- const state = core.readRangeWidget(source.extra, lowKey, highKey);
178
- const [op, setOp] = react.useState(state.op);
179
- const single = op === "lte" ? state.b : state.a;
180
- const commit = (next, a, b) => source.setExtras(core.writeRangeWidget(next, a, b, lowKey, highKey));
181
- const changeOp = (raw) => {
182
- const next = core.RANGE_OPS.find((candidate) => candidate === raw);
183
- setOp(next);
184
- commit(next, single, op === "between" ? state.b : "");
185
- };
186
- const input = (label, value, write) => /* @__PURE__ */ jsxRuntime.jsx(
187
- material.TextField,
188
- {
189
- size: "small",
190
- sx: { flex: "1 1 7rem", minWidth: "7rem" },
191
- type: type === "dateRange" ? "date" : "number",
192
- label,
193
- value,
194
- onChange: (e) => write(e.target.value),
195
- slotProps: { inputLabel: { shrink: true } }
196
- }
197
- );
198
- let bounds = null;
199
- if (op === "between") {
200
- bounds = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
201
- input(labels.from, state.a, (raw) => commit("between", raw, state.b)),
202
- input(labels.to, state.b, (raw) => commit("between", state.a, raw))
203
- ] });
204
- } else if (op !== void 0) {
205
- bounds = input(labels.value, single, (raw) => commit(op, raw, ""));
206
- }
207
- return /* @__PURE__ */ jsxRuntime.jsxs(material.FormControl, { component: "fieldset", variant: "standard", sx: { width: "100%" }, children: [
208
- /* @__PURE__ */ jsxRuntime.jsx(material.FormLabel, { component: "legend", sx: { mb: 0.75 }, children: core.filterLabel(def) }),
209
- /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { direction: "row", spacing: 1, useFlexGap: true, flexWrap: "wrap", children: [
210
- /* @__PURE__ */ jsxRuntime.jsxs(
211
- material.TextField,
212
- {
213
- select: true,
214
- size: "small",
215
- label: labels.operator,
216
- value: op ?? "",
217
- onChange: (e) => changeOp(e.target.value),
218
- slotProps: {
219
- select: { native: true },
220
- inputLabel: { shrink: true }
221
- },
222
- sx: { flex: "0 0 8.5rem", width: "8.5rem" },
223
- children: [
224
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "" }),
225
- core.RANGE_OPS.map((candidate) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: candidate, children: labels[opLabelKeys[candidate]] }, candidate))
226
- ]
227
- }
228
- ),
229
- bounds
230
- ] })
231
- ] });
160
+ /**
161
+ * Operator-first range widget: a comparison select, then one value input —
162
+ * or a From/To pair for "between". The UI state is the operator alone; the
163
+ * bounds always live in the source's `Min`/`Max` (`From`/`To`) pair, so
164
+ * URLs, chips, and predicates are untouched by the operator presentation.
165
+ */
166
+ function RangeFilter({ def, type, source, labels }) {
167
+ const suffixes = _adapttable_core.RANGE_SUFFIXES[type];
168
+ const lowKey = def.key + suffixes.start;
169
+ const highKey = def.key + suffixes.end;
170
+ const opLabelKeys = _adapttable_core.RANGE_OP_LABEL_KEYS[type === "dateRange" ? "date" : "number"];
171
+ const state = (0, _adapttable_core.readRangeWidget)(source.extra, lowKey, highKey);
172
+ const [op, setOp] = (0, react.useState)(state.op);
173
+ const single = op === "lte" ? state.b : state.a;
174
+ const commit = (next, a, b) => source.setExtras((0, _adapttable_core.writeRangeWidget)(next, a, b, lowKey, highKey));
175
+ const changeOp = (raw) => {
176
+ const next = _adapttable_core.RANGE_OPS.find((candidate) => candidate === raw);
177
+ setOp(next);
178
+ commit(next, single, op === "between" ? state.b : "");
179
+ };
180
+ const input = (label, value, write) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TextField, {
181
+ size: "small",
182
+ sx: {
183
+ flex: "1 1 7rem",
184
+ minWidth: "7rem"
185
+ },
186
+ type: type === "dateRange" ? "date" : "number",
187
+ label,
188
+ value,
189
+ onChange: (e) => write(e.target.value),
190
+ slotProps: { inputLabel: { shrink: true } }
191
+ });
192
+ let bounds = null;
193
+ if (op === "between") bounds = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [input(labels.from, state.a, (raw) => commit("between", raw, state.b)), input(labels.to, state.b, (raw) => commit("between", state.a, raw))] });
194
+ else if (op !== void 0) bounds = input(labels.value, single, (raw) => commit(op, raw, ""));
195
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.FormControl, {
196
+ component: "fieldset",
197
+ variant: "standard",
198
+ sx: { width: "100%" },
199
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.FormLabel, {
200
+ component: "legend",
201
+ sx: { mb: .75 },
202
+ children: (0, _adapttable_core.filterLabel)(def)
203
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
204
+ direction: "row",
205
+ spacing: 1,
206
+ useFlexGap: true,
207
+ sx: { flexWrap: "wrap" },
208
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TextField, {
209
+ select: true,
210
+ size: "small",
211
+ label: labels.operator,
212
+ value: op ?? "",
213
+ onChange: (e) => changeOp(e.target.value),
214
+ slotProps: {
215
+ select: { native: true },
216
+ inputLabel: { shrink: true }
217
+ },
218
+ sx: {
219
+ flex: "0 0 8.5rem",
220
+ width: "8.5rem"
221
+ },
222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { value: "" }), _adapttable_core.RANGE_OPS.map((candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
223
+ value: candidate,
224
+ children: labels[opLabelKeys[candidate]]
225
+ }, candidate))]
226
+ }), bounds]
227
+ })]
228
+ });
232
229
  }
233
- function FilterField({
234
- def,
235
- source,
236
- labels
237
- }) {
238
- switch (def.type) {
239
- case "text":
240
- return /* @__PURE__ */ jsxRuntime.jsx(TextFilter, { def, source });
241
- case "select":
242
- return /* @__PURE__ */ jsxRuntime.jsx(SelectFilter, { def, source });
243
- case "multiSelect":
244
- return /* @__PURE__ */ jsxRuntime.jsx(MultiSelectFilter, { def, source });
245
- case "dateRange":
246
- case "numberRange":
247
- return /* @__PURE__ */ jsxRuntime.jsx(
248
- RangeFilter,
249
- {
250
- def,
251
- type: def.type,
252
- source,
253
- labels
254
- }
255
- );
256
- }
230
+ function FilterField({ def, source, labels }) {
231
+ switch (def.type) {
232
+ case "text": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextFilter, {
233
+ def,
234
+ source
235
+ });
236
+ case "select": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectFilter, {
237
+ def,
238
+ source
239
+ });
240
+ case "multiSelect": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiSelectFilter, {
241
+ def,
242
+ source
243
+ });
244
+ case "dateRange":
245
+ case "numberRange": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RangeFilter, {
246
+ def,
247
+ type: def.type,
248
+ source,
249
+ labels
250
+ });
251
+ }
257
252
  }
258
- function AutoFilterForm({
259
- defs,
260
- source,
261
- labels
262
- }) {
263
- return /* @__PURE__ */ jsxRuntime.jsx(material.Stack, { spacing: 1.5, children: defs.map((def) => /* @__PURE__ */ jsxRuntime.jsx(FilterField, { def, source, labels }, def.key)) });
253
+ /**
254
+ * The auto-built filter form: one MUI widget per declarative definition,
255
+ * reading and writing the source's extra-filter bag (so chips, URL state
256
+ * and — on frontend data — the row predicate all stay in sync). The selects
257
+ * render natively, so every control works inline, without portal menus.
258
+ *
259
+ * @typeParam TRow - The row type.
260
+ */
261
+ function AutoFilterForm({ defs, source, labels }) {
262
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Stack, {
263
+ spacing: 1.5,
264
+ children: defs.map((def) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterField, {
265
+ def,
266
+ source,
267
+ labels
268
+ }, def.key))
269
+ });
264
270
  }
265
- function FilterPopover({
266
- open,
267
- onClose,
268
- anchorEl,
269
- filters,
270
- activeFilterCount,
271
- onClearFilters,
272
- labels,
273
- dir = "ltr"
274
- }) {
275
- react.useEffect(() => {
276
- if (!open) return;
277
- const onKeyDown = (event) => {
278
- if (event.key === "Escape") onClose();
279
- };
280
- document.addEventListener("keydown", onKeyDown);
281
- return () => document.removeEventListener("keydown", onKeyDown);
282
- }, [open, onClose]);
283
- return /* @__PURE__ */ jsxRuntime.jsx(
284
- material.Popper,
285
- {
286
- open: open && anchorEl !== null,
287
- anchorEl,
288
- placement: dir === "rtl" ? "bottom-start" : "bottom-end",
289
- modifiers: [{ name: "offset", options: { offset: [0, 4] } }],
290
- style: { zIndex: 1300 },
291
- children: /* @__PURE__ */ jsxRuntime.jsx(material.ClickAwayListener, { mouseEvent: "onMouseDown", onClickAway: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
292
- material.Paper,
293
- {
294
- elevation: 8,
295
- sx: {
296
- width: 360,
297
- maxWidth: "calc(100vw - 32px)",
298
- borderRadius: 2
299
- },
300
- children: /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { sx: { p: 2, display: "flex", flexDirection: "column", gap: 2 }, children: [
301
- /* @__PURE__ */ jsxRuntime.jsxs(
302
- material.Stack,
303
- {
304
- direction: "row",
305
- justifyContent: "space-between",
306
- alignItems: "center",
307
- children: [
308
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "subtitle1", fontWeight: 600, children: labels.filters }),
309
- /* @__PURE__ */ jsxRuntime.jsx(
310
- material.Button,
311
- {
312
- size: "small",
313
- onClick: onClearFilters,
314
- disabled: activeFilterCount === 0,
315
- children: labels.clearAll
316
- }
317
- )
318
- ]
319
- }
320
- ),
321
- /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: filters })
322
- ] })
323
- }
324
- ) })
325
- }
326
- );
271
+ //#endregion
272
+ //#region src/components/FilterPopover.tsx
273
+ /**
274
+ * Anchored filter card (the default filter container). Opens under the Filters
275
+ * button. Built on a non-modal `Popper` (+ `ClickAwayListener`) rather than the
276
+ * Modal-based `Popover`, so it renders NO backdrop/scrim — the background stays
277
+ * visible and interactive. Closes on outside click or Escape. Placement is
278
+ * `bottom-end` (flips to `bottom-start` for RTL). Pair with
279
+ * `filtersMode="drawer"` for the slide-in panel instead.
280
+ */
281
+ function FilterPopover({ open, onClose, anchorEl, filters, activeFilterCount, onClearFilters, labels, dir = "ltr" }) {
282
+ (0, react.useEffect)(() => {
283
+ if (!open) return;
284
+ const onKeyDown = (event) => {
285
+ if (event.key === "Escape") onClose();
286
+ };
287
+ document.addEventListener("keydown", onKeyDown);
288
+ return () => document.removeEventListener("keydown", onKeyDown);
289
+ }, [open, onClose]);
290
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Popper, {
291
+ open: open && anchorEl !== null,
292
+ anchorEl,
293
+ placement: dir === "rtl" ? "bottom-start" : "bottom-end",
294
+ modifiers: [{
295
+ name: "offset",
296
+ options: { offset: [0, 4] }
297
+ }],
298
+ style: { zIndex: 1300 },
299
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.ClickAwayListener, {
300
+ mouseEvent: "onMouseDown",
301
+ onClickAway: onClose,
302
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Paper, {
303
+ elevation: 8,
304
+ sx: {
305
+ width: 360,
306
+ maxWidth: "calc(100vw - 32px)",
307
+ borderRadius: 2
308
+ },
309
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Box, {
310
+ sx: {
311
+ p: 2,
312
+ display: "flex",
313
+ flexDirection: "column",
314
+ gap: 2
315
+ },
316
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
317
+ direction: "row",
318
+ sx: {
319
+ justifyContent: "space-between",
320
+ alignItems: "center"
321
+ },
322
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
323
+ variant: "subtitle1",
324
+ sx: { fontWeight: 600 },
325
+ children: labels.filters
326
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
327
+ size: "small",
328
+ onClick: onClearFilters,
329
+ disabled: activeFilterCount === 0,
330
+ children: labels.clearAll
331
+ })]
332
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
333
+ sx: {
334
+ display: "flex",
335
+ flexDirection: "column",
336
+ gap: 2
337
+ },
338
+ children: filters
339
+ })]
340
+ })
341
+ })
342
+ })
343
+ });
327
344
  }
345
+ //#endregion
346
+ //#region src/components/chrome.tsx
347
+ /** Funnel/filter glyph (currentColor, no icon-lib dependency). */
328
348
  function FiltersIcon() {
329
- return /* @__PURE__ */ jsxRuntime.jsx(
330
- "svg",
331
- {
332
- width: "16",
333
- height: "16",
334
- viewBox: "0 0 24 24",
335
- fill: "none",
336
- stroke: "currentColor",
337
- strokeWidth: "1.8",
338
- strokeLinecap: "round",
339
- strokeLinejoin: "round",
340
- "aria-hidden": "true",
341
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 5h18l-7 8v5l-4 2v-7L3 5Z" })
342
- }
343
- );
349
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
350
+ width: "16",
351
+ height: "16",
352
+ viewBox: "0 0 24 24",
353
+ fill: "none",
354
+ stroke: "currentColor",
355
+ strokeWidth: "1.8",
356
+ strokeLinecap: "round",
357
+ strokeLinejoin: "round",
358
+ "aria-hidden": "true",
359
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 5h18l-7 8v5l-4 2v-7L3 5Z" })
360
+ });
344
361
  }
362
+ /** Magnifying-glass glyph for the search field (currentColor, no icon-lib). */
345
363
  function SearchIcon() {
346
- return /* @__PURE__ */ jsxRuntime.jsxs(
347
- "svg",
348
- {
349
- width: "18",
350
- height: "18",
351
- viewBox: "0 0 24 24",
352
- fill: "none",
353
- stroke: "currentColor",
354
- strokeWidth: "1.8",
355
- strokeLinecap: "round",
356
- strokeLinejoin: "round",
357
- "aria-hidden": "true",
358
- children: [
359
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "7" }),
360
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 21-4.3-4.3" })
361
- ]
362
- }
363
- );
364
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
365
+ width: "18",
366
+ height: "18",
367
+ viewBox: "0 0 24 24",
368
+ fill: "none",
369
+ stroke: "currentColor",
370
+ strokeWidth: "1.8",
371
+ strokeLinecap: "round",
372
+ strokeLinejoin: "round",
373
+ "aria-hidden": "true",
374
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
375
+ cx: "11",
376
+ cy: "11",
377
+ r: "7"
378
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m21 21-4.3-4.3" })]
379
+ });
364
380
  }
365
- var srOnly = {
366
- position: "absolute",
367
- width: 1,
368
- height: 1,
369
- padding: 0,
370
- margin: -1,
371
- overflow: "hidden",
372
- clip: "rect(0 0 0 0)",
373
- whiteSpace: "nowrap",
374
- border: 0
381
+ /** Inline equivalent of `@mui/utils` visuallyHidden (avoids an extra dep). */
382
+ const srOnly = {
383
+ position: "absolute",
384
+ width: 1,
385
+ height: 1,
386
+ padding: 0,
387
+ margin: -1,
388
+ overflow: "hidden",
389
+ clip: "rect(0 0 0 0)",
390
+ whiteSpace: "nowrap",
391
+ border: 0
375
392
  };
376
- function Toolbar({
377
- table,
378
- hideSearch,
379
- searchPlaceholder,
380
- sortByOptions,
381
- customToolbar,
382
- hasFilters,
383
- activeFilterCount,
384
- showRowsPerPage,
385
- filtersMode,
386
- filters,
387
- filtersOpen,
388
- onToggleFilters,
389
- onFiltersTriggerPointerDown,
390
- onCloseFilters,
391
- onClearFilters,
392
- dir,
393
- columnMenu
394
- }) {
395
- const { labels, source } = table;
396
- const sortOptions = sortByOptions ?? (table.isMobile ? table.sortByOptions : void 0);
397
- const searchProps = table.getSearchInputProps(
398
- searchPlaceholder ? { placeholder: searchPlaceholder } : void 0
399
- );
400
- const filtersAnchorRef = react.useRef(null);
401
- const filtersButton = hasFilters ? /* @__PURE__ */ jsxRuntime.jsx(material.Badge, { color: "primary", badgeContent: activeFilterCount, children: /* @__PURE__ */ jsxRuntime.jsx(
402
- material.Button,
403
- {
404
- ref: filtersAnchorRef,
405
- variant: "outlined",
406
- size: "small",
407
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(FiltersIcon, {}),
408
- "aria-expanded": filtersMode === "popover" ? filtersOpen : void 0,
409
- onPointerDown: onFiltersTriggerPointerDown,
410
- onClick: onToggleFilters,
411
- children: labels.filters
412
- }
413
- ) }) : null;
414
- return /* @__PURE__ */ jsxRuntime.jsxs(
415
- material.Stack,
416
- {
417
- direction: "row",
418
- spacing: 1,
419
- flexWrap: "wrap",
420
- useFlexGap: true,
421
- alignItems: "center",
422
- justifyContent: "space-between",
423
- children: [
424
- !hideSearch && /* @__PURE__ */ jsxRuntime.jsx(
425
- material.TextField,
426
- {
427
- size: "small",
428
- value: searchProps.value,
429
- placeholder: searchProps.placeholder,
430
- slotProps: {
431
- htmlInput: { "aria-label": labels.search, type: "search" },
432
- input: {
433
- startAdornment: /* @__PURE__ */ jsxRuntime.jsx(material.InputAdornment, { position: "start", children: /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, {}) })
434
- }
435
- },
436
- onChange: searchProps.onChange,
437
- sx: { flex: 1, minWidth: 160, maxWidth: 360 }
438
- }
439
- ),
440
- /* @__PURE__ */ jsxRuntime.jsxs(
441
- material.Stack,
442
- {
443
- direction: "row",
444
- spacing: 1,
445
- alignItems: "center",
446
- flexWrap: "wrap",
447
- useFlexGap: true,
448
- children: [
449
- sortOptions && sortOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
450
- material.TextField,
451
- {
452
- select: true,
453
- size: "small",
454
- label: labels.sortBy,
455
- value: source.sortBy ?? "",
456
- onChange: (e) => source.setSort(
457
- e.target.value || void 0,
458
- source.sortDir ?? "asc"
459
- ),
460
- sx: { minWidth: 160 },
461
- children: [
462
- /* @__PURE__ */ jsxRuntime.jsx(material.MenuItem, { value: "", children: "\u2014" }),
463
- sortOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsx(material.MenuItem, { value: o.value, children: o.label }, o.value))
464
- ]
465
- }
466
- ),
467
- customToolbar,
468
- filtersButton,
469
- hasFilters && filtersMode === "popover" && /* @__PURE__ */ jsxRuntime.jsx(
470
- FilterPopover,
471
- {
472
- open: filtersOpen,
473
- onClose: onCloseFilters,
474
- anchorEl: filtersAnchorRef.current,
475
- filters,
476
- activeFilterCount,
477
- onClearFilters,
478
- labels,
479
- dir
480
- }
481
- ),
482
- columnMenu,
483
- showRowsPerPage && /* @__PURE__ */ jsxRuntime.jsx(
484
- material.TextField,
485
- {
486
- select: true,
487
- size: "small",
488
- label: labels.rowsPerPage,
489
- value: source.limit,
490
- onChange: (e) => source.setLimit(Number(e.target.value)),
491
- sx: { minWidth: 110 },
492
- children: core.pageSizeOptions(source.limit).map((n) => /* @__PURE__ */ jsxRuntime.jsx(material.MenuItem, { value: n, children: n }, n))
493
- }
494
- )
495
- ]
496
- }
497
- )
498
- ]
499
- }
500
- );
393
+ /** Search field + sort select + filters button + rows-per-page. */
394
+ function Toolbar({ table, hideSearch, searchPlaceholder, sortByOptions, customToolbar, hasFilters, activeFilterCount, showRowsPerPage, filtersMode, filters, filtersOpen, onToggleFilters, onFiltersTriggerPointerDown, onCloseFilters, onClearFilters, dir, columnMenu }) {
395
+ const { labels, source } = table;
396
+ const sortOptions = sortByOptions ?? (table.isMobile ? table.sortByOptions : void 0);
397
+ const searchProps = table.getSearchInputProps(searchPlaceholder ? { placeholder: searchPlaceholder } : void 0);
398
+ const filtersAnchorRef = (0, react.useRef)(null);
399
+ const filtersButton = hasFilters ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Badge, {
400
+ color: "primary",
401
+ badgeContent: activeFilterCount,
402
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
403
+ ref: filtersAnchorRef,
404
+ variant: "outlined",
405
+ size: "small",
406
+ startIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FiltersIcon, {}),
407
+ "aria-expanded": filtersMode === "popover" ? filtersOpen : void 0,
408
+ onPointerDown: onFiltersTriggerPointerDown,
409
+ onClick: onToggleFilters,
410
+ children: labels.filters
411
+ })
412
+ }) : null;
413
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
414
+ direction: "row",
415
+ spacing: 1,
416
+ useFlexGap: true,
417
+ sx: {
418
+ flexWrap: "wrap",
419
+ alignItems: "center",
420
+ justifyContent: "space-between"
421
+ },
422
+ children: [!hideSearch && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TextField, {
423
+ size: "small",
424
+ value: searchProps.value,
425
+ placeholder: searchProps.placeholder,
426
+ slotProps: {
427
+ htmlInput: {
428
+ "aria-label": labels.search,
429
+ type: "search"
430
+ },
431
+ input: { startAdornment: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.InputAdornment, {
432
+ position: "start",
433
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchIcon, {})
434
+ }) }
435
+ },
436
+ onChange: searchProps.onChange,
437
+ sx: {
438
+ flex: 1,
439
+ minWidth: 160,
440
+ maxWidth: 360
441
+ }
442
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
443
+ direction: "row",
444
+ spacing: 1,
445
+ useFlexGap: true,
446
+ sx: {
447
+ alignItems: "center",
448
+ flexWrap: "wrap"
449
+ },
450
+ children: [
451
+ sortOptions && sortOptions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TextField, {
452
+ select: true,
453
+ size: "small",
454
+ label: labels.sortBy,
455
+ value: source.sortBy ?? "",
456
+ onChange: (e) => source.setSort(e.target.value || void 0, source.sortDir ?? "asc"),
457
+ sx: { minWidth: 160 },
458
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.MenuItem, {
459
+ value: "",
460
+ children: ""
461
+ }), sortOptions.map((o) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.MenuItem, {
462
+ value: o.value,
463
+ children: o.label
464
+ }, o.value))]
465
+ }),
466
+ customToolbar,
467
+ filtersButton,
468
+ hasFilters && filtersMode === "popover" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterPopover, {
469
+ open: filtersOpen,
470
+ onClose: onCloseFilters,
471
+ anchorEl: filtersAnchorRef.current,
472
+ filters,
473
+ activeFilterCount,
474
+ onClearFilters,
475
+ labels,
476
+ dir
477
+ }),
478
+ columnMenu,
479
+ showRowsPerPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TextField, {
480
+ select: true,
481
+ size: "small",
482
+ label: labels.rowsPerPage,
483
+ value: source.limit,
484
+ onChange: (e) => source.setLimit(Number(e.target.value)),
485
+ sx: { minWidth: 110 },
486
+ children: (0, _adapttable_core.pageSizeOptions)(source.limit).map((n) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.MenuItem, {
487
+ value: n,
488
+ children: n
489
+ }, n))
490
+ })
491
+ ]
492
+ })]
493
+ });
501
494
  }
502
- function Chips({
503
- chips,
504
- onClearAll,
505
- labels
506
- }) {
507
- if (chips.length === 0) return null;
508
- return /* @__PURE__ */ jsxRuntime.jsxs(
509
- material.Stack,
510
- {
511
- direction: "row",
512
- spacing: 0.5,
513
- flexWrap: "wrap",
514
- useFlexGap: true,
515
- component: "ul",
516
- sx: { listStyle: "none", p: 0, m: 0 },
517
- "aria-label": labels.filters,
518
- children: [
519
- chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
520
- material.Chip,
521
- {
522
- label: chip.label,
523
- size: "small",
524
- onDelete: chip.onRemove,
525
- deleteIcon: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": `${labels.clearAll}: ${chip.label}`, children: "\xD7" })
526
- }
527
- ) }, chip.key)),
528
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(material.Button, { size: "small", onClick: onClearAll, children: labels.clearAll }) })
529
- ]
530
- }
531
- );
495
+ /** Removable MUI chips. */
496
+ function Chips({ chips, onClearAll, labels }) {
497
+ if (chips.length === 0) return null;
498
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
499
+ direction: "row",
500
+ spacing: .5,
501
+ useFlexGap: true,
502
+ component: "ul",
503
+ "aria-label": labels.filters,
504
+ sx: {
505
+ listStyle: "none",
506
+ p: 0,
507
+ m: 0,
508
+ flexWrap: "wrap"
509
+ },
510
+ children: [chips.map((chip) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Chip, {
511
+ label: chip.label,
512
+ size: "small",
513
+ onDelete: chip.onRemove,
514
+ deleteIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
515
+ "aria-label": `${labels.clearAll}: ${chip.label}`,
516
+ children: "×"
517
+ })
518
+ }) }, chip.key)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
519
+ size: "small",
520
+ onClick: onClearAll,
521
+ children: labels.clearAll
522
+ }) })]
523
+ });
532
524
  }
533
- function BulkBar({
534
- selection,
535
- total,
536
- bulkActions,
537
- confirm,
538
- labels
539
- }) {
540
- const {
541
- selectedIds,
542
- selectedCount,
543
- headerState,
544
- visibleIds,
545
- allMatching,
546
- selectAllMatching,
547
- clear
548
- } = selection;
549
- const { pending, run } = core.useBulkActionRunner({
550
- confirm,
551
- cancelLabel: labels.cancel,
552
- onComplete: clear
553
- });
554
- if (selectedCount === 0) return null;
555
- const ids = [...selectedIds];
556
- const showBanner = headerState === "all" && total > visibleIds.length;
557
- return /* @__PURE__ */ jsxRuntime.jsxs(
558
- material.Stack,
559
- {
560
- direction: "row",
561
- spacing: 1,
562
- alignItems: "center",
563
- justifyContent: "space-between",
564
- flexWrap: "wrap",
565
- useFlexGap: true,
566
- children: [
567
- /* @__PURE__ */ jsxRuntime.jsxs(
568
- material.Stack,
569
- {
570
- direction: "row",
571
- spacing: 1,
572
- alignItems: "center",
573
- flexWrap: "wrap",
574
- useFlexGap: true,
575
- children: [
576
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", children: labels.selectedCount(selectedCount) }),
577
- showBanner && (allMatching ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
578
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", color: "text.secondary", children: labels.allMatchingSelected(total) }),
579
- /* @__PURE__ */ jsxRuntime.jsx(
580
- material.Button,
581
- {
582
- size: "small",
583
- variant: "text",
584
- onClick: clear,
585
- disabled: pending !== null,
586
- children: labels.clearAll
587
- }
588
- )
589
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
590
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", color: "text.secondary", children: labels.pageSelected(visibleIds.length) }),
591
- /* @__PURE__ */ jsxRuntime.jsx(
592
- material.Button,
593
- {
594
- size: "small",
595
- variant: "text",
596
- onClick: selectAllMatching,
597
- disabled: pending !== null,
598
- children: labels.selectAllMatching(total)
599
- }
600
- )
601
- ] }))
602
- ]
603
- }
604
- ),
605
- /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { direction: "row", spacing: 1, flexWrap: "wrap", useFlexGap: true, children: [
606
- /* @__PURE__ */ jsxRuntime.jsx(
607
- material.Button,
608
- {
609
- size: "small",
610
- variant: "text",
611
- onClick: clear,
612
- disabled: pending !== null,
613
- children: labels.clearAll
614
- }
615
- ),
616
- bulkActions.map((action) => {
617
- const reason = core.resolveDisabledReason(action.disabledReason?.(ids));
618
- return /* @__PURE__ */ jsxRuntime.jsx(material.Tooltip, { title: reason ?? "", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: /* @__PURE__ */ jsxRuntime.jsx(
619
- material.Button,
620
- {
621
- size: "small",
622
- variant: "contained",
623
- color: action.color,
624
- startIcon: action.icon,
625
- disabled: reason !== void 0 || pending !== null,
626
- onClick: () => run(
627
- action,
628
- ids,
629
- allMatching ? { allMatching: true, total } : void 0
630
- ),
631
- children: action.label
632
- }
633
- ) }) }, action.key);
634
- })
635
- ] })
636
- ]
637
- }
638
- );
525
+ /** Selection toolbar. */
526
+ function BulkBar({ selection, total, bulkActions, confirm, labels }) {
527
+ const { selectedIds, selectedCount, headerState, visibleIds, allMatching, selectAllMatching, clear } = selection;
528
+ const { pending, run } = (0, _adapttable_core.useBulkActionRunner)({
529
+ confirm,
530
+ cancelLabel: labels.cancel,
531
+ onComplete: clear
532
+ });
533
+ if (selectedCount === 0) return null;
534
+ const ids = [...selectedIds];
535
+ const showBanner = headerState === "all" && total > visibleIds.length;
536
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
537
+ direction: "row",
538
+ spacing: 1,
539
+ useFlexGap: true,
540
+ sx: {
541
+ alignItems: "center",
542
+ justifyContent: "space-between",
543
+ flexWrap: "wrap"
544
+ },
545
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
546
+ direction: "row",
547
+ spacing: 1,
548
+ useFlexGap: true,
549
+ sx: {
550
+ alignItems: "center",
551
+ flexWrap: "wrap"
552
+ },
553
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
554
+ variant: "body2",
555
+ children: labels.selectedCount(selectedCount)
556
+ }), showBanner && (allMatching ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
557
+ variant: "body2",
558
+ color: "text.secondary",
559
+ children: labels.allMatchingSelected(total)
560
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
561
+ size: "small",
562
+ variant: "text",
563
+ onClick: clear,
564
+ disabled: pending !== null,
565
+ children: labels.clearAll
566
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
567
+ variant: "body2",
568
+ color: "text.secondary",
569
+ children: labels.pageSelected(visibleIds.length)
570
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
571
+ size: "small",
572
+ variant: "text",
573
+ onClick: selectAllMatching,
574
+ disabled: pending !== null,
575
+ children: labels.selectAllMatching(total)
576
+ })] }))]
577
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
578
+ direction: "row",
579
+ spacing: 1,
580
+ useFlexGap: true,
581
+ sx: { flexWrap: "wrap" },
582
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
583
+ size: "small",
584
+ variant: "text",
585
+ onClick: clear,
586
+ disabled: pending !== null,
587
+ children: labels.clearAll
588
+ }), bulkActions.map((action) => {
589
+ const reason = (0, _adapttable_core.resolveDisabledReason)(action.disabledReason?.(ids));
590
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Tooltip, {
591
+ title: reason ?? "",
592
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
593
+ size: "small",
594
+ variant: "contained",
595
+ color: action.color,
596
+ startIcon: action.icon,
597
+ disabled: reason !== void 0 || pending !== null,
598
+ onClick: () => run(action, ids, allMatching ? {
599
+ allMatching: true,
600
+ total
601
+ } : void 0),
602
+ children: action.label
603
+ }) })
604
+ }, action.key);
605
+ })]
606
+ })]
607
+ });
639
608
  }
640
- function Footer({
641
- pagination,
642
- total,
643
- limit,
644
- setPage,
645
- setLimit,
646
- labels
647
- }) {
648
- return /* @__PURE__ */ jsxRuntime.jsxs(
649
- material.Stack,
650
- {
651
- direction: "row",
652
- spacing: 2,
653
- alignItems: "center",
654
- justifyContent: "space-between",
655
- flexWrap: "wrap",
656
- useFlexGap: true,
657
- children: [
658
- /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { direction: "row", spacing: 1.5, alignItems: "center", useFlexGap: true, children: [
659
- /* @__PURE__ */ jsxRuntime.jsx(
660
- material.TextField,
661
- {
662
- select: true,
663
- size: "small",
664
- label: labels.rowsPerPage,
665
- value: String(limit),
666
- onChange: (e) => setLimit(Number(e.target.value)),
667
- sx: { minWidth: 100 },
668
- children: core.pageSizeOptions(limit).map((n) => /* @__PURE__ */ jsxRuntime.jsx(material.MenuItem, { value: String(n), children: n }, n))
669
- }
670
- ),
671
- total > 0 && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "caption", color: "text.secondary", children: labels.showing({
672
- from: pagination.fromIndex,
673
- to: pagination.toIndex,
674
- total
675
- }) })
676
- ] }),
677
- /* @__PURE__ */ jsxRuntime.jsx(
678
- material.Pagination,
679
- {
680
- count: pagination.totalPages,
681
- page: pagination.safePage,
682
- onChange: (_, page) => setPage(page),
683
- size: "small",
684
- getItemAriaLabel: (type, page) => {
685
- if (type === "page") return labels.goToPage(page);
686
- return type === "previous" ? labels.previousPage : labels.nextPage;
687
- }
688
- }
689
- )
690
- ]
691
- }
692
- );
609
+ /** Paged footer: rows-per-page + range on the left, pager on the right. */
610
+ function Footer({ pagination, total, limit, setPage, setLimit, labels }) {
611
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
612
+ direction: "row",
613
+ spacing: 2,
614
+ useFlexGap: true,
615
+ sx: {
616
+ alignItems: "center",
617
+ justifyContent: "space-between",
618
+ flexWrap: "wrap"
619
+ },
620
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
621
+ direction: "row",
622
+ spacing: 1.5,
623
+ useFlexGap: true,
624
+ sx: { alignItems: "center" },
625
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TextField, {
626
+ select: true,
627
+ size: "small",
628
+ label: labels.rowsPerPage,
629
+ value: String(limit),
630
+ onChange: (e) => setLimit(Number(e.target.value)),
631
+ sx: { minWidth: 100 },
632
+ children: (0, _adapttable_core.pageSizeOptions)(limit).map((n) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.MenuItem, {
633
+ value: String(n),
634
+ children: n
635
+ }, n))
636
+ }), total > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
637
+ variant: "caption",
638
+ color: "text.secondary",
639
+ children: labels.showing({
640
+ from: pagination.fromIndex,
641
+ to: pagination.toIndex,
642
+ total
643
+ })
644
+ })]
645
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Pagination, {
646
+ count: pagination.totalPages,
647
+ page: pagination.safePage,
648
+ onChange: (_, page) => setPage(page),
649
+ size: "small",
650
+ getItemAriaLabel: (type, page) => {
651
+ if (type === "page") return labels.goToPage(page);
652
+ return type === "previous" ? labels.previousPage : labels.nextPage;
653
+ }
654
+ })]
655
+ });
693
656
  }
694
- function ErrorState({
695
- error,
696
- labels,
697
- onRetry
698
- }) {
699
- return /* @__PURE__ */ jsxRuntime.jsxs(
700
- material.Alert,
701
- {
702
- severity: "error",
703
- action: onRetry ? /* @__PURE__ */ jsxRuntime.jsx(material.Button, { color: "inherit", size: "small", onClick: onRetry, children: labels.retry }) : void 0,
704
- children: [
705
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: labels.errorTitle }),
706
- " \u2014 ",
707
- error.message
708
- ]
709
- }
710
- );
657
+ /** Error alert. */
658
+ function ErrorState({ error, labels, onRetry }) {
659
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Alert, {
660
+ severity: "error",
661
+ action: onRetry ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
662
+ color: "inherit",
663
+ size: "small",
664
+ onClick: onRetry,
665
+ children: labels.retry
666
+ }) : void 0,
667
+ children: [
668
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: labels.errorTitle }),
669
+ " ",
670
+ error.message
671
+ ]
672
+ });
711
673
  }
712
- function LoadingState({
713
- rows,
714
- columns,
715
- loadingLabel
716
- }) {
717
- return /* @__PURE__ */ jsxRuntime.jsxs(
718
- material.Box,
719
- {
720
- role: "status",
721
- "aria-busy": "true",
722
- "aria-live": "polite",
723
- "data-testid": "adapttable-loading",
724
- children: [
725
- Array.from({ length: rows }, (_, r) => /* @__PURE__ */ jsxRuntime.jsx(material.Stack, { direction: "row", spacing: 2, sx: { py: 1 }, children: Array.from({ length: Math.max(columns, 1) }, (_2, c) => /* @__PURE__ */ jsxRuntime.jsx(material.Skeleton, { variant: "text", width: c === 0 ? "30%" : "20%" }, c)) }, r)),
726
- loadingLabel ? /* @__PURE__ */ jsxRuntime.jsx(material.Box, { component: "span", sx: srOnly, children: loadingLabel }) : null
727
- ]
728
- }
729
- );
674
+ /** Skeleton loading placeholder. */
675
+ function LoadingState({ rows, columns, loadingLabel }) {
676
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Box, {
677
+ role: "status",
678
+ "aria-busy": "true",
679
+ "aria-live": "polite",
680
+ "data-testid": "adapttable-loading",
681
+ children: [Array.from({ length: rows }, (_, r) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Stack, {
682
+ direction: "row",
683
+ spacing: 2,
684
+ sx: { py: 1 },
685
+ children: Array.from({ length: Math.max(columns, 1) }, (_, c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Skeleton, {
686
+ variant: "text",
687
+ width: c === 0 ? "30%" : "20%"
688
+ }, c))
689
+ }, r)), loadingLabel ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
690
+ component: "span",
691
+ sx: srOnly,
692
+ children: loadingLabel
693
+ }) : null]
694
+ });
730
695
  }
731
- function FilterDrawer({
732
- open,
733
- onClose,
734
- filters,
735
- activeFilterCount,
736
- onClearFilters,
737
- labels,
738
- dir = "ltr"
739
- }) {
740
- return /* @__PURE__ */ jsxRuntime.jsx(
741
- material.Drawer,
742
- {
743
- anchor: dir === "rtl" ? "left" : "right",
744
- open,
745
- onClose,
746
- children: /* @__PURE__ */ jsxRuntime.jsxs(
747
- material.Box,
748
- {
749
- sx: {
750
- width: 360,
751
- p: 2,
752
- display: "flex",
753
- flexDirection: "column",
754
- gap: 2,
755
- height: "100%"
756
- },
757
- children: [
758
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "h6", children: labels.filters }),
759
- /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { flex: 1, display: "flex", flexDirection: "column", gap: 2 }, children: filters }),
760
- /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { direction: "row", justifyContent: "space-between", children: [
761
- /* @__PURE__ */ jsxRuntime.jsx(material.Button, { onClick: onClearFilters, disabled: activeFilterCount === 0, children: labels.clearAll }),
762
- /* @__PURE__ */ jsxRuntime.jsx(material.Button, { variant: "contained", onClick: onClose, children: labels.applyFilters })
763
- ] })
764
- ]
765
- }
766
- )
767
- }
768
- );
696
+ /** Filters drawer. */
697
+ function FilterDrawer({ open, onClose, filters, activeFilterCount, onClearFilters, labels, dir = "ltr" }) {
698
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Drawer, {
699
+ anchor: dir === "rtl" ? "left" : "right",
700
+ open,
701
+ onClose,
702
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Box, {
703
+ sx: {
704
+ width: 360,
705
+ p: 2,
706
+ display: "flex",
707
+ flexDirection: "column",
708
+ gap: 2,
709
+ height: "100%"
710
+ },
711
+ children: [
712
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
713
+ variant: "h6",
714
+ children: labels.filters
715
+ }),
716
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
717
+ sx: {
718
+ flex: 1,
719
+ display: "flex",
720
+ flexDirection: "column",
721
+ gap: 2
722
+ },
723
+ children: filters
724
+ }),
725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
726
+ direction: "row",
727
+ sx: { justifyContent: "space-between" },
728
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
729
+ onClick: onClearFilters,
730
+ disabled: activeFilterCount === 0,
731
+ children: labels.clearAll
732
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
733
+ variant: "contained",
734
+ onClick: onClose,
735
+ children: labels.applyFilters
736
+ })]
737
+ })
738
+ ]
739
+ })
740
+ });
769
741
  }
770
- function VisibilityToggle({
771
- hidden,
772
- name,
773
- labels,
774
- onToggle
775
- }) {
776
- return /* @__PURE__ */ jsxRuntime.jsx(
777
- material.IconButton,
778
- {
779
- size: "small",
780
- "aria-label": `${hidden ? labels.showColumn : labels.hideColumn}: ${name}`,
781
- "aria-pressed": !hidden,
782
- color: hidden ? "default" : "primary",
783
- onClick: onToggle,
784
- children: /* @__PURE__ */ jsxRuntime.jsx(core.EyeIcon, { off: hidden })
785
- }
786
- );
742
+ //#endregion
743
+ //#region src/components/ColumnMenu.tsx
744
+ /** Eye show/hide toggle — shared by the data rows and the actions row. */
745
+ function VisibilityToggle({ hidden, name, labels, onToggle }) {
746
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.IconButton, {
747
+ size: "small",
748
+ "aria-label": `${hidden ? labels.showColumn : labels.hideColumn}: ${name}`,
749
+ "aria-pressed": !hidden,
750
+ color: hidden ? "default" : "primary",
751
+ onClick: onToggle,
752
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_adapttable_core.EyeIcon, { off: hidden })
753
+ });
787
754
  }
788
- function RowName({
789
- hidden,
790
- name
791
- }) {
792
- return /* @__PURE__ */ jsxRuntime.jsx(
793
- material.Typography,
794
- {
795
- variant: "body2",
796
- sx: {
797
- flex: 1,
798
- color: hidden ? "text.disabled" : "text.primary",
799
- textDecoration: hidden ? "line-through" : "none"
800
- },
801
- children: name
802
- }
803
- );
755
+ /** Row name — struck through and dimmed while the column is hidden. */
756
+ function RowName({ hidden, name }) {
757
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
758
+ variant: "body2",
759
+ sx: {
760
+ flex: 1,
761
+ color: hidden ? "text.disabled" : "text.primary",
762
+ textDecoration: hidden ? "line-through" : "none"
763
+ },
764
+ children: name
765
+ });
804
766
  }
805
- function PinToggle({
806
- active,
807
- label,
808
- onClick
809
- }) {
810
- return /* @__PURE__ */ jsxRuntime.jsx(
811
- material.IconButton,
812
- {
813
- size: "small",
814
- color: active ? "primary" : "default",
815
- "aria-label": label,
816
- onClick,
817
- children: /* @__PURE__ */ jsxRuntime.jsx(core.PinIcon, {})
818
- }
819
- );
767
+ /** Pin button — shared markup; the caller decides the cycle and the label. */
768
+ function PinToggle({ active, label, onClick }) {
769
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.IconButton, {
770
+ size: "small",
771
+ color: active ? "primary" : "default",
772
+ "aria-label": label,
773
+ onClick,
774
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_adapttable_core.PinIcon, {})
775
+ });
820
776
  }
821
- function ActionsRow({
822
- layout,
823
- labels
824
- }) {
825
- const hidden = layout.isHidden(core.ACTIONS_COLUMN_KEY);
826
- const pinned = layout.state.pinned[core.ACTIONS_COLUMN_KEY] === "right";
827
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
828
- /* @__PURE__ */ jsxRuntime.jsx(material.Divider, { sx: { my: 0.5 } }),
829
- /* @__PURE__ */ jsxRuntime.jsxs(
830
- material.Stack,
831
- {
832
- direction: "row",
833
- alignItems: "center",
834
- spacing: 0.5,
835
- sx: { px: 0.5, py: 0.25 },
836
- children: [
837
- /* @__PURE__ */ jsxRuntime.jsx(material.Box, { "aria-hidden": true, sx: { width: 24 } }),
838
- /* @__PURE__ */ jsxRuntime.jsx(
839
- VisibilityToggle,
840
- {
841
- hidden,
842
- name: labels.actions,
843
- labels,
844
- onToggle: () => layout.toggleVisible(core.ACTIONS_COLUMN_KEY)
845
- }
846
- ),
847
- /* @__PURE__ */ jsxRuntime.jsx(RowName, { hidden, name: labels.actions }),
848
- /* @__PURE__ */ jsxRuntime.jsx(
849
- PinToggle,
850
- {
851
- active: pinned,
852
- label: `${pinned ? labels.unpin : labels.pinRight}: ${labels.actions}`,
853
- onClick: () => layout.setPinned(core.ACTIONS_COLUMN_KEY, pinned ? void 0 : "right")
854
- }
855
- )
856
- ]
857
- }
858
- )
859
- ] });
777
+ /**
778
+ * Trailing menu row for the injected row-actions column. It is not a data
779
+ * column — no reorder grip, no left pin — but the layout state treats the
780
+ * reserved `"actions"` key like any other, so the eye hides it and the pin is
781
+ * a ONE-CLICK right↔unpinned toggle (the column always trails, so a left pin
782
+ * would be meaningless).
783
+ */
784
+ function ActionsRow({ layout, labels }) {
785
+ const hidden = layout.isHidden(_adapttable_core.ACTIONS_COLUMN_KEY);
786
+ const pinned = layout.state.pinned[_adapttable_core.ACTIONS_COLUMN_KEY] === "right";
787
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Divider, { sx: { my: .5 } }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
788
+ direction: "row",
789
+ spacing: .5,
790
+ sx: {
791
+ px: .5,
792
+ py: .25,
793
+ alignItems: "center"
794
+ },
795
+ children: [
796
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
797
+ "aria-hidden": true,
798
+ sx: { width: 24 }
799
+ }),
800
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VisibilityToggle, {
801
+ hidden,
802
+ name: labels.actions,
803
+ labels,
804
+ onToggle: () => layout.toggleVisible(_adapttable_core.ACTIONS_COLUMN_KEY)
805
+ }),
806
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowName, {
807
+ hidden,
808
+ name: labels.actions
809
+ }),
810
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PinToggle, {
811
+ active: pinned,
812
+ label: `${pinned ? labels.unpin : labels.pinRight}: ${labels.actions}`,
813
+ onClick: () => layout.setPinned(_adapttable_core.ACTIONS_COLUMN_KEY, pinned ? void 0 : "right")
814
+ })
815
+ ]
816
+ })] });
860
817
  }
861
- function ColumnMenu({
862
- allColumns,
863
- layout,
864
- labels,
865
- hasRowActions
866
- }) {
867
- const drag = core.useColumnDragState();
868
- const [anchor, setAnchor] = react.useState(null);
869
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
870
- /* @__PURE__ */ jsxRuntime.jsx(
871
- material.Button,
872
- {
873
- size: "small",
874
- variant: "outlined",
875
- "aria-expanded": anchor !== null,
876
- "aria-haspopup": "true",
877
- onClick: (e) => setAnchor(e.currentTarget),
878
- children: labels.columns
879
- }
880
- ),
881
- /* @__PURE__ */ jsxRuntime.jsx(
882
- material.Popover,
883
- {
884
- anchorEl: anchor,
885
- open: anchor !== null,
886
- onClose: () => setAnchor(null),
887
- anchorOrigin: { vertical: "bottom", horizontal: "left" },
888
- children: /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { sx: { p: 0.75, minWidth: 250 }, children: [
889
- /* @__PURE__ */ jsxRuntime.jsx(
890
- material.Typography,
891
- {
892
- variant: "caption",
893
- sx: {
894
- display: "block",
895
- px: 1,
896
- pb: 0.5,
897
- fontWeight: 600,
898
- textTransform: "uppercase",
899
- letterSpacing: "0.06em",
900
- color: "text.secondary"
901
- },
902
- children: labels.columns
903
- }
904
- ),
905
- core.columnMenuRows(allColumns, layout).map((r) => {
906
- const indicator = drag.rowAttrs(r.key, r.index);
907
- const edge = indicator["data-drop"];
908
- const edgeOffset = edge === "before" ? "2px" : "-2px";
909
- return /* @__PURE__ */ jsxRuntime.jsxs(
910
- material.Stack,
911
- {
912
- direction: "row",
913
- alignItems: "center",
914
- spacing: 0.5,
915
- sx: {
916
- px: 0.5,
917
- py: 0.25,
918
- cursor: "grab",
919
- opacity: "data-dragging" in indicator ? 0.4 : void 0,
920
- boxShadow: edge ? (theme) => `inset 0 ${edgeOffset} 0 0 ${theme.palette.primary.main}` : void 0
921
- },
922
- ...drag.rowDragProps(r.key, r.index),
923
- ...drag.dropProps(r.index, layout.move),
924
- ...indicator,
925
- children: [
926
- /* @__PURE__ */ jsxRuntime.jsx(
927
- material.IconButton,
928
- {
929
- size: "small",
930
- sx: { cursor: "grab", color: "text.disabled" },
931
- ...core.columnReorderKeyProps(
932
- r.key,
933
- r.index,
934
- layout.move,
935
- `${labels.moveLeft} / ${labels.moveRight}: ${r.name}`
936
- ),
937
- children: /* @__PURE__ */ jsxRuntime.jsx(core.GripIcon, {})
938
- }
939
- ),
940
- /* @__PURE__ */ jsxRuntime.jsx(
941
- VisibilityToggle,
942
- {
943
- hidden: r.hidden,
944
- name: r.name,
945
- labels,
946
- onToggle: () => layout.toggleVisible(r.key)
947
- }
948
- ),
949
- /* @__PURE__ */ jsxRuntime.jsx(RowName, { hidden: r.hidden, name: r.name }),
950
- /* @__PURE__ */ jsxRuntime.jsx(
951
- PinToggle,
952
- {
953
- active: r.pinned !== void 0,
954
- label: `${core.pinActionLabel(r.pinned, labels)}: ${r.name}`,
955
- onClick: () => layout.setPinned(r.key, core.nextPinSide(r.pinned))
956
- }
957
- )
958
- ]
959
- },
960
- r.key
961
- );
962
- }),
963
- hasRowActions && /* @__PURE__ */ jsxRuntime.jsx(ActionsRow, { layout, labels }),
964
- /* @__PURE__ */ jsxRuntime.jsx(material.Divider, { sx: { my: 0.5 } }),
965
- /* @__PURE__ */ jsxRuntime.jsx(
966
- material.Button,
967
- {
968
- size: "small",
969
- fullWidth: true,
970
- sx: { justifyContent: "flex-start" },
971
- onClick: () => layout.reset(),
972
- children: labels.resetColumns
973
- }
974
- )
975
- ] })
976
- }
977
- )
978
- ] });
818
+ /**
819
+ * MUI column-management popover: per-column drag grip (reorder), eye
820
+ * (show/hide), and pin toggle. A `Popover` (not a `Menu`) so list keyboard
821
+ * navigation never fights the grip's arrow-key reorder. With row actions, a
822
+ * separated trailing row manages the injected actions column too.
823
+ */
824
+ function ColumnMenu({ allColumns, layout, labels, hasRowActions }) {
825
+ const drag = (0, _adapttable_core.useColumnDragState)();
826
+ const [anchor, setAnchor] = (0, react.useState)(null);
827
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
828
+ size: "small",
829
+ variant: "outlined",
830
+ "aria-expanded": anchor !== null,
831
+ "aria-haspopup": "true",
832
+ onClick: (e) => setAnchor(e.currentTarget),
833
+ children: labels.columns
834
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Popover, {
835
+ anchorEl: anchor,
836
+ open: anchor !== null,
837
+ onClose: () => setAnchor(null),
838
+ anchorOrigin: {
839
+ vertical: "bottom",
840
+ horizontal: "left"
841
+ },
842
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Box, {
843
+ sx: {
844
+ p: .75,
845
+ minWidth: 250
846
+ },
847
+ children: [
848
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
849
+ variant: "caption",
850
+ sx: {
851
+ display: "block",
852
+ px: 1,
853
+ pb: .5,
854
+ fontWeight: 600,
855
+ textTransform: "uppercase",
856
+ letterSpacing: "0.06em",
857
+ color: "text.secondary"
858
+ },
859
+ children: labels.columns
860
+ }),
861
+ (0, _adapttable_core.columnMenuRows)(allColumns, layout).map((r) => {
862
+ const indicator = drag.rowAttrs(r.key, r.index);
863
+ const edge = indicator["data-drop"];
864
+ const edgeOffset = edge === "before" ? "2px" : "-2px";
865
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
866
+ direction: "row",
867
+ spacing: .5,
868
+ sx: {
869
+ alignItems: "center",
870
+ px: .5,
871
+ py: .25,
872
+ cursor: "grab",
873
+ opacity: "data-dragging" in indicator ? .4 : void 0,
874
+ boxShadow: edge ? (theme) => `inset 0 ${edgeOffset} 0 0 ${theme.palette.primary.main}` : void 0
875
+ },
876
+ ...drag.rowDragProps(r.key, r.index),
877
+ ...drag.dropProps(r.index, layout.move),
878
+ ...indicator,
879
+ children: [
880
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.IconButton, {
881
+ size: "small",
882
+ sx: {
883
+ cursor: "grab",
884
+ color: "text.disabled"
885
+ },
886
+ ...(0, _adapttable_core.columnReorderKeyProps)(r.key, r.index, layout.move, `${labels.moveLeft} / ${labels.moveRight}: ${r.name}`),
887
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_adapttable_core.GripIcon, {})
888
+ }),
889
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VisibilityToggle, {
890
+ hidden: r.hidden,
891
+ name: r.name,
892
+ labels,
893
+ onToggle: () => layout.toggleVisible(r.key)
894
+ }),
895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowName, {
896
+ hidden: r.hidden,
897
+ name: r.name
898
+ }),
899
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PinToggle, {
900
+ active: r.pinned !== void 0,
901
+ label: `${(0, _adapttable_core.pinActionLabel)(r.pinned, labels)}: ${r.name}`,
902
+ onClick: () => layout.setPinned(r.key, (0, _adapttable_core.nextPinSide)(r.pinned))
903
+ })
904
+ ]
905
+ }, r.key);
906
+ }),
907
+ hasRowActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionsRow, {
908
+ layout,
909
+ labels
910
+ }),
911
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Divider, { sx: { my: .5 } }),
912
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
913
+ size: "small",
914
+ fullWidth: true,
915
+ sx: { justifyContent: "flex-start" },
916
+ onClick: () => layout.reset(),
917
+ children: labels.resetColumns
918
+ })
919
+ ]
920
+ })
921
+ })] });
979
922
  }
980
- var RESIZE_HANDLE_SX = {
981
- position: "absolute",
982
- insetInlineEnd: 0,
983
- top: 0,
984
- height: "100%",
985
- width: 8,
986
- cursor: "col-resize",
987
- touchAction: "none",
988
- userSelect: "none"
923
+ //#endregion
924
+ //#region src/components/tables.tsx
925
+ /** Sx for an absolutely-positioned column-resize handle. */
926
+ const RESIZE_HANDLE_SX = {
927
+ position: "absolute",
928
+ insetInlineEnd: 0,
929
+ top: 0,
930
+ height: "100%",
931
+ width: 8,
932
+ cursor: "col-resize",
933
+ touchAction: "none",
934
+ userSelect: "none"
989
935
  };
936
+ /** Map a destructive colour token to MUI's `"error"` palette, else default. */
990
937
  function muiColor(color) {
991
- return color === "danger" || color === "red" || color === "error" ? "error" : "default";
938
+ return color === "danger" || color === "red" || color === "error" ? "error" : "default";
992
939
  }
993
- var EXPAND_WIDTH = 48;
940
+ /** Width (px) of the leading expand-chevron column (MUI's checkbox cell). */
941
+ const EXPAND_WIDTH = 48;
942
+ /**
943
+ * Identity-stable dispatcher for `selection.toggle` / `expansion.toggle`.
944
+ * `selection.toggle` is recreated whenever the selection changes, so handing
945
+ * it straight to a memoized row would either defeat the memo (if compared)
946
+ * or go stale (if not — in the controlled mode it computes from the captured
947
+ * set). This wrapper never changes identity and always dispatches to the
948
+ * CURRENT target, so skipped rows still toggle against fresh state.
949
+ */
994
950
  function useStableToggle(target) {
995
- const ref = react.useRef(target);
996
- ref.current = target;
997
- return react.useCallback((id) => ref.current?.toggle(id), []);
951
+ const ref = (0, react.useRef)(target);
952
+ ref.current = target;
953
+ return (0, react.useCallback)((id) => ref.current?.toggle(id), []);
998
954
  }
999
- function ExpandChevron({
1000
- expanded,
1001
- dir
1002
- }) {
1003
- let transform;
1004
- if (expanded) transform = "rotate(90deg)";
1005
- else if (dir === "rtl") transform = "rotate(180deg)";
1006
- return /* @__PURE__ */ jsxRuntime.jsx(
1007
- material.Box,
1008
- {
1009
- component: "span",
1010
- "aria-hidden": true,
1011
- sx: { display: "inline-flex", transition: "transform 150ms", transform },
1012
- children: /* @__PURE__ */ jsxRuntime.jsx(
1013
- "svg",
1014
- {
1015
- width: "1em",
1016
- height: "1em",
1017
- viewBox: "0 0 24 24",
1018
- fill: "none",
1019
- stroke: "currentColor",
1020
- strokeWidth: "2",
1021
- strokeLinecap: "round",
1022
- strokeLinejoin: "round",
1023
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 6l6 6-6 6" })
1024
- }
1025
- )
1026
- }
1027
- );
955
+ /** Inline chevron pointing at the reading end; rotates down when open. */
956
+ function ExpandChevron({ expanded, dir }) {
957
+ let transform;
958
+ if (expanded) transform = "rotate(90deg)";
959
+ else if (dir === "rtl") transform = "rotate(180deg)";
960
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
961
+ component: "span",
962
+ "aria-hidden": true,
963
+ sx: {
964
+ display: "inline-flex",
965
+ transition: "transform 150ms",
966
+ transform
967
+ },
968
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
969
+ width: "1em",
970
+ height: "1em",
971
+ viewBox: "0 0 24 24",
972
+ fill: "none",
973
+ stroke: "currentColor",
974
+ strokeWidth: "2",
975
+ strokeLinecap: "round",
976
+ strokeLinejoin: "round",
977
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M9 6l6 6-6 6" })
978
+ })
979
+ });
1028
980
  }
1029
- function ExpandToggle({
1030
- id,
1031
- expanded,
1032
- onToggle,
1033
- dir,
1034
- expandLabel,
1035
- collapseLabel
1036
- }) {
1037
- return /* @__PURE__ */ jsxRuntime.jsx(
1038
- material.IconButton,
1039
- {
1040
- size: "small",
1041
- "aria-expanded": expanded,
1042
- "aria-label": expanded ? collapseLabel : expandLabel,
1043
- onClick: () => onToggle(id),
1044
- children: /* @__PURE__ */ jsxRuntime.jsx(ExpandChevron, { expanded, dir })
1045
- }
1046
- );
981
+ /** The per-row expand/collapse chevron button (desktop cell + mobile card). */
982
+ function ExpandToggle({ id, expanded, onToggle, dir, expandLabel, collapseLabel }) {
983
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.IconButton, {
984
+ size: "small",
985
+ "aria-expanded": expanded,
986
+ "aria-label": expanded ? collapseLabel : expandLabel,
987
+ onClick: () => onToggle(id),
988
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpandChevron, {
989
+ expanded,
990
+ dir
991
+ })
992
+ });
1047
993
  }
994
+ /**
995
+ * Logical (RTL-aware) `text-align` for a column. Applied via `sx` rather
996
+ * than MUI's physical `align` prop so `"end"` follows the writing direction
997
+ * (right in LTR, left in RTL).
998
+ */
1048
999
  function muiAlign(align) {
1049
- if (align === "center") return "center";
1050
- if (align === "end") return "end";
1051
- return "start";
1000
+ if (align === "center") return "center";
1001
+ if (align === "end") return "end";
1002
+ return "start";
1052
1003
  }
1053
- function RowActionButtons({
1054
- row,
1055
- actions,
1056
- confirm,
1057
- cancelLabel
1058
- }) {
1059
- return /* @__PURE__ */ jsxRuntime.jsx(material.Stack, { direction: "row", spacing: 0.5, justifyContent: "flex-end", children: actions.map((action) => {
1060
- if (action.isHidden?.(row)) return null;
1061
- const reason = core.resolveDisabledReason(action.disabledReason?.(row));
1062
- const disabled = reason !== void 0 || (action.isDisabled?.(row) ?? false);
1063
- return /* @__PURE__ */ jsxRuntime.jsx(material.Tooltip, { title: reason ?? action.label, children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: /* @__PURE__ */ jsxRuntime.jsx(
1064
- material.IconButton,
1065
- {
1066
- size: "small",
1067
- color: muiColor(action.color),
1068
- disabled,
1069
- "aria-label": action.label,
1070
- onClick: (
1071
- // The disabled attribute already blocks activation, so
1072
- // attach the handler only when the action can run.
1073
- disabled ? void 0 : (e) => {
1074
- e.stopPropagation();
1075
- core.runRowAction(action, row, confirm, cancelLabel);
1076
- }
1077
- ),
1078
- children: action.icon ?? /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "caption", children: action.label })
1079
- }
1080
- ) }) }, action.key);
1081
- }) });
1004
+ function RowActionButtons({ row, actions, confirm, cancelLabel }) {
1005
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Stack, {
1006
+ direction: "row",
1007
+ spacing: .5,
1008
+ sx: { justifyContent: "flex-end" },
1009
+ children: actions.map((action) => {
1010
+ if (action.isHidden?.(row)) return null;
1011
+ const reason = (0, _adapttable_core.resolveDisabledReason)(action.disabledReason?.(row));
1012
+ const disabled = reason !== void 0 || (action.isDisabled?.(row) ?? false);
1013
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Tooltip, {
1014
+ title: reason ?? action.label,
1015
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.IconButton, {
1016
+ size: "small",
1017
+ color: muiColor(action.color),
1018
+ disabled,
1019
+ "aria-label": action.label,
1020
+ onClick: disabled ? void 0 : (e) => {
1021
+ e.stopPropagation();
1022
+ (0, _adapttable_core.runRowAction)(action, row, confirm, cancelLabel);
1023
+ },
1024
+ children: action.icon ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
1025
+ variant: "caption",
1026
+ children: action.label
1027
+ })
1028
+ }) })
1029
+ }, action.key);
1030
+ })
1031
+ });
1082
1032
  }
1083
- var DESKTOP_ROW_COMPARED = [
1084
- "row",
1085
- "index",
1086
- "selected",
1087
- "expanded",
1088
- "columns",
1089
- "sx",
1090
- "columnSpan",
1091
- "size",
1092
- "dir",
1093
- "className",
1094
- "hasSelection",
1095
- "hasExpansion",
1096
- "showActions",
1097
- "selectRowLabel",
1098
- "cancelLabel",
1099
- "expandLabel",
1100
- "collapseLabel"
1033
+ /**
1034
+ * The visual inputs a desktop row re-renders for. Deliberately NOT the
1035
+ * per-render `table` object or the handler props: handlers are stable (or
1036
+ * latest-dispatching wrappers), and everything that changes what the row
1037
+ * LOOKS like is listed here — row identity, selection/expansion flags,
1038
+ * density, column set, the precomputed sx map (pins + widths), the
1039
+ * rowClassName output, and direction.
1040
+ */
1041
+ const DESKTOP_ROW_COMPARED = [
1042
+ "row",
1043
+ "index",
1044
+ "selected",
1045
+ "expanded",
1046
+ "columns",
1047
+ "sx",
1048
+ "columnSpan",
1049
+ "size",
1050
+ "dir",
1051
+ "className",
1052
+ "hasSelection",
1053
+ "hasExpansion",
1054
+ "showActions",
1055
+ "selectRowLabel",
1056
+ "cancelLabel",
1057
+ "expandLabel",
1058
+ "collapseLabel"
1101
1059
  ];
1102
1060
  function desktopRowPropsAreEqual(prev, next) {
1103
- return DESKTOP_ROW_COMPARED.every((key) => prev[key] === next[key]);
1061
+ return DESKTOP_ROW_COMPARED.every((key) => prev[key] === next[key]);
1104
1062
  }
1105
- function DesktopRowImpl({
1106
- row,
1107
- index,
1108
- selected,
1109
- expanded,
1110
- columns,
1111
- sx,
1112
- columnSpan,
1113
- dir,
1114
- className,
1115
- hasSelection,
1116
- hasExpansion,
1117
- showActions,
1118
- selectRowLabel,
1119
- cancelLabel,
1120
- expandLabel,
1121
- collapseLabel,
1122
- id,
1123
- rowActions,
1124
- confirm,
1125
- renderRowDetail,
1126
- onToggleSelect,
1127
- onToggleExpand,
1128
- onRowClick,
1129
- prefetch,
1130
- measureElement
1131
- }) {
1132
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1133
- /* @__PURE__ */ jsxRuntime.jsxs(
1134
- material.TableRow,
1135
- {
1136
- ...core.rowClickProps(row, onRowClick),
1137
- className,
1138
- ref: measureElement,
1139
- "data-index": index,
1140
- hover: true,
1141
- selected,
1142
- onMouseEnter: prefetch ? () => prefetch(row) : void 0,
1143
- children: [
1144
- hasExpansion && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { padding: "checkbox", sx: sx.expand, children: /* @__PURE__ */ jsxRuntime.jsx(
1145
- ExpandToggle,
1146
- {
1147
- id,
1148
- expanded,
1149
- onToggle: onToggleExpand,
1150
- dir,
1151
- expandLabel,
1152
- collapseLabel
1153
- }
1154
- ) }),
1155
- hasSelection && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { padding: "checkbox", sx: sx.selection, children: /* @__PURE__ */ jsxRuntime.jsx(
1156
- material.Checkbox,
1157
- {
1158
- slotProps: { input: { "aria-label": selectRowLabel } },
1159
- checked: selected,
1160
- onChange: () => onToggleSelect(id)
1161
- }
1162
- ) }),
1163
- columns.map((column) => /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { sx: sx.cells[column.key], children: column.Cell ? /* @__PURE__ */ jsxRuntime.jsx(column.Cell, { row, rowIndex: index }) : column.accessor?.(row) }, column.key)),
1164
- showActions && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { sx: sx.actions, children: /* @__PURE__ */ jsxRuntime.jsx(
1165
- RowActionButtons,
1166
- {
1167
- row,
1168
- actions: rowActions,
1169
- confirm,
1170
- cancelLabel
1171
- }
1172
- ) })
1173
- ]
1174
- }
1175
- ),
1176
- expanded && /* @__PURE__ */ jsxRuntime.jsx(material.TableRow, { children: /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { colSpan: columnSpan, children: renderRowDetail(row) }) })
1177
- ] });
1063
+ function DesktopRowImpl({ row, index, selected, expanded, columns, sx, columnSpan, dir, className, hasSelection, hasExpansion, showActions, selectRowLabel, cancelLabel, expandLabel, collapseLabel, id, rowActions, confirm, renderRowDetail, onToggleSelect, onToggleExpand, onRowClick, prefetch, measureElement }) {
1064
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableRow, {
1065
+ ...(0, _adapttable_core.rowClickProps)(row, onRowClick),
1066
+ className,
1067
+ ref: measureElement,
1068
+ "data-index": index,
1069
+ hover: true,
1070
+ selected,
1071
+ onMouseEnter: prefetch ? () => prefetch(row) : void 0,
1072
+ children: [
1073
+ hasExpansion && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1074
+ padding: "checkbox",
1075
+ sx: sx.expand,
1076
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpandToggle, {
1077
+ id,
1078
+ expanded,
1079
+ onToggle: onToggleExpand,
1080
+ dir,
1081
+ expandLabel,
1082
+ collapseLabel
1083
+ })
1084
+ }),
1085
+ hasSelection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1086
+ padding: "checkbox",
1087
+ sx: sx.selection,
1088
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Checkbox, {
1089
+ slotProps: { input: { "aria-label": selectRowLabel } },
1090
+ checked: selected,
1091
+ onChange: () => onToggleSelect(id)
1092
+ })
1093
+ }),
1094
+ columns.map((column) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1095
+ sx: sx.cells[column.key],
1096
+ children: column.Cell ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(column.Cell, {
1097
+ row,
1098
+ rowIndex: index
1099
+ }) : column.accessor?.(row)
1100
+ }, column.key)),
1101
+ showActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1102
+ sx: sx.actions,
1103
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowActionButtons, {
1104
+ row,
1105
+ actions: rowActions,
1106
+ confirm,
1107
+ cancelLabel
1108
+ })
1109
+ })
1110
+ ]
1111
+ }), expanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableRow, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1112
+ colSpan: columnSpan,
1113
+ children: renderRowDetail(row)
1114
+ }) })] });
1178
1115
  }
1179
- var DesktopRow = react.memo(
1180
- DesktopRowImpl,
1181
- desktopRowPropsAreEqual
1182
- );
1183
- function DesktopTable({
1184
- table,
1185
- rows,
1186
- rowActions,
1187
- confirm,
1188
- getRowId,
1189
- size,
1190
- dir,
1191
- prefetch,
1192
- onRowClick,
1193
- rowClassName,
1194
- renderRowDetail,
1195
- summaryRow,
1196
- expansion,
1197
- rowEntries,
1198
- paddingTop = 0,
1199
- paddingBottom = 0,
1200
- measureElement,
1201
- stickyHeader = false,
1202
- stickyTop = 0,
1203
- pinOffset,
1204
- maxHeight,
1205
- virtualScrollRef,
1206
- setWidth,
1207
- columnWidths,
1208
- resizeLabel = "Resize column",
1209
- actionsPinned = false
1210
- }) {
1211
- const { columns, selection, labels, showActions, entries, columnSpan } = core.tableRenderModel({
1212
- table,
1213
- rows,
1214
- rowActions,
1215
- getRowId,
1216
- rowEntries,
1217
- renderRowDetail,
1218
- expansion
1219
- });
1220
- const groupRow = core.headerGroupRow(columns);
1221
- const summaryCells = summaryRow?.(rows);
1222
- const isExpanded = expansion && renderRowDetail ? expansion.isExpanded : void 0;
1223
- const expandActive = isExpanded !== void 0;
1224
- const onToggleSelect = useStableToggle(selection);
1225
- const onToggleExpand = useStableToggle(expansion);
1226
- const hasPinned = actionsPinned || table.columns.some((c) => pinOffset?.(c.key) != null);
1227
- const overflow = core.useHorizontalOverflow();
1228
- const inScrollBox = maxHeight != null || hasPinned || overflow.overflowing;
1229
- const headSx = stickyHeader ? {
1230
- position: "sticky",
1231
- top: inScrollBox ? 0 : stickyTop,
1232
- zIndex: core.PIN_Z.header,
1233
- bgcolor: "background.paper"
1234
- } : void 0;
1235
- const selectionWidth = 48;
1236
- const actionsWidth = 120;
1237
- const leadLeft = (expandActive ? EXPAND_WIDTH : 0) + (selection ? selectionWidth : 0);
1238
- const leadRight = showActions ? actionsWidth : 0;
1239
- const leads = { left: leadLeft, right: leadRight };
1240
- const selectionLead = expandActive ? EXPAND_WIDTH : 0;
1241
- const hasLeftPin = table.columns.some(
1242
- (c) => pinOffset?.(c.key)?.side === "left"
1243
- );
1244
- const hasRightPin = table.columns.some(
1245
- (c) => pinOffset?.(c.key)?.side === "right"
1246
- );
1247
- const stickActions = hasRightPin || actionsPinned;
1248
- const headCellSx = (column) => {
1249
- const pin = core.pinnedCellStyle(
1250
- pinOffset?.(column.key),
1251
- core.PIN_Z.headerPinned,
1252
- leads
1253
- );
1254
- const width = pin ? core.pinnedColumnWidth(column, columnWidths) : columnWidths?.[column.key] ?? column.width;
1255
- const needsRelative = Boolean(setWidth) && !headSx && !pin;
1256
- return {
1257
- ...headSx,
1258
- ...pin && { ...pin, bgcolor: "background.paper" },
1259
- ...needsRelative && { position: "relative" },
1260
- textAlign: muiAlign(column.align),
1261
- ...width != null && { width }
1262
- };
1263
- };
1264
- const edgeHeadSx = (side, active, lead = 0) => {
1265
- const pin = core.edgePinStyle(side, active, core.PIN_Z.headerPinned);
1266
- return {
1267
- ...headSx,
1268
- ...pin && { ...pin, bgcolor: "background.paper" },
1269
- ...pin && lead > 0 && { insetInlineStart: lead }
1270
- };
1271
- };
1272
- const rowSx = react.useMemo(() => {
1273
- const edge = (side, active, lead = 0) => {
1274
- const pin = core.edgePinStyle(side, active, core.PIN_Z.body);
1275
- if (!pin) return void 0;
1276
- return {
1277
- ...pin,
1278
- ...lead > 0 && { insetInlineStart: lead },
1279
- bgcolor: "background.paper"
1280
- };
1281
- };
1282
- const cells = {};
1283
- for (const column of columns) {
1284
- const pin = core.pinnedCellStyle(pinOffset?.(column.key), core.PIN_Z.body, {
1285
- left: leadLeft,
1286
- right: leadRight
1287
- });
1288
- cells[column.key] = {
1289
- ...pin && { ...pin, bgcolor: "background.paper" },
1290
- textAlign: muiAlign(column.align)
1291
- };
1292
- }
1293
- return {
1294
- cells,
1295
- expand: edge("left", hasLeftPin),
1296
- selection: edge("left", hasLeftPin, selectionLead),
1297
- actions: { ...edge("right", stickActions), textAlign: "end" }
1298
- };
1299
- }, [
1300
- columns,
1301
- pinOffset,
1302
- leadLeft,
1303
- leadRight,
1304
- hasLeftPin,
1305
- stickActions,
1306
- selectionLead
1307
- ]);
1308
- let boxSx;
1309
- if (maxHeight != null) {
1310
- boxSx = { maxHeight, overflow: "auto" };
1311
- } else if (hasPinned || overflow.overflowing) {
1312
- boxSx = { overflowX: "auto" };
1313
- }
1314
- const minWidth = core.tableMinWidth(columns, {
1315
- widths: columnWidths,
1316
- extra: leadLeft + leadRight
1317
- });
1318
- return /* @__PURE__ */ jsxRuntime.jsx(
1319
- material.Box,
1320
- {
1321
- ref: (node) => {
1322
- overflow.ref(node);
1323
- virtualScrollRef?.(node);
1324
- },
1325
- sx: boxSx,
1326
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1327
- material.Table,
1328
- {
1329
- size,
1330
- "aria-label": table.getTableProps()["aria-label"],
1331
- sx: minWidth > 0 ? { minWidth } : void 0,
1332
- children: [
1333
- /* @__PURE__ */ jsxRuntime.jsxs(material.TableHead, { children: [
1334
- groupRow && // Decorative group row. It deliberately skips the sticky `headSx`
1335
- // treatment: sticking both header rows at the same `top` would
1336
- // overlap them, so only the sortable header row pins.
1337
- /* @__PURE__ */ jsxRuntime.jsxs(material.TableRow, { children: [
1338
- expandActive && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { padding: "checkbox" }),
1339
- selection && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { padding: "checkbox" }),
1340
- groupRow.map((cell) => /* @__PURE__ */ jsxRuntime.jsx(
1341
- material.TableCell,
1342
- {
1343
- colSpan: cell.span,
1344
- sx: { textAlign: "center", fontWeight: 600 },
1345
- children: cell.label
1346
- },
1347
- cell.key
1348
- )),
1349
- showActions && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, {})
1350
- ] }),
1351
- /* @__PURE__ */ jsxRuntime.jsxs(material.TableRow, { children: [
1352
- expandActive && /* @__PURE__ */ jsxRuntime.jsx(
1353
- material.TableCell,
1354
- {
1355
- padding: "checkbox",
1356
- sx: edgeHeadSx("left", hasLeftPin)
1357
- }
1358
- ),
1359
- selection && /* @__PURE__ */ jsxRuntime.jsx(
1360
- material.TableCell,
1361
- {
1362
- padding: "checkbox",
1363
- sx: edgeHeadSx("left", hasLeftPin, selectionLead),
1364
- children: /* @__PURE__ */ jsxRuntime.jsx(
1365
- material.Checkbox,
1366
- {
1367
- slotProps: { input: { "aria-label": labels.selectAll } },
1368
- checked: selection.headerState === "all",
1369
- indeterminate: selection.headerState === "some",
1370
- onChange: selection.toggleAll
1371
- }
1372
- )
1373
- }
1374
- ),
1375
- columns.map((column) => {
1376
- const headerCellProps = table.getHeaderCellProps(column);
1377
- const ariaSort = headerCellProps["aria-sort"];
1378
- const active = ariaSort === "ascending" || ariaSort === "descending";
1379
- const sortIndex = headerCellProps["data-sort-index"];
1380
- return /* @__PURE__ */ jsxRuntime.jsxs(
1381
- material.TableCell,
1382
- {
1383
- "aria-sort": ariaSort,
1384
- "data-sort-index": sortIndex,
1385
- sx: headCellSx(column),
1386
- children: [
1387
- column.sortable ? /* @__PURE__ */ jsxRuntime.jsxs(
1388
- material.TableSortLabel,
1389
- {
1390
- active,
1391
- direction: ariaSort === "descending" ? "desc" : "asc",
1392
- onClick: table.getSortButtonProps(column).onClick,
1393
- children: [
1394
- column.header,
1395
- sortIndex !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(material.Box, { component: "span", sx: { fontSize: 10, ml: 0.5 }, children: sortIndex })
1396
- ]
1397
- }
1398
- ) : column.header,
1399
- setWidth && /* @__PURE__ */ jsxRuntime.jsx(
1400
- material.Box,
1401
- {
1402
- component: "span",
1403
- sx: RESIZE_HANDLE_SX,
1404
- ...core.columnResizeHandleProps(
1405
- column.key,
1406
- setWidth,
1407
- `${resizeLabel}: ${typeof column.header === "string" ? column.header : column.key}`
1408
- )
1409
- }
1410
- )
1411
- ]
1412
- },
1413
- column.key
1414
- );
1415
- }),
1416
- showActions && /* @__PURE__ */ jsxRuntime.jsx(
1417
- material.TableCell,
1418
- {
1419
- sx: { ...edgeHeadSx("right", stickActions), textAlign: "end" },
1420
- children: labels.actions
1421
- }
1422
- )
1423
- ] })
1424
- ] }),
1425
- /* @__PURE__ */ jsxRuntime.jsxs(material.TableBody, { children: [
1426
- paddingTop > 0 && /* @__PURE__ */ jsxRuntime.jsx(material.TableRow, { "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
1427
- material.TableCell,
1428
- {
1429
- colSpan: columnSpan,
1430
- sx: { height: paddingTop, p: 0 }
1431
- }
1432
- ) }),
1433
- entries.map(({ row, index, key }) => {
1434
- const id = getRowId(row);
1435
- return /* @__PURE__ */ jsxRuntime.jsx(
1436
- DesktopRow,
1437
- {
1438
- row,
1439
- index,
1440
- selected: selection?.isSelected(id) ?? false,
1441
- expanded: isExpanded ? isExpanded(id) : false,
1442
- columns,
1443
- sx: rowSx,
1444
- columnSpan,
1445
- size,
1446
- dir,
1447
- className: rowClassName?.(row, index),
1448
- hasSelection: Boolean(selection),
1449
- hasExpansion: expandActive,
1450
- showActions,
1451
- selectRowLabel: labels.selectRow,
1452
- cancelLabel: labels.cancel,
1453
- expandLabel: labels.expandRow,
1454
- collapseLabel: labels.collapseRow,
1455
- id,
1456
- rowActions,
1457
- confirm,
1458
- renderRowDetail,
1459
- onToggleSelect,
1460
- onToggleExpand,
1461
- onRowClick,
1462
- prefetch,
1463
- measureElement
1464
- },
1465
- key
1466
- );
1467
- }),
1468
- paddingBottom > 0 && /* @__PURE__ */ jsxRuntime.jsx(material.TableRow, { "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
1469
- material.TableCell,
1470
- {
1471
- colSpan: columnSpan,
1472
- sx: { height: paddingBottom, p: 0 }
1473
- }
1474
- ) })
1475
- ] }),
1476
- summaryCells && /* @__PURE__ */ jsxRuntime.jsx(material.TableFooter, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.TableRow, { children: [
1477
- expandActive && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { padding: "checkbox" }),
1478
- selection && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, { padding: "checkbox" }),
1479
- columns.map((column) => (
1480
- // One cell per column keeps the summary aligned under its
1481
- // column; keys absent from the result render empty cells.
1482
- /* @__PURE__ */ jsxRuntime.jsx(
1483
- material.TableCell,
1484
- {
1485
- sx: { textAlign: muiAlign(column.align) },
1486
- children: summaryCells[column.key]
1487
- },
1488
- column.key
1489
- )
1490
- )),
1491
- showActions && /* @__PURE__ */ jsxRuntime.jsx(material.TableCell, {})
1492
- ] }) })
1493
- ]
1494
- }
1495
- )
1496
- }
1497
- );
1116
+ /**
1117
+ * Memoized desktop row: a search keystroke (or any chrome re-render) must
1118
+ * not re-run every cell accessor, and toggling one row's checkbox must
1119
+ * re-render only that row. The cast restores the generic signature `memo`
1120
+ * erases.
1121
+ */
1122
+ const DesktopRow = (0, react.memo)(DesktopRowImpl, desktopRowPropsAreEqual);
1123
+ /** Desktop MUI table. */
1124
+ function DesktopTable({ table, rows, rowActions, confirm, getRowId, size, dir, prefetch, onRowClick, rowClassName, renderRowDetail, summaryRow, expansion, rowEntries, paddingTop = 0, paddingBottom = 0, measureElement, stickyHeader = false, stickyTop = 0, pinOffset, maxHeight, virtualScrollRef, setWidth, columnWidths, resizeLabel = "Resize column", actionsPinned = false }) {
1125
+ const { columns, selection, labels, showActions, entries, columnSpan } = (0, _adapttable_core.tableRenderModel)({
1126
+ table,
1127
+ rows,
1128
+ rowActions,
1129
+ getRowId,
1130
+ rowEntries,
1131
+ renderRowDetail,
1132
+ expansion
1133
+ });
1134
+ const groupRow = (0, _adapttable_core.headerGroupRow)(columns);
1135
+ const summaryCells = summaryRow?.(rows);
1136
+ const isExpanded = expansion && renderRowDetail ? expansion.isExpanded : void 0;
1137
+ const expandActive = isExpanded !== void 0;
1138
+ const onToggleSelect = useStableToggle(selection);
1139
+ const onToggleExpand = useStableToggle(expansion);
1140
+ const hasPinned = actionsPinned || table.columns.some((c) => pinOffset?.(c.key) != null);
1141
+ const overflow = (0, _adapttable_core.useHorizontalOverflow)();
1142
+ const inScrollBox = maxHeight != null || hasPinned || overflow.overflowing;
1143
+ const headSx = stickyHeader ? {
1144
+ position: "sticky",
1145
+ top: inScrollBox ? 0 : stickyTop,
1146
+ zIndex: _adapttable_core.PIN_Z.header,
1147
+ bgcolor: "background.paper"
1148
+ } : void 0;
1149
+ const selectionWidth = 48;
1150
+ const actionsWidth = 120;
1151
+ const leadLeft = (expandActive ? EXPAND_WIDTH : 0) + (selection ? selectionWidth : 0);
1152
+ const leadRight = showActions ? actionsWidth : 0;
1153
+ const leads = {
1154
+ left: leadLeft,
1155
+ right: leadRight
1156
+ };
1157
+ const selectionLead = expandActive ? EXPAND_WIDTH : 0;
1158
+ const hasLeftPin = table.columns.some((c) => pinOffset?.(c.key)?.side === "left");
1159
+ const stickActions = table.columns.some((c) => pinOffset?.(c.key)?.side === "right") || actionsPinned;
1160
+ const headCellSx = (column) => {
1161
+ const pin = (0, _adapttable_core.pinnedCellStyle)(pinOffset?.(column.key), _adapttable_core.PIN_Z.headerPinned, leads);
1162
+ const width = pin ? (0, _adapttable_core.pinnedColumnWidth)(column, columnWidths) : columnWidths?.[column.key] ?? column.width;
1163
+ const needsRelative = Boolean(setWidth) && !headSx && !pin;
1164
+ return {
1165
+ ...headSx,
1166
+ ...pin && {
1167
+ ...pin,
1168
+ bgcolor: "background.paper"
1169
+ },
1170
+ ...needsRelative && { position: "relative" },
1171
+ textAlign: muiAlign(column.align),
1172
+ ...width != null && { width }
1173
+ };
1174
+ };
1175
+ const edgeHeadSx = (side, active, lead = 0) => {
1176
+ const pin = (0, _adapttable_core.edgePinStyle)(side, active, _adapttable_core.PIN_Z.headerPinned);
1177
+ return {
1178
+ ...headSx,
1179
+ ...pin && {
1180
+ ...pin,
1181
+ bgcolor: "background.paper"
1182
+ },
1183
+ ...pin && lead > 0 && { insetInlineStart: lead }
1184
+ };
1185
+ };
1186
+ const rowSx = (0, react.useMemo)(() => {
1187
+ const edge = (side, active, lead = 0) => {
1188
+ const pin = (0, _adapttable_core.edgePinStyle)(side, active, _adapttable_core.PIN_Z.body);
1189
+ if (!pin) return void 0;
1190
+ return {
1191
+ ...pin,
1192
+ ...lead > 0 && { insetInlineStart: lead },
1193
+ bgcolor: "background.paper"
1194
+ };
1195
+ };
1196
+ const cells = {};
1197
+ for (const column of columns) {
1198
+ const pin = (0, _adapttable_core.pinnedCellStyle)(pinOffset?.(column.key), _adapttable_core.PIN_Z.body, {
1199
+ left: leadLeft,
1200
+ right: leadRight
1201
+ });
1202
+ cells[column.key] = {
1203
+ ...pin && {
1204
+ ...pin,
1205
+ bgcolor: "background.paper"
1206
+ },
1207
+ textAlign: muiAlign(column.align)
1208
+ };
1209
+ }
1210
+ return {
1211
+ cells,
1212
+ expand: edge("left", hasLeftPin),
1213
+ selection: edge("left", hasLeftPin, selectionLead),
1214
+ actions: {
1215
+ ...edge("right", stickActions),
1216
+ textAlign: "end"
1217
+ }
1218
+ };
1219
+ }, [
1220
+ columns,
1221
+ pinOffset,
1222
+ leadLeft,
1223
+ leadRight,
1224
+ hasLeftPin,
1225
+ stickActions,
1226
+ selectionLead
1227
+ ]);
1228
+ let boxSx;
1229
+ if (maxHeight != null) boxSx = {
1230
+ maxHeight,
1231
+ overflow: "auto"
1232
+ };
1233
+ else if (hasPinned || overflow.overflowing) boxSx = { overflowX: "auto" };
1234
+ const minWidth = (0, _adapttable_core.tableMinWidth)(columns, {
1235
+ widths: columnWidths,
1236
+ extra: leadLeft + leadRight
1237
+ });
1238
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
1239
+ ref: (node) => {
1240
+ overflow.ref(node);
1241
+ virtualScrollRef?.(node);
1242
+ },
1243
+ sx: boxSx,
1244
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Table, {
1245
+ size,
1246
+ "aria-label": table.getTableProps()["aria-label"],
1247
+ sx: minWidth > 0 ? { minWidth } : void 0,
1248
+ children: [
1249
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableHead, { children: [groupRow && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableRow, { children: [
1250
+ expandActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, { padding: "checkbox" }),
1251
+ selection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, { padding: "checkbox" }),
1252
+ groupRow.map((cell) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1253
+ colSpan: cell.span,
1254
+ sx: {
1255
+ textAlign: "center",
1256
+ fontWeight: 600
1257
+ },
1258
+ children: cell.label
1259
+ }, cell.key)),
1260
+ showActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {})
1261
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableRow, { children: [
1262
+ expandActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1263
+ padding: "checkbox",
1264
+ sx: edgeHeadSx("left", hasLeftPin)
1265
+ }),
1266
+ selection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1267
+ padding: "checkbox",
1268
+ sx: edgeHeadSx("left", hasLeftPin, selectionLead),
1269
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Checkbox, {
1270
+ slotProps: { input: { "aria-label": labels.selectAll } },
1271
+ checked: selection.headerState === "all",
1272
+ indeterminate: selection.headerState === "some",
1273
+ onChange: selection.toggleAll
1274
+ })
1275
+ }),
1276
+ columns.map((column) => {
1277
+ const headerCellProps = table.getHeaderCellProps(column);
1278
+ const ariaSort = headerCellProps["aria-sort"];
1279
+ const active = ariaSort === "ascending" || ariaSort === "descending";
1280
+ const sortIndex = headerCellProps["data-sort-index"];
1281
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableCell, {
1282
+ "aria-sort": ariaSort,
1283
+ "data-sort-index": sortIndex,
1284
+ sx: headCellSx(column),
1285
+ children: [column.sortable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableSortLabel, {
1286
+ active,
1287
+ direction: ariaSort === "descending" ? "desc" : "asc",
1288
+ onClick: table.getSortButtonProps(column).onClick,
1289
+ children: [column.header, sortIndex !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
1290
+ component: "span",
1291
+ sx: {
1292
+ fontSize: 10,
1293
+ ml: .5
1294
+ },
1295
+ children: sortIndex
1296
+ })]
1297
+ }) : column.header, setWidth && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
1298
+ component: "span",
1299
+ sx: RESIZE_HANDLE_SX,
1300
+ ...(0, _adapttable_core.columnResizeHandleProps)(column.key, setWidth, `${resizeLabel}: ${typeof column.header === "string" ? column.header : column.key}`)
1301
+ })]
1302
+ }, column.key);
1303
+ }),
1304
+ showActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1305
+ sx: {
1306
+ ...edgeHeadSx("right", stickActions),
1307
+ textAlign: "end"
1308
+ },
1309
+ children: labels.actions
1310
+ })
1311
+ ] })] }),
1312
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableBody, { children: [
1313
+ paddingTop > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableRow, {
1314
+ "aria-hidden": true,
1315
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1316
+ colSpan: columnSpan,
1317
+ sx: {
1318
+ height: paddingTop,
1319
+ p: 0
1320
+ }
1321
+ })
1322
+ }),
1323
+ entries.map(({ row, index, key }) => {
1324
+ const id = getRowId(row);
1325
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesktopRow, {
1326
+ row,
1327
+ index,
1328
+ selected: selection?.isSelected(id) ?? false,
1329
+ expanded: isExpanded ? isExpanded(id) : false,
1330
+ columns,
1331
+ sx: rowSx,
1332
+ columnSpan,
1333
+ size,
1334
+ dir,
1335
+ className: rowClassName?.(row, index),
1336
+ hasSelection: Boolean(selection),
1337
+ hasExpansion: expandActive,
1338
+ showActions,
1339
+ selectRowLabel: labels.selectRow,
1340
+ cancelLabel: labels.cancel,
1341
+ expandLabel: labels.expandRow,
1342
+ collapseLabel: labels.collapseRow,
1343
+ id,
1344
+ rowActions,
1345
+ confirm,
1346
+ renderRowDetail,
1347
+ onToggleSelect,
1348
+ onToggleExpand,
1349
+ onRowClick,
1350
+ prefetch,
1351
+ measureElement
1352
+ }, key);
1353
+ }),
1354
+ paddingBottom > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableRow, {
1355
+ "aria-hidden": true,
1356
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1357
+ colSpan: columnSpan,
1358
+ sx: {
1359
+ height: paddingBottom,
1360
+ p: 0
1361
+ }
1362
+ })
1363
+ })
1364
+ ] }),
1365
+ summaryCells && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableFooter, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.TableRow, { children: [
1366
+ expandActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, { padding: "checkbox" }),
1367
+ selection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, { padding: "checkbox" }),
1368
+ columns.map((column) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {
1369
+ sx: { textAlign: muiAlign(column.align) },
1370
+ children: summaryCells[column.key]
1371
+ }, column.key)),
1372
+ showActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.TableCell, {})
1373
+ ] }) })
1374
+ ]
1375
+ })
1376
+ });
1498
1377
  }
1499
1378
  function mobileLabel(column) {
1500
- return column.mobileLabel ?? (typeof column.header === "string" ? column.header : column.key);
1379
+ return column.mobileLabel ?? (typeof column.header === "string" ? column.header : column.key);
1501
1380
  }
1502
- function MobileCards({
1503
- table,
1504
- rows,
1505
- rowActions,
1506
- confirm,
1507
- getRowId,
1508
- size,
1509
- dir,
1510
- onRowClick,
1511
- rowClassName,
1512
- renderRowDetail,
1513
- summaryRow,
1514
- expansion,
1515
- rowEntries,
1516
- paddingTop = 0,
1517
- paddingBottom = 0,
1518
- measureElement
1519
- }) {
1520
- const { columns, selection, labels } = table;
1521
- const entries = core.resolveVirtualRows(rows, getRowId, rowEntries);
1522
- const compact = size === "small";
1523
- const expand = expansion && renderRowDetail ? expansion : void 0;
1524
- const summaryCells = summaryRow?.(rows);
1525
- return /* @__PURE__ */ jsxRuntime.jsxs(
1526
- material.Stack,
1527
- {
1528
- spacing: compact ? 1 : 1.5,
1529
- role: "list",
1530
- "aria-label": table.getTableProps()["aria-label"],
1531
- children: [
1532
- paddingTop > 0 && /* @__PURE__ */ jsxRuntime.jsx(material.Box, { "aria-hidden": true, sx: { height: paddingTop } }),
1533
- entries.map(({ row, index, key }) => {
1534
- const id = getRowId(row);
1535
- return /* @__PURE__ */ jsxRuntime.jsx(
1536
- material.Card,
1537
- {
1538
- ref: measureElement,
1539
- "data-index": index,
1540
- variant: "outlined",
1541
- role: "listitem",
1542
- className: rowClassName?.(row, index),
1543
- ...core.rowClickProps(row, onRowClick),
1544
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1545
- material.CardContent,
1546
- {
1547
- sx: compact ? { p: 1.25, "&:last-child": { pb: 1.25 } } : void 0,
1548
- children: [
1549
- selection && /* @__PURE__ */ jsxRuntime.jsx(
1550
- material.Checkbox,
1551
- {
1552
- slotProps: { input: { "aria-label": labels.selectRow } },
1553
- checked: selection.isSelected(id),
1554
- onChange: () => selection.toggle(id)
1555
- }
1556
- ),
1557
- expand && /* @__PURE__ */ jsxRuntime.jsx(
1558
- ExpandToggle,
1559
- {
1560
- id,
1561
- expanded: expand.isExpanded(id),
1562
- onToggle: expand.toggle,
1563
- dir,
1564
- expandLabel: labels.expandRow,
1565
- collapseLabel: labels.collapseRow
1566
- }
1567
- ),
1568
- columns.map((column) => /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { sx: { mb: compact ? 0.5 : 1 }, children: [
1569
- /* @__PURE__ */ jsxRuntime.jsx(
1570
- material.Typography,
1571
- {
1572
- variant: "caption",
1573
- color: "text.secondary",
1574
- display: "block",
1575
- children: mobileLabel(column)
1576
- }
1577
- ),
1578
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { component: "div", variant: "body2", children: column.Cell ? /* @__PURE__ */ jsxRuntime.jsx(column.Cell, { row, rowIndex: index }) : column.accessor?.(row) })
1579
- ] }, column.key)),
1580
- rowActions && rowActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1581
- RowActionButtons,
1582
- {
1583
- row,
1584
- actions: rowActions,
1585
- confirm,
1586
- cancelLabel: labels.cancel
1587
- }
1588
- ),
1589
- expand?.isExpanded(id) && // Inside the card — and therefore inside the measured
1590
- // element — so virtualization keeps accurate card heights.
1591
- /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { mt: 1 }, children: renderRowDetail(row) })
1592
- ]
1593
- }
1594
- )
1595
- },
1596
- key
1597
- );
1598
- }),
1599
- summaryCells && /* @__PURE__ */ jsxRuntime.jsx(material.Card, { variant: "outlined", role: "listitem", children: /* @__PURE__ */ jsxRuntime.jsx(
1600
- material.CardContent,
1601
- {
1602
- sx: compact ? { p: 1.25, "&:last-child": { pb: 1.25 } } : void 0,
1603
- children: columns.map((column) => {
1604
- const value = summaryCells[column.key];
1605
- if (value === void 0) return null;
1606
- return /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { sx: { mb: compact ? 0.5 : 1 }, children: [
1607
- /* @__PURE__ */ jsxRuntime.jsx(
1608
- material.Typography,
1609
- {
1610
- variant: "caption",
1611
- color: "text.secondary",
1612
- display: "block",
1613
- children: mobileLabel(column)
1614
- }
1615
- ),
1616
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { component: "div", variant: "body2", children: value })
1617
- ] }, column.key);
1618
- })
1619
- }
1620
- ) }),
1621
- paddingBottom > 0 && /* @__PURE__ */ jsxRuntime.jsx(material.Box, { "aria-hidden": true, sx: { height: paddingBottom } })
1622
- ]
1623
- }
1624
- );
1381
+ /** Mobile MUI card list. */
1382
+ function MobileCards({ table, rows, rowActions, confirm, getRowId, size, dir, onRowClick, rowClassName, renderRowDetail, summaryRow, expansion, rowEntries, paddingTop = 0, paddingBottom = 0, measureElement }) {
1383
+ const { columns, selection, labels } = table;
1384
+ const entries = (0, _adapttable_core.resolveVirtualRows)(rows, getRowId, rowEntries);
1385
+ const compact = size === "small";
1386
+ const expand = expansion && renderRowDetail ? expansion : void 0;
1387
+ const summaryCells = summaryRow?.(rows);
1388
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
1389
+ spacing: compact ? 1 : 1.5,
1390
+ role: "list",
1391
+ "aria-label": table.getTableProps()["aria-label"],
1392
+ children: [
1393
+ paddingTop > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
1394
+ "aria-hidden": true,
1395
+ sx: { height: paddingTop }
1396
+ }),
1397
+ entries.map(({ row, index, key }) => {
1398
+ const id = getRowId(row);
1399
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Card, {
1400
+ ref: measureElement,
1401
+ "data-index": index,
1402
+ variant: "outlined",
1403
+ role: "listitem",
1404
+ className: rowClassName?.(row, index),
1405
+ ...(0, _adapttable_core.rowClickProps)(row, onRowClick),
1406
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.CardContent, {
1407
+ sx: compact ? {
1408
+ p: 1.25,
1409
+ "&:last-child": { pb: 1.25 }
1410
+ } : void 0,
1411
+ children: [
1412
+ selection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Checkbox, {
1413
+ slotProps: { input: { "aria-label": labels.selectRow } },
1414
+ checked: selection.isSelected(id),
1415
+ onChange: () => selection.toggle(id)
1416
+ }),
1417
+ expand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpandToggle, {
1418
+ id,
1419
+ expanded: expand.isExpanded(id),
1420
+ onToggle: expand.toggle,
1421
+ dir,
1422
+ expandLabel: labels.expandRow,
1423
+ collapseLabel: labels.collapseRow
1424
+ }),
1425
+ columns.map((column) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Box, {
1426
+ sx: { mb: compact ? .5 : 1 },
1427
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
1428
+ variant: "caption",
1429
+ color: "text.secondary",
1430
+ sx: { display: "block" },
1431
+ children: mobileLabel(column)
1432
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
1433
+ component: "div",
1434
+ variant: "body2",
1435
+ children: column.Cell ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(column.Cell, {
1436
+ row,
1437
+ rowIndex: index
1438
+ }) : column.accessor?.(row)
1439
+ })]
1440
+ }, column.key)),
1441
+ rowActions && rowActions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowActionButtons, {
1442
+ row,
1443
+ actions: rowActions,
1444
+ confirm,
1445
+ cancelLabel: labels.cancel
1446
+ }),
1447
+ expand?.isExpanded(id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
1448
+ sx: { mt: 1 },
1449
+ children: renderRowDetail(row)
1450
+ })
1451
+ ]
1452
+ })
1453
+ }, key);
1454
+ }),
1455
+ summaryCells && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Card, {
1456
+ variant: "outlined",
1457
+ role: "listitem",
1458
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.CardContent, {
1459
+ sx: compact ? {
1460
+ p: 1.25,
1461
+ "&:last-child": { pb: 1.25 }
1462
+ } : void 0,
1463
+ children: columns.map((column) => {
1464
+ const value = summaryCells[column.key];
1465
+ if (value === void 0) return null;
1466
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Box, {
1467
+ sx: { mb: compact ? .5 : 1 },
1468
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
1469
+ variant: "caption",
1470
+ color: "text.secondary",
1471
+ sx: { display: "block" },
1472
+ children: mobileLabel(column)
1473
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
1474
+ component: "div",
1475
+ variant: "body2",
1476
+ children: value
1477
+ })]
1478
+ }, column.key);
1479
+ })
1480
+ })
1481
+ }),
1482
+ paddingBottom > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
1483
+ "aria-hidden": true,
1484
+ sx: { height: paddingBottom }
1485
+ })
1486
+ ]
1487
+ });
1625
1488
  }
1489
+ //#endregion
1490
+ //#region src/DataTable.tsx
1491
+ /**
1492
+ * Map row density to MUI's table `size`, independent of column pinning. An
1493
+ * explicit `size` prop still wins for backward compatibility.
1494
+ */
1626
1495
  function tableSize(size, density) {
1627
- if (size) return size;
1628
- return density === "compact" ? "small" : "medium";
1496
+ if (size) return size;
1497
+ return density === "compact" ? "small" : "medium";
1629
1498
  }
1499
+ /** The width setter only when column resize is enabled (opt-in). */
1630
1500
  function resizeSetter(enabled, setWidth) {
1631
- return enabled ? setWidth : void 0;
1501
+ return enabled ? setWidth : void 0;
1632
1502
  }
1503
+ /**
1504
+ * The injected actions column is first-class in column management: the
1505
+ * layout state treats keys opaquely, so the reserved "actions" key hides and
1506
+ * end-pins like any data column. Hiding strips `rowActions` BEFORE the
1507
+ * renderers, so the column, its pin lead, and the colSpans all disappear
1508
+ * consistently (desktop and mobile alike). `actionsPinned` reports the
1509
+ * Columns-menu end pin — only meaningful while the column renders.
1510
+ */
1633
1511
  function resolveActionsColumn(declared, layout) {
1634
- const hasRowActions = (declared?.length ?? 0) > 0;
1635
- const rowActions = hasRowActions && !layout.isHidden(core.ACTIONS_COLUMN_KEY) ? declared : void 0;
1636
- const actionsPinned = rowActions !== void 0 && layout.state.pinned[core.ACTIONS_COLUMN_KEY] === "right";
1637
- return { hasRowActions, rowActions, actionsPinned };
1512
+ const hasRowActions = (declared?.length ?? 0) > 0;
1513
+ const rowActions = hasRowActions && !layout.isHidden(_adapttable_core.ACTIONS_COLUMN_KEY) ? declared : void 0;
1514
+ return {
1515
+ hasRowActions,
1516
+ rowActions,
1517
+ actionsPinned: rowActions !== void 0 && layout.state.pinned[_adapttable_core.ACTIONS_COLUMN_KEY] === "right"
1518
+ };
1638
1519
  }
1520
+ /**
1521
+ * Resolve the data tier (`source` > `data` + `onQueryChange` > `data`) and
1522
+ * the filter content, then overlay them on the caller's props: caller JSX
1523
+ * filters pass through; the declarative array becomes the auto-built
1524
+ * {@link AutoFilterForm} (or nothing, when no definitions resolved); the
1525
+ * runtime's chip-label resolvers merge under any caller overrides.
1526
+ */
1639
1527
  function useChromeProps(props) {
1640
- const { source, runtime } = core.useTableData({
1641
- locale: props.locale,
1642
- source: props.source,
1643
- data: props.data,
1644
- total: props.total,
1645
- loading: props.loading,
1646
- onQueryChange: props.onQueryChange,
1647
- columns: props.columns,
1648
- filters: props.filters,
1649
- adapter: props.urlSync === false ? void 0 : props.urlAdapter,
1650
- enabled: props.urlSync,
1651
- urlKey: props.urlKey
1652
- });
1653
- let filtersNode;
1654
- if (core.isDeclarativeFilters(props.filters) || props.filters === void 0) {
1655
- filtersNode = runtime.defs.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1656
- AutoFilterForm,
1657
- {
1658
- defs: runtime.defs,
1659
- source,
1660
- labels: core.resolveLabels(props.labels)
1661
- }
1662
- ) : void 0;
1663
- } else {
1664
- filtersNode = props.filters;
1665
- }
1666
- return {
1667
- ...props,
1668
- source,
1669
- filters: filtersNode,
1670
- filterLabels: { ...runtime.filterLabels, ...props.filterLabels }
1671
- };
1528
+ const { source, runtime } = (0, _adapttable_core.useTableData)({
1529
+ locale: props.locale,
1530
+ source: props.source,
1531
+ data: props.data,
1532
+ total: props.total,
1533
+ loading: props.loading,
1534
+ onQueryChange: props.onQueryChange,
1535
+ columns: props.columns,
1536
+ filters: props.filters,
1537
+ adapter: props.urlSync === false ? void 0 : props.urlAdapter,
1538
+ enabled: props.urlSync,
1539
+ urlKey: props.urlKey
1540
+ });
1541
+ let filtersNode;
1542
+ if ((0, _adapttable_core.isDeclarativeFilters)(props.filters) || props.filters === void 0) filtersNode = runtime.defs.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoFilterForm, {
1543
+ defs: runtime.defs,
1544
+ source,
1545
+ labels: (0, _adapttable_core.resolveLabels)(props.labels)
1546
+ }) : void 0;
1547
+ else filtersNode = props.filters;
1548
+ return {
1549
+ ...props,
1550
+ source,
1551
+ filters: filtersNode,
1552
+ filterLabels: {
1553
+ ...runtime.filterLabels,
1554
+ ...props.filterLabels
1555
+ }
1556
+ };
1672
1557
  }
1558
+ /**
1559
+ * Batteries-included Material UI data table. Drop in `columns`, `data` (or
1560
+ * `data` + `onQueryChange` for server fetching, or a full `source`), and a
1561
+ * `rowKey` for a fully styled, sortable, filterable, paginated MUI table
1562
+ * with selection, bulk actions, RTL, and dark mode — a free DataGrid-style
1563
+ * experience on the headless `@adapttable/core` engine. Declarative
1564
+ * `filters` (and column `filter` shorthands) render an auto-built form.
1565
+ *
1566
+ * @typeParam TRow - The row type.
1567
+ */
1673
1568
  function DataTable(props) {
1674
- const { slots, className } = props;
1675
- const size = tableSize(props.size, props.density);
1676
- const { filtersMode = "popover" } = props;
1677
- const chromeProps = useChromeProps(props);
1678
- const { source, filters: filtersNode } = chromeProps;
1679
- const c = core.useTableChrome(chromeProps);
1680
- const { table, confirm, getRowId } = c;
1681
- const { labels } = table;
1682
- const [filtersOpen, setFiltersOpen] = react.useState(false);
1683
- const filtersTrigger = core.useFilterTriggerToggle(filtersOpen, setFiltersOpen);
1684
- const rootRef = react.useRef(null);
1685
- core.useChromeScrollReset(rootRef, c, chromeProps);
1686
- const { virtualization, loadMoreRef, canLoadMore, virtualScrollRef } = core.useChromeBodyData(c, chromeProps);
1687
- const { hasRowActions, rowActions, actionsPinned } = resolveActionsColumn(
1688
- props.rowActions,
1689
- c.columnLayout
1690
- );
1691
- const columnMenu = props.enableColumnMenu && !c.isMobile && /* @__PURE__ */ jsxRuntime.jsx(
1692
- ColumnMenu,
1693
- {
1694
- allColumns: c.allColumns,
1695
- layout: c.columnLayout,
1696
- labels,
1697
- hasRowActions
1698
- }
1699
- );
1700
- const savedViewsMenu = props.savedViews && /* @__PURE__ */ jsxRuntime.jsx(
1701
- SavedViewsMenu,
1702
- {
1703
- options: {
1704
- adapter: props.urlAdapter,
1705
- urlKey: props.urlKey,
1706
- ...props.savedViews
1707
- },
1708
- labels
1709
- }
1710
- );
1711
- let body;
1712
- if (c.body === "skeleton") {
1713
- body = slots?.skeleton ?? /* @__PURE__ */ jsxRuntime.jsx(
1714
- LoadingState,
1715
- {
1716
- rows: props.skeletonRows ?? source.limit,
1717
- columns: table.columns.length,
1718
- loadingLabel: labels.loading
1719
- }
1720
- );
1721
- } else if (c.body === "empty") {
1722
- body = slots?.empty ?? /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { role: "status", spacing: 1.5, alignItems: "center", sx: { py: 6 }, children: [
1723
- /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { color: "text.secondary", align: "center", children: c.emptyVariant === "noResults" ? labels.noResults : labels.noData }),
1724
- c.emptyVariant === "noResults" && /* @__PURE__ */ jsxRuntime.jsx(material.Button, { variant: "outlined", size: "small", onClick: c.clearFilters, children: labels.clearAll })
1725
- ] });
1726
- } else if (c.body === "mobile") {
1727
- body = /* @__PURE__ */ jsxRuntime.jsx(
1728
- MobileCards,
1729
- {
1730
- table,
1731
- rows: source.rows,
1732
- rowActions,
1733
- confirm,
1734
- getRowId,
1735
- size,
1736
- dir: props.dir,
1737
- onRowClick: props.onRowClick,
1738
- rowClassName: props.rowClassName,
1739
- renderRowDetail: props.renderRowDetail,
1740
- summaryRow: props.summaryRow,
1741
- expansion: c.detail?.expansion,
1742
- rowEntries: virtualization.enabled ? virtualization.rows : void 0,
1743
- paddingTop: virtualization.paddingTop,
1744
- paddingBottom: virtualization.paddingBottom,
1745
- measureElement: virtualization.measureElement
1746
- }
1747
- );
1748
- } else {
1749
- body = /* @__PURE__ */ jsxRuntime.jsx(
1750
- DesktopTable,
1751
- {
1752
- table,
1753
- rows: source.rows,
1754
- rowActions,
1755
- actionsPinned,
1756
- confirm,
1757
- getRowId,
1758
- size,
1759
- dir: props.dir,
1760
- prefetch: props.prefetch,
1761
- onRowClick: props.onRowClick,
1762
- rowClassName: props.rowClassName,
1763
- renderRowDetail: props.renderRowDetail,
1764
- summaryRow: props.summaryRow,
1765
- expansion: c.detail?.expansion,
1766
- rowEntries: virtualization.enabled ? virtualization.rows : void 0,
1767
- paddingTop: virtualization.paddingTop,
1768
- paddingBottom: virtualization.paddingBottom,
1769
- measureElement: virtualization.measureElement,
1770
- stickyHeader: props.stickyHeader,
1771
- stickyTop: props.stickyTop,
1772
- pinOffset: c.columnLayout.pinOffset,
1773
- maxHeight: props.maxHeight,
1774
- virtualScrollRef,
1775
- setWidth: resizeSetter(props.resizableColumns, c.columnLayout.setWidth),
1776
- columnWidths: c.columnLayout.state.widths,
1777
- resizeLabel: labels.resizeColumn
1778
- }
1779
- );
1780
- }
1781
- return /* @__PURE__ */ jsxRuntime.jsxs(
1782
- material.Paper,
1783
- {
1784
- ref: rootRef,
1785
- variant: "outlined",
1786
- dir: props.dir,
1787
- className,
1788
- "aria-busy": c.isRefreshing || void 0,
1789
- sx: { p: 1.5 },
1790
- children: [
1791
- /* @__PURE__ */ jsxRuntime.jsxs(material.Stack, { spacing: 1.5, children: [
1792
- /* @__PURE__ */ jsxRuntime.jsx(
1793
- Toolbar,
1794
- {
1795
- table,
1796
- hideSearch: props.hideSearch,
1797
- searchPlaceholder: props.searchPlaceholder,
1798
- sortByOptions: props.sortByOptions,
1799
- customToolbar: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1800
- savedViewsMenu,
1801
- props.toolbar
1802
- ] }),
1803
- hasFilters: Boolean(filtersNode),
1804
- activeFilterCount: c.activeFilterCount,
1805
- showRowsPerPage: canLoadMore,
1806
- filtersMode,
1807
- filters: filtersNode,
1808
- filtersOpen,
1809
- onToggleFilters: filtersTrigger.onClick,
1810
- onFiltersTriggerPointerDown: filtersTrigger.onPointerDown,
1811
- onCloseFilters: () => setFiltersOpen(false),
1812
- onClearFilters: c.clearFilters,
1813
- dir: props.dir,
1814
- columnMenu
1815
- }
1816
- ),
1817
- c.isRefreshing && /* @__PURE__ */ jsxRuntime.jsx(material.LinearProgress, { "aria-label": labels.loading }),
1818
- /* @__PURE__ */ jsxRuntime.jsx(
1819
- Chips,
1820
- {
1821
- chips: c.mergedChips,
1822
- onClearAll: c.clearFilters,
1823
- labels
1824
- }
1825
- ),
1826
- table.selection && props.bulkActions && /* @__PURE__ */ jsxRuntime.jsx(
1827
- BulkBar,
1828
- {
1829
- selection: table.selection,
1830
- total: source.total,
1831
- bulkActions: props.bulkActions,
1832
- confirm,
1833
- labels
1834
- }
1835
- ),
1836
- source.error ? /* @__PURE__ */ jsxRuntime.jsx(
1837
- ErrorState,
1838
- {
1839
- error: source.error,
1840
- labels,
1841
- onRetry: source.refetch ? () => void source.refetch?.() : void 0
1842
- }
1843
- ) : body,
1844
- canLoadMore && source.hasNextPage && /* @__PURE__ */ jsxRuntime.jsx(material.Box, { ref: loadMoreRef, display: "flex", justifyContent: "center", py: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
1845
- material.Button,
1846
- {
1847
- variant: "outlined",
1848
- size: "small",
1849
- disabled: source.isFetchingNextPage,
1850
- onClick: () => source.fetchNextPage(),
1851
- children: labels.loadMore
1852
- }
1853
- ) }),
1854
- c.showFooter && /* @__PURE__ */ jsxRuntime.jsx(
1855
- Footer,
1856
- {
1857
- pagination: table.pagination,
1858
- total: source.total,
1859
- limit: source.limit,
1860
- setPage: source.setPage,
1861
- setLimit: source.setLimit,
1862
- labels
1863
- }
1864
- )
1865
- ] }),
1866
- filtersNode && filtersMode === "drawer" && /* @__PURE__ */ jsxRuntime.jsx(
1867
- FilterDrawer,
1868
- {
1869
- open: filtersOpen,
1870
- onClose: () => setFiltersOpen(false),
1871
- filters: filtersNode,
1872
- activeFilterCount: c.activeFilterCount,
1873
- onClearFilters: c.clearFilters,
1874
- labels,
1875
- dir: props.dir
1876
- }
1877
- )
1878
- ]
1879
- }
1880
- );
1569
+ const { slots, className } = props;
1570
+ const size = tableSize(props.size, props.density);
1571
+ const { filtersMode = "popover" } = props;
1572
+ const chromeProps = useChromeProps(props);
1573
+ const { source, filters: filtersNode } = chromeProps;
1574
+ const c = (0, _adapttable_core.useTableChrome)(chromeProps);
1575
+ const { table, confirm, getRowId } = c;
1576
+ const { labels } = table;
1577
+ const [filtersOpen, setFiltersOpen] = (0, react.useState)(false);
1578
+ const filtersTrigger = (0, _adapttable_core.useFilterTriggerToggle)(filtersOpen, setFiltersOpen);
1579
+ const rootRef = (0, react.useRef)(null);
1580
+ (0, _adapttable_core.useChromeScrollReset)(rootRef, c, chromeProps);
1581
+ const { virtualization, loadMoreRef, canLoadMore, virtualScrollRef } = (0, _adapttable_core.useChromeBodyData)(c, chromeProps);
1582
+ const { hasRowActions, rowActions, actionsPinned } = resolveActionsColumn(props.rowActions, c.columnLayout);
1583
+ const columnMenu = props.enableColumnMenu && !c.isMobile && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColumnMenu, {
1584
+ allColumns: c.allColumns,
1585
+ layout: c.columnLayout,
1586
+ labels,
1587
+ hasRowActions
1588
+ });
1589
+ const savedViewsMenu = props.savedViews && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SavedViewsMenu, {
1590
+ options: {
1591
+ adapter: props.urlAdapter,
1592
+ urlKey: props.urlKey,
1593
+ ...props.savedViews
1594
+ },
1595
+ labels
1596
+ });
1597
+ let body;
1598
+ if (c.body === "skeleton") body = slots?.skeleton ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoadingState, {
1599
+ rows: props.skeletonRows ?? source.limit,
1600
+ columns: table.columns.length,
1601
+ loadingLabel: labels.loading
1602
+ });
1603
+ else if (c.body === "empty") body = slots?.empty ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
1604
+ role: "status",
1605
+ spacing: 1.5,
1606
+ sx: {
1607
+ py: 6,
1608
+ alignItems: "center"
1609
+ },
1610
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Typography, {
1611
+ color: "text.secondary",
1612
+ align: "center",
1613
+ children: c.emptyVariant === "noResults" ? labels.noResults : labels.noData
1614
+ }), c.emptyVariant === "noResults" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
1615
+ variant: "outlined",
1616
+ size: "small",
1617
+ onClick: c.clearFilters,
1618
+ children: labels.clearAll
1619
+ })]
1620
+ });
1621
+ else if (c.body === "mobile") body = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MobileCards, {
1622
+ table,
1623
+ rows: source.rows,
1624
+ rowActions,
1625
+ confirm,
1626
+ getRowId,
1627
+ size,
1628
+ dir: props.dir,
1629
+ onRowClick: props.onRowClick,
1630
+ rowClassName: props.rowClassName,
1631
+ renderRowDetail: props.renderRowDetail,
1632
+ summaryRow: props.summaryRow,
1633
+ expansion: c.detail?.expansion,
1634
+ rowEntries: virtualization.enabled ? virtualization.rows : void 0,
1635
+ paddingTop: virtualization.paddingTop,
1636
+ paddingBottom: virtualization.paddingBottom,
1637
+ measureElement: virtualization.measureElement
1638
+ });
1639
+ else body = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DesktopTable, {
1640
+ table,
1641
+ rows: source.rows,
1642
+ rowActions,
1643
+ actionsPinned,
1644
+ confirm,
1645
+ getRowId,
1646
+ size,
1647
+ dir: props.dir,
1648
+ prefetch: props.prefetch,
1649
+ onRowClick: props.onRowClick,
1650
+ rowClassName: props.rowClassName,
1651
+ renderRowDetail: props.renderRowDetail,
1652
+ summaryRow: props.summaryRow,
1653
+ expansion: c.detail?.expansion,
1654
+ rowEntries: virtualization.enabled ? virtualization.rows : void 0,
1655
+ paddingTop: virtualization.paddingTop,
1656
+ paddingBottom: virtualization.paddingBottom,
1657
+ measureElement: virtualization.measureElement,
1658
+ stickyHeader: props.stickyHeader,
1659
+ stickyTop: props.stickyTop,
1660
+ pinOffset: c.columnLayout.pinOffset,
1661
+ maxHeight: props.maxHeight,
1662
+ virtualScrollRef,
1663
+ setWidth: resizeSetter(props.resizableColumns, c.columnLayout.setWidth),
1664
+ columnWidths: c.columnLayout.state.widths,
1665
+ resizeLabel: labels.resizeColumn
1666
+ });
1667
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Paper, {
1668
+ ref: rootRef,
1669
+ variant: "outlined",
1670
+ dir: props.dir,
1671
+ className,
1672
+ "aria-busy": c.isRefreshing || void 0,
1673
+ sx: { p: 1.5 },
1674
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_mui_material.Stack, {
1675
+ spacing: 1.5,
1676
+ children: [
1677
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toolbar, {
1678
+ table,
1679
+ hideSearch: props.hideSearch,
1680
+ searchPlaceholder: props.searchPlaceholder,
1681
+ sortByOptions: props.sortByOptions,
1682
+ customToolbar: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [savedViewsMenu, props.toolbar] }),
1683
+ hasFilters: Boolean(filtersNode),
1684
+ activeFilterCount: c.activeFilterCount,
1685
+ showRowsPerPage: canLoadMore,
1686
+ filtersMode,
1687
+ filters: filtersNode,
1688
+ filtersOpen,
1689
+ onToggleFilters: filtersTrigger.onClick,
1690
+ onFiltersTriggerPointerDown: filtersTrigger.onPointerDown,
1691
+ onCloseFilters: () => setFiltersOpen(false),
1692
+ onClearFilters: c.clearFilters,
1693
+ dir: props.dir,
1694
+ columnMenu
1695
+ }),
1696
+ c.isRefreshing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.LinearProgress, { "aria-label": labels.loading }),
1697
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chips, {
1698
+ chips: c.mergedChips,
1699
+ onClearAll: c.clearFilters,
1700
+ labels
1701
+ }),
1702
+ table.selection && props.bulkActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BulkBar, {
1703
+ selection: table.selection,
1704
+ total: source.total,
1705
+ bulkActions: props.bulkActions,
1706
+ confirm,
1707
+ labels
1708
+ }),
1709
+ source.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorState, {
1710
+ error: source.error,
1711
+ labels,
1712
+ onRetry: source.refetch ? () => void source.refetch?.() : void 0
1713
+ }) : body,
1714
+ canLoadMore && source.hasNextPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Box, {
1715
+ ref: loadMoreRef,
1716
+ sx: {
1717
+ display: "flex",
1718
+ justifyContent: "center",
1719
+ py: 1
1720
+ },
1721
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_mui_material.Button, {
1722
+ variant: "outlined",
1723
+ size: "small",
1724
+ disabled: source.isFetchingNextPage,
1725
+ onClick: () => source.fetchNextPage(),
1726
+ children: labels.loadMore
1727
+ })
1728
+ }),
1729
+ c.showFooter && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Footer, {
1730
+ pagination: table.pagination,
1731
+ total: source.total,
1732
+ limit: source.limit,
1733
+ setPage: source.setPage,
1734
+ setLimit: source.setLimit,
1735
+ labels
1736
+ })
1737
+ ]
1738
+ }), filtersNode && filtersMode === "drawer" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterDrawer, {
1739
+ open: filtersOpen,
1740
+ onClose: () => setFiltersOpen(false),
1741
+ filters: filtersNode,
1742
+ activeFilterCount: c.activeFilterCount,
1743
+ onClearFilters: c.clearFilters,
1744
+ labels,
1745
+ dir: props.dir
1746
+ })]
1747
+ });
1881
1748
  }
1882
-
1749
+ //#endregion
1750
+ exports.DataTable = DataTable;
1751
+ exports.SavedViewsMenu = SavedViewsMenu;
1883
1752
  Object.defineProperty(exports, "createHistoryAdapter", {
1884
- enumerable: true,
1885
- get: function () { return core.createHistoryAdapter; }
1753
+ enumerable: true,
1754
+ get: function() {
1755
+ return _adapttable_core.createHistoryAdapter;
1756
+ }
1886
1757
  });
1887
1758
  Object.defineProperty(exports, "createMemoryAdapter", {
1888
- enumerable: true,
1889
- get: function () { return core.createMemoryAdapter; }
1759
+ enumerable: true,
1760
+ get: function() {
1761
+ return _adapttable_core.createMemoryAdapter;
1762
+ }
1890
1763
  });
1891
1764
  Object.defineProperty(exports, "defaultConfirm", {
1892
- enumerable: true,
1893
- get: function () { return core.defaultConfirm; }
1765
+ enumerable: true,
1766
+ get: function() {
1767
+ return _adapttable_core.defaultConfirm;
1768
+ }
1894
1769
  });
1895
1770
  Object.defineProperty(exports, "defaultLabels", {
1896
- enumerable: true,
1897
- get: function () { return core.defaultLabels; }
1771
+ enumerable: true,
1772
+ get: function() {
1773
+ return _adapttable_core.defaultLabels;
1774
+ }
1898
1775
  });
1899
1776
  Object.defineProperty(exports, "deriveSortByOptions", {
1900
- enumerable: true,
1901
- get: function () { return core.deriveSortByOptions; }
1777
+ enumerable: true,
1778
+ get: function() {
1779
+ return _adapttable_core.deriveSortByOptions;
1780
+ }
1902
1781
  });
1903
1782
  Object.defineProperty(exports, "getHistoryAdapter", {
1904
- enumerable: true,
1905
- get: function () { return core.getHistoryAdapter; }
1783
+ enumerable: true,
1784
+ get: function() {
1785
+ return _adapttable_core.getHistoryAdapter;
1786
+ }
1906
1787
  });
1907
1788
  Object.defineProperty(exports, "useBackendData", {
1908
- enumerable: true,
1909
- get: function () { return core.useBackendData; }
1789
+ enumerable: true,
1790
+ get: function() {
1791
+ return _adapttable_core.useBackendData;
1792
+ }
1910
1793
  });
1911
1794
  Object.defineProperty(exports, "useDataTable", {
1912
- enumerable: true,
1913
- get: function () { return core.useDataTable; }
1795
+ enumerable: true,
1796
+ get: function() {
1797
+ return _adapttable_core.useDataTable;
1798
+ }
1914
1799
  });
1915
1800
  Object.defineProperty(exports, "useFrontendData", {
1916
- enumerable: true,
1917
- get: function () { return core.useFrontendData; }
1801
+ enumerable: true,
1802
+ get: function() {
1803
+ return _adapttable_core.useFrontendData;
1804
+ }
1918
1805
  });
1919
1806
  Object.defineProperty(exports, "useSavedViews", {
1920
- enumerable: true,
1921
- get: function () { return core.useSavedViews; }
1807
+ enumerable: true,
1808
+ get: function() {
1809
+ return _adapttable_core.useSavedViews;
1810
+ }
1922
1811
  });
1923
1812
  Object.defineProperty(exports, "useTableUrlState", {
1924
- enumerable: true,
1925
- get: function () { return core.useTableUrlState; }
1813
+ enumerable: true,
1814
+ get: function() {
1815
+ return _adapttable_core.useTableUrlState;
1816
+ }
1926
1817
  });
1927
- exports.DataTable = DataTable;
1928
- exports.SavedViewsMenu = SavedViewsMenu;
1929
- //# sourceMappingURL=index.cjs.map
1818
+
1930
1819
  //# sourceMappingURL=index.cjs.map