@mantine-vue/table 3.0.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
  /**
@@ -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,11 +1987,11 @@ 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,
@@ -1108,7 +2007,7 @@ var MVT_SelectCheckbox = (0, vue.defineComponent)({
1108
2007
  }
1109
2008
  };
1110
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, {
1111
- indeterminate: !checked && selectAll ? table.getIsSomeRowsSelected() : row?.getIsSomeSelected() && row.getCanSelectSubRows(),
2010
+ indeterminate: serverGroupingSummary ? serverGroupingSummary.isSome : !checked && selectAll ? table.getIsSomeRowsSelected() : row?.getIsSomeSelected() && row.getCanSelectSubRows(),
1112
2011
  ...common
1113
2012
  });
1114
2013
  return (0, vue.h)(_mantine_vue_core.Tooltip, {
@@ -1238,12 +2137,14 @@ var MVT_Default_Icons = {
1238
2137
  //#region src/locales/en.ts
1239
2138
  var MVT_Localization_EN = {
1240
2139
  actions: "Actions",
2140
+ allMatchingRecordsSelected: "All records matching the current filters are selected",
1241
2141
  and: "and",
1242
2142
  cancel: "Cancel",
1243
2143
  changeFilterMode: "Change filter mode",
1244
2144
  changeSearchMode: "Change search mode",
1245
2145
  clearFilter: "Clear filter",
1246
2146
  clearSearch: "Clear search",
2147
+ clearGrouping: "Clear grouping",
1247
2148
  clearSelection: "Clear selection",
1248
2149
  clearSort: "Clear sort",
1249
2150
  clickToCopy: "Click to copy",
@@ -1254,6 +2155,7 @@ var MVT_Localization_EN = {
1254
2155
  copiedToClipboard: "Copied to clipboard",
1255
2156
  dropToGroupBy: "Drop to group by {column}",
1256
2157
  edit: "Edit",
2158
+ errorLoadingData: "Error loading data",
1257
2159
  expand: "Expand",
1258
2160
  expandAll: "Expand all",
1259
2161
  filterArrIncludes: "Includes",
@@ -1286,13 +2188,18 @@ var MVT_Localization_EN = {
1286
2188
  goToNextPage: "Go to next page",
1287
2189
  goToPreviousPage: "Go to previous page",
1288
2190
  grab: "Grab",
2191
+ groupBy: "Group by",
1289
2192
  groupByColumn: "Group by {column}",
1290
2193
  groupedBy: "Grouped by ",
1291
2194
  hideAll: "Hide all",
1292
2195
  hideColumn: "Hide {column} column",
2196
+ loading: "Loading…",
1293
2197
  max: "Max",
1294
2198
  min: "Min",
1295
2199
  move: "Move",
2200
+ moveDown: "Move down",
2201
+ moveUp: "Move up",
2202
+ noGroupableColumns: "No groupable columns",
1296
2203
  noRecordsToDisplay: "No records to display",
1297
2204
  noResultsFound: "No results found",
1298
2205
  of: "of",
@@ -1302,6 +2209,7 @@ var MVT_Localization_EN = {
1302
2209
  pinToRight: "Pin to right",
1303
2210
  resetColumnSize: "Reset column size",
1304
2211
  resetOrder: "Reset order",
2212
+ retry: "Retry",
1305
2213
  rowActions: "Row Actions",
1306
2214
  rowNumber: "#",
1307
2215
  rowNumbers: "Row Numbers",
@@ -1309,7 +2217,9 @@ var MVT_Localization_EN = {
1309
2217
  save: "Save",
1310
2218
  search: "Search",
1311
2219
  selectedCountOfRowCountRowsSelected: "{selectedCount} of {rowCount} row(s) selected",
2220
+ selectedCountRowsSelected: "{selectedCount} row(s) selected",
1312
2221
  select: "Select",
2222
+ selectAllMatching: "Select all matching records",
1313
2223
  showAll: "Show all",
1314
2224
  showAllColumns: "Show all columns",
1315
2225
  showHideColumns: "Show/Hide columns",
@@ -1396,6 +2306,33 @@ var useMVT_TableOptions = (rawOptions) => {
1396
2306
  manualPagination = true;
1397
2307
  manualSorting = true;
1398
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
+ }
1399
2336
  const definedOptions = {
1400
2337
  aggregationFns,
1401
2338
  autoResetExpanded,
@@ -1644,6 +2581,12 @@ var useMVT_TableInstance = (definedTableOptions) => {
1644
2581
  get: getter
1645
2582
  });
1646
2583
  };
2584
+ let serverGroupingManager;
2585
+ defineState("serverGroupingExpanded", () => serverGroupingManager?.getExpanded() ?? {});
2586
+ defineState("serverGroupingSelectAll", () => serverGroupingManager?.getSelectAll() ?? {
2587
+ active: false,
2588
+ excludedRowIds: []
2589
+ });
1647
2590
  defineState("columnFilterFns", () => columnFilterFns.value);
1648
2591
  defineState("columnOrder", () => columnOrder.value);
1649
2592
  defineState("columnSizingInfo", () => columnSizingInfo.value);
@@ -1746,6 +2689,58 @@ var useMVT_TableInstance = (definedTableOptions) => {
1746
2689
  table.setShowColumnFilters = statefulTableOptions.onShowColumnFiltersChange ?? ((updater) => applyUpdater(showColumnFilters, updater));
1747
2690
  table.setShowGlobalFilter = statefulTableOptions.onShowGlobalFilterChange ?? ((updater) => applyUpdater(showGlobalFilter, updater));
1748
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);
1749
2744
  table._mvtSlots = mvtSlots;
1750
2745
  table.setMVTSlots = (slots) => {
1751
2746
  mvtSlots.value = slots;
@@ -1848,10 +2843,10 @@ var MVT_TablePagination = (0, vue.defineComponent)({
1848
2843
  const { rowsPerPageOptions = defaultRowsPerPage, showRowsPerPage = true, withEdges = showEdges, ...rest } = paginationProps;
1849
2844
  const controls = o.paginationDisplayMode === "pages" ? (0, vue.h)(_mantine_vue_core.Pagination, {
1850
2845
  total: numberOfPages,
1851
- value: pageIndex + 1,
2846
+ modelValue: pageIndex + 1,
1852
2847
  withEdges,
1853
2848
  ...rest,
1854
- onChange: (page) => table.setPageIndex(page - 1)
2849
+ "onUpdate:modelValue": (page) => table.setPageIndex(page - 1)
1855
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 }, () => [
1856
2851
  withEdges && (0, vue.h)(_mantine_vue_core.ActionIcon, {
1857
2852
  "aria-label": o.localization.goToFirstPage,
@@ -1887,8 +2882,8 @@ var MVT_TablePagination = (0, vue.defineComponent)({
1887
2882
  "aria-labelledby": "rpp-label",
1888
2883
  class: MVT_TablePagination_module_default.pagesize,
1889
2884
  data: rowsPerPageOptions,
1890
- value: pageSize.toString(),
1891
- onChange: (value) => value && table.setPageSize(+value)
2885
+ modelValue: pageSize.toString(),
2886
+ "onUpdate:modelValue": (value) => value && table.setPageSize(+value)
1892
2887
  })]), controls]);
1893
2888
  };
1894
2889
  }
@@ -1928,26 +2923,50 @@ var MVT_ToolbarAlertBanner = (0, vue.defineComponent)({
1928
2923
  const badgeProps = parseFromValuesOrFunc(o.mantineToolbarAlertBannerBadgeProps, { table });
1929
2924
  const total = o.rowCount ?? table.getPrePaginationRowModel().flatRows.length;
1930
2925
  const selectedCount = o.manualPagination ? Object.values(state.rowSelection).filter(Boolean).length : table.getFilteredSelectedRowModel().rows.length;
1931
- 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, {
1932
2932
  align: "center",
1933
2933
  gap: "sm"
1934
- }, () => [o.localization.selectedCountOfRowCountRowsSelected.replace("{selectedCount}", String(selectedCount)).replace("{rowCount}", String(total)), (0, vue.h)(_mantine_vue_core.Button, {
1935
- size: "compact-xs",
1936
- variant: "subtle",
1937
- onClick: (event) => getMVT_SelectAllHandler({ table })(event, false, true)
1938
- }, () => o.localization.clearSelection)]) : null;
1939
- 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, {
1940
- key: columnId,
1941
- class: MVT_ToolbarAlertBanner_module_default["alert-badge"],
1942
- variant: "filled",
1943
- ...badgeProps,
1944
- rightSection: (0, vue.h)(_mantine_vue_core.ActionIcon, {
1945
- color: "white",
1946
- size: "xs",
2934
+ }, () => [
2935
+ selectedText,
2936
+ showSelectAllMatching && (0, vue.h)(_mantine_vue_core.Button, {
2937
+ size: "compact-xs",
2938
+ variant: "subtle",
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",
1947
2943
  variant: "subtle",
1948
- onClick: () => table.getColumn(columnId).toggleGrouping()
1949
- }, () => (0, vue.h)(o.icons.IconX, { style: { transform: "scale(0.8)" } }))
1950
- }, () => `${table.getColumn(columnId).columnDef.header} `)])]) : null;
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;
1951
2970
  const custom = slots.default?.({
1952
2971
  groupedAlert,
1953
2972
  selectedAlert,
@@ -2069,6 +3088,207 @@ var MVT_BottomToolbar = (0, vue.defineComponent)({
2069
3088
  };
2070
3089
  }
2071
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
+ });
2072
3292
  var MVT_FilterOptionMenu_module_default = { symbol: "_symbol_1b3vj_1" };
2073
3293
  //#endregion
2074
3294
  //#region src/components/menus/MVT_FilterOptionMenu.ts
@@ -2457,7 +3677,7 @@ var MVT_ShowHideColumnsMenuItems = (0, vue.defineComponent)({
2457
3677
  openDelay: 1e3,
2458
3678
  withinPortal: true
2459
3679
  }, () => (0, vue.h)(_mantine_vue_core.Switch, {
2460
- checked,
3680
+ modelValue: checked,
2461
3681
  class: MVT_ShowHideColumnsMenuItems_module_default.switch,
2462
3682
  disabled: !column.getCanHide(),
2463
3683
  label: def.header,
@@ -2776,6 +3996,7 @@ var MVT_TopToolbar = (0, vue.defineComponent)({
2776
3996
  ["both", "top"].includes(positionToolbarDropZone ?? "") && (0, vue.h)(MVT_ToolbarDropZone, { table }),
2777
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] }, () => [
2778
3998
  enableGlobalFilter && positionGlobalFilter === "left" && (0, vue.h)(MVT_GlobalFilterTextInput, globalFilterProps),
3999
+ table._serverGrouping && (0, vue.h)(MVT_ServerGroupingGroupBy, { table }),
2779
4000
  parseFromValuesOrFunc(renderTopToolbarCustomActions, { table }) ?? (0, vue.h)("span"),
2780
4001
  enableToolbarInternalActions ? (0, vue.h)(_mantine_vue_core.Flex, {
2781
4002
  justify: "end",
@@ -2952,6 +4173,21 @@ var useMVT_ColumnVirtualizer = (table) => {
2952
4173
  //#endregion
2953
4174
  //#region src/utils/style.utils.ts
2954
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
+ };
2955
4191
  var getPrimaryShade = (theme) => typeof theme.primaryShade === "number" ? theme.primaryShade : theme.primaryShade?.dark ?? 7;
2956
4192
  var getPrimaryColor = (theme, shade) => theme.colors[theme.primaryColor][shade ?? getPrimaryShade(theme)];
2957
4193
  function dataVariable(name, value) {
@@ -3279,8 +4515,9 @@ var MVT_TableBodyCell = (0, vue.defineComponent)({
3279
4515
  if (!columnDef.enableCellHoverReveal) return;
3280
4516
  isCellContentOverflowing.value = false;
3281
4517
  };
4518
+ const isServerGrouping = !!table._serverGrouping;
3282
4519
  const renderCellContent = () => {
3283
- if (cell.getIsPlaceholder()) return columnDef.PlaceholderCell?.({
4520
+ if (!isServerGrouping && cell.getIsPlaceholder()) return columnDef.PlaceholderCell?.({
3284
4521
  cell,
3285
4522
  column,
3286
4523
  row,
@@ -3606,6 +4843,271 @@ var MVT_TableBodyRow = (0, vue.defineComponent)({
3606
4843
  }
3607
4844
  });
3608
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
+ });
3609
5111
  var MVT_TableBody_module_default = {
3610
5112
  root: "_root_17rwk_1",
3611
5113
  "root-grid": "_root-grid_17rwk_6",
@@ -3735,7 +5237,10 @@ var MVT_TableBody = (0, vue.defineComponent)({
3735
5237
  ...tableBodyProps?.style
3736
5238
  },
3737
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)
3738
- }, () => 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) => {
3739
5244
  let index = renderedRowIndex;
3740
5245
  if (rowVirtualizer) if (renderDetailPanel) {
3741
5246
  if (rowOrVirtualRow.index % 2 === 1) return null;
@@ -4003,6 +5508,7 @@ var MVT_ColumnActionMenu = (0, vue.defineComponent)({
4003
5508
  leftSection: (0, vue.h)(icon, { class: iconClass }),
4004
5509
  onClick
4005
5510
  }, () => label);
5511
+ const serverGroupBy = getServerGroupingColumnActions(table, column.id);
4006
5512
  const internal = [];
4007
5513
  if (o.enableSorting && column.getCanSort()) {
4008
5514
  if (o.enableSortingRemoval !== false) internal.push(item(l.clearSort, icons.IconClearAll, () => column.clearSorting(), !column.getIsSorted()));
@@ -4024,6 +5530,11 @@ var MVT_ColumnActionMenu = (0, vue.defineComponent)({
4024
5530
  table.setColumnOrder((old) => ["mvt-row-expand", ...old]);
4025
5531
  }));
4026
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));
4027
5538
  }
4028
5539
  if (o.enableColumnPinning && column.getCanPin()) {
4029
5540
  internal.push(item(l.pinToLeft, icons.IconPinned, () => column.pin("left"), column.getIsPinned() === "left", MVT_ColumnActionMenu_module_default.left));
@@ -4054,7 +5565,7 @@ var MVT_ColumnActionMenu = (0, vue.defineComponent)({
4054
5565
  withinPortal: true,
4055
5566
  ...attrs,
4056
5567
  opened: props.opened,
4057
- onChange: props.onChange
5568
+ "onUpdate:opened": props.onChange
4058
5569
  }, () => [(0, vue.h)(_mantine_vue_core.Tooltip, {
4059
5570
  label: actionProps.title ?? l.columnActions,
4060
5571
  openDelay: 1e3,
@@ -4426,7 +5937,7 @@ var MVT_FilterRangeSlider = (0, vue.defineComponent)({
4426
5937
  class: (0, clsx.default)("mvt-filter-range-slider", MVT_FilterRangeSlider_module_default.root, rangeProps.class),
4427
5938
  min,
4428
5939
  max,
4429
- value: values.value,
5940
+ modelValue: values.value,
4430
5941
  ...rangeProps,
4431
5942
  onChange: (value) => {
4432
5943
  values.value = value;
@@ -5248,6 +6759,44 @@ var MantineVueTable = (0, vue.defineComponent)({
5248
6759
  }
5249
6760
  });
5250
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
5251
6800
  exports.MVT_AggregationFns = MVT_AggregationFns;
5252
6801
  exports.MVT_BottomToolbar = MVT_BottomToolbar;
5253
6802
  exports.MVT_ColumnActionMenu = MVT_ColumnActionMenu;
@@ -5272,7 +6821,10 @@ exports.MVT_GrabHandleButton = MVT_GrabHandleButton;
5272
6821
  exports.MVT_ProgressBar = MVT_ProgressBar;
5273
6822
  exports.MVT_RowActionMenu = MVT_RowActionMenu;
5274
6823
  exports.MVT_RowPinButton = MVT_RowPinButton;
6824
+ exports.MVT_SERVER_GROUPING_ROOT_PATH_ID = MVT_SERVER_GROUPING_ROOT_PATH_ID;
5275
6825
  exports.MVT_SelectCheckbox = MVT_SelectCheckbox;
6826
+ exports.MVT_ServerGroupingBody = MVT_ServerGroupingBody;
6827
+ exports.MVT_ServerGroupingGroupBy = MVT_ServerGroupingGroupBy;
5276
6828
  exports.MVT_ShowHideColumnsButton = MVT_ShowHideColumnsButton;
5277
6829
  exports.MVT_ShowHideColumnsMenu = MVT_ShowHideColumnsMenu;
5278
6830
  exports.MVT_ShowHideColumnsMenuItems = MVT_ShowHideColumnsMenuItems;
@@ -5314,9 +6866,14 @@ exports.Memo_MVT_TableBody = Memo_MVT_TableBody;
5314
6866
  exports.Memo_MVT_TableBodyCell = Memo_MVT_TableBodyCell;
5315
6867
  exports.Memo_MVT_TableBodyRow = Memo_MVT_TableBodyRow;
5316
6868
  exports.createMVTColumnHelper = createMVTColumnHelper;
6869
+ exports.createMVT_ServerGroupingManager = createMVT_ServerGroupingManager;
5317
6870
  exports.createRow = createRow;
6871
+ exports.createServerGroupPathId = createServerGroupPathId;
6872
+ exports.createServerGroupingProvider = createServerGroupingProvider;
5318
6873
  exports.dataVariable = dataVariable;
5319
6874
  exports.defaultDisplayColumnProps = defaultDisplayColumnProps;
6875
+ exports.deselectServerGroupingRecords = deselectServerGroupingRecords;
6876
+ exports.escapeServerGroupPathPart = escapeServerGroupPathPart;
5320
6877
  exports.flexRender = flexRender;
5321
6878
  exports.getAllLeafColumnDefs = getAllLeafColumnDefs;
5322
6879
  exports.getCanRankRows = getCanRankRows;
@@ -5326,21 +6883,36 @@ exports.getDefaultColumnOrderIds = getDefaultColumnOrderIds;
5326
6883
  exports.getIsRankingRows = getIsRankingRows;
5327
6884
  exports.getIsRowSelected = getIsRowSelected;
5328
6885
  exports.getLeadingDisplayColumnIds = getLeadingDisplayColumnIds;
6886
+ exports.getMVT_ColumnWidthStyles = getMVT_ColumnWidthStyles;
5329
6887
  exports.getMVT_RowSelectionHandler = getMVT_RowSelectionHandler;
5330
6888
  exports.getMVT_Rows = getMVT_Rows;
5331
6889
  exports.getMVT_SelectAllHandler = getMVT_SelectAllHandler;
5332
6890
  exports.getPrimaryColor = getPrimaryColor;
5333
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;
5334
6900
  exports.getTrailingDisplayColumnIds = getTrailingDisplayColumnIds;
6901
+ exports.isAbortError = isAbortError;
6902
+ exports.isServerGroupPathOrDescendant = isServerGroupPathOrDescendant;
6903
+ exports.isServerGroupingRecordSelected = isServerGroupingRecordSelected;
5335
6904
  exports.localizedFilterOption = localizedFilterOption;
5336
6905
  exports.mrtFilterOptions = mrtFilterOptions;
5337
6906
  exports.parseCSSVarId = parseCSSVarId;
5338
6907
  exports.parseFromValuesOrFunc = parseFromValuesOrFunc;
6908
+ exports.parseServerGroupPathId = parseServerGroupPathId;
5339
6909
  exports.prepareColumns = prepareColumns;
5340
6910
  exports.provideMVT_Slots = provideMVT_Slots;
5341
6911
  exports.rankGlobalFuzzy = rankGlobalFuzzy;
5342
6912
  exports.renderMVT_Renderable = renderMVT_Renderable;
5343
6913
  exports.reorderColumn = reorderColumn;
6914
+ exports.selectAllMatchingServerGroupingRecords = selectAllMatchingServerGroupingRecords;
6915
+ exports.serializeServerGroupingCacheKey = serializeServerGroupingCacheKey;
5344
6916
  exports.showRowActionsColumn = showRowActionsColumn;
5345
6917
  exports.showRowDragColumn = showRowDragColumn;
5346
6918
  exports.showRowExpandColumn = showRowExpandColumn;
@@ -5348,6 +6920,9 @@ exports.showRowNumbersColumn = showRowNumbersColumn;
5348
6920
  exports.showRowPinningColumn = showRowPinningColumn;
5349
6921
  exports.showRowSelectionColumn = showRowSelectionColumn;
5350
6922
  exports.showRowSpacerColumn = showRowSpacerColumn;
6923
+ exports.toggleAllServerGroupingRecordsSelected = toggleAllServerGroupingRecordsSelected;
6924
+ exports.toggleServerGroupingRecordSelected = toggleServerGroupingRecordSelected;
6925
+ exports.unescapeServerGroupPathPart = unescapeServerGroupPathPart;
5351
6926
  exports.useMVT_ColumnVirtualizer = useMVT_ColumnVirtualizer;
5352
6927
  exports.useMVT_Effects = useMVT_Effects;
5353
6928
  exports.useMVT_RowVirtualizer = useMVT_RowVirtualizer;