@mantine-vue/table 2.4.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cjs/index.cjs CHANGED
@@ -149,6 +149,896 @@ var getDefaultColumnOrderIds = (tableOptions, reset = false) => {
149
149
  ];
150
150
  };
151
151
  //#endregion
152
+ //#region src/server-grouping/serverGrouping.types.ts
153
+ var MVT_SERVER_GROUPING_ROOT_PATH_ID = "__root__";
154
+ //#endregion
155
+ //#region src/utils/utils.ts
156
+ var parseFromValuesOrFunc = (fn, arg) => fn instanceof Function ? fn(arg) : fn;
157
+ //#endregion
158
+ //#region src/server-grouping/serverGroupingSelection.ts
159
+ /** The server-grouping engine of a table, when server grouping is active. */
160
+ var getServerGroupingManager = (table) => table._serverGrouping;
161
+ /**
162
+ * Record rows that the user may currently select: loaded records inside
163
+ * expanded groups only (never group rows, never collapsed or unloaded
164
+ * groups), filtered by `enableRowSelection`.
165
+ */
166
+ var getServerGroupingSelectableRows = (table) => {
167
+ const manager = getServerGroupingManager(table);
168
+ if (!manager) return [];
169
+ const { enableRowSelection } = table.options;
170
+ return manager.getVisibleRecordRows().filter((row) => parseFromValuesOrFunc(enableRowSelection, row) !== false);
171
+ };
172
+ /** Whether one record row is selected, honoring exclude mode. */
173
+ var isServerGroupingRecordSelected = (table, row) => {
174
+ const selectAll = getServerGroupingManager(table)?.getSelectAll();
175
+ if (selectAll?.active) return !selectAll.excludedRowIds.includes(row.id);
176
+ return !!table.getState().rowSelection?.[row.id];
177
+ };
178
+ /**
179
+ * Header-checkbox state for a server-grouped table.
180
+ *
181
+ * In *include* mode `isAll` means "every selectable loaded record is
182
+ * selected". In *exclude* mode ("all matching records") the selection is a
183
+ * query, so the checkbox is checked, and turns indeterminate as soon as the
184
+ * user unchecks individual rows.
185
+ */
186
+ var getServerGroupingSelectionSummary = (table) => {
187
+ const rows = getServerGroupingSelectableRows(table);
188
+ const selectAll = getServerGroupingManager(table)?.getSelectAll() ?? {
189
+ active: false,
190
+ excludedRowIds: []
191
+ };
192
+ if (selectAll.active) {
193
+ const excludedVisible = rows.filter((row) => selectAll.excludedRowIds.includes(row.id)).length;
194
+ return {
195
+ excludedRowIds: selectAll.excludedRowIds,
196
+ isAll: selectAll.excludedRowIds.length === 0,
197
+ isSelectAllMatching: true,
198
+ isSome: selectAll.excludedRowIds.length > 0,
199
+ mode: "exclude",
200
+ selectableCount: rows.length,
201
+ selectedCount: rows.length - excludedVisible,
202
+ selectedRowIds: []
203
+ };
204
+ }
205
+ const rowSelection = table.getState().rowSelection ?? {};
206
+ const selectedCount = rows.filter((row) => rowSelection[row.id]).length;
207
+ const selectedRowIds = Object.keys(rowSelection).filter((rowId) => rowSelection[rowId]);
208
+ const isAll = rows.length > 0 && selectedCount === rows.length;
209
+ return {
210
+ excludedRowIds: [],
211
+ isAll,
212
+ isSelectAllMatching: false,
213
+ isSome: !isAll && selectedRowIds.length > 0,
214
+ mode: "include",
215
+ selectableCount: rows.length,
216
+ selectedCount,
217
+ selectedRowIds
218
+ };
219
+ };
220
+ /**
221
+ * Everything a bulk action needs to send to the server.
222
+ *
223
+ * In exclude mode this is the query plus the user's deselections — no row ids
224
+ * are transferred, so it scales to millions of matching records.
225
+ */
226
+ var getServerGroupingSelectionPayload = (table) => {
227
+ const manager = getServerGroupingManager(table);
228
+ const summary = getServerGroupingSelectionSummary(table);
229
+ const tableState = table.getState();
230
+ return {
231
+ columnFilters: tableState.columnFilters ?? [],
232
+ excludedRowIds: summary.excludedRowIds,
233
+ globalFilter: tableState.globalFilter,
234
+ grouping: manager?.getGroupingFields() ?? [],
235
+ mode: summary.mode,
236
+ rowIds: summary.selectedRowIds,
237
+ sorting: tableState.sorting ?? []
238
+ };
239
+ };
240
+ /**
241
+ * Header-checkbox toggle.
242
+ *
243
+ * - **Checking** selects every loaded record inside expanded groups. Records in
244
+ * collapsed or unloaded groups are never implicitly selected.
245
+ * - **Unchecking** clears the *entire* selection — including records whose
246
+ * group was collapsed since, and everything added by "select all matching
247
+ * records". Removing only the on-screen ids would leave invisible records
248
+ * selected while the header checkbox reads unchecked.
249
+ */
250
+ var toggleAllServerGroupingRecordsSelected = (table, value) => {
251
+ const manager = getServerGroupingManager(table);
252
+ const summary = getServerGroupingSelectionSummary(table);
253
+ if (!(value ?? !(summary.isAll || summary.isSelectAllMatching))) {
254
+ manager?.setSelectAll({
255
+ active: false,
256
+ excludedRowIds: []
257
+ });
258
+ table.setRowSelection({});
259
+ return;
260
+ }
261
+ if (summary.isSelectAllMatching) {
262
+ manager?.setSelectAll({
263
+ active: true,
264
+ excludedRowIds: []
265
+ });
266
+ return;
267
+ }
268
+ const rows = getServerGroupingSelectableRows(table);
269
+ table.setRowSelection((prev) => {
270
+ const selection = { ...prev };
271
+ rows.forEach((row) => {
272
+ selection[row.id] = true;
273
+ });
274
+ return selection;
275
+ });
276
+ };
277
+ /** Deselect the given record ids (used by `selection.clearOnCollapse`). */
278
+ var deselectServerGroupingRecords = (table, rowIds) => {
279
+ if (!rowIds.length) return;
280
+ table.setRowSelection((prev) => {
281
+ const selection = { ...prev };
282
+ rowIds.forEach((rowId) => delete selection[rowId]);
283
+ return selection;
284
+ });
285
+ };
286
+ /**
287
+ * Toggle a single server-grouping record.
288
+ *
289
+ * Provider rows live outside the table's row model (`data` is empty), so
290
+ * `row.toggleSelected()` — which resolves the row through `table.getRow()` —
291
+ * would throw. Selection state is mutated directly instead.
292
+ *
293
+ * Supports shift-click range selection across the loaded records currently on
294
+ * screen, matching the behavior of a regular MVT table.
295
+ */
296
+ var toggleServerGroupingRecordSelected = (table, row, value, options) => {
297
+ const manager = getServerGroupingManager(table);
298
+ const rows = getServerGroupingSelectableRows(table);
299
+ const targetIds = new Set([row.id]);
300
+ if (options?.batch && options.lastSelectedRowId) {
301
+ const lastIndex = rows.findIndex((candidate) => candidate.id === options.lastSelectedRowId);
302
+ const currentIndex = rows.findIndex((candidate) => candidate.id === row.id);
303
+ if (lastIndex !== -1 && currentIndex !== -1) {
304
+ const [start, end] = lastIndex < currentIndex ? [lastIndex, currentIndex] : [currentIndex, lastIndex];
305
+ for (let index = start; index <= end; index++) targetIds.add(rows[index].id);
306
+ }
307
+ }
308
+ const selectAll = manager?.getSelectAll();
309
+ if (selectAll?.active) {
310
+ const excluded = new Set(selectAll.excludedRowIds);
311
+ targetIds.forEach((rowId) => {
312
+ if (value) excluded.delete(rowId);
313
+ else excluded.add(rowId);
314
+ });
315
+ manager.setSelectAll({
316
+ active: true,
317
+ excludedRowIds: [...excluded]
318
+ });
319
+ return;
320
+ }
321
+ table.setRowSelection((prev) => {
322
+ const selection = { ...prev };
323
+ targetIds.forEach((rowId) => {
324
+ if (value) selection[rowId] = true;
325
+ else delete selection[rowId];
326
+ });
327
+ return selection;
328
+ });
329
+ };
330
+ /**
331
+ * Opt-in "select all matching records" action: asks the consumer-provided
332
+ * `serverGrouping.selection.loadAllMatchingRowIds` for every row id matching
333
+ * the current filters (across all groups and pages) and selects them.
334
+ */
335
+ var selectAllMatchingServerGroupingRecords = async (table) => {
336
+ const manager = getServerGroupingManager(table);
337
+ if (!manager) return;
338
+ const selectionOptions = manager.options.selection;
339
+ if ((selectionOptions?.selectAllMode ?? "query") === "query") {
340
+ table.setRowSelection({});
341
+ manager.setSelectAll({
342
+ active: true,
343
+ excludedRowIds: []
344
+ });
345
+ return;
346
+ }
347
+ const loadAllMatchingRowIds = selectionOptions?.loadAllMatchingRowIds;
348
+ if (!loadAllMatchingRowIds) return;
349
+ const tableState = table.getState();
350
+ const rowIds = await loadAllMatchingRowIds({
351
+ columnFilters: tableState.columnFilters ?? [],
352
+ globalFilter: tableState.globalFilter,
353
+ grouping: manager.getGroupingFields(),
354
+ sorting: tableState.sorting ?? [],
355
+ table
356
+ });
357
+ table.setRowSelection((prev) => {
358
+ const selection = { ...prev };
359
+ rowIds.forEach((rowId) => {
360
+ selection[rowId] = true;
361
+ });
362
+ return selection;
363
+ });
364
+ };
365
+ //#endregion
366
+ //#region src/server-grouping/serverGrouping.utils.ts
367
+ /**
368
+ * Escape a path segment part so group ids may safely contain the reserved
369
+ * characters `/` and `:` (and `%`, used for escaping).
370
+ */
371
+ var escapeServerGroupPathPart = (part) => part.replace(/%/g, "%25").replace(/:/g, "%3A").replace(/\//g, "%2F");
372
+ var unescapeServerGroupPathPart = (part) => part.replace(/%2F/g, "/").replace(/%3A/g, ":").replace(/%25/g, "%");
373
+ /**
374
+ * Build the stable path id of a group's content, e.g.
375
+ * `createServerGroupPathId('__root__', 'country', 'BH')` → `'country:BH'` and
376
+ * `createServerGroupPathId('country:BH', 'salesperson', '42')` →
377
+ * `'country:BH/salesperson:42'`. Never derived from row indexes.
378
+ */
379
+ var createServerGroupPathId = (parentPathId, field, groupId) => {
380
+ const segment = `${escapeServerGroupPathPart(field)}:${escapeServerGroupPathPart(groupId)}`;
381
+ return parentPathId === "__root__" ? segment : `${parentPathId}/${segment}`;
382
+ };
383
+ /** Parse a path id back into its `{ field, groupId }` segments. Root → `[]`. */
384
+ var parseServerGroupPathId = (pathId) => {
385
+ if (pathId === "__root__") return [];
386
+ return pathId.split("/").map((segment) => {
387
+ const separatorIndex = segment.indexOf(":");
388
+ return {
389
+ field: unescapeServerGroupPathPart(segment.slice(0, separatorIndex)),
390
+ groupId: unescapeServerGroupPathPart(segment.slice(separatorIndex + 1))
391
+ };
392
+ });
393
+ };
394
+ /** Depth of the content addressed by a path id (root → 0). */
395
+ var getServerGroupPathDepth = (pathId) => pathId === "__root__" ? 0 : pathId.split("/").length;
396
+ /** Whether `pathId` equals `ancestorPathId` or is nested anywhere below it. */
397
+ var isServerGroupPathOrDescendant = (pathId, ancestorPathId) => ancestorPathId === "__root__" || pathId === ancestorPathId || pathId.startsWith(`${ancestorPathId}/`);
398
+ /** JSON replacer producing deterministic output for plain objects. */
399
+ var stableReplacer = (_key, value) => {
400
+ if (value && typeof value === "object" && !Array.isArray(value)) return Object.keys(value).sort().reduce((acc, key) => {
401
+ acc[key] = value[key];
402
+ return acc;
403
+ }, {});
404
+ return value;
405
+ };
406
+ /**
407
+ * Serialize cache key parts deterministically (object key order does not
408
+ * affect the result).
409
+ */
410
+ var serializeServerGroupingCacheKey = (parts) => JSON.stringify(parts, stableReplacer);
411
+ /** Whether an error was caused by request cancellation (never shown as an error). */
412
+ var isAbortError = (error) => error instanceof DOMException ? error.name === "AbortError" : error?.name === "AbortError";
413
+ //#endregion
414
+ //#region src/server-grouping/useMVT_ServerGrouping.ts
415
+ var createMVT_ServerGroupingManager = (table, controlledExpandedReader, controlledSelectAllReader) => {
416
+ const serverGroupingOptions = table.options.serverGrouping;
417
+ if (!serverGroupingOptions?.provider || serverGroupingOptions.enabled === false) return;
418
+ const options = serverGroupingOptions;
419
+ const provider = options.provider;
420
+ const sortingMode = options.sortingMode ?? "independent";
421
+ const filteringMode = options.filteringMode ?? "shared";
422
+ const cacheOptions = {
423
+ enabled: options.cache?.enabled ?? false,
424
+ gcTime: options.cache?.gcTime ?? 3e5,
425
+ keepCollapsedGroups: options.cache?.keepCollapsedGroups ?? true,
426
+ staleTime: options.cache?.staleTime ?? 0
427
+ };
428
+ const configuredPageSize = options.pagination?.defaultPageSize ?? options.initialPageSize ?? 10;
429
+ let defaultPageSize = configuredPageSize;
430
+ if (!Number.isInteger(configuredPageSize) || configuredPageSize <= 0) {
431
+ console.warn(`[mantine-vue-table] serverGrouping page size ${JSON.stringify(configuredPageSize)} is invalid — expected a positive integer. Falling back to 10.`);
432
+ defaultPageSize = 10;
433
+ }
434
+ const pathStates = (0, vue.ref)({});
435
+ const internalExpanded = (0, vue.ref)(table.options.initialState?.serverGroupingExpanded ?? {});
436
+ const internalSelectAll = (0, vue.ref)(table.options.initialState?.serverGroupingSelectAll ?? {
437
+ active: false,
438
+ excludedRowIds: []
439
+ });
440
+ const nodeRegistry = /* @__PURE__ */ new Map();
441
+ const controllers = /* @__PURE__ */ new Map();
442
+ const versions = /* @__PURE__ */ new Map();
443
+ const cache = /* @__PURE__ */ new Map();
444
+ const rowCache = /* @__PURE__ */ new Map();
445
+ const readControlledExpanded = controlledExpandedReader ?? (() => table.options.state?.serverGroupingExpanded);
446
+ const isExpandedControlled = readControlledExpanded() !== void 0;
447
+ const getExpanded = () => readControlledExpanded() ?? internalExpanded.value;
448
+ const setExpanded = (updater) => {
449
+ const next = updater instanceof Function ? updater(getExpanded()) : updater;
450
+ const onChange = table.options.onServerGroupingExpandedChange;
451
+ if (onChange) onChange(next);
452
+ if (!isExpandedControlled) internalExpanded.value = next;
453
+ };
454
+ const isExpanded = (pathId) => getExpanded()[pathId] === true;
455
+ const readControlledSelectAll = controlledSelectAllReader ?? (() => table.options.state?.serverGroupingSelectAll);
456
+ const isSelectAllControlled = readControlledSelectAll() !== void 0;
457
+ const getSelectAll = () => readControlledSelectAll() ?? internalSelectAll.value;
458
+ const setSelectAll = (updater) => {
459
+ const next = updater instanceof Function ? updater(getSelectAll()) : updater;
460
+ table.options.onServerGroupingSelectAllChange?.(next);
461
+ if (!isSelectAllControlled) internalSelectAll.value = next;
462
+ };
463
+ /** The "all matching" selection is tied to the query it was made under. */
464
+ const resetSelectAll = () => {
465
+ if (getSelectAll().active || getSelectAll().excludedRowIds.length) setSelectAll({
466
+ active: false,
467
+ excludedRowIds: []
468
+ });
469
+ };
470
+ const getGroupingFields = () => options.grouping ?? table.getState().grouping ?? [];
471
+ const setPathState = (pathId, state) => {
472
+ pathStates.value = {
473
+ ...pathStates.value,
474
+ [pathId]: state
475
+ };
476
+ };
477
+ const getPathState = (pathId) => pathStates.value[pathId];
478
+ /**
479
+ * Table rows for one loaded record page. Rows are created (and cached) here
480
+ * rather than in the renderer so row ids — and therefore selection state —
481
+ * have a single source of truth shared by the body, the header checkbox, and
482
+ * the public selection APIs.
483
+ */
484
+ const getRecordRows = (pathId) => {
485
+ const state = getPathState(pathId);
486
+ if (!state || state.kind !== "records" || !state.rows) return [];
487
+ const cached = rowCache.get(pathId);
488
+ if (cached && cached.source === state.rows) return cached.rows;
489
+ const depth = getServerGroupPathDepth(pathId);
490
+ const rows = state.rows.map((original, index) => {
491
+ return (0, _tanstack_vue_table.createRow)(table, provider.getRowId?.(original) ?? table.options.getRowId?.(original, index, void 0) ?? `${pathId}::${index}`, original, index, depth);
492
+ });
493
+ rowCache.set(pathId, {
494
+ rows,
495
+ source: state.rows
496
+ });
497
+ return rows;
498
+ };
499
+ /**
500
+ * Every loaded record row that is currently visible — i.e. inside expanded
501
+ * groups on the current pages. Records in collapsed or never-loaded groups
502
+ * are deliberately excluded, so selection can never reach them.
503
+ */
504
+ const getVisibleRecordRows = () => Object.keys(pathStates.value).filter((pathId) => pathStates.value[pathId]?.kind === "records" && isPathVisible(pathId)).flatMap((pathId) => getRecordRows(pathId));
505
+ const getNode = (pathId) => {
506
+ const item = nodeRegistry.get(pathId);
507
+ if (!item) return void 0;
508
+ return {
509
+ count: void 0,
510
+ id: item.id,
511
+ label: item.label,
512
+ meta: item.meta,
513
+ original: item.original,
514
+ value: item.value,
515
+ ...item.__node
516
+ };
517
+ };
518
+ const getParentItems = (pathId) => {
519
+ if (pathId === "__root__") return [];
520
+ const segments = pathId.split("/");
521
+ const items = [];
522
+ for (let index = 0; index < segments.length; index++) {
523
+ const ancestorPathId = segments.slice(0, index + 1).join("/");
524
+ const item = nodeRegistry.get(ancestorPathId);
525
+ if (item) items.push(item);
526
+ }
527
+ return items;
528
+ };
529
+ const getEffectiveSorting = (pathId, kind) => {
530
+ const override = getPathState(pathId)?.sorting;
531
+ if (override) return override;
532
+ const tableSorting = table.getState().sorting ?? [];
533
+ if (sortingMode === "shared") return tableSorting;
534
+ if (sortingMode === "records-only") return kind === "records" ? tableSorting : [];
535
+ return pathId === "__root__" ? tableSorting : [];
536
+ };
537
+ const getEffectiveFilters = (pathId, kind) => {
538
+ const state = getPathState(pathId);
539
+ if (state?.columnFilters || state?.globalFilter !== void 0) return {
540
+ columnFilters: state.columnFilters ?? [],
541
+ globalFilter: state.globalFilter
542
+ };
543
+ if (filteringMode === "independent") return {
544
+ columnFilters: [],
545
+ globalFilter: void 0
546
+ };
547
+ if (filteringMode === "records-only" && kind !== "records") return {
548
+ columnFilters: [],
549
+ globalFilter: void 0
550
+ };
551
+ const tableState = table.getState();
552
+ return {
553
+ columnFilters: tableState.columnFilters ?? [],
554
+ globalFilter: tableState.globalFilter
555
+ };
556
+ };
557
+ const isRootPath = (pathId) => pathId === MVT_SERVER_GROUPING_ROOT_PATH_ID;
558
+ const getDefaultPagination = () => ({
559
+ pageIndex: 0,
560
+ pageSize: defaultPageSize
561
+ });
562
+ /**
563
+ * Effective pagination of a path. The root level is driven by the table's
564
+ * own `pagination` state, so the standard bottom toolbar (page navigation +
565
+ * rows-per-page) controls it exactly like an ungrouped server-side table.
566
+ * Nested levels keep their own independent, fixed-page-size state.
567
+ */
568
+ const getEffectivePagination = (pathId) => {
569
+ if (isRootPath(pathId)) {
570
+ const { pageIndex, pageSize } = table.getState().pagination;
571
+ return {
572
+ ...getPathState(pathId)?.pagination,
573
+ pageIndex,
574
+ pageSize
575
+ };
576
+ }
577
+ return getPathState(pathId)?.pagination ?? getDefaultPagination();
578
+ };
579
+ /** Reset a path to page 0. Returns `true` when it triggered a root reload. */
580
+ const resetPageIndex = (pathId) => {
581
+ if (isRootPath(pathId)) {
582
+ const { pageIndex } = table.getState().pagination;
583
+ if (pageIndex === 0) return false;
584
+ table.setPageIndex(0);
585
+ return true;
586
+ }
587
+ const state = getPathState(pathId);
588
+ if (state) setPathState(pathId, {
589
+ ...state,
590
+ pagination: {
591
+ ...state.pagination,
592
+ cursor: void 0,
593
+ pageIndex: 0
594
+ }
595
+ });
596
+ return false;
597
+ };
598
+ const buildRequest = (pathId, kind, signal) => {
599
+ const grouping = getGroupingFields();
600
+ const depth = getServerGroupPathDepth(pathId);
601
+ const parentGroups = getParentItems(pathId);
602
+ const { columnFilters, globalFilter } = getEffectiveFilters(pathId, kind);
603
+ const base = {
604
+ columnFilters,
605
+ depth,
606
+ globalFilter,
607
+ grouping,
608
+ pagination: getEffectivePagination(pathId),
609
+ parentGroup: parentGroups.at(-1),
610
+ parentGroups,
611
+ pathId,
612
+ signal,
613
+ sorting: getEffectiveSorting(pathId, kind),
614
+ table
615
+ };
616
+ return kind === "groups" ? {
617
+ ...base,
618
+ groupingField: grouping[depth]
619
+ } : base;
620
+ };
621
+ const getCacheKey = (request) => serializeServerGroupingCacheKey([
622
+ request.pathId,
623
+ request.grouping,
624
+ request.depth,
625
+ request.pagination,
626
+ request.sorting,
627
+ request.columnFilters,
628
+ request.globalFilter ?? null,
629
+ ...provider.getCacheKey?.(request) ?? []
630
+ ]);
631
+ const gcCache = () => {
632
+ const now = Date.now();
633
+ cache.forEach((entry, key) => {
634
+ if (now - entry.updatedAt > cacheOptions.gcTime) cache.delete(key);
635
+ });
636
+ };
637
+ const load = (pathId) => {
638
+ const grouping = getGroupingFields();
639
+ const depth = getServerGroupPathDepth(pathId);
640
+ const kind = depth >= grouping.length ? "records" : "groups";
641
+ const previous = getPathState(pathId);
642
+ const hasData = previous?.groups !== void 0 || previous?.rows !== void 0;
643
+ controllers.get(pathId)?.abort();
644
+ const controller = new AbortController();
645
+ controllers.set(pathId, controller);
646
+ const version = (versions.get(pathId) ?? 0) + 1;
647
+ versions.set(pathId, version);
648
+ const request = buildRequest(pathId, kind, controller.signal);
649
+ const isCurrent = () => versions.get(pathId) === version && !controller.signal.aborted;
650
+ let servedFromCache = false;
651
+ let cacheKey;
652
+ if (cacheOptions.enabled) {
653
+ cacheKey = getCacheKey(request);
654
+ const entry = cache.get(cacheKey);
655
+ if (entry) {
656
+ servedFromCache = true;
657
+ const fresh = Date.now() - entry.updatedAt <= cacheOptions.staleTime;
658
+ setPathState(pathId, {
659
+ ...entry.payload,
660
+ columnFilters: previous?.columnFilters,
661
+ depth,
662
+ error: null,
663
+ globalFilter: previous?.globalFilter,
664
+ isFetching: !fresh,
665
+ isLoading: false,
666
+ pagination: request.pagination,
667
+ pathId,
668
+ sorting: previous?.sorting,
669
+ updatedAt: entry.updatedAt
670
+ });
671
+ if (entry.payload.kind === "groups") registerGroups(pathId, entry.payload.groups ?? [], grouping[depth], depth);
672
+ if (fresh) return;
673
+ }
674
+ }
675
+ if (!servedFromCache) setPathState(pathId, {
676
+ ...previous,
677
+ columnFilters: previous?.columnFilters,
678
+ depth,
679
+ error: null,
680
+ groups: kind === "groups" ? previous?.groups : void 0,
681
+ isFetching: true,
682
+ isLoading: !hasData,
683
+ kind,
684
+ pagination: request.pagination,
685
+ pathId,
686
+ rowCount: previous?.rowCount ?? 0,
687
+ rows: kind === "records" ? previous?.rows : void 0,
688
+ sorting: previous?.sorting
689
+ });
690
+ const eventPayload = {
691
+ path: request.parentGroups,
692
+ pathId
693
+ };
694
+ options.onGroupLoadStart?.({
695
+ ...eventPayload,
696
+ request
697
+ });
698
+ const promise = kind === "groups" ? provider.loadGroups(request) : provider.loadRecords(request);
699
+ Promise.resolve(promise).then((response) => {
700
+ if (!isCurrent()) return;
701
+ const updatedAt = Date.now();
702
+ let payload;
703
+ if (kind === "groups") {
704
+ const context = {
705
+ depth,
706
+ field: grouping[depth],
707
+ request
708
+ };
709
+ const groups = (provider.getGroups(response) ?? []).map((group) => ({
710
+ count: provider.getGroupCount?.(group, context),
711
+ hasChildren: provider.hasGroupChildren?.(group, context) ?? true,
712
+ id: provider.getGroupId(group, context),
713
+ label: provider.getGroupLabel(group, context),
714
+ meta: provider.getGroupMeta?.(group, context) ?? {},
715
+ original: group,
716
+ value: provider.getGroupValue ? provider.getGroupValue(group, context) : group
717
+ }));
718
+ registerGroups(pathId, groups, grouping[depth], depth);
719
+ payload = {
720
+ groups,
721
+ kind,
722
+ rowCount: provider.getGroupRowCount(response)
723
+ };
724
+ options.onGroupLoadSuccess?.({
725
+ ...eventPayload,
726
+ groups,
727
+ request,
728
+ response
729
+ });
730
+ } else {
731
+ const rows = provider.getRecords(response) ?? [];
732
+ payload = {
733
+ kind,
734
+ rowCount: provider.getRecordRowCount(response),
735
+ rows
736
+ };
737
+ options.onRecordLoadSuccess?.({
738
+ ...eventPayload,
739
+ request,
740
+ response,
741
+ rows
742
+ });
743
+ }
744
+ setPathState(pathId, {
745
+ ...getPathState(pathId),
746
+ ...payload,
747
+ error: null,
748
+ isFetching: false,
749
+ isLoading: false,
750
+ updatedAt
751
+ });
752
+ if (cacheOptions.enabled && cacheKey) {
753
+ cache.set(cacheKey, {
754
+ pathId,
755
+ payload,
756
+ updatedAt
757
+ });
758
+ gcCache();
759
+ }
760
+ }).catch((error) => {
761
+ if (!isCurrent() || isAbortError(error)) return;
762
+ setPathState(pathId, {
763
+ ...getPathState(pathId),
764
+ error,
765
+ isFetching: false,
766
+ isLoading: false
767
+ });
768
+ options.onError?.({
769
+ ...eventPayload,
770
+ error,
771
+ request
772
+ });
773
+ });
774
+ };
775
+ const registerGroups = (parentPathId, groups, field, depth) => {
776
+ groups.forEach((node) => {
777
+ const childPathId = createServerGroupPathId(parentPathId, field, node.id);
778
+ const item = {
779
+ depth,
780
+ field,
781
+ id: node.id,
782
+ label: node.label,
783
+ meta: node.meta,
784
+ original: node.original,
785
+ value: node.value
786
+ };
787
+ item.__node = node;
788
+ nodeRegistry.set(childPathId, item);
789
+ });
790
+ };
791
+ const ensureLoaded = (pathId) => {
792
+ const state = getPathState(pathId);
793
+ if (!state || state.groups === void 0 && state.rows === void 0 && !state.isFetching) load(pathId);
794
+ };
795
+ const isPathVisible = (pathId) => {
796
+ if (pathId === "__root__") return true;
797
+ const segments = pathId.split("/");
798
+ for (let index = 0; index < segments.length; index++) if (!isExpanded(segments.slice(0, index + 1).join("/"))) return false;
799
+ return true;
800
+ };
801
+ const getLoadedVisiblePaths = () => Object.keys(pathStates.value).filter(isPathVisible);
802
+ const abortPathAndDescendants = (pathId) => {
803
+ controllers.forEach((controller, key) => {
804
+ if (!isServerGroupPathOrDescendant(key, pathId)) return;
805
+ controller.abort();
806
+ const state = getPathState(key);
807
+ if (state?.isFetching || state?.isLoading) setPathState(key, {
808
+ ...state,
809
+ isFetching: false,
810
+ isLoading: false
811
+ });
812
+ });
813
+ };
814
+ const dropPathAndDescendants = (pathId) => {
815
+ const next = { ...pathStates.value };
816
+ Object.keys(next).forEach((key) => {
817
+ if (isServerGroupPathOrDescendant(key, pathId)) delete next[key];
818
+ });
819
+ pathStates.value = next;
820
+ cache.forEach((entry, key) => {
821
+ if (isServerGroupPathOrDescendant(entry.pathId, pathId)) cache.delete(key);
822
+ });
823
+ rowCache.forEach((_entry, key) => {
824
+ if (isServerGroupPathOrDescendant(key, pathId)) rowCache.delete(key);
825
+ });
826
+ };
827
+ const expand = (pathId) => {
828
+ if (isExpanded(pathId)) return;
829
+ setExpanded((prev) => ({
830
+ ...prev,
831
+ [pathId]: true
832
+ }));
833
+ };
834
+ const collapse = (pathId) => {
835
+ if (!isExpanded(pathId)) return;
836
+ setExpanded((prev) => ({
837
+ ...prev,
838
+ [pathId]: false
839
+ }));
840
+ };
841
+ const toggle = (pathId) => {
842
+ if (isExpanded(pathId)) collapse(pathId);
843
+ else expand(pathId);
844
+ };
845
+ const invalidate = (pathId) => {
846
+ if (pathId === void 0) cache.clear();
847
+ else cache.forEach((entry, key) => {
848
+ if (isServerGroupPathOrDescendant(entry.pathId, pathId)) cache.delete(key);
849
+ });
850
+ };
851
+ const reload = (pathId) => {
852
+ if (pathId !== void 0) {
853
+ invalidate(pathId);
854
+ if (getPathState(pathId)) load(pathId);
855
+ return;
856
+ }
857
+ invalidate();
858
+ getLoadedVisiblePaths().forEach((visiblePathId) => load(visiblePathId));
859
+ };
860
+ const retry = (pathId) => {
861
+ load(pathId);
862
+ };
863
+ const reset = () => {
864
+ controllers.forEach((controller) => controller.abort());
865
+ controllers.clear();
866
+ versions.clear();
867
+ nodeRegistry.clear();
868
+ cache.clear();
869
+ rowCache.clear();
870
+ pathStates.value = {};
871
+ if (options.resetExpansionOnGroupingChange !== false) setExpanded({});
872
+ load(MVT_SERVER_GROUPING_ROOT_PATH_ID);
873
+ };
874
+ const setPagination = (pathId, updater) => {
875
+ const previous = getEffectivePagination(pathId);
876
+ const next = updater instanceof Function ? updater(previous) : {
877
+ ...previous,
878
+ ...updater
879
+ };
880
+ if (isRootPath(pathId)) {
881
+ const state = getPathState(pathId);
882
+ if (state) setPathState(pathId, {
883
+ ...state,
884
+ pagination: next
885
+ });
886
+ const current = table.getState().pagination;
887
+ if (current.pageIndex === next.pageIndex && current.pageSize === next.pageSize) load(pathId);
888
+ else table.setPagination({
889
+ pageIndex: next.pageIndex,
890
+ pageSize: next.pageSize
891
+ });
892
+ return;
893
+ }
894
+ const state = getPathState(pathId);
895
+ if (state) setPathState(pathId, {
896
+ ...state,
897
+ pagination: next
898
+ });
899
+ else setPathState(pathId, {
900
+ depth: getServerGroupPathDepth(pathId),
901
+ error: null,
902
+ isFetching: false,
903
+ isLoading: false,
904
+ kind: getServerGroupPathDepth(pathId) >= getGroupingFields().length ? "records" : "groups",
905
+ pagination: next,
906
+ pathId,
907
+ rowCount: 0
908
+ });
909
+ load(pathId);
910
+ };
911
+ const setSorting = (pathId, updater) => {
912
+ const state = getPathState(pathId);
913
+ const previous = state?.sorting ?? [];
914
+ const next = updater instanceof Function ? updater(previous) : updater;
915
+ if (!state) return;
916
+ setPathState(pathId, {
917
+ ...state,
918
+ sorting: next
919
+ });
920
+ if (!resetPageIndex(pathId)) load(pathId);
921
+ };
922
+ const setColumnFilters = (pathId, updater) => {
923
+ const state = getPathState(pathId);
924
+ if (!state) return;
925
+ const previous = state.columnFilters ?? [];
926
+ const next = updater instanceof Function ? updater(previous) : updater;
927
+ setPathState(pathId, {
928
+ ...state,
929
+ columnFilters: next
930
+ });
931
+ if (!resetPageIndex(pathId)) load(pathId);
932
+ };
933
+ const setGlobalFilter = (pathId, value) => {
934
+ const state = getPathState(pathId);
935
+ if (!state) return;
936
+ setPathState(pathId, {
937
+ ...state,
938
+ globalFilter: value
939
+ });
940
+ if (!resetPageIndex(pathId)) load(pathId);
941
+ };
942
+ const getState = () => ({
943
+ enabled: true,
944
+ expanded: getExpanded(),
945
+ grouping: getGroupingFields(),
946
+ paths: pathStates.value
947
+ });
948
+ (0, vue.watch)(() => ({ ...getExpanded() }), (next, prev) => {
949
+ Object.keys(next).forEach((pathId) => {
950
+ if (next[pathId] && !prev?.[pathId]) {
951
+ options.onGroupExpand?.({
952
+ group: getNode(pathId),
953
+ path: getParentItems(pathId),
954
+ pathId
955
+ });
956
+ ensureLoaded(pathId);
957
+ }
958
+ });
959
+ Object.keys(prev ?? {}).forEach((pathId) => {
960
+ if (prev?.[pathId] && !next[pathId]) {
961
+ options.onGroupCollapse?.({
962
+ group: getNode(pathId),
963
+ path: getParentItems(pathId),
964
+ pathId
965
+ });
966
+ abortPathAndDescendants(pathId);
967
+ if (options.selection?.clearOnCollapse) deselectServerGroupingRecords(table, Object.keys(pathStates.value).filter((key) => pathStates.value[key]?.kind === "records" && isServerGroupPathOrDescendant(key, pathId)).flatMap((key) => getRecordRows(key).map((row) => row.id)));
968
+ if (!cacheOptions.keepCollapsedGroups) dropPathAndDescendants(pathId);
969
+ }
970
+ });
971
+ });
972
+ (0, vue.watch)(() => JSON.stringify(getGroupingFields()), (next, prev) => {
973
+ if (next !== prev) {
974
+ resetSelectAll();
975
+ reset();
976
+ }
977
+ });
978
+ (0, vue.watch)(() => JSON.stringify([table.getState().columnFilters ?? [], table.getState().globalFilter ?? null]), (next, prev) => {
979
+ if (next === prev || filteringMode === "independent") return;
980
+ resetSelectAll();
981
+ invalidate();
982
+ getLoadedVisiblePaths().filter((pathId) => filteringMode === "shared" || getPathState(pathId)?.kind === "records").forEach((pathId) => {
983
+ setPathState(pathId, {
984
+ ...getPathState(pathId),
985
+ groups: void 0,
986
+ rows: void 0
987
+ });
988
+ if (!resetPageIndex(pathId)) load(pathId);
989
+ });
990
+ });
991
+ (0, vue.watch)(() => JSON.stringify(table.getState().sorting ?? []), (next, prev) => {
992
+ if (next === prev) return;
993
+ getLoadedVisiblePaths().filter((pathId) => {
994
+ if (getPathState(pathId)?.sorting) return false;
995
+ if (sortingMode === "shared") return true;
996
+ if (sortingMode === "records-only") return getPathState(pathId)?.kind === "records";
997
+ return pathId === MVT_SERVER_GROUPING_ROOT_PATH_ID;
998
+ }).forEach((pathId) => {
999
+ if (!resetPageIndex(pathId)) load(pathId);
1000
+ });
1001
+ });
1002
+ (0, vue.watch)(() => {
1003
+ const { pageIndex, pageSize } = table.getState().pagination;
1004
+ return `${pageIndex}:${pageSize}`;
1005
+ }, (next, prev) => {
1006
+ if (next !== prev) load(MVT_SERVER_GROUPING_ROOT_PATH_ID);
1007
+ });
1008
+ load(MVT_SERVER_GROUPING_ROOT_PATH_ID);
1009
+ if ((0, vue.getCurrentScope)()) (0, vue.onScopeDispose)(() => {
1010
+ controllers.forEach((controller) => controller.abort());
1011
+ controllers.clear();
1012
+ });
1013
+ return {
1014
+ collapse,
1015
+ ensureLoaded,
1016
+ expand,
1017
+ getExpanded,
1018
+ getGroupingFields,
1019
+ getNode,
1020
+ getParentItems,
1021
+ getPathState,
1022
+ getRecordRows,
1023
+ getSelectAll,
1024
+ getState,
1025
+ getVisibleRecordRows,
1026
+ invalidate,
1027
+ isExpanded,
1028
+ options,
1029
+ reload,
1030
+ reset,
1031
+ retry,
1032
+ setColumnFilters,
1033
+ setExpanded,
1034
+ setGlobalFilter,
1035
+ setSelectAll,
1036
+ setPagination,
1037
+ setSorting,
1038
+ toggle
1039
+ };
1040
+ };
1041
+ //#endregion
152
1042
  //#region src/utils/tanstack.helpers.ts
153
1043
  /**
154
1044
  * Vue equivalent of `@tanstack/react-table`'s `flexRender`.
@@ -178,9 +1068,6 @@ function createMVTColumnHelper() {
178
1068
  }
179
1069
  var createRow = (table, originalRow, rowIndex = -1, depth = 0, subRows, parentId) => (0, _tanstack_vue_table.createRow)(table, "mvt-row-create", originalRow ?? Object.assign({}, ...getAllLeafColumnDefs(table.options.columns).map((col) => ({ [getColumnId(col)]: "" }))), rowIndex, depth, subRows, parentId);
180
1070
  //#endregion
181
- //#region src/utils/utils.ts
182
- var parseFromValuesOrFunc = (fn, arg) => fn instanceof Function ? fn(arg) : fn;
183
- //#endregion
184
1071
  //#region src/components/MVT_TableSlots.ts
185
1072
  var MVT_SLOTS_KEY = Symbol("MVT_Slots");
186
1073
  /**
@@ -656,7 +1543,7 @@ var MVT_EditCellTextInput = (0, vue.defineComponent)({
656
1543
  label: modal ? def.header : void 0,
657
1544
  name: cell.id,
658
1545
  placeholder: !modal ? def.header : void 0,
659
- value: value.value,
1546
+ modelValue: value.value,
660
1547
  variant: table.options.editDisplayMode === "table" ? "unstyled" : "default",
661
1548
  onClick: (e) => e.stopPropagation(),
662
1549
  ref: setRef
@@ -665,30 +1552,30 @@ var MVT_EditCellTextInput = (0, vue.defineComponent)({
665
1552
  ...common,
666
1553
  searchable: true,
667
1554
  ...selectProps,
668
- onChange: (newValue, option) => {
1555
+ "onUpdate:modelValue": (newValue) => {
669
1556
  value.value = newValue;
670
- selectProps.onChange?.(newValue, option);
671
1557
  },
1558
+ onChange: selectProps.onChange,
672
1559
  onBlur: blur
673
1560
  });
674
1561
  if (def.editVariant === "multi-select") return (0, vue.h)(_mantine_vue_core.MultiSelect, {
675
1562
  ...common,
676
1563
  searchable: true,
677
1564
  ...selectProps,
678
- onChange: (newValue) => {
1565
+ "onUpdate:modelValue": (newValue) => {
679
1566
  value.value = newValue;
680
- selectProps.onChange?.(newValue);
681
1567
  },
1568
+ onChange: selectProps.onChange,
682
1569
  onBlur: blur
683
1570
  });
684
1571
  return (0, vue.h)(_mantine_vue_core.TextInput, {
685
1572
  ...common,
686
- value: value.value ?? "",
1573
+ modelValue: value.value ?? "",
687
1574
  ...textProps,
688
- onChange: (event) => {
689
- value.value = typeof event === "string" ? event : event.target.value;
690
- textProps.onChange?.(event);
1575
+ "onUpdate:modelValue": (nextValue) => {
1576
+ value.value = nextValue;
691
1577
  },
1578
+ onChange: textProps.onChange,
692
1579
  onBlur: blur,
693
1580
  onKeydown: (event) => {
694
1581
  textProps.onKeyDown?.(event);
@@ -1002,6 +1889,7 @@ var getIsRankingRows = (table) => {
1002
1889
  };
1003
1890
  var getIsRowSelected = ({ row, table }) => {
1004
1891
  const { options: { enableRowSelection } } = table;
1892
+ if (getServerGroupingManager(table)) return isServerGroupingRecordSelected(table, row);
1005
1893
  return row.getIsSelected() || parseFromValuesOrFunc(enableRowSelection, row) && row.getCanSelectSubRows() && row.getIsAllSubRowsSelected();
1006
1894
  };
1007
1895
  var getMVT_RowSelectionHandler = ({ renderedRowIndex = 0, row, table }) => (event, value) => {
@@ -1012,6 +1900,14 @@ var getMVT_RowSelectionHandler = ({ renderedRowIndex = 0, row, table }) => (even
1012
1900
  row,
1013
1901
  table
1014
1902
  });
1903
+ if (getServerGroupingManager(table)) {
1904
+ toggleServerGroupingRecordSelected(table, row, value ?? !wasCurrentRowChecked, {
1905
+ batch: !!enableBatchRowSelection && enableMultiRowSelection !== false && event.shiftKey,
1906
+ lastSelectedRowId: lastSelectedRowId.value
1907
+ });
1908
+ lastSelectedRowId.value = row.id;
1909
+ return;
1910
+ }
1015
1911
  row.toggleSelected(value ?? !wasCurrentRowChecked);
1016
1912
  const changedRowIds = new Set([row.id]);
1017
1913
  if (enableBatchRowSelection && enableMultiRowSelection && event.shiftKey && lastSelectedRowId.value !== null) {
@@ -1072,7 +1968,10 @@ var MVT_SelectCheckbox = (0, vue.defineComponent)({
1072
1968
  const o = table.options;
1073
1969
  const state = table.getState();
1074
1970
  const selectAll = !row;
1075
- const checked = selectAll ? o.selectAllMode === "page" ? table.getIsAllPageRowsSelected() : table.getIsAllRowsSelected() : getIsRowSelected({
1971
+ const anyTable = table;
1972
+ const serverGroupingSummary = selectAll && getServerGroupingManager(anyTable) ? getServerGroupingSelectionSummary(anyTable) : void 0;
1973
+ const isServerGrouping = !!getServerGroupingManager(anyTable);
1974
+ const checked = serverGroupingSummary ? serverGroupingSummary.isAll : selectAll ? o.selectAllMode === "page" ? table.getIsAllPageRowsSelected() : table.getIsAllRowsSelected() : isServerGrouping && row ? isServerGroupingRecordSelected(anyTable, row) : getIsRowSelected({
1076
1975
  row,
1077
1976
  table
1078
1977
  });
@@ -1088,19 +1987,18 @@ var MVT_SelectCheckbox = (0, vue.defineComponent)({
1088
1987
  renderedRowIndex: props.renderedRowIndex,
1089
1988
  row,
1090
1989
  table
1091
- }) : getMVT_SelectAllHandler({ table });
1990
+ }) : serverGroupingSummary ? (event) => toggleAllServerGroupingRecordsSelected(anyTable, event.target?.checked ?? !serverGroupingSummary.isAll) : getMVT_SelectAllHandler({ table });
1092
1991
  const common = {
1093
1992
  "aria-label": label,
1094
1993
  checked,
1095
- disabled: state.isLoading || row && !row.getCanSelect() || row?.id === "mvt-row-create",
1994
+ disabled: state.isLoading || row && !row.getCanSelect() || row?.id === "mvt-row-create" || serverGroupingSummary?.selectableCount === 0 && serverGroupingSummary.selectedRowIds.length === 0,
1096
1995
  size: state.density === "xs" ? "sm" : "md",
1097
1996
  ...checkboxProps,
1098
1997
  title: void 0,
1099
- onChange: (event) => {
1100
- event.stopPropagation();
1101
- if (event.shiftKey === void 0) event.shiftKey = lastShiftKey;
1102
- selection(event);
1103
- checkboxProps.onChange?.(event);
1998
+ onChange: (nextChecked) => {
1999
+ selection({ shiftKey: lastShiftKey }, nextChecked);
2000
+ checkboxProps.onChange?.(nextChecked);
2001
+ lastShiftKey = false;
1104
2002
  },
1105
2003
  onClick: (event) => {
1106
2004
  event.stopPropagation();
@@ -1109,7 +2007,7 @@ var MVT_SelectCheckbox = (0, vue.defineComponent)({
1109
2007
  }
1110
2008
  };
1111
2009
  const control = o.selectDisplayMode === "switch" ? (0, vue.h)(_mantine_vue_core.Switch, common) : o.selectDisplayMode === "radio" || o.enableMultiRowSelection === false ? (0, vue.h)(_mantine_vue_core.Radio, common) : (0, vue.h)(_mantine_vue_core.Checkbox, {
1112
- indeterminate: !checked && selectAll ? table.getIsSomeRowsSelected() : row?.getIsSomeSelected() && row.getCanSelectSubRows(),
2010
+ indeterminate: serverGroupingSummary ? serverGroupingSummary.isSome : !checked && selectAll ? table.getIsSomeRowsSelected() : row?.getIsSomeSelected() && row.getCanSelectSubRows(),
1113
2011
  ...common
1114
2012
  });
1115
2013
  return (0, vue.h)(_mantine_vue_core.Tooltip, {
@@ -1239,12 +2137,14 @@ var MVT_Default_Icons = {
1239
2137
  //#region src/locales/en.ts
1240
2138
  var MVT_Localization_EN = {
1241
2139
  actions: "Actions",
2140
+ allMatchingRecordsSelected: "All records matching the current filters are selected",
1242
2141
  and: "and",
1243
2142
  cancel: "Cancel",
1244
2143
  changeFilterMode: "Change filter mode",
1245
2144
  changeSearchMode: "Change search mode",
1246
2145
  clearFilter: "Clear filter",
1247
2146
  clearSearch: "Clear search",
2147
+ clearGrouping: "Clear grouping",
1248
2148
  clearSelection: "Clear selection",
1249
2149
  clearSort: "Clear sort",
1250
2150
  clickToCopy: "Click to copy",
@@ -1255,6 +2155,7 @@ var MVT_Localization_EN = {
1255
2155
  copiedToClipboard: "Copied to clipboard",
1256
2156
  dropToGroupBy: "Drop to group by {column}",
1257
2157
  edit: "Edit",
2158
+ errorLoadingData: "Error loading data",
1258
2159
  expand: "Expand",
1259
2160
  expandAll: "Expand all",
1260
2161
  filterArrIncludes: "Includes",
@@ -1287,13 +2188,18 @@ var MVT_Localization_EN = {
1287
2188
  goToNextPage: "Go to next page",
1288
2189
  goToPreviousPage: "Go to previous page",
1289
2190
  grab: "Grab",
2191
+ groupBy: "Group by",
1290
2192
  groupByColumn: "Group by {column}",
1291
2193
  groupedBy: "Grouped by ",
1292
2194
  hideAll: "Hide all",
1293
2195
  hideColumn: "Hide {column} column",
2196
+ loading: "Loading…",
1294
2197
  max: "Max",
1295
2198
  min: "Min",
1296
2199
  move: "Move",
2200
+ moveDown: "Move down",
2201
+ moveUp: "Move up",
2202
+ noGroupableColumns: "No groupable columns",
1297
2203
  noRecordsToDisplay: "No records to display",
1298
2204
  noResultsFound: "No results found",
1299
2205
  of: "of",
@@ -1303,6 +2209,7 @@ var MVT_Localization_EN = {
1303
2209
  pinToRight: "Pin to right",
1304
2210
  resetColumnSize: "Reset column size",
1305
2211
  resetOrder: "Reset order",
2212
+ retry: "Retry",
1306
2213
  rowActions: "Row Actions",
1307
2214
  rowNumber: "#",
1308
2215
  rowNumbers: "Row Numbers",
@@ -1310,7 +2217,9 @@ var MVT_Localization_EN = {
1310
2217
  save: "Save",
1311
2218
  search: "Search",
1312
2219
  selectedCountOfRowCountRowsSelected: "{selectedCount} of {rowCount} row(s) selected",
2220
+ selectedCountRowsSelected: "{selectedCount} row(s) selected",
1313
2221
  select: "Select",
2222
+ selectAllMatching: "Select all matching records",
1314
2223
  showAll: "Show all",
1315
2224
  showAllColumns: "Show all columns",
1316
2225
  showHideColumns: "Show/Hide columns",
@@ -1397,6 +2306,33 @@ var useMVT_TableOptions = (rawOptions) => {
1397
2306
  manualPagination = true;
1398
2307
  manualSorting = true;
1399
2308
  }
2309
+ if (rest.serverGrouping?.provider && rest.serverGrouping.enabled !== false) {
2310
+ if (enableGrouping || rawOptions.manualGrouping) console.warn("[mantine-vue-table] `serverGrouping` cannot be combined with client-side `enableGrouping` or `manualGrouping`. Disable one of them — `serverGrouping` will take precedence for rendering.");
2311
+ manualFiltering = true;
2312
+ manualPagination = true;
2313
+ manualSorting = true;
2314
+ if (rawOptions.groupedColumnMode === void 0) rest.groupedColumnMode = false;
2315
+ const configuredPageSize = rest.serverGrouping.pagination?.defaultPageSize ?? rest.serverGrouping.initialPageSize;
2316
+ const seededPageSize = Number.isInteger(configuredPageSize) && configuredPageSize > 0 ? configuredPageSize : void 0;
2317
+ if (seededPageSize && rawOptions.state?.pagination === void 0) rest.initialState = {
2318
+ ...rest.initialState,
2319
+ pagination: {
2320
+ pageIndex: rest.initialState?.pagination?.pageIndex ?? 0,
2321
+ pageSize: rest.initialState?.pagination?.pageSize ?? seededPageSize
2322
+ }
2323
+ };
2324
+ const { defaultGrouping } = rest.serverGrouping;
2325
+ if (defaultGrouping?.length && !rest.serverGrouping.grouping && rawOptions.state?.grouping === void 0 && rest.initialState?.grouping === void 0) rest.initialState = {
2326
+ ...rest.initialState,
2327
+ grouping: [...defaultGrouping]
2328
+ };
2329
+ const configuredFields = [...rest.serverGrouping.grouping ?? [], ...defaultGrouping ?? []];
2330
+ if (configuredFields.length) {
2331
+ const knownFields = new Set([...getAllLeafColumnDefs(rawOptions.columns).map((col) => getColumnId(col)), ...rest.serverGrouping.groupBy?.columns ?? []]);
2332
+ const unknownFields = [...new Set(configuredFields)].filter((field) => !knownFields.has(field));
2333
+ if (unknownFields.length) console.warn(`[mantine-vue-table] serverGrouping field(s) ${unknownFields.map((field) => `"${field}"`).join(", ")} do not match any column id. Custom backend descriptors are supported — declare them in \`serverGrouping.groupBy.columns\` to silence this warning and make them selectable in the group-by toolbar.`);
2334
+ }
2335
+ }
1400
2336
  const definedOptions = {
1401
2337
  aggregationFns,
1402
2338
  autoResetExpanded,
@@ -1645,6 +2581,12 @@ var useMVT_TableInstance = (definedTableOptions) => {
1645
2581
  get: getter
1646
2582
  });
1647
2583
  };
2584
+ let serverGroupingManager;
2585
+ defineState("serverGroupingExpanded", () => serverGroupingManager?.getExpanded() ?? {});
2586
+ defineState("serverGroupingSelectAll", () => serverGroupingManager?.getSelectAll() ?? {
2587
+ active: false,
2588
+ excludedRowIds: []
2589
+ });
1648
2590
  defineState("columnFilterFns", () => columnFilterFns.value);
1649
2591
  defineState("columnOrder", () => columnOrder.value);
1650
2592
  defineState("columnSizingInfo", () => columnSizingInfo.value);
@@ -1747,6 +2689,58 @@ var useMVT_TableInstance = (definedTableOptions) => {
1747
2689
  table.setShowColumnFilters = statefulTableOptions.onShowColumnFiltersChange ?? ((updater) => applyUpdater(showColumnFilters, updater));
1748
2690
  table.setShowGlobalFilter = statefulTableOptions.onShowGlobalFilterChange ?? ((updater) => applyUpdater(showGlobalFilter, updater));
1749
2691
  table.setShowToolbarDropZone = statefulTableOptions.onShowToolbarDropZoneChange ?? ((updater) => applyUpdater(showToolbarDropZone, updater));
2692
+ serverGroupingManager = createMVT_ServerGroupingManager(table, () => userStateKeys.has("serverGroupingExpanded") ? readUserState().serverGroupingExpanded : void 0, () => userStateKeys.has("serverGroupingSelectAll") ? readUserState().serverGroupingSelectAll : void 0);
2693
+ table._serverGrouping = serverGroupingManager;
2694
+ if (serverGroupingManager) {
2695
+ const readUserRowCount = Object.getOwnPropertyDescriptor(definedTableOptions, "rowCount")?.get;
2696
+ Object.defineProperty(definedTableOptions, "rowCount", {
2697
+ configurable: true,
2698
+ enumerable: true,
2699
+ get: () => readUserRowCount?.call(definedTableOptions) ?? serverGroupingManager?.getPathState("__root__")?.rowCount
2700
+ });
2701
+ }
2702
+ table.getServerGroupingState = () => serverGroupingManager?.getState() ?? {
2703
+ enabled: false,
2704
+ expanded: {},
2705
+ grouping: [],
2706
+ paths: {}
2707
+ };
2708
+ table.getServerGroupingPathState = (pathId) => serverGroupingManager?.getPathState(pathId);
2709
+ table.expandServerGroup = (pathId) => serverGroupingManager?.expand(pathId);
2710
+ table.collapseServerGroup = (pathId) => serverGroupingManager?.collapse(pathId);
2711
+ table.toggleServerGroup = (pathId) => serverGroupingManager?.toggle(pathId);
2712
+ table.reloadServerGrouping = () => serverGroupingManager?.reload();
2713
+ table.reloadServerGroupingPath = (pathId) => serverGroupingManager?.reload(pathId);
2714
+ table.invalidateServerGrouping = () => serverGroupingManager?.invalidate();
2715
+ table.invalidateServerGroupingPath = (pathId) => serverGroupingManager?.invalidate(pathId);
2716
+ table.resetServerGroupingState = () => serverGroupingManager?.reset();
2717
+ table.setServerGroupingExpanded = (updater) => serverGroupingManager?.setExpanded(updater);
2718
+ table.setServerGroupingPagination = (pathId, pagination) => serverGroupingManager?.setPagination(pathId, pagination);
2719
+ table.setServerGroupingSorting = (pathId, sorting) => serverGroupingManager?.setSorting(pathId, sorting);
2720
+ table.setServerGroupingColumnFilters = (pathId, columnFilters) => serverGroupingManager?.setColumnFilters(pathId, columnFilters);
2721
+ table.setServerGroupingGlobalFilter = (pathId, globalFilter) => serverGroupingManager?.setGlobalFilter(pathId, globalFilter);
2722
+ table.getServerGroupingSelectionSummary = () => serverGroupingManager ? getServerGroupingSelectionSummary(table) : {
2723
+ excludedRowIds: [],
2724
+ isAll: false,
2725
+ isSelectAllMatching: false,
2726
+ isSome: false,
2727
+ mode: "include",
2728
+ selectableCount: 0,
2729
+ selectedCount: 0,
2730
+ selectedRowIds: []
2731
+ };
2732
+ table.toggleAllServerGroupingRecordsSelected = (value) => {
2733
+ if (serverGroupingManager) toggleAllServerGroupingRecordsSelected(table, value);
2734
+ };
2735
+ table.selectAllMatchingServerGroupingRecords = async () => {
2736
+ if (serverGroupingManager) await selectAllMatchingServerGroupingRecords(table);
2737
+ };
2738
+ table.getServerGroupingSelectAllState = () => serverGroupingManager?.getSelectAll() ?? {
2739
+ active: false,
2740
+ excludedRowIds: []
2741
+ };
2742
+ table.setServerGroupingSelectAllState = (updater) => serverGroupingManager?.setSelectAll(updater);
2743
+ table.getServerGroupingSelectionPayload = () => getServerGroupingSelectionPayload(table);
1750
2744
  table._mvtSlots = mvtSlots;
1751
2745
  table.setMVTSlots = (slots) => {
1752
2746
  mvtSlots.value = slots;
@@ -1849,10 +2843,10 @@ var MVT_TablePagination = (0, vue.defineComponent)({
1849
2843
  const { rowsPerPageOptions = defaultRowsPerPage, showRowsPerPage = true, withEdges = showEdges, ...rest } = paginationProps;
1850
2844
  const controls = o.paginationDisplayMode === "pages" ? (0, vue.h)(_mantine_vue_core.Pagination, {
1851
2845
  total: numberOfPages,
1852
- value: pageIndex + 1,
2846
+ modelValue: pageIndex + 1,
1853
2847
  withEdges,
1854
2848
  ...rest,
1855
- onChange: (page) => table.setPageIndex(page - 1)
2849
+ "onUpdate:modelValue": (page) => table.setPageIndex(page - 1)
1856
2850
  }) : o.paginationDisplayMode === "default" ? [(0, vue.h)(_mantine_vue_core.Text, null, () => `${lastRowIndex === 0 ? 0 : (firstRowIndex + 1).toLocaleString()}-${lastRowIndex.toLocaleString()} ${o.localization.of} ${totalRowCount.toLocaleString()}`), (0, vue.h)(_mantine_vue_core.Group, { gap: 6 }, () => [
1857
2851
  withEdges && (0, vue.h)(_mantine_vue_core.ActionIcon, {
1858
2852
  "aria-label": o.localization.goToFirstPage,
@@ -1888,8 +2882,8 @@ var MVT_TablePagination = (0, vue.defineComponent)({
1888
2882
  "aria-labelledby": "rpp-label",
1889
2883
  class: MVT_TablePagination_module_default.pagesize,
1890
2884
  data: rowsPerPageOptions,
1891
- value: pageSize.toString(),
1892
- onChange: (value) => value && table.setPageSize(+value)
2885
+ modelValue: pageSize.toString(),
2886
+ "onUpdate:modelValue": (value) => value && table.setPageSize(+value)
1893
2887
  })]), controls]);
1894
2888
  };
1895
2889
  }
@@ -1929,26 +2923,50 @@ var MVT_ToolbarAlertBanner = (0, vue.defineComponent)({
1929
2923
  const badgeProps = parseFromValuesOrFunc(o.mantineToolbarAlertBannerBadgeProps, { table });
1930
2924
  const total = o.rowCount ?? table.getPrePaginationRowModel().flatRows.length;
1931
2925
  const selectedCount = o.manualPagination ? Object.values(state.rowSelection).filter(Boolean).length : table.getFilteredSelectedRowModel().rows.length;
1932
- const selectedAlert = selectedCount ? (0, vue.h)(_mantine_vue_core.Flex, {
2926
+ const serverGroupingSelection = o.serverGrouping?.selection;
2927
+ const serverGroupingSummary = table._serverGrouping ? table.getServerGroupingSelectionSummary() : void 0;
2928
+ const showSelectAllMatching = !!serverGroupingSelection?.enableSelectAllMatching && ((serverGroupingSelection.selectAllMode ?? "query") === "query" || !!serverGroupingSelection.loadAllMatchingRowIds) && !!serverGroupingSummary?.isAll && !serverGroupingSummary.isSelectAllMatching;
2929
+ const serverGroupingListsRecords = !serverGroupingSummary || table.getServerGroupingPathState("__root__")?.kind === "records";
2930
+ const selectedText = serverGroupingSummary?.isSelectAllMatching ? (o.localization.allMatchingRecordsSelected ?? "All records matching the current filters are selected") + (serverGroupingSummary.excludedRowIds.length ? ` (${serverGroupingSummary.excludedRowIds.length} excluded)` : "") : serverGroupingListsRecords ? o.localization.selectedCountOfRowCountRowsSelected.replace("{selectedCount}", String(selectedCount)).replace("{rowCount}", String(total)) : (o.localization.selectedCountRowsSelected ?? "{selectedCount} row(s) selected").replace("{selectedCount}", String(selectedCount));
2931
+ const selectedAlert = selectedCount || serverGroupingSummary?.isSelectAllMatching ? (0, vue.h)(_mantine_vue_core.Flex, {
1933
2932
  align: "center",
1934
2933
  gap: "sm"
1935
- }, () => [o.localization.selectedCountOfRowCountRowsSelected.replace("{selectedCount}", String(selectedCount)).replace("{rowCount}", String(total)), (0, vue.h)(_mantine_vue_core.Button, {
1936
- size: "compact-xs",
1937
- variant: "subtle",
1938
- onClick: (event) => getMVT_SelectAllHandler({ table })(event, false, true)
1939
- }, () => o.localization.clearSelection)]) : null;
1940
- const groupedAlert = state.grouping.length ? (0, vue.h)(_mantine_vue_core.Flex, null, () => [`${o.localization.groupedBy} `, ...state.grouping.flatMap((columnId, index) => [index > 0 ? o.localization.thenBy : null, (0, vue.h)(_mantine_vue_core.Badge, {
1941
- key: columnId,
1942
- class: MVT_ToolbarAlertBanner_module_default["alert-badge"],
1943
- variant: "filled",
1944
- ...badgeProps,
1945
- rightSection: (0, vue.h)(_mantine_vue_core.ActionIcon, {
1946
- color: "white",
1947
- size: "xs",
2934
+ }, () => [
2935
+ selectedText,
2936
+ showSelectAllMatching && (0, vue.h)(_mantine_vue_core.Button, {
2937
+ size: "compact-xs",
1948
2938
  variant: "subtle",
1949
- onClick: () => table.getColumn(columnId).toggleGrouping()
1950
- }, () => (0, vue.h)(o.icons.IconX, { style: { transform: "scale(0.8)" } }))
1951
- }, () => `${table.getColumn(columnId).columnDef.header} `)])]) : null;
2939
+ onClick: () => void table.selectAllMatchingServerGroupingRecords()
2940
+ }, () => o.localization.selectAllMatching ?? "Select all matching records"),
2941
+ (0, vue.h)(_mantine_vue_core.Button, {
2942
+ size: "compact-xs",
2943
+ variant: "subtle",
2944
+ onClick: (event) => {
2945
+ if (serverGroupingSummary) {
2946
+ table.toggleAllServerGroupingRecordsSelected(false);
2947
+ return;
2948
+ }
2949
+ getMVT_SelectAllHandler({ table })(event, false, true);
2950
+ }
2951
+ }, () => o.localization.clearSelection)
2952
+ ]) : null;
2953
+ const groupedAlert = state.grouping.length ? (0, vue.h)(_mantine_vue_core.Flex, null, () => [`${o.localization.groupedBy} `, ...state.grouping.flatMap((columnId, index) => {
2954
+ const column = table.getAllLeafColumns().find((col) => col.id === columnId);
2955
+ const header = column?.columnDef.header;
2956
+ const label = typeof header === "string" && header.length ? header : columnId;
2957
+ return [index > 0 ? o.localization.thenBy : null, (0, vue.h)(_mantine_vue_core.Badge, {
2958
+ key: columnId,
2959
+ class: MVT_ToolbarAlertBanner_module_default["alert-badge"],
2960
+ variant: "filled",
2961
+ ...badgeProps,
2962
+ rightSection: (0, vue.h)(_mantine_vue_core.ActionIcon, {
2963
+ color: "white",
2964
+ size: "xs",
2965
+ variant: "subtle",
2966
+ onClick: () => column ? column.toggleGrouping() : table.setGrouping((prev) => prev.filter((field) => field !== columnId))
2967
+ }, () => (0, vue.h)(o.icons.IconX, { style: { transform: "scale(0.8)" } }))
2968
+ }, () => `${label} `)];
2969
+ })]) : null;
1952
2970
  const custom = slots.default?.({
1953
2971
  groupedAlert,
1954
2972
  selectedAlert,
@@ -2070,6 +3088,207 @@ var MVT_BottomToolbar = (0, vue.defineComponent)({
2070
3088
  };
2071
3089
  }
2072
3090
  });
3091
+ var MVT_ServerGrouping_module_default = {
3092
+ "group-row": "_group-row_u9zpp_1",
3093
+ "row-grid": "_row-grid_u9zpp_7",
3094
+ "group-cell": "_group-cell_u9zpp_11",
3095
+ "aggregate-cell": "_aggregate-cell_u9zpp_15",
3096
+ "expand-spacer": "_expand-spacer_u9zpp_19",
3097
+ "aux-row": "_aux-row_u9zpp_24",
3098
+ "aux-cell": "_aux-cell_u9zpp_28",
3099
+ pagination: "_pagination_u9zpp_32",
3100
+ pagesize: "_pagesize_u9zpp_36",
3101
+ "groupby-control": "_groupby-control_u9zpp_40",
3102
+ "groupby-active-row": "_groupby-active-row_u9zpp_45",
3103
+ "groupby-active-label": "_groupby-active-label_u9zpp_52"
3104
+ };
3105
+ //#endregion
3106
+ //#region src/server-grouping/MVT_ServerGroupingGroupBy.ts
3107
+ /**
3108
+ * Resolve the columns/fields offered by the group-by toolbar control.
3109
+ *
3110
+ * With `serverGrouping.groupBy.columns` set, exactly that whitelist is used
3111
+ * (entries may be custom descriptors like `'createdAt:month'`). Otherwise
3112
+ * every leaf `data` column whose `enableGrouping` is not `false` is offered —
3113
+ * display columns and columns with `enableGrouping: false` never appear.
3114
+ */
3115
+ var getServerGroupableColumns = (table) => {
3116
+ const groupByOptions = table.options.serverGrouping?.groupBy;
3117
+ const getFieldLabel = groupByOptions?.getFieldLabel;
3118
+ const labelFor = (field) => {
3119
+ if (getFieldLabel) return getFieldLabel(field, table);
3120
+ const header = table.getAllLeafColumns().find((leafColumn) => leafColumn.id === field)?.columnDef.header;
3121
+ return typeof header === "string" && header.length ? header : field;
3122
+ };
3123
+ if (groupByOptions?.columns) return groupByOptions.columns.map((field) => ({
3124
+ id: field,
3125
+ label: labelFor(field)
3126
+ }));
3127
+ return table.getAllLeafColumns().filter((column) => column.columnDef.columnDefType === "data" && column.columnDef.enableGrouping !== false).map((column) => ({
3128
+ id: column.id,
3129
+ label: labelFor(column.id)
3130
+ }));
3131
+ };
3132
+ /** Build the scoped context handed to the group-by slot / render callback. */
3133
+ var getServerGroupingGroupByContext = (table, manager) => {
3134
+ const grouping = manager.getGroupingFields();
3135
+ const setGrouping = (next) => table.setGrouping(next);
3136
+ return {
3137
+ addGroup: (field) => {
3138
+ if (!grouping.includes(field)) setGrouping([...grouping, field]);
3139
+ },
3140
+ clearGrouping: () => setGrouping([]),
3141
+ groupableColumns: getServerGroupableColumns(table),
3142
+ grouping,
3143
+ moveGroup: (field, direction) => {
3144
+ const index = grouping.indexOf(field);
3145
+ const target = index + direction;
3146
+ if (index === -1 || target < 0 || target >= grouping.length) return;
3147
+ const next = [...grouping];
3148
+ [next[index], next[target]] = [next[target], next[index]];
3149
+ setGrouping(next);
3150
+ },
3151
+ removeGroup: (field) => setGrouping(grouping.filter((activeField) => activeField !== field)),
3152
+ table
3153
+ };
3154
+ };
3155
+ /**
3156
+ * Grouping actions for one column's action menu, or `undefined` when the
3157
+ * column cannot be grouped by.
3158
+ *
3159
+ * Uses exactly the same rules as the toolbar "Group by" control: server
3160
+ * grouping must be active and driven by the table's `grouping` state, the
3161
+ * control must not be disabled, and the column must be offered by
3162
+ * {@link getServerGroupableColumns}.
3163
+ */
3164
+ var getServerGroupingColumnActions = (table, columnId) => {
3165
+ const manager = table._serverGrouping;
3166
+ if (!manager) return void 0;
3167
+ const groupByOptions = manager.options.groupBy ?? {};
3168
+ if (groupByOptions.enabled === false || groupByOptions.showInColumnActions === false) return;
3169
+ if (manager.options.grouping && groupByOptions.enabled !== true) return void 0;
3170
+ const context = getServerGroupingGroupByContext(table, manager);
3171
+ return context.groupableColumns.some((column) => column.id === columnId) ? context : void 0;
3172
+ };
3173
+ /**
3174
+ * Built-in group-by toolbar control for server-side grouping. Renders a
3175
+ * "Group by" menu (add / reorder / remove / clear levels) plus the active
3176
+ * levels as removable pills. Hidden when server grouping is inactive, when
3177
+ * `groupBy.enabled` is `false`, or when grouping is a static
3178
+ * `serverGrouping.grouping` array (which the control could not change).
3179
+ */
3180
+ var MVT_ServerGroupingGroupBy = (0, vue.defineComponent)({
3181
+ name: "MVTServerGroupingGroupBy",
3182
+ inheritAttrs: false,
3183
+ props: { table: {
3184
+ type: Object,
3185
+ required: true
3186
+ } },
3187
+ setup(props) {
3188
+ const mvtSlots = useMVT_Slots();
3189
+ return () => {
3190
+ const table = props.table;
3191
+ const manager = table._serverGrouping;
3192
+ if (!manager) return null;
3193
+ const sgOptions = manager.options;
3194
+ const groupByOptions = sgOptions.groupBy ?? {};
3195
+ if (groupByOptions.enabled === false) return null;
3196
+ if (sgOptions.grouping && groupByOptions.enabled !== true) return null;
3197
+ const localization = table.options.localization;
3198
+ const icons = table.options.icons;
3199
+ const allowReorder = groupByOptions.allowReorder ?? true;
3200
+ const allowClearAll = groupByOptions.allowClearAll ?? true;
3201
+ const context = getServerGroupingGroupByContext(table, manager);
3202
+ const { addGroup, clearGrouping, groupableColumns, grouping, moveGroup, removeGroup } = context;
3203
+ const custom = mvtSlots.serverGroupByControl?.(context) ?? parseFromValuesOrFunc(groupByOptions.renderControl, context);
3204
+ if (custom !== void 0 && custom !== null) return custom;
3205
+ const labelFor = (field) => groupableColumns.find((column) => column.id === field)?.label ?? field;
3206
+ const activeRows = grouping.map((field, index) => (0, vue.h)(_mantine_vue_core.Box, {
3207
+ class: MVT_ServerGrouping_module_default["groupby-active-row"],
3208
+ key: field
3209
+ }, () => [
3210
+ allowReorder && (0, vue.h)(_mantine_vue_core.ActionIcon, {
3211
+ "aria-label": `${localization.moveUp ?? "Move up"} ${labelFor(field)}`,
3212
+ color: "gray",
3213
+ disabled: index === 0,
3214
+ onClick: () => moveGroup(field, -1),
3215
+ size: "xs",
3216
+ variant: "subtle"
3217
+ }, () => (0, vue.h)(icons.IconChevronDown, { style: { transform: "rotate(180deg)" } })),
3218
+ allowReorder && (0, vue.h)(_mantine_vue_core.ActionIcon, {
3219
+ "aria-label": `${localization.moveDown ?? "Move down"} ${labelFor(field)}`,
3220
+ color: "gray",
3221
+ disabled: index === grouping.length - 1,
3222
+ onClick: () => moveGroup(field, 1),
3223
+ size: "xs",
3224
+ variant: "subtle"
3225
+ }, () => (0, vue.h)(icons.IconChevronDown)),
3226
+ (0, vue.h)(_mantine_vue_core.Text, {
3227
+ class: MVT_ServerGrouping_module_default["groupby-active-label"],
3228
+ size: "sm"
3229
+ }, () => `${index + 1}. ${labelFor(field)}`),
3230
+ (0, vue.h)(_mantine_vue_core.ActionIcon, {
3231
+ "aria-label": (localization.ungroupByColumn ?? "Ungroup by {column}").replace("{column}", labelFor(field)),
3232
+ color: "gray",
3233
+ onClick: () => removeGroup(field),
3234
+ size: "xs",
3235
+ variant: "subtle"
3236
+ }, () => (0, vue.h)(icons.IconX))
3237
+ ]));
3238
+ const menu = (0, vue.h)(_mantine_vue_core.Menu, {
3239
+ closeOnItemClick: false,
3240
+ position: "bottom-start",
3241
+ shadow: "md",
3242
+ width: 260,
3243
+ ...groupByOptions.mantineMenuProps
3244
+ }, () => [(0, vue.h)(_mantine_vue_core.Menu.Target, null, () => (0, vue.h)(_mantine_vue_core.Button, {
3245
+ size: "xs",
3246
+ variant: "default",
3247
+ ...groupByOptions.mantineButtonProps,
3248
+ leftSection: groupByOptions.mantineButtonProps?.leftSection ?? (0, vue.h)(icons.IconColumns)
3249
+ }, () => localization.groupBy ?? "Group by")), (0, vue.h)(_mantine_vue_core.Menu.Dropdown, null, () => [
3250
+ groupableColumns.length ? groupableColumns.map((column) => (0, vue.h)(_mantine_vue_core.Menu.Item, {
3251
+ disabled: grouping.includes(column.id),
3252
+ key: column.id,
3253
+ onClick: () => addGroup(column.id)
3254
+ }, () => column.label)) : (0, vue.h)(_mantine_vue_core.Menu.Label, null, () => localization.noGroupableColumns ?? "No groupable columns"),
3255
+ grouping.length > 0 && [
3256
+ (0, vue.h)(_mantine_vue_core.Menu.Divider),
3257
+ (0, vue.h)(_mantine_vue_core.Menu.Label, null, () => (localization.groupedBy ?? "Grouped by ").trim()),
3258
+ ...activeRows
3259
+ ],
3260
+ allowClearAll && grouping.length > 0 && [(0, vue.h)(_mantine_vue_core.Menu.Divider), (0, vue.h)(_mantine_vue_core.Menu.Item, {
3261
+ color: "red",
3262
+ onClick: () => clearGrouping()
3263
+ }, () => localization.clearGrouping ?? "Clear grouping")]
3264
+ ])]);
3265
+ const pills = grouping.length > 0 && (0, vue.h)(_mantine_vue_core.PillGroup, { gap: 4 }, () => grouping.map((field, index) => {
3266
+ const pillProps = parseFromValuesOrFunc(groupByOptions.mantinePillProps, {
3267
+ field,
3268
+ index
3269
+ }) ?? {};
3270
+ return (0, vue.h)(_mantine_vue_core.Pill, {
3271
+ size: "sm",
3272
+ withRemoveButton: true,
3273
+ ...pillProps,
3274
+ key: field,
3275
+ onRemove: () => removeGroup(field),
3276
+ removeButtonProps: {
3277
+ "aria-label": (localization.ungroupByColumn ?? "Ungroup by {column}").replace("{column}", labelFor(field)),
3278
+ ...pillProps?.removeButtonProps
3279
+ }
3280
+ }, () => `${index + 1}. ${labelFor(field)}`);
3281
+ }));
3282
+ return (0, vue.h)(_mantine_vue_core.Box, {
3283
+ class: (0, clsx.default)("mvt-server-groupby-control", MVT_ServerGrouping_module_default["groupby-control"]),
3284
+ style: {
3285
+ display: "flex",
3286
+ gap: "8px"
3287
+ }
3288
+ }, () => [menu, pills]);
3289
+ };
3290
+ }
3291
+ });
2073
3292
  var MVT_FilterOptionMenu_module_default = { symbol: "_symbol_1b3vj_1" };
2074
3293
  //#endregion
2075
3294
  //#region src/components/menus/MVT_FilterOptionMenu.ts
@@ -2458,7 +3677,7 @@ var MVT_ShowHideColumnsMenuItems = (0, vue.defineComponent)({
2458
3677
  openDelay: 1e3,
2459
3678
  withinPortal: true
2460
3679
  }, () => (0, vue.h)(_mantine_vue_core.Switch, {
2461
- checked,
3680
+ modelValue: checked,
2462
3681
  class: MVT_ShowHideColumnsMenuItems_module_default.switch,
2463
3682
  disabled: !column.getCanHide(),
2464
3683
  label: def.header,
@@ -2777,6 +3996,7 @@ var MVT_TopToolbar = (0, vue.defineComponent)({
2777
3996
  ["both", "top"].includes(positionToolbarDropZone ?? "") && (0, vue.h)(MVT_ToolbarDropZone, { table }),
2778
3997
  (0, vue.h)(_mantine_vue_core.Flex, { class: [MVT_TopToolbar_module_default["actions-container"], stackAlertBanner ? MVT_TopToolbar_module_default["actions-container-stack-alert"] : void 0] }, () => [
2779
3998
  enableGlobalFilter && positionGlobalFilter === "left" && (0, vue.h)(MVT_GlobalFilterTextInput, globalFilterProps),
3999
+ table._serverGrouping && (0, vue.h)(MVT_ServerGroupingGroupBy, { table }),
2780
4000
  parseFromValuesOrFunc(renderTopToolbarCustomActions, { table }) ?? (0, vue.h)("span"),
2781
4001
  enableToolbarInternalActions ? (0, vue.h)(_mantine_vue_core.Flex, {
2782
4002
  justify: "end",
@@ -2953,6 +4173,21 @@ var useMVT_ColumnVirtualizer = (table) => {
2953
4173
  //#endregion
2954
4174
  //#region src/utils/style.utils.ts
2955
4175
  var parseCSSVarId = (id) => id.replace(/[^a-zA-Z0-9]/g, "_");
4176
+ /**
4177
+ * Width/flex styles that bind a cell to its column's live size variables, so
4178
+ * custom cells (server-grouping group rows, …) stay aligned with the root
4179
+ * table while columns are resized or reordered.
4180
+ */
4181
+ var getMVT_ColumnWidthStyles = ({ columnId, grow, layoutMode, minSize }) => {
4182
+ const cssVar = `--col-${parseCSSVarId(columnId)}-size`;
4183
+ const styles = {
4184
+ minWidth: `max(calc(var(${cssVar}) * 1px), ${minSize ?? 30}px)`,
4185
+ width: `calc(var(${cssVar}) * 1px)`
4186
+ };
4187
+ if (layoutMode === "grid") styles.flex = `${[0, false].includes(grow) ? 0 : `var(${cssVar})`} 0 auto`;
4188
+ else if (layoutMode === "grid-no-grow") styles.flex = `${+(grow || 0)} 0 auto`;
4189
+ return styles;
4190
+ };
2956
4191
  var getPrimaryShade = (theme) => typeof theme.primaryShade === "number" ? theme.primaryShade : theme.primaryShade?.dark ?? 7;
2957
4192
  var getPrimaryColor = (theme, shade) => theme.colors[theme.primaryColor][shade ?? getPrimaryShade(theme)];
2958
4193
  function dataVariable(name, value) {
@@ -3280,8 +4515,9 @@ var MVT_TableBodyCell = (0, vue.defineComponent)({
3280
4515
  if (!columnDef.enableCellHoverReveal) return;
3281
4516
  isCellContentOverflowing.value = false;
3282
4517
  };
4518
+ const isServerGrouping = !!table._serverGrouping;
3283
4519
  const renderCellContent = () => {
3284
- if (cell.getIsPlaceholder()) return columnDef.PlaceholderCell?.({
4520
+ if (!isServerGrouping && cell.getIsPlaceholder()) return columnDef.PlaceholderCell?.({
3285
4521
  cell,
3286
4522
  column,
3287
4523
  row,
@@ -3607,6 +4843,271 @@ var MVT_TableBodyRow = (0, vue.defineComponent)({
3607
4843
  }
3608
4844
  });
3609
4845
  var Memo_MVT_TableBodyRow = MVT_TableBodyRow;
4846
+ //#endregion
4847
+ //#region src/server-grouping/MVT_ServerGroupingBody.ts
4848
+ var INDENT_PX = 24;
4849
+ /** Renders the server-grouping hierarchy as rows of the root table body. */
4850
+ var MVT_ServerGroupingBody = (0, vue.defineComponent)({
4851
+ name: "MVTServerGroupingBody",
4852
+ inheritAttrs: false,
4853
+ props: {
4854
+ table: {
4855
+ type: Object,
4856
+ required: true
4857
+ },
4858
+ tableProps: {
4859
+ type: Object,
4860
+ required: true
4861
+ }
4862
+ },
4863
+ setup(props) {
4864
+ const mvtSlots = useMVT_Slots();
4865
+ return () => {
4866
+ const { tableProps } = props;
4867
+ const table = props.table;
4868
+ const manager = table._serverGrouping;
4869
+ if (!manager) return null;
4870
+ const { options: sgOptions } = manager;
4871
+ const tableOptions = table.options;
4872
+ const localization = tableOptions.localization;
4873
+ const icons = tableOptions.icons;
4874
+ const visibleColumns = table.getVisibleLeafColumns();
4875
+ const colSpan = visibleColumns.length || 1;
4876
+ const layoutMode = tableOptions.layoutMode;
4877
+ const paginationOptions = {
4878
+ enabled: sgOptions.pagination?.enabled ?? true,
4879
+ mantineActionIconProps: sgOptions.pagination?.mantineActionIconProps ?? {},
4880
+ showAtRoot: sgOptions.pagination?.showAtRoot ?? (tableOptions.enablePagination === false || tableOptions.positionPagination === "none"),
4881
+ showForGroups: sgOptions.pagination?.showForGroups ?? true,
4882
+ showForRecords: sgOptions.pagination?.showForRecords ?? true
4883
+ };
4884
+ const indentStyle = (depth) => ({ paddingInlineStart: `${8 + depth * INDENT_PX}px` });
4885
+ const columnStyle = (column, extra) => ({
4886
+ ...getMVT_ColumnWidthStyles({
4887
+ columnId: column.id,
4888
+ grow: column.columnDef.grow,
4889
+ layoutMode,
4890
+ minSize: column.columnDef.minSize
4891
+ }),
4892
+ ...extra
4893
+ });
4894
+ const auxRow = (key, depth, children) => (0, vue.h)(_mantine_vue_core.TableTr, {
4895
+ class: (0, clsx.default)(MVT_ServerGrouping_module_default["aux-row"], layoutMode?.startsWith("grid") && MVT_ServerGrouping_module_default["row-grid"]),
4896
+ key
4897
+ }, () => (0, vue.h)(_mantine_vue_core.TableTd, {
4898
+ class: MVT_ServerGrouping_module_default["aux-cell"],
4899
+ colspan: colSpan,
4900
+ style: {
4901
+ ...indentStyle(depth),
4902
+ ...layoutMode?.startsWith("grid") ? {
4903
+ flex: "1 0 auto",
4904
+ width: "100%"
4905
+ } : {}
4906
+ }
4907
+ }, () => children));
4908
+ const loadingContent = (pathId, depth) => mvtSlots.serverGroupLoading?.({
4909
+ depth,
4910
+ pathId,
4911
+ table
4912
+ }) ?? parseFromValuesOrFunc(sgOptions.renderGroupLoading, {
4913
+ depth,
4914
+ pathId,
4915
+ table
4916
+ }) ?? (0, vue.h)(_mantine_vue_core.Group, { gap: "xs" }, () => [(0, vue.h)(_mantine_vue_core.Loader, { size: "xs" }), (0, vue.h)(_mantine_vue_core.Text, {
4917
+ c: "dimmed",
4918
+ size: "sm"
4919
+ }, () => localization.loading ?? "Loading…")]);
4920
+ const errorContent = (pathId, error, retry) => mvtSlots.serverGroupError?.({
4921
+ error,
4922
+ pathId,
4923
+ retry,
4924
+ table
4925
+ }) ?? parseFromValuesOrFunc(sgOptions.renderGroupError, {
4926
+ error,
4927
+ pathId,
4928
+ retry,
4929
+ table
4930
+ }) ?? (0, vue.h)(_mantine_vue_core.Alert, {
4931
+ color: "red",
4932
+ role: "alert",
4933
+ variant: "light"
4934
+ }, () => (0, vue.h)(_mantine_vue_core.Group, { gap: "sm" }, () => [(0, vue.h)(_mantine_vue_core.Text, { size: "sm" }, () => localization.errorLoadingData ?? (error instanceof Error && error.message) ?? "Error loading data"), (0, vue.h)(_mantine_vue_core.Button, {
4935
+ color: "red",
4936
+ onClick: retry,
4937
+ size: "compact-xs",
4938
+ variant: "outline"
4939
+ }, () => localization.retry ?? "Retry")]));
4940
+ const emptyContent = (pathId, field) => {
4941
+ const parentGroup = manager.getParentItems(pathId).at(-1);
4942
+ return mvtSlots.serverGroupEmpty?.({
4943
+ field,
4944
+ parentGroup,
4945
+ pathId,
4946
+ table
4947
+ }) ?? parseFromValuesOrFunc(sgOptions.renderGroupEmpty, {
4948
+ field,
4949
+ parentGroup,
4950
+ pathId,
4951
+ table
4952
+ }) ?? (0, vue.h)(_mantine_vue_core.Text, {
4953
+ c: "dimmed",
4954
+ size: "sm"
4955
+ }, () => localization.noRecordsToDisplay ?? "No records to display");
4956
+ };
4957
+ const paginationRow = (pathId, depth, state) => {
4958
+ const isRoot = pathId === MVT_SERVER_GROUPING_ROOT_PATH_ID;
4959
+ const { pageIndex, pageSize } = state.pagination;
4960
+ const totalRowCount = state.rowCount;
4961
+ if (!(paginationOptions.enabled && (state.kind === "groups" ? paginationOptions.showForGroups : paginationOptions.showForRecords) && (!isRoot || paginationOptions.showAtRoot) && (totalRowCount > pageSize || pageIndex > 0))) return null;
4962
+ const firstRowIndex = pageIndex * pageSize;
4963
+ const lastRowIndex = Math.min(firstRowIndex + pageSize, totalRowCount);
4964
+ const setPageIndex = (nextPageIndex) => manager.setPagination(pathId, { pageIndex: nextPageIndex });
4965
+ return auxRow(`${pathId}:pagination`, depth, (0, vue.h)(_mantine_vue_core.Group, {
4966
+ class: MVT_ServerGrouping_module_default.pagination,
4967
+ gap: "xs",
4968
+ wrap: "nowrap"
4969
+ }, () => [
4970
+ (0, vue.h)(_mantine_vue_core.Text, {
4971
+ c: "dimmed",
4972
+ size: "sm"
4973
+ }, () => `${totalRowCount === 0 ? 0 : (firstRowIndex + 1).toLocaleString()}-${lastRowIndex.toLocaleString()} ${localization.of} ${totalRowCount.toLocaleString()}`),
4974
+ (0, vue.h)(_mantine_vue_core.ActionIcon, {
4975
+ color: "gray",
4976
+ size: "sm",
4977
+ variant: "subtle",
4978
+ ...paginationOptions.mantineActionIconProps,
4979
+ "aria-label": localization.goToPreviousPage,
4980
+ disabled: pageIndex <= 0 || state.isFetching,
4981
+ onClick: () => setPageIndex(pageIndex - 1)
4982
+ }, () => (0, vue.h)(icons.IconChevronLeft)),
4983
+ (0, vue.h)(_mantine_vue_core.ActionIcon, {
4984
+ color: "gray",
4985
+ size: "sm",
4986
+ variant: "subtle",
4987
+ ...paginationOptions.mantineActionIconProps,
4988
+ "aria-label": localization.goToNextPage,
4989
+ disabled: lastRowIndex >= totalRowCount || state.isFetching,
4990
+ onClick: () => setPageIndex(pageIndex + 1)
4991
+ }, () => (0, vue.h)(icons.IconChevronRight))
4992
+ ]));
4993
+ };
4994
+ const groupRow = (node, parentPathId, field, depth) => {
4995
+ const pathId = createServerGroupPathId(parentPathId, field, node.id);
4996
+ const expanded = manager.isExpanded(pathId);
4997
+ const childState = manager.getPathState(pathId);
4998
+ const context = {
4999
+ depth,
5000
+ field,
5001
+ group: node,
5002
+ isExpanded: expanded,
5003
+ parentGroups: manager.getParentItems(pathId).slice(0, -1),
5004
+ pathId,
5005
+ table
5006
+ };
5007
+ const hasChildren = node.hasChildren !== false;
5008
+ const rowProps = parseFromValuesOrFunc(sgOptions.mantineGroupRowProps, context) ?? {};
5009
+ const label = mvtSlots.serverGroupLabel?.(context) ?? parseFromValuesOrFunc(sgOptions.renderGroupLabel, context) ?? node.label;
5010
+ const count = mvtSlots.serverGroupCount?.(context) ?? parseFromValuesOrFunc(sgOptions.renderGroupCount, context) ?? (node.count !== void 0 ? `(${node.count.toLocaleString()})` : null);
5011
+ const labelColumn = visibleColumns.find((column) => column.columnDef.columnDefType !== "display");
5012
+ const labelCell = (column) => (0, vue.h)(_mantine_vue_core.TableTd, {
5013
+ class: MVT_ServerGrouping_module_default["group-cell"],
5014
+ key: column?.id ?? "mvt-server-group-label",
5015
+ style: column ? columnStyle(column, indentStyle(depth)) : indentStyle(depth)
5016
+ }, () => (0, vue.h)(_mantine_vue_core.Group, {
5017
+ gap: "xs",
5018
+ wrap: "nowrap"
5019
+ }, () => [
5020
+ hasChildren ? (0, vue.h)(_mantine_vue_core.ActionIcon, {
5021
+ "aria-expanded": String(expanded),
5022
+ "aria-label": `${expanded ? localization.collapse : localization.expand} ${node.label}`,
5023
+ color: "gray",
5024
+ onClick: () => manager.toggle(pathId),
5025
+ size: "sm",
5026
+ variant: "subtle"
5027
+ }, () => (0, vue.h)(expanded ? icons.IconChevronDown : icons.IconChevronRight)) : (0, vue.h)("span", { class: MVT_ServerGrouping_module_default["expand-spacer"] }),
5028
+ (0, vue.h)(_mantine_vue_core.Text, {
5029
+ fw: 600,
5030
+ size: "sm"
5031
+ }, () => [label]),
5032
+ count !== null && (0, vue.h)(_mantine_vue_core.Text, {
5033
+ c: "dimmed",
5034
+ size: "sm"
5035
+ }, () => [count]),
5036
+ expanded && childState?.isFetching && !childState.isLoading && (0, vue.h)(_mantine_vue_core.Loader, { size: "xs" })
5037
+ ]));
5038
+ const aggregateCell = (column) => {
5039
+ const columnServerGrouping = column.columnDef.serverGrouping;
5040
+ let content = null;
5041
+ if (columnServerGrouping?.visible !== false && columnServerGrouping?.getValue) {
5042
+ const value = columnServerGrouping.getValue({
5043
+ ...context,
5044
+ column
5045
+ });
5046
+ const cellContext = {
5047
+ ...context,
5048
+ column,
5049
+ value
5050
+ };
5051
+ content = mvtSlots.serverGroupCell?.(cellContext) ?? (columnServerGrouping.Cell ? flexRender(columnServerGrouping.Cell, cellContext) : value ?? null);
5052
+ } else if (mvtSlots.serverGroupCell) content = mvtSlots.serverGroupCell({
5053
+ ...context,
5054
+ column,
5055
+ value: void 0
5056
+ });
5057
+ return (0, vue.h)(_mantine_vue_core.TableTd, {
5058
+ class: MVT_ServerGrouping_module_default["aggregate-cell"],
5059
+ key: column.id,
5060
+ style: columnStyle(column)
5061
+ }, () => [content]);
5062
+ };
5063
+ const spacerCell = (column) => (0, vue.h)(_mantine_vue_core.TableTd, {
5064
+ key: column.id,
5065
+ style: columnStyle(column)
5066
+ }, () => null);
5067
+ const cells = visibleColumns.length ? visibleColumns.map((column) => column.id === labelColumn?.id ? labelCell(column) : column.columnDef.columnDefType === "display" ? spacerCell(column) : aggregateCell(column)) : [labelCell()];
5068
+ const rows = [(0, vue.h)(_mantine_vue_core.TableTr, {
5069
+ "aria-expanded": hasChildren ? String(expanded) : void 0,
5070
+ "aria-level": depth + 1,
5071
+ "data-depth": depth,
5072
+ "data-expanded": expanded || void 0,
5073
+ key: pathId,
5074
+ ...rowProps,
5075
+ class: (0, clsx.default)(MVT_ServerGrouping_module_default["group-row"], layoutMode?.startsWith("grid") && MVT_ServerGrouping_module_default["row-grid"], rowProps?.class)
5076
+ }, () => cells)];
5077
+ if (expanded) rows.push(...levelRows(pathId, depth + 1));
5078
+ return rows;
5079
+ };
5080
+ const recordRows = (pathId) => {
5081
+ const rows = manager.getRecordRows(pathId);
5082
+ return rows.map((row, index) => (0, vue.h)(MVT_TableBodyRow, {
5083
+ key: row.id,
5084
+ numRows: rows.length,
5085
+ renderedRowIndex: index,
5086
+ row,
5087
+ table,
5088
+ tableProps
5089
+ }));
5090
+ };
5091
+ const levelRows = (pathId, depth) => {
5092
+ const state = manager.getPathState(pathId);
5093
+ const field = manager.getGroupingFields()[getServerGroupPathDepth(pathId)];
5094
+ if (!state || state.isLoading) return [auxRow(`${pathId}:loading`, depth, loadingContent(pathId, depth))];
5095
+ if (state.error != null) return [auxRow(`${pathId}:error`, depth, errorContent(pathId, state.error, () => manager.retry(pathId)))];
5096
+ const out = [];
5097
+ if (state.kind === "groups") {
5098
+ const groups = state.groups ?? [];
5099
+ if (!groups.length) out.push(auxRow(`${pathId}:empty`, depth, emptyContent(pathId, field)));
5100
+ else groups.forEach((node) => out.push(...groupRow(node, pathId, field, depth)));
5101
+ } else if (!(state.rows ?? []).length) out.push(auxRow(`${pathId}:empty`, depth, emptyContent(pathId, field)));
5102
+ else out.push(...recordRows(pathId));
5103
+ const pagination = paginationRow(pathId, depth, state);
5104
+ if (pagination) out.push(pagination);
5105
+ return out;
5106
+ };
5107
+ return (0, vue.h)(vue.Fragment, null, levelRows(MVT_SERVER_GROUPING_ROOT_PATH_ID, 0));
5108
+ };
5109
+ }
5110
+ });
3610
5111
  var MVT_TableBody_module_default = {
3611
5112
  root: "_root_17rwk_1",
3612
5113
  "root-grid": "_root-grid_17rwk_6",
@@ -3736,7 +5237,10 @@ var MVT_TableBody = (0, vue.defineComponent)({
3736
5237
  ...tableBodyProps?.style
3737
5238
  },
3738
5239
  class: (0, clsx.default)(MVT_TableBody_module_default.root, layoutMode?.startsWith("grid") && MVT_TableBody_module_default["root-grid"], !currentRows.length && MVT_TableBody_module_default["root-no-rows"], rowVirtualizer && MVT_TableBody_module_default["root-virtualized"], tableBodyProps?.class)
3739
- }, () => tableBodyProps?.children ?? (!currentRows.length ? (0, vue.h)(MVT_TableBodyEmptyRow, commonRowProps) : (virtualRows ?? currentRows).map((rowOrVirtualRow, renderedRowIndex) => {
5240
+ }, () => tableBodyProps?.children ?? (table._serverGrouping ? (0, vue.h)(MVT_ServerGroupingBody, {
5241
+ table,
5242
+ tableProps
5243
+ }) : null) ?? (!currentRows.length ? (0, vue.h)(MVT_TableBodyEmptyRow, commonRowProps) : (virtualRows ?? currentRows).map((rowOrVirtualRow, renderedRowIndex) => {
3740
5244
  let index = renderedRowIndex;
3741
5245
  if (rowVirtualizer) if (renderDetailPanel) {
3742
5246
  if (rowOrVirtualRow.index % 2 === 1) return null;
@@ -4004,6 +5508,7 @@ var MVT_ColumnActionMenu = (0, vue.defineComponent)({
4004
5508
  leftSection: (0, vue.h)(icon, { class: iconClass }),
4005
5509
  onClick
4006
5510
  }, () => label);
5511
+ const serverGroupBy = getServerGroupingColumnActions(table, column.id);
4007
5512
  const internal = [];
4008
5513
  if (o.enableSorting && column.getCanSort()) {
4009
5514
  if (o.enableSortingRemoval !== false) internal.push(item(l.clearSort, icons.IconClearAll, () => column.clearSorting(), !column.getIsSorted()));
@@ -4025,6 +5530,11 @@ var MVT_ColumnActionMenu = (0, vue.defineComponent)({
4025
5530
  table.setColumnOrder((old) => ["mvt-row-expand", ...old]);
4026
5531
  }));
4027
5532
  if (o.enableColumnPinning) internal.push((0, vue.h)(_mantine_vue_core.Menu.Divider));
5533
+ } else if (serverGroupBy) {
5534
+ const { addGroup, grouping, removeGroup } = serverGroupBy;
5535
+ const isGrouped = grouping.includes(column.id);
5536
+ internal.push(isGrouped ? item(l.clearGrouping ?? "Clear grouping", icons.IconClearAll, () => removeGroup(column.id)) : item(l.groupByColumn.replace("{column}", String(def.header)), icons.IconBoxMultiple, () => addGroup(column.id)));
5537
+ if (o.enableColumnPinning) internal.push((0, vue.h)(_mantine_vue_core.Menu.Divider));
4028
5538
  }
4029
5539
  if (o.enableColumnPinning && column.getCanPin()) {
4030
5540
  internal.push(item(l.pinToLeft, icons.IconPinned, () => column.pin("left"), column.getIsPinned() === "left", MVT_ColumnActionMenu_module_default.left));
@@ -4055,7 +5565,7 @@ var MVT_ColumnActionMenu = (0, vue.defineComponent)({
4055
5565
  withinPortal: true,
4056
5566
  ...attrs,
4057
5567
  opened: props.opened,
4058
- onChange: props.onChange
5568
+ "onUpdate:opened": props.onChange
4059
5569
  }, () => [(0, vue.h)(_mantine_vue_core.Tooltip, {
4060
5570
  label: actionProps.title ?? l.columnActions,
4061
5571
  openDelay: 1e3,
@@ -4112,9 +5622,9 @@ var MVT_FilterCheckbox = (0, vue.defineComponent)({
4112
5622
  size: table.getState().density === "xs" ? "sm" : "md",
4113
5623
  ...checkboxProps,
4114
5624
  title: void 0,
4115
- onChange: (e) => {
5625
+ onChange: (checked) => {
4116
5626
  column.setFilterValue(value === void 0 ? "true" : value === "true" ? "false" : void 0);
4117
- checkboxProps.onChange?.(e);
5627
+ checkboxProps.onChange?.(checked);
4118
5628
  },
4119
5629
  onClick: (e) => {
4120
5630
  e.stopPropagation();
@@ -4427,7 +5937,7 @@ var MVT_FilterRangeSlider = (0, vue.defineComponent)({
4427
5937
  class: (0, clsx.default)("mvt-filter-range-slider", MVT_FilterRangeSlider_module_default.root, rangeProps.class),
4428
5938
  min,
4429
5939
  max,
4430
- value: values.value,
5940
+ modelValue: values.value,
4431
5941
  ...rangeProps,
4432
5942
  onChange: (value) => {
4433
5943
  values.value = value;
@@ -5249,6 +6759,44 @@ var MantineVueTable = (0, vue.defineComponent)({
5249
6759
  }
5250
6760
  });
5251
6761
  //#endregion
6762
+ //#region src/server-grouping/createServerGroupingProvider.ts
6763
+ var asString = (value) => value === null || value === void 0 ? "" : String(value);
6764
+ /**
6765
+ * Build a {@link MVT_ServerGroupingProvider} with sensible defaults for common
6766
+ * response formats:
6767
+ *
6768
+ * - `groups`/`records` default to the response itself when it is an array.
6769
+ * - `groupRowCount`/`recordRowCount` default to the mapped array length.
6770
+ * - `groupId`/`groupLabel` default to stringifying `groupValue` (or the group
6771
+ * itself when it is a primitive).
6772
+ * - `groupValue` defaults to the group object itself.
6773
+ * - `groupMeta` defaults to `{}`; `groupCount` and aggregates stay optional.
6774
+ * - `hasChildren` defaults to `true` (every level up to and including the last
6775
+ * grouping level is expandable).
6776
+ * - `rowId` falls back to the table's existing `getRowId` logic.
6777
+ */
6778
+ var createServerGroupingProvider = (options) => {
6779
+ const getGroups = options.getGroups ?? options.groups ?? ((response) => Array.isArray(response) ? response : []);
6780
+ const getRecords = options.getRecords ?? options.records ?? ((response) => Array.isArray(response) ? response : []);
6781
+ const getGroupValue = options.getGroupValue ?? options.groupValue ?? ((group) => group);
6782
+ return {
6783
+ getGroupId: options.getGroupId ?? options.groupId ?? ((group, context) => asString(getGroupValue(group, context))),
6784
+ getGroupLabel: options.getGroupLabel ?? options.groupLabel ?? ((group, context) => asString(getGroupValue(group, context))),
6785
+ getGroupValue,
6786
+ getGroupCount: options.getGroupCount ?? options.groupCount,
6787
+ getGroupMeta: options.getGroupMeta ?? options.groupMeta ?? (() => ({})),
6788
+ hasGroupChildren: options.hasGroupChildren ?? options.hasChildren,
6789
+ loadGroups: options.loadGroups,
6790
+ getGroups,
6791
+ getGroupRowCount: options.getGroupRowCount ?? options.groupRowCount ?? ((response) => getGroups(response).length),
6792
+ loadRecords: options.loadRecords,
6793
+ getRecords,
6794
+ getRecordRowCount: options.getRecordRowCount ?? options.recordRowCount ?? ((response) => getRecords(response).length),
6795
+ getRowId: options.getRowId ?? options.rowId,
6796
+ getCacheKey: options.getCacheKey ?? options.cacheKey
6797
+ };
6798
+ };
6799
+ //#endregion
5252
6800
  exports.MVT_AggregationFns = MVT_AggregationFns;
5253
6801
  exports.MVT_BottomToolbar = MVT_BottomToolbar;
5254
6802
  exports.MVT_ColumnActionMenu = MVT_ColumnActionMenu;
@@ -5273,7 +6821,10 @@ exports.MVT_GrabHandleButton = MVT_GrabHandleButton;
5273
6821
  exports.MVT_ProgressBar = MVT_ProgressBar;
5274
6822
  exports.MVT_RowActionMenu = MVT_RowActionMenu;
5275
6823
  exports.MVT_RowPinButton = MVT_RowPinButton;
6824
+ exports.MVT_SERVER_GROUPING_ROOT_PATH_ID = MVT_SERVER_GROUPING_ROOT_PATH_ID;
5276
6825
  exports.MVT_SelectCheckbox = MVT_SelectCheckbox;
6826
+ exports.MVT_ServerGroupingBody = MVT_ServerGroupingBody;
6827
+ exports.MVT_ServerGroupingGroupBy = MVT_ServerGroupingGroupBy;
5277
6828
  exports.MVT_ShowHideColumnsButton = MVT_ShowHideColumnsButton;
5278
6829
  exports.MVT_ShowHideColumnsMenu = MVT_ShowHideColumnsMenu;
5279
6830
  exports.MVT_ShowHideColumnsMenuItems = MVT_ShowHideColumnsMenuItems;
@@ -5315,9 +6866,14 @@ exports.Memo_MVT_TableBody = Memo_MVT_TableBody;
5315
6866
  exports.Memo_MVT_TableBodyCell = Memo_MVT_TableBodyCell;
5316
6867
  exports.Memo_MVT_TableBodyRow = Memo_MVT_TableBodyRow;
5317
6868
  exports.createMVTColumnHelper = createMVTColumnHelper;
6869
+ exports.createMVT_ServerGroupingManager = createMVT_ServerGroupingManager;
5318
6870
  exports.createRow = createRow;
6871
+ exports.createServerGroupPathId = createServerGroupPathId;
6872
+ exports.createServerGroupingProvider = createServerGroupingProvider;
5319
6873
  exports.dataVariable = dataVariable;
5320
6874
  exports.defaultDisplayColumnProps = defaultDisplayColumnProps;
6875
+ exports.deselectServerGroupingRecords = deselectServerGroupingRecords;
6876
+ exports.escapeServerGroupPathPart = escapeServerGroupPathPart;
5321
6877
  exports.flexRender = flexRender;
5322
6878
  exports.getAllLeafColumnDefs = getAllLeafColumnDefs;
5323
6879
  exports.getCanRankRows = getCanRankRows;
@@ -5327,21 +6883,36 @@ exports.getDefaultColumnOrderIds = getDefaultColumnOrderIds;
5327
6883
  exports.getIsRankingRows = getIsRankingRows;
5328
6884
  exports.getIsRowSelected = getIsRowSelected;
5329
6885
  exports.getLeadingDisplayColumnIds = getLeadingDisplayColumnIds;
6886
+ exports.getMVT_ColumnWidthStyles = getMVT_ColumnWidthStyles;
5330
6887
  exports.getMVT_RowSelectionHandler = getMVT_RowSelectionHandler;
5331
6888
  exports.getMVT_Rows = getMVT_Rows;
5332
6889
  exports.getMVT_SelectAllHandler = getMVT_SelectAllHandler;
5333
6890
  exports.getPrimaryColor = getPrimaryColor;
5334
6891
  exports.getPrimaryShade = getPrimaryShade;
6892
+ exports.getServerGroupPathDepth = getServerGroupPathDepth;
6893
+ exports.getServerGroupableColumns = getServerGroupableColumns;
6894
+ exports.getServerGroupingColumnActions = getServerGroupingColumnActions;
6895
+ exports.getServerGroupingGroupByContext = getServerGroupingGroupByContext;
6896
+ exports.getServerGroupingManager = getServerGroupingManager;
6897
+ exports.getServerGroupingSelectableRows = getServerGroupingSelectableRows;
6898
+ exports.getServerGroupingSelectionPayload = getServerGroupingSelectionPayload;
6899
+ exports.getServerGroupingSelectionSummary = getServerGroupingSelectionSummary;
5335
6900
  exports.getTrailingDisplayColumnIds = getTrailingDisplayColumnIds;
6901
+ exports.isAbortError = isAbortError;
6902
+ exports.isServerGroupPathOrDescendant = isServerGroupPathOrDescendant;
6903
+ exports.isServerGroupingRecordSelected = isServerGroupingRecordSelected;
5336
6904
  exports.localizedFilterOption = localizedFilterOption;
5337
6905
  exports.mrtFilterOptions = mrtFilterOptions;
5338
6906
  exports.parseCSSVarId = parseCSSVarId;
5339
6907
  exports.parseFromValuesOrFunc = parseFromValuesOrFunc;
6908
+ exports.parseServerGroupPathId = parseServerGroupPathId;
5340
6909
  exports.prepareColumns = prepareColumns;
5341
6910
  exports.provideMVT_Slots = provideMVT_Slots;
5342
6911
  exports.rankGlobalFuzzy = rankGlobalFuzzy;
5343
6912
  exports.renderMVT_Renderable = renderMVT_Renderable;
5344
6913
  exports.reorderColumn = reorderColumn;
6914
+ exports.selectAllMatchingServerGroupingRecords = selectAllMatchingServerGroupingRecords;
6915
+ exports.serializeServerGroupingCacheKey = serializeServerGroupingCacheKey;
5345
6916
  exports.showRowActionsColumn = showRowActionsColumn;
5346
6917
  exports.showRowDragColumn = showRowDragColumn;
5347
6918
  exports.showRowExpandColumn = showRowExpandColumn;
@@ -5349,6 +6920,9 @@ exports.showRowNumbersColumn = showRowNumbersColumn;
5349
6920
  exports.showRowPinningColumn = showRowPinningColumn;
5350
6921
  exports.showRowSelectionColumn = showRowSelectionColumn;
5351
6922
  exports.showRowSpacerColumn = showRowSpacerColumn;
6923
+ exports.toggleAllServerGroupingRecordsSelected = toggleAllServerGroupingRecordsSelected;
6924
+ exports.toggleServerGroupingRecordSelected = toggleServerGroupingRecordSelected;
6925
+ exports.unescapeServerGroupPathPart = unescapeServerGroupPathPart;
5352
6926
  exports.useMVT_ColumnVirtualizer = useMVT_ColumnVirtualizer;
5353
6927
  exports.useMVT_Effects = useMVT_Effects;
5354
6928
  exports.useMVT_RowVirtualizer = useMVT_RowVirtualizer;