@rebasepro/admin 0.14.0 → 0.14.1-canary.g7e666eb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/{CollectionEditorDialog-CSheud_E.js → CollectionEditorDialog-BykOIZH4.js} +3 -3
  2. package/dist/{CollectionEditorDialog-CSheud_E.js.map → CollectionEditorDialog-BykOIZH4.js.map} +1 -1
  3. package/dist/{PropertyEditView-CWZlC1gq.js → PropertyEditView-C9Der_Ej.js} +2 -2
  4. package/dist/{PropertyEditView-CWZlC1gq.js.map → PropertyEditView-C9Der_Ej.js.map} +1 -1
  5. package/dist/{RouterCollectionsStudioView-CwTpdzWF.js → RouterCollectionsStudioView-CfTHbBCA.js} +4 -4
  6. package/dist/{RouterCollectionsStudioView-CwTpdzWF.js.map → RouterCollectionsStudioView-CfTHbBCA.js.map} +1 -1
  7. package/dist/collection_editor_ui.js +4 -4
  8. package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +2 -1
  9. package/dist/components/CollectionViewBinding/SortButton.d.ts +14 -4
  10. package/dist/{export-L53WektO.js → export-BDynF1En.js} +2 -2
  11. package/dist/{export-L53WektO.js.map → export-BDynF1En.js.map} +1 -1
  12. package/dist/{history-D5SKygpJ.js → history-DS2iXiGi.js} +2 -2
  13. package/dist/{history-D5SKygpJ.js.map → history-DS2iXiGi.js.map} +1 -1
  14. package/dist/{import-C_4edkoD.js → import-J5vb86xj.js} +2 -2
  15. package/dist/{import-C_4edkoD.js.map → import-J5vb86xj.js.map} +1 -1
  16. package/dist/index.js +6 -6
  17. package/dist/{util-C-D5yD2n.js → util-WQP-ljKW.js} +206 -90
  18. package/dist/util-WQP-ljKW.js.map +1 -0
  19. package/package.json +9 -9
  20. package/src/collection_editor/ui/EditorCollectionActionStart.tsx +3 -2
  21. package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +80 -39
  22. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +19 -8
  23. package/src/components/CollectionViewBinding/FilterPresetsButton.tsx +6 -5
  24. package/src/components/CollectionViewBinding/SortButton.tsx +149 -37
  25. package/src/components/SelectableTable/SelectableTable.tsx +1 -1
  26. package/dist/util-C-D5yD2n.js.map +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/admin",
3
3
  "type": "module",
4
- "version": "0.14.0",
4
+ "version": "0.14.1-canary.g7e666eb",
5
5
  "description": "Rebase CMS — content management views, forms, and routing",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -83,14 +83,14 @@
83
83
  "react-dropzone": "^19.1.1",
84
84
  "react-use-measure": "^2.1.7",
85
85
  "zod": "^4.4.3",
86
- "@rebasepro/admin-types": "0.14.0",
87
- "@rebasepro/app": "0.14.0",
88
- "@rebasepro/forms": "0.14.0",
89
- "@rebasepro/inference": "0.14.0",
90
- "@rebasepro/types": "0.14.0",
91
- "@rebasepro/utils": "0.14.0",
92
- "@rebasepro/common": "0.14.0",
93
- "@rebasepro/ui": "0.14.0"
86
+ "@rebasepro/common": "0.14.1-canary.g7e666eb",
87
+ "@rebasepro/app": "0.14.1-canary.g7e666eb",
88
+ "@rebasepro/forms": "0.14.1-canary.g7e666eb",
89
+ "@rebasepro/inference": "0.14.1-canary.g7e666eb",
90
+ "@rebasepro/ui": "0.14.1-canary.g7e666eb",
91
+ "@rebasepro/utils": "0.14.1-canary.g7e666eb",
92
+ "@rebasepro/admin-types": "0.14.1-canary.g7e666eb",
93
+ "@rebasepro/types": "0.14.1-canary.g7e666eb"
94
94
  },
95
95
  "peerDependencies": {
96
96
  "react": ">=19.2.7",
@@ -12,6 +12,7 @@ import { Button, SaveIcon, Tooltip, UndoIcon } from "@rebasepro/ui";
12
12
  import { useCollectionEditorController } from "../useCollectionEditorController";
13
13
  import { useCollectionsConfigController } from "../useCollectionsConfigController";
14
14
  import { mergeDeep } from "@rebasepro/utils";
15
+ import { normalizeOrderBy } from "@rebasepro/common";
15
16
 
16
17
  export function EditorCollectionActionStart({
17
18
  path,
@@ -30,7 +31,7 @@ export function EditorCollectionActionStart({
30
31
 
31
32
  let saveDefaultFilterButton = null;
32
33
  if (!equal(getObjectOrNull(tableController.filterValues), getObjectOrNull(collection.defaultFilter)) ||
33
- !equal(getObjectOrNull(tableController.sortBy), getObjectOrNull(collection.sort))) {
34
+ !equal(getObjectOrNull(tableController.sortBy), getObjectOrNull(normalizeOrderBy(collection.sort)))) {
34
35
  saveDefaultFilterButton = <>
35
36
  <Tooltip
36
37
  asChild={true}
@@ -68,7 +69,7 @@ parentEntityIds,
68
69
  if (collection?.defaultFilter)
69
70
  tableController.setFilterValues?.(collection?.defaultFilter);
70
71
  if (collection?.sort)
71
- tableController.setSortBy?.(collection?.sort);
72
+ tableController.setSortBy?.(normalizeOrderBy(collection.sort) as Parameters<NonNullable<typeof tableController.setSortBy>>[0]);
72
73
  }}>
73
74
  <UndoIcon/>
74
75
  </Button>
@@ -1,5 +1,5 @@
1
1
 
2
- import type { Properties, Property } from "@rebasepro/types";
2
+ import type { OrderByTuple, Properties, Property } from "@rebasepro/types";
3
3
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { Entity } from "@rebasepro/types";
5
5
  import { CollectionSize, EntityAction, EntityTableController, SelectionController, AdminCollection } from "@rebasepro/admin-types";
@@ -540,11 +540,11 @@ export function CollectionListViewBinding<M extends Record<string, unknown> = Re
540
540
  ...(columnMode ? [] : slotKeys.relationKeys)
541
541
  ]), [titleColumn, imagePropertyKey, subtitleKey, declaredColumns, columnMode, slotKeys.relationKeys]);
542
542
 
543
- /** The property the sort names, when no cell already shows it. */
544
- const sortedKey = useMemo(() => {
545
- const key = sortBy?.[0];
546
- return key && !shownKeys.has(key) ? key : undefined;
547
- }, [sortBy, shownKeys]);
543
+ /** The properties the sort names, minus the ones a cell already shows. */
544
+ const sortedKeys = useMemo(
545
+ () => (sortBy ?? []).map(([key]) => key as string).filter(key => !shownKeys.has(key)),
546
+ [sortBy, shownKeys]
547
+ );
548
548
 
549
549
  /** The properties the filters name, minus the ones a cell already shows. */
550
550
  const filteredKeys = useMemo(
@@ -561,23 +561,28 @@ export function CollectionListViewBinding<M extends Record<string, unknown> = Re
561
561
  * and clearing a sort says nothing about wanting to stop seeing the property
562
562
  * it sorted on.
563
563
  *
564
- * What it does *not* do is accumulate. There is only ever one sort, so
565
- * ordering by four properties in turn is changing one's mind four times, not
566
- * asking for four columns — and asking for four is how the row lost its
567
- * status and its date to a wall of numbers. A new sort therefore replaces
568
- * the property the last one left behind, and a new set of filters replaces
569
- * the set before it. At most one sort column and one set of filter columns
570
- * outlive their request.
564
+ * What it does *not* do is accumulate. A sort names the properties it names
565
+ * *now*, so ordering by four properties in turn is changing one's mind four
566
+ * times, not asking for four columns — and asking for four is how the row
567
+ * lost its status and its date to a wall of numbers. A new sort therefore
568
+ * replaces the properties the last one left behind, and a new set of filters
569
+ * replaces the set before it. At most one sort's columns and one set of
570
+ * filter columns outlive their request.
571
571
  *
572
572
  * The list remounts per collection (it is keyed by path), so this is the
573
573
  * memory of one visit to one collection, not a preference.
574
574
  */
575
- const [lastSortedKey, setLastSortedKey] = useState<string | undefined>(undefined);
575
+ const [lastSortedKeys, setLastSortedKeys] = useState<string[]>([]);
576
576
  const [lastFilteredKeys, setLastFilteredKeys] = useState<string[]>([]);
577
577
 
578
578
  useEffect(() => {
579
- if (sortedKey) setLastSortedKey(sortedKey);
580
- }, [sortedKey]);
579
+ if (sortedKeys.length === 0) return;
580
+ // Same members, same array — see the filter effect below.
581
+ setLastSortedKeys(previous =>
582
+ previous.length === sortedKeys.length && previous.every((key, i) => key === sortedKeys[i])
583
+ ? previous
584
+ : sortedKeys);
585
+ }, [sortedKeys]);
581
586
 
582
587
  useEffect(() => {
583
588
  if (filteredKeys.length === 0) return;
@@ -602,14 +607,14 @@ export function CollectionListViewBinding<M extends Record<string, unknown> = Re
602
607
  // read in that order rather than from state alone, because the effects
603
608
  // above have not run yet on the render that first sees a request, and
604
609
  // waiting a frame would flash the row without its new column.
605
- const active = new Set([...filteredKeys, ...(sortedKey ? [sortedKey] : [])]);
610
+ const active = new Set([...filteredKeys, ...sortedKeys]);
606
611
  const keys: string[] = [];
607
612
  const request = (key: string | undefined) => {
608
613
  if (!key || keys.includes(key) || shownKeys.has(key)) return;
609
614
  keys.push(key);
610
615
  };
611
616
  (filteredKeys.length > 0 ? filteredKeys : lastFilteredKeys).forEach(request);
612
- request(sortedKey ?? lastSortedKey);
617
+ (sortedKeys.length > 0 ? sortedKeys : lastSortedKeys).forEach(request);
613
618
 
614
619
  return keys.flatMap((key, index) => {
615
620
  const property = getResolvedPropertyInPath(resolvedCollection.properties, key) as Property | undefined;
@@ -624,7 +629,7 @@ export function CollectionListViewBinding<M extends Record<string, unknown> = Re
624
629
  priority: (active.has(key) ? PRIORITY_ACTIVE_REQUEST : PRIORITY_STALE_REQUEST) - index
625
630
  }];
626
631
  });
627
- }, [filteredKeys, sortedKey, lastFilteredKeys, lastSortedKey, shownKeys, resolvedCollection, sortableKeys]);
632
+ }, [filteredKeys, sortedKeys, lastFilteredKeys, lastSortedKeys, shownKeys, resolvedCollection, sortableKeys]);
628
633
 
629
634
  // ── Compute list-view-visible actions per entity ──
630
635
  const getListViewActions = useCallback((entity: Entity<M>): EntityAction[] => {
@@ -733,26 +738,51 @@ customEntityActions });
733
738
  handleSelectionChange(entity, selected);
734
739
  }, [handleSelectionChange]);
735
740
 
741
+ /** Where each sorted column sits in the order, so a header can show its rank. */
742
+ const sortIndex = useMemo(() => {
743
+ const index = new Map<string, { direction: "asc" | "desc"; position: number }>();
744
+ (sortBy ?? []).forEach(([key, direction], position) => {
745
+ if (!index.has(key as string)) index.set(key as string, { direction,
746
+ position });
747
+ });
748
+ return index;
749
+ }, [sortBy]);
750
+
736
751
  /**
737
752
  * Order by a column, on the same cycle a table header runs: unordered →
738
- * ascending → descending → unordered. Clicking a header here and clicking it
753
+ * ascending → descending → unordered, and shift-click to add the column
754
+ * under the sort already in place. Clicking a header here and clicking it
739
755
  * in the table view have to mean the same thing, because they are the same
740
756
  * collection under the same controller.
741
757
  */
742
- const onColumnSort = useCallback((key: string) => {
758
+ const onColumnSort = useCallback((key: string, additive = false) => {
743
759
  if (!setSortBy) return;
744
- const active = sortBy?.[0] === key ? sortBy[1] : undefined;
745
- const next: [string, "asc" | "desc"] | undefined = active === "asc"
746
- ? [key, "desc"]
760
+ const active = sortIndex.get(key)?.direction;
761
+ const next: "asc" | "desc" | undefined = active === "asc"
762
+ ? "desc"
747
763
  : active === "desc"
748
764
  ? undefined
749
- : [key, "asc"];
750
- setSortBy(next);
765
+ : "asc";
766
+
767
+ const existing = (sortBy ?? []) as OrderByTuple[];
768
+ let updated: OrderByTuple[] | undefined;
769
+ if (!additive) {
770
+ updated = next ? [[key, next]] : undefined;
771
+ } else if (next === undefined) {
772
+ updated = existing.filter(([existingKey]) => existingKey !== key);
773
+ } else if (active === undefined) {
774
+ updated = [...existing, [key, next]];
775
+ } else {
776
+ updated = existing.map((entry) => entry[0] === key ? [key, next] as OrderByTuple : entry);
777
+ }
778
+ if (updated?.length === 0) updated = undefined;
779
+
780
+ setSortBy(updated as Parameters<NonNullable<typeof setSortBy>>[0]);
751
781
  // Re-ordering a partially loaded collection invalidates the pages
752
782
  // already fetched — they are no longer the rows the query answers with —
753
783
  // so pagination starts over, as it does for the table and the sort menu.
754
784
  setItemCount?.(pageSize);
755
- }, [setSortBy, sortBy, setItemCount, pageSize]);
785
+ }, [setSortBy, sortBy, sortIndex, setItemCount, pageSize]);
756
786
 
757
787
  /**
758
788
  * Whether a sort can be offered at all next to the filter already applied.
@@ -763,11 +793,11 @@ customEntityActions });
763
793
  const sortIsAvailable = useCallback((key: string) => {
764
794
  if (!setSortBy) return false;
765
795
  if (!checkFilterCombination) return true;
766
- const active = sortBy?.[0] === key ? sortBy[1] : undefined;
796
+ const active = sortIndex.get(key)?.direction;
767
797
  // Clearing the sort is always available: it asks nothing of the driver.
768
798
  if (active === "desc") return true;
769
- return checkFilterCombination(filterValues ?? {}, [key, active === "asc" ? "desc" : "asc"]);
770
- }, [setSortBy, checkFilterCombination, filterValues, sortBy]);
799
+ return checkFilterCombination(filterValues ?? {}, [[key, active === "asc" ? "desc" : "asc"]]);
800
+ }, [setSortBy, checkFilterCombination, filterValues, sortIndex]);
771
801
 
772
802
  const header = (titleColumn || visibleColumns.length > 0) && (
773
803
  <ListHeader
@@ -776,7 +806,7 @@ customEntityActions });
776
806
  selectionEnabled={selectionEnabled && !combineSelection}
777
807
  showImage={showImage}
778
808
  actionsWidth={actionsWidth}
779
- sortBy={sortBy as [string, "asc" | "desc"] | undefined}
809
+ sortIndex={sortIndex}
780
810
  onColumnSort={onColumnSort}
781
811
  sortIsAvailable={sortIsAvailable}
782
812
  />
@@ -1264,7 +1294,7 @@ function ListHeader({
1264
1294
  selectionEnabled,
1265
1295
  showImage,
1266
1296
  actionsWidth,
1267
- sortBy,
1297
+ sortIndex,
1268
1298
  onColumnSort,
1269
1299
  sortIsAvailable
1270
1300
  }: {
@@ -1273,15 +1303,18 @@ function ListHeader({
1273
1303
  selectionEnabled?: boolean;
1274
1304
  showImage: boolean;
1275
1305
  actionsWidth: number;
1276
- sortBy?: [string, "asc" | "desc"];
1277
- onColumnSort: (key: string) => void;
1306
+ sortIndex: Map<string, { direction: "asc" | "desc"; position: number }>;
1307
+ onColumnSort: (key: string, additive?: boolean) => void;
1278
1308
  sortIsAvailable: (key: string) => boolean;
1279
1309
  }) {
1280
1310
  const headerCell = (column: ListColumn) => (
1281
1311
  <ListHeaderLabel
1282
1312
  column={column}
1283
- direction={sortBy?.[0] === column.key ? sortBy[1] : undefined}
1284
- onSort={column.sortable && sortIsAvailable(column.key) ? () => onColumnSort(column.key) : undefined}
1313
+ direction={sortIndex.get(column.key)?.direction}
1314
+ position={sortIndex.size > 1 ? sortIndex.get(column.key)?.position : undefined}
1315
+ onSort={column.sortable && sortIsAvailable(column.key)
1316
+ ? (additive: boolean) => onColumnSort(column.key, additive)
1317
+ : undefined}
1285
1318
  />
1286
1319
  );
1287
1320
 
@@ -1329,17 +1362,25 @@ function ListHeader({
1329
1362
  function ListHeaderLabel({
1330
1363
  column,
1331
1364
  direction,
1365
+ position,
1332
1366
  onSort
1333
1367
  }: {
1334
1368
  column: ListColumn;
1335
1369
  direction?: "asc" | "desc";
1336
- onSort?: () => void;
1370
+ /** Rank in a multi-key sort, or `undefined` when one key says it all. */
1371
+ position?: number;
1372
+ onSort?: (additive: boolean) => void;
1337
1373
  }) {
1338
1374
  const content = (
1339
1375
  <>
1340
1376
  <span className="truncate">{column.label}</span>
1341
1377
  {direction === "asc" && <ArrowUpIcon size={12} className="flex-shrink-0"/>}
1342
1378
  {direction === "desc" && <ArrowDownIcon size={12} className="flex-shrink-0"/>}
1379
+ {direction && position !== undefined && (
1380
+ <span className="flex-shrink-0 text-[9px] font-bold leading-none tabular-nums">
1381
+ {position + 1}
1382
+ </span>
1383
+ )}
1343
1384
  </>
1344
1385
  );
1345
1386
 
@@ -1355,8 +1396,8 @@ function ListHeaderLabel({
1355
1396
  return (
1356
1397
  <button
1357
1398
  type="button"
1358
- title={`Sort by ${column.label}`}
1359
- onClick={onSort}
1399
+ title={`Sort by ${column.label} — shift-click to add it under the current sort`}
1400
+ onClick={(event) => onSort(event.shiftKey)}
1360
1401
  className={cls(base, tone, "cursor-pointer hover:text-surface-700 dark:hover:text-surface-200 transition-colors")}
1361
1402
  >
1362
1403
  {content}
@@ -1,4 +1,5 @@
1
- import type { Property } from "@rebasepro/types";
1
+ import type { OrderByTuple, Property } from "@rebasepro/types";
2
+ import { serializeOrderBy } from "@rebasepro/common";
2
3
  import type { AdditionalFieldDelegate, EntityAction, AdminCollection } from "@rebasepro/admin-types";
3
4
  import {
4
5
  Entity,
@@ -1322,7 +1323,7 @@ function DefaultCollectionEmptyState({
1322
1323
 
1323
1324
  /**
1324
1325
  * Inflight count request deduplication map.
1325
- * Keyed by `path|filterKey|sortByProperty|sortDir` so that concurrent
1326
+ * Keyed by `path|filterKey|sort|search` so that concurrent
1326
1327
  * callers (e.g. React StrictMode double-mount) share the same promise.
1327
1328
  */
1328
1329
  const inflightCountRequests = new Map<string, Promise<number>>();
@@ -1338,7 +1339,7 @@ export function EntitiesCount({
1338
1339
  path: string,
1339
1340
  collection: AdminCollection,
1340
1341
  filter?: FilterValues<any>,
1341
- sortBy?: [string, "asc" | "desc"],
1342
+ sortBy?: OrderByTuple[],
1342
1343
  /**
1343
1344
  * Required, not optional. The term sits in the same scope as the element
1344
1345
  * that mounts this and is passed to the toolbar and the empty state beside
@@ -1352,8 +1353,9 @@ export function EntitiesCount({
1352
1353
 
1353
1354
  const dataClient = useData();
1354
1355
 
1355
- const sortByProperty = sortBy ? sortBy[0] : undefined;
1356
- const currentSort = sortBy ? sortBy[1] : undefined;
1356
+ // The whole sort as one string: it keys the dedup cache and drives the
1357
+ // effect, and a `sortBy` array is a new reference on every render.
1358
+ const sortKey = React.useMemo(() => sortBy ? serializeOrderBy(sortBy) ?? "" : "", [sortBy]);
1357
1359
 
1358
1360
  // Use refs for values that should NOT trigger re-fetches
1359
1361
  const dataClientRef = React.useRef(dataClient);
@@ -1375,14 +1377,16 @@ export function EntitiesCount({
1375
1377
 
1376
1378
  // filterValues is already FilterValues — pass directly
1377
1379
  const whereParams = filter && Object.keys(filter).length > 0 ? filter : undefined;
1378
- const orderByParams: [string, "asc" | "desc"] | undefined = sortByProperty && currentSort ? [String(sortByProperty), currentSort] : undefined;
1380
+ const orderByParams = sortBy && sortBy.length > 0
1381
+ ? sortBy.map(([field, direction]) => [String(field), direction] as OrderByTuple)
1382
+ : undefined;
1379
1383
 
1380
1384
  // Deduplicate inflight count requests (e.g. React StrictMode double-mount)
1381
1385
  // The search term is part of the query, so it is part of the key. The
1382
1386
  // cache is module-level and outlives an unmount, so a key that omits it
1383
1387
  // does not merely lose precision — it answers one search with a
1384
1388
  // different search's total.
1385
- const cacheKey = `${path}|${filterKey}|${sortByProperty ?? ""}|${currentSort ?? ""}|${searchString ?? ""}`;
1389
+ const cacheKey = `${path}|${filterKey}|${sortKey}|${searchString ?? ""}`;
1386
1390
  let countPromise = inflightCountRequests.get(cacheKey);
1387
1391
  if (!countPromise) {
1388
1392
  countPromise = accessor.count({
@@ -1403,7 +1407,14 @@ export function EntitiesCount({
1403
1407
  });
1404
1408
 
1405
1409
  return () => { cancelled = true; };
1406
- }, [path, filterKey, sortByProperty, currentSort, searchString]);
1410
+ // `filterKey` and `sortKey` ARE `filter` and `sortBy` — the memoized
1411
+ // serializations a few lines up, and the only stable identity either
1412
+ // has. Both arrive as freshly built objects on every render, so
1413
+ // depending on them directly would re-run this effect each time and
1414
+ // fire a count request per render. The keys change exactly when the
1415
+ // values do, which is the condition this effect actually wants.
1416
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1417
+ }, [path, filterKey, sortKey, searchString]);
1407
1418
 
1408
1419
  // Count is now displayed in the breadcrumb bar, this component only fetches and reports
1409
1420
  return null;
@@ -1,6 +1,7 @@
1
1
  import React, { useCallback, useMemo } from "react";
2
2
  import { CheckIcon, ChevronsUpDownIcon, cls, FilterChip, Menu, MenuItem, Tooltip } from "@rebasepro/ui";
3
- import type { FilterValues, FilterPreset } from "@rebasepro/types";
3
+ import type { FilterValues, FilterPreset, OrderByTuple } from "@rebasepro/types";
4
+ import { normalizeOrderBy } from "@rebasepro/common";
4
5
  import type { EntityTableController, PropertyPath } from "@rebasepro/admin-types";
5
6
 
6
7
  export interface FilterPresetsButtonProps<M extends Record<string, unknown>> {
@@ -216,19 +217,19 @@ export function FilterPresetsButton<M extends Record<string, unknown>>({
216
217
  // otherwise find sort from remaining active presets
217
218
  if (!wasActive && preset.sort) {
218
219
  tableController.setSortBy?.(
219
- preset.sort as [Extract<keyof M, string> | (string & {}), "asc" | "desc"]
220
+ normalizeOrderBy(preset.sort) as OrderByTuple<Extract<keyof M, string> | (string & {})>[]
220
221
  );
221
222
  } else if (wasActive) {
222
- let remainingSort: [string, "asc" | "desc"] | undefined;
223
+ let remainingSort: OrderByTuple[] | undefined;
223
224
  for (let i = 0; i < filterPresets.length; i++) {
224
225
  if (i === index) continue;
225
226
  const other = filterPresets[i] as FilterPreset<string>;
226
227
  if (isPresetActive(other, currentFilters) && other.sort) {
227
- remainingSort = other.sort;
228
+ remainingSort = normalizeOrderBy(other.sort);
228
229
  }
229
230
  }
230
231
  tableController.setSortBy?.(
231
- remainingSort as [Extract<keyof M, string> | (string & {}), "asc" | "desc"] | undefined
232
+ remainingSort as OrderByTuple<Extract<keyof M, string> | (string & {})>[] | undefined
232
233
  );
233
234
  }
234
235
  }