@rebasepro/admin 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/{CollectionEditorDialog-CSheud_E.js → CollectionEditorDialog-BeS4lxGh.js} +3 -3
  2. package/dist/{CollectionEditorDialog-CSheud_E.js.map → CollectionEditorDialog-BeS4lxGh.js.map} +1 -1
  3. package/dist/{PropertyEditView-CWZlC1gq.js → PropertyEditView-BFJ6H9-Q.js} +2 -2
  4. package/dist/{PropertyEditView-CWZlC1gq.js.map → PropertyEditView-BFJ6H9-Q.js.map} +1 -1
  5. package/dist/{RouterCollectionsStudioView-CwTpdzWF.js → RouterCollectionsStudioView-sfsUpEJz.js} +4 -4
  6. package/dist/{RouterCollectionsStudioView-CwTpdzWF.js.map → RouterCollectionsStudioView-sfsUpEJz.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-KEf57Jso.js} +2 -2
  11. package/dist/{export-L53WektO.js.map → export-KEf57Jso.js.map} +1 -1
  12. package/dist/{history-D5SKygpJ.js → history-Ctd2cRn_.js} +2 -2
  13. package/dist/{history-D5SKygpJ.js.map → history-Ctd2cRn_.js.map} +1 -1
  14. package/dist/{import-C_4edkoD.js → import-BujhycMq.js} +2 -2
  15. package/dist/{import-C_4edkoD.js.map → import-BujhycMq.js.map} +1 -1
  16. package/dist/index.js +6 -6
  17. package/dist/{util-C-D5yD2n.js → util-bL-fk6C6.js} +217 -92
  18. package/dist/util-bL-fk6C6.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 +104 -39
  22. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +19 -8
  23. package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +11 -8
  24. package/src/components/CollectionViewBinding/FilterPresetsButton.tsx +6 -5
  25. package/src/components/CollectionViewBinding/SortButton.tsx +191 -38
  26. package/src/components/DetailViewBinding.tsx +6 -1
  27. package/src/components/SelectableTable/SelectableTable.tsx +1 -1
  28. package/dist/util-C-D5yD2n.js.map +0 -1
@@ -2,7 +2,7 @@ import * as React$1 from "react";
2
2
  import React, { Suspense, createContext, createElement, forwardRef, lazy, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { deepEqual } from "fast-equals";
4
4
  import { Alert, AlertCircleIcon, AlertTriangleIcon, AlignLeftIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightToLineIcon, ArrowUpDownIcon, ArrowUpIcon, Badge, BooleanSwitchWithLabel, Button, CHIP_COLORS, CHIP_SEED_KEYS, CalendarIcon, Card, CardView, CenteredView, CheckCircleIcon, CheckIcon, Checkbox, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, Chip, CircleIcon, CircularProgress, CircularProgressCenter, CodeIcon, Collapse, ColumnsIcon, CopyIcon, DateTimeField, Dialog, DialogActions, DialogContent, DialogTitle, ErrorBoundary, ExpandablePanel, ExternalLinkIcon, EyeIcon, EyeOffIcon, FileTextIcon, FileUpload, FilterChip, FilterIcon, FilterXIcon, FlagIcon, FolderUpIcon, FunctionSquareIcon, GlobeIcon, HandleIcon, HashIcon, HistoryIcon, IconButton, ImageOffIcon, KanbanIcon, KanbanView, KeyRoundIcon, Label, LayoutGridIcon, Link2Icon, LinkIcon, ListIcon, ListOrderedIcon, ListView, LoadingButton, LucideIconByName, MailIcon, Markdown, Maximize2Icon, Menu, MenuItem, MinusIcon, MoreVerticalIcon, MultiSelect, MultiSelectItem, PanelLeftCloseIcon, PanelLeftOpenIcon, PencilIcon, PlusIcon, Popover, PopoverPrimitive, Portal, QuoteIcon, RadioGroup, RadioGroupItem, RefreshCwIcon, RepeatIcon, ResizablePanels, Rows3Icon, SearchBar, SearchIcon, Select, SelectItem, Separator, SettingsIcon, Sheet, Skeleton, Tab, Table, TableBody, TableCell, TableHeader, TableIcon, TableRow, Tabs, TextField, TextIcon, TextareaAutosize, ToggleButtonGroup, Tooltip, Trash2Icon, Typography, Unlink2Icon, UploadIcon, UserIcon, VirtualTable, VirtualTableDateField, VirtualTableInput, VirtualTableInput as VirtualTableInput$1, VirtualTableNumberInput, VirtualTableSwitch, VoteIcon, XCircleIcon, XIcon, cls, coolIconKeys, createVirtualTableSelectionStore, debounce, defaultBorderMixin, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, fieldBackgroundInvisibleMixin, fieldBackgroundMixin, focusedDisabled, getColorSchemeForSeed, iconSize, paperMixin, useDebouncedCallback, useOutsideAlerter, usePortalContainer } from "@rebasepro/ui";
5
- import { CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, canReadCollection, enumToObjectEntries, getArrayResolvedProperties, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEntityChildViews, getLabelOrConfigFrom, getReferenceFrom, getRelationFrom, getRelationTargetPath, getSubcollections, getTableName, isPropertyBuilder, normalizeToEntityRelation, resolveArrayProperties, resolveEnumValues, resolveProperty, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource } from "@rebasepro/common";
5
+ import { CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, canReadCollection, enumToObjectEntries, getArrayResolvedProperties, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEntityChildViews, getLabelOrConfigFrom, getReferenceFrom, getRelationFrom, getRelationTargetPath, getSubcollections, getTableName, isPropertyBuilder, normalizeOrderBy, normalizeToEntityRelation, resolveArrayProperties, resolveEnumValues, resolveProperty, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, serializeOrderBy } from "@rebasepro/common";
6
6
  import { CONTAINER_FULL_WIDTH, CollectionScopeProvider, DEFAULT_PAGE_SIZE, EntityDisplayCache, ErrorTooltip, ErrorView, IconForView, PluginProviderStack, SIDE_PANEL_DEFAULT_WIDTH, StorageSourceContext, UnsavedChangesDialog, UserDisplay, addInitialSlash, apiBaseOf, buildEnumLabel, createFormexStub, deleteEntityWithCallbacks, entityDisplayKey, flattenKeys, getAdminEntityChildViews, getCollectionBySlugWithin, getCollectionPathsCombinations, getColorScheme, getColumnKeysForProperty, getDisplayPropertyKey, getDisplayResolver, getEntityFromCache, getEntityFromMemoryCache, getEntityImagePreviewPropertyKey, getEntityPreviewKeys, getFormFieldKeys, getIcon, getLastSegment as getLastSegment$1, getLeadingRelationTitleKey, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getPropertyInPath, getRedundantChildViewColumnIds, getRelationIncludeParams, getRowHeight, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, hasDeclaredDisplay, iconsSearch, isDisabled, isFilterableRelation, isHidden, isReadOnly, looksLikeIdentifierValue, removeEntityFromCache, removeEntityFromMemoryCache, removeInitialAndTrailingSlashes, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1, removeInitialSlash, removeTrailingSlash, removeTrailingSlash as removeTrailingSlash$1, resolveCollectionPathIds, resolveCollectionPathIds as resolveCollectionPathIds$1, resolveComponentRef, resolveDefaultSelectedView, resolveFilterOperators, resolveFormLayout, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAnalyticsController, useApiBase, useApiConfig, useAuthController, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataTableController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseClient, useRebaseContext, useRelationSelector, useResolvedComponent, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence } from "@rebasepro/app";
7
7
  import { Field, Formex, getIn, setIn, useCreateFormex, useFormex } from "@rebasepro/forms";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -9318,8 +9318,8 @@ function EditorCollectionAction({ path, parentCollectionSlugs, parentEntityIds,
9318
9318
  }
9319
9319
  //#endregion
9320
9320
  //#region src/components/CollectionViewBinding/CollectionViewActions.tsx
9321
- var ImportCollectionAction = lazy(() => import("./import-C_4edkoD.js").then((n) => n.t).then((m) => ({ default: m.ImportCollectionAction })));
9322
- var ExportCollectionAction = lazy(() => import("./export-L53WektO.js").then((n) => n.t).then((m) => ({ default: m.ExportCollectionAction })));
9321
+ var ImportCollectionAction = lazy(() => import("./import-BujhycMq.js").then((n) => n.t).then((m) => ({ default: m.ImportCollectionAction })));
9322
+ var ExportCollectionAction = lazy(() => import("./export-KEf57Jso.js").then((n) => n.t).then((m) => ({ default: m.ExportCollectionAction })));
9323
9323
  function CollectionViewActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, onNewClick, onAddExistingClick, onMultipleDeleteClick, selectionEnabled, path, selectionController, tableController, collectionEntitiesCount, compact, children, openNewDocument }) {
9324
9324
  const context = useAdminContext();
9325
9325
  const { canCreate, canDelete } = usePermissions();
@@ -10397,11 +10397,8 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10397
10397
  columnMode,
10398
10398
  slotKeys.relationKeys
10399
10399
  ]);
10400
- /** The property the sort names, when no cell already shows it. */
10401
- const sortedKey = useMemo(() => {
10402
- const key = sortBy?.[0];
10403
- return key && !shownKeys.has(key) ? key : void 0;
10404
- }, [sortBy, shownKeys]);
10400
+ /** The properties the sort names, minus the ones a cell already shows. */
10401
+ const sortedKeys = useMemo(() => (sortBy ?? []).map(([key]) => key).filter((key) => !shownKeys.has(key)), [sortBy, shownKeys]);
10405
10402
  /** The properties the filters name, minus the ones a cell already shows. */
10406
10403
  const filteredKeys = useMemo(() => Object.keys(filterValues ?? {}).filter((key) => !shownKeys.has(key)), [filterValues, shownKeys]);
10407
10404
  /**
@@ -10413,22 +10410,23 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10413
10410
  * and clearing a sort says nothing about wanting to stop seeing the property
10414
10411
  * it sorted on.
10415
10412
  *
10416
- * What it does *not* do is accumulate. There is only ever one sort, so
10417
- * ordering by four properties in turn is changing one's mind four times, not
10418
- * asking for four columns — and asking for four is how the row lost its
10419
- * status and its date to a wall of numbers. A new sort therefore replaces
10420
- * the property the last one left behind, and a new set of filters replaces
10421
- * the set before it. At most one sort column and one set of filter columns
10422
- * outlive their request.
10413
+ * What it does *not* do is accumulate. A sort names the properties it names
10414
+ * *now*, so ordering by four properties in turn is changing one's mind four
10415
+ * times, not asking for four columns — and asking for four is how the row
10416
+ * lost its status and its date to a wall of numbers. A new sort therefore
10417
+ * replaces the properties the last one left behind, and a new set of filters
10418
+ * replaces the set before it. At most one sort's columns and one set of
10419
+ * filter columns outlive their request.
10423
10420
  *
10424
10421
  * The list remounts per collection (it is keyed by path), so this is the
10425
10422
  * memory of one visit to one collection, not a preference.
10426
10423
  */
10427
- const [lastSortedKey, setLastSortedKey] = useState(void 0);
10424
+ const [lastSortedKeys, setLastSortedKeys] = useState([]);
10428
10425
  const [lastFilteredKeys, setLastFilteredKeys] = useState([]);
10429
10426
  useEffect(() => {
10430
- if (sortedKey) setLastSortedKey(sortedKey);
10431
- }, [sortedKey]);
10427
+ if (sortedKeys.length === 0) return;
10428
+ setLastSortedKeys((previous) => previous.length === sortedKeys.length && previous.every((key, i) => key === sortedKeys[i]) ? previous : sortedKeys);
10429
+ }, [sortedKeys]);
10432
10430
  useEffect(() => {
10433
10431
  if (filteredKeys.length === 0) return;
10434
10432
  setLastFilteredKeys((previous) => previous.length === filteredKeys.length && previous.every((key, i) => key === filteredKeys[i]) ? previous : filteredKeys);
@@ -10442,14 +10440,14 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10442
10440
  * their own order, then the sort's.
10443
10441
  */
10444
10442
  const requestedColumns = useMemo(() => {
10445
- const active = /* @__PURE__ */ new Set([...filteredKeys, ...sortedKey ? [sortedKey] : []]);
10443
+ const active = /* @__PURE__ */ new Set([...filteredKeys, ...sortedKeys]);
10446
10444
  const keys = [];
10447
10445
  const request = (key) => {
10448
10446
  if (!key || keys.includes(key) || shownKeys.has(key)) return;
10449
10447
  keys.push(key);
10450
10448
  };
10451
10449
  (filteredKeys.length > 0 ? filteredKeys : lastFilteredKeys).forEach(request);
10452
- request(sortedKey ?? lastSortedKey);
10450
+ (sortedKeys.length > 0 ? sortedKeys : lastSortedKeys).forEach(request);
10453
10451
  return keys.flatMap((key, index) => {
10454
10452
  const property = getResolvedPropertyInPath(resolvedCollection.properties, key);
10455
10453
  if (!property) return [];
@@ -10465,9 +10463,9 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10465
10463
  });
10466
10464
  }, [
10467
10465
  filteredKeys,
10468
- sortedKey,
10466
+ sortedKeys,
10469
10467
  lastFilteredKeys,
10470
- lastSortedKey,
10468
+ lastSortedKeys,
10471
10469
  shownKeys,
10472
10470
  resolvedCollection,
10473
10471
  sortableKeys
@@ -10559,20 +10557,41 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10559
10557
  const handleRowSelectionChange = useCallback((entity, selected) => {
10560
10558
  handleSelectionChange(entity, selected);
10561
10559
  }, [handleSelectionChange]);
10560
+ /** Where each sorted column sits in the order, so a header can show its rank. */
10561
+ const sortIndex = useMemo(() => {
10562
+ const index = /* @__PURE__ */ new Map();
10563
+ (sortBy ?? []).forEach(([key, direction], position) => {
10564
+ if (!index.has(key)) index.set(key, {
10565
+ direction,
10566
+ position
10567
+ });
10568
+ });
10569
+ return index;
10570
+ }, [sortBy]);
10562
10571
  /**
10563
10572
  * Order by a column, on the same cycle a table header runs: unordered →
10564
- * ascending → descending → unordered. Clicking a header here and clicking it
10573
+ * ascending → descending → unordered, and shift-click to add the column
10574
+ * under the sort already in place. Clicking a header here and clicking it
10565
10575
  * in the table view have to mean the same thing, because they are the same
10566
10576
  * collection under the same controller.
10567
10577
  */
10568
- const onColumnSort = useCallback((key) => {
10578
+ const onColumnSort = useCallback((key, additive = false) => {
10569
10579
  if (!setSortBy) return;
10570
- const active = sortBy?.[0] === key ? sortBy[1] : void 0;
10571
- setSortBy(active === "asc" ? [key, "desc"] : active === "desc" ? void 0 : [key, "asc"]);
10580
+ const active = sortIndex.get(key)?.direction;
10581
+ const next = active === "asc" ? "desc" : active === "desc" ? void 0 : "asc";
10582
+ const existing = sortBy ?? [];
10583
+ let updated;
10584
+ if (!additive) updated = next ? [[key, next]] : void 0;
10585
+ else if (next === void 0) updated = existing.filter(([existingKey]) => existingKey !== key);
10586
+ else if (active === void 0) updated = [...existing, [key, next]];
10587
+ else updated = existing.map((entry) => entry[0] === key ? [key, next] : entry);
10588
+ if (updated?.length === 0) updated = void 0;
10589
+ setSortBy(updated);
10572
10590
  setItemCount?.(pageSize);
10573
10591
  }, [
10574
10592
  setSortBy,
10575
10593
  sortBy,
10594
+ sortIndex,
10576
10595
  setItemCount,
10577
10596
  pageSize
10578
10597
  ]);
@@ -10585,14 +10604,14 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10585
10604
  const sortIsAvailable = useCallback((key) => {
10586
10605
  if (!setSortBy) return false;
10587
10606
  if (!checkFilterCombination) return true;
10588
- const active = sortBy?.[0] === key ? sortBy[1] : void 0;
10607
+ const active = sortIndex.get(key)?.direction;
10589
10608
  if (active === "desc") return true;
10590
- return checkFilterCombination(filterValues ?? {}, [key, active === "asc" ? "desc" : "asc"]);
10609
+ return checkFilterCombination(filterValues ?? {}, [[key, active === "asc" ? "desc" : "asc"]]);
10591
10610
  }, [
10592
10611
  setSortBy,
10593
10612
  checkFilterCombination,
10594
10613
  filterValues,
10595
- sortBy
10614
+ sortIndex
10596
10615
  ]);
10597
10616
  return /* @__PURE__ */ jsx("div", {
10598
10617
  ref: containerRef,
@@ -10619,7 +10638,7 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10619
10638
  selectionEnabled: selectionEnabled && !combineSelection,
10620
10639
  showImage,
10621
10640
  actionsWidth,
10622
- sortBy,
10641
+ sortIndex,
10623
10642
  onColumnSort,
10624
10643
  sortIsAvailable
10625
10644
  }),
@@ -10926,11 +10945,12 @@ function EmptyCell() {
10926
10945
  * against the same widths the rows do, and skips the checkbox and the image —
10927
10946
  * neither is a column, and neither has anything to be called.
10928
10947
  */
10929
- function ListHeader({ titleColumn, columns, selectionEnabled, showImage, actionsWidth, sortBy, onColumnSort, sortIsAvailable }) {
10948
+ function ListHeader({ titleColumn, columns, selectionEnabled, showImage, actionsWidth, sortIndex, onColumnSort, sortIsAvailable }) {
10930
10949
  const headerCell = (column) => /* @__PURE__ */ jsx(ListHeaderLabel, {
10931
10950
  column,
10932
- direction: sortBy?.[0] === column.key ? sortBy[1] : void 0,
10933
- onSort: column.sortable && sortIsAvailable(column.key) ? () => onColumnSort(column.key) : void 0
10951
+ direction: sortIndex.get(column.key)?.direction,
10952
+ position: sortIndex.size > 1 ? sortIndex.get(column.key)?.position : void 0,
10953
+ onSort: column.sortable && sortIsAvailable(column.key) ? (additive) => onColumnSort(column.key, additive) : void 0
10934
10954
  });
10935
10955
  return /* @__PURE__ */ jsxs("div", {
10936
10956
  className: cls("flex items-center gap-4 px-5 py-1.5 select-none border-b bg-surface-50 dark:bg-surface-900", defaultBorderMixin),
@@ -10962,7 +10982,8 @@ function ListHeader({ titleColumn, columns, selectionEnabled, showImage, actions
10962
10982
  * in a header row are three invitations, and only one of them is ever the
10963
10983
  * answer to "how is this sorted".
10964
10984
  */
10965
- function ListHeaderLabel({ column, direction, onSort }) {
10985
+ function ListHeaderLabel({ column, direction, position, onSort }) {
10986
+ const { t } = useTranslation();
10966
10987
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
10967
10988
  /* @__PURE__ */ jsx("span", {
10968
10989
  className: "truncate",
@@ -10975,6 +10996,10 @@ function ListHeaderLabel({ column, direction, onSort }) {
10975
10996
  direction === "desc" && /* @__PURE__ */ jsx(ArrowDownIcon, {
10976
10997
  size: 12,
10977
10998
  className: "flex-shrink-0"
10999
+ }),
11000
+ direction && position !== void 0 && /* @__PURE__ */ jsx("span", {
11001
+ className: "flex-shrink-0 text-[9px] font-bold leading-none tabular-nums",
11002
+ children: position + 1
10978
11003
  })
10979
11004
  ] });
10980
11005
  const base = "inline-flex items-center gap-1 max-w-full text-[11px] leading-none uppercase tracking-wider font-medium";
@@ -10983,10 +11008,14 @@ function ListHeaderLabel({ column, direction, onSort }) {
10983
11008
  className: cls(base, tone),
10984
11009
  children: content
10985
11010
  });
11011
+ const nextAction = direction === "asc" ? t("sort_descending") : direction === "desc" ? t("sort_remove") : t("sort_ascending");
11012
+ const rank = direction && position !== void 0 ? ` (${t("sort_key_position", { position: position + 1 })})` : "";
11013
+ const label = `${column.label}${rank} — ${nextAction}. ${t("sort_shift_click_hint")}`;
10986
11014
  return /* @__PURE__ */ jsx("button", {
10987
11015
  type: "button",
10988
- title: `Sort by ${column.label}`,
10989
- onClick: onSort,
11016
+ title: label,
11017
+ "aria-label": label,
11018
+ onClick: (event) => onSort(event.shiftKey),
10990
11019
  className: cls(base, tone, "cursor-pointer hover:text-surface-700 dark:hover:text-surface-200 transition-colors"),
10991
11020
  children: content
10992
11021
  });
@@ -11644,7 +11673,7 @@ function JsonPreviewBinding({ values }) {
11644
11673
  }
11645
11674
  //#endregion
11646
11675
  //#region src/components/EntityInspector.tsx
11647
- var EntityHistoryView = lazy(() => import("./history-D5SKygpJ.js").then((m) => ({ default: m.EntityHistoryView })));
11676
+ var EntityHistoryView = lazy(() => import("./history-Ctd2cRn_.js").then((m) => ({ default: m.EntityHistoryView })));
11648
11677
  /**
11649
11678
  * Raw values and revision history, as an inspector rather than as tabs.
11650
11679
  *
@@ -11954,7 +11983,7 @@ function DetailViewBindingInner({ path, entityId, selectedTab: selectedTabProp,
11954
11983
  className: "flex items-center justify-center w-full h-full p-3",
11955
11984
  children: /* @__PURE__ */ jsx(Typography, {
11956
11985
  variant: "label",
11957
- children: t("save_entity_before_subcollections") ?? "You need to save your entity before adding additional collections"
11986
+ children: t("save_entity_before_subcollections")
11958
11987
  })
11959
11988
  }))]
11960
11989
  }, `subcol_${subcollectionId}`);
@@ -15101,13 +15130,13 @@ function FilterPresetsButton({ filterPresets, tableController, compact }) {
15101
15130
  tableController.setSortBy?.(void 0);
15102
15131
  } else {
15103
15132
  tableController.setFilterValues?.(currentFilters);
15104
- if (!wasActive && preset.sort) tableController.setSortBy?.(preset.sort);
15133
+ if (!wasActive && preset.sort) tableController.setSortBy?.(normalizeOrderBy(preset.sort));
15105
15134
  else if (wasActive) {
15106
15135
  let remainingSort;
15107
15136
  for (let i = 0; i < filterPresets.length; i++) {
15108
15137
  if (i === index) continue;
15109
15138
  const other = filterPresets[i];
15110
- if (isPresetActive(other, currentFilters) && other.sort) remainingSort = other.sort;
15139
+ if (isPresetActive(other, currentFilters) && other.sort) remainingSort = normalizeOrderBy(other.sort);
15111
15140
  }
15112
15141
  tableController.setSortBy?.(remainingSort);
15113
15142
  }
@@ -15153,59 +15182,100 @@ function FilterPresetsButton({ filterPresets, tableController, compact }) {
15153
15182
  //#endregion
15154
15183
  //#region src/components/CollectionViewBinding/SortButton.tsx
15155
15184
  /**
15156
- * Toolbar control that orders the collection by one property.
15185
+ * Toolbar control that orders the collection.
15157
15186
  *
15158
15187
  * The table orders by clicking a column header, which leaves every other view
15159
15188
  * mode — list, cards, and either of them inside a split view — with no way to
15160
15189
  * order at all, even though the controller has always carried `sortBy` and the
15161
15190
  * query has always honoured it. This binds a control to it.
15162
15191
  *
15163
- * Clicking the active property flips its direction, so ascending descending
15164
- * is one click rather than a trip through a direction selector; clicking a
15165
- * different one starts it ascending. The popover stays open through both.
15192
+ * It edits the *whole* order rather than a single key. A sort can have several
15193
+ * keys, applied in the order shown: the second decides between rows the first
15194
+ * calls equal, the third between rows the first two do. So the active keys sit
15195
+ * at the top, numbered and movable, and the properties below them are the ones
15196
+ * that can still be added under the last. Shift-clicking a column header does
15197
+ * the same thing from the table; this is the view for the modes that have no
15198
+ * headers, and the only place the order between keys can be changed.
15199
+ *
15200
+ * Clicking an active key flips its direction, so ascending → descending is one
15201
+ * click rather than a trip through a direction selector; clicking an inactive
15202
+ * one appends it ascending. The popover stays open through all of it — building
15203
+ * a two-key order out of a control that closes on every click is four round
15204
+ * trips through a button.
15166
15205
  */
15167
15206
  function SortButton({ tableController, properties, compact }) {
15168
15207
  const { t } = useTranslation();
15169
15208
  const [open, setOpen] = useState(false);
15170
15209
  const options = useMemo(() => getSortablePropertyOptions(properties), [properties]);
15171
15210
  const { sortBy, setSortBy, setItemCount, pageSize, filterValues, checkFilterCombination } = tableController;
15172
- const activeKey = sortBy?.[0];
15173
- const activeDirection = sortBy?.[1];
15211
+ const keys = useMemo(() => sortBy ?? [], [sortBy]);
15212
+ const activeIndex = useMemo(() => {
15213
+ const index = /* @__PURE__ */ new Map();
15214
+ keys.forEach(([key, direction], position) => {
15215
+ if (!index.has(key)) index.set(key, {
15216
+ direction,
15217
+ position
15218
+ });
15219
+ });
15220
+ return index;
15221
+ }, [keys]);
15174
15222
  const applySort = useCallback((next) => {
15175
- setSortBy?.(next);
15223
+ setSortBy?.(next && next.length > 0 ? next : void 0);
15176
15224
  if (pageSize !== void 0) setItemCount?.(pageSize);
15177
15225
  }, [
15178
15226
  setSortBy,
15179
15227
  setItemCount,
15180
15228
  pageSize
15181
15229
  ]);
15230
+ /** Flip an active key's direction, or append an inactive one ascending. */
15182
15231
  const onOptionClick = useCallback((key) => {
15183
- if (key === activeKey) applySort([key, activeDirection === "asc" ? "desc" : "asc"]);
15184
- else applySort([key, "asc"]);
15232
+ const active = activeIndex.get(key);
15233
+ if (!active) {
15234
+ applySort([...keys, [key, "asc"]]);
15235
+ return;
15236
+ }
15237
+ applySort(keys.map((entry) => entry[0] === key ? [key, active.direction === "asc" ? "desc" : "asc"] : entry));
15185
15238
  }, [
15186
- activeKey,
15187
- activeDirection,
15188
- applySort
15239
+ activeIndex,
15240
+ applySort,
15241
+ keys
15189
15242
  ]);
15243
+ const removeKey = useCallback((key) => {
15244
+ applySort(keys.filter(([existing]) => existing !== key));
15245
+ }, [applySort, keys]);
15246
+ /** What a click on an active key will do to it, for the tooltip and the label. */
15247
+ const flipLabel = useCallback((direction) => direction === "asc" ? t("sort_descending") : t("sort_ascending"), [t]);
15248
+ /** Swap a key with its neighbour, which is what changes which one decides. */
15249
+ const moveKey = useCallback((position, delta) => {
15250
+ const target = position + delta;
15251
+ if (target < 0 || target >= keys.length) return;
15252
+ const next = [...keys];
15253
+ [next[position], next[target]] = [next[target], next[position]];
15254
+ applySort(next);
15255
+ }, [applySort, keys]);
15190
15256
  if (!setSortBy || options.length === 0) return null;
15191
- const activeOption = options.find((option) => option.key === activeKey);
15192
- const label = activeOption ? `${t("sort_by")}: ${activeOption.title}` : t("sort");
15257
+ const primary = keys[0];
15258
+ const primaryTitle = primary ? options.find((option) => option.key === primary[0])?.title ?? primary[0] : void 0;
15259
+ const label = primaryTitle ? keys.length > 1 ? `${t("sort_by")}: ${primaryTitle} +${keys.length - 1}` : `${t("sort_by")}: ${primaryTitle}` : t("sort");
15193
15260
  const arrowSize = compact ? iconSize.smallest : iconSize.small;
15261
+ const primaryDirection = primary?.[1];
15262
+ const trigger = /* @__PURE__ */ jsx(IconButton, {
15263
+ size: "small",
15264
+ "aria-label": label,
15265
+ title: label,
15266
+ className: cls(primaryTitle && "text-primary"),
15267
+ children: primaryDirection === "desc" ? /* @__PURE__ */ jsx(ArrowDownIcon, { size: arrowSize }) : primaryDirection === "asc" ? /* @__PURE__ */ jsx(ArrowUpIcon, { size: arrowSize }) : /* @__PURE__ */ jsx(ArrowUpDownIcon, { size: arrowSize })
15268
+ });
15269
+ const inactiveOptions = options.filter((option) => !activeIndex.has(option.key));
15194
15270
  return /* @__PURE__ */ jsx(Popover, {
15195
15271
  open,
15196
15272
  onOpenChange: setOpen,
15197
15273
  modal: false,
15198
15274
  side: "bottom",
15199
15275
  align: "start",
15200
- trigger: /* @__PURE__ */ jsx(IconButton, {
15201
- size: "small",
15202
- "aria-label": label,
15203
- title: label,
15204
- className: cls(activeOption && "text-primary"),
15205
- children: activeDirection === "desc" ? /* @__PURE__ */ jsx(ArrowDownIcon, { size: arrowSize }) : activeDirection === "asc" ? /* @__PURE__ */ jsx(ArrowUpIcon, { size: arrowSize }) : /* @__PURE__ */ jsx(ArrowUpDownIcon, { size: arrowSize })
15206
- }),
15276
+ trigger,
15207
15277
  children: /* @__PURE__ */ jsxs("div", {
15208
- className: "py-2 min-w-[240px] flex flex-col",
15278
+ className: "py-2 min-w-[280px] max-w-[360px] flex flex-col",
15209
15279
  children: [
15210
15280
  /* @__PURE__ */ jsx(Typography, {
15211
15281
  variant: "caption",
@@ -15213,42 +15283,99 @@ function SortButton({ tableController, properties, compact }) {
15213
15283
  className: "font-medium uppercase tracking-wider px-3 pb-1",
15214
15284
  children: t("sort_by")
15215
15285
  }),
15216
- /* @__PURE__ */ jsx("div", {
15217
- className: "max-h-[320px] overflow-y-auto flex flex-col",
15218
- children: options.map(({ key, title, property }) => {
15219
- const active = key === activeKey;
15220
- const disabled = checkFilterCombination ? !checkFilterCombination(filterValues ?? {}, [key, active && activeDirection === "asc" ? "desc" : "asc"]) : false;
15221
- return /* @__PURE__ */ jsxs("button", {
15222
- type: "button",
15223
- role: "menuitemradio",
15224
- "aria-checked": active,
15225
- disabled,
15226
- onClick: () => onOptionClick(key),
15227
- className: cls("flex items-center gap-2 px-3 py-1.5 text-sm text-left w-full", disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:bg-surface-accent-100 dark:hover:bg-surface-accent-900", active && "text-primary"),
15286
+ keys.length > 0 && /* @__PURE__ */ jsx("div", {
15287
+ className: "flex flex-col max-h-[200px] overflow-y-auto",
15288
+ children: keys.map(([key, direction], position) => {
15289
+ const option = options.find((o) => o.key === key);
15290
+ return /* @__PURE__ */ jsxs("div", {
15291
+ className: "flex items-center gap-1 pl-3 pr-2 py-1 text-sm text-primary",
15228
15292
  children: [
15229
15293
  /* @__PURE__ */ jsx("span", {
15230
- className: "flex-shrink-0 flex items-center",
15231
- children: getIconForProperty(property, "smallest")
15294
+ className: "flex-shrink-0 w-4 text-xs tabular-nums text-text-secondary dark:text-text-secondary-dark",
15295
+ children: position + 1
15232
15296
  }),
15233
- /* @__PURE__ */ jsx("span", {
15234
- className: "flex-grow truncate",
15235
- children: title
15297
+ /* @__PURE__ */ jsxs("button", {
15298
+ type: "button",
15299
+ onClick: () => onOptionClick(key),
15300
+ title: flipLabel(direction),
15301
+ "aria-label": `${option?.title ?? key} — ${t("sort_key_position", { position: position + 1 })}. ${flipLabel(direction)}`,
15302
+ className: "flex items-center gap-2 flex-grow min-w-0 text-left cursor-pointer",
15303
+ children: [
15304
+ option && /* @__PURE__ */ jsx("span", {
15305
+ className: "flex-shrink-0 flex items-center",
15306
+ children: getIconForProperty(option.property, "smallest")
15307
+ }),
15308
+ /* @__PURE__ */ jsx("span", {
15309
+ className: "flex-grow truncate",
15310
+ children: option?.title ?? key
15311
+ }),
15312
+ /* @__PURE__ */ jsx("span", {
15313
+ className: "flex-shrink-0 flex items-center",
15314
+ children: direction === "desc" ? /* @__PURE__ */ jsx(ArrowDownIcon, { size: iconSize.smallest }) : /* @__PURE__ */ jsx(ArrowUpIcon, { size: iconSize.smallest })
15315
+ })
15316
+ ]
15236
15317
  }),
15237
- active && /* @__PURE__ */ jsx("span", {
15238
- className: "flex-shrink-0 flex items-center",
15239
- children: activeDirection === "desc" ? /* @__PURE__ */ jsx(ArrowDownIcon, { size: iconSize.smallest }) : /* @__PURE__ */ jsx(ArrowUpIcon, { size: iconSize.smallest })
15318
+ /* @__PURE__ */ jsxs("div", {
15319
+ className: cls("flex-shrink-0 flex items-center gap-0.5 ml-1 pl-1 border-l", defaultBorderMixin),
15320
+ children: [keys.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(IconButton, {
15321
+ size: "smallest",
15322
+ "aria-label": t("sort_move_up"),
15323
+ title: t("sort_move_up"),
15324
+ disabled: position === 0,
15325
+ onClick: () => moveKey(position, -1),
15326
+ children: /* @__PURE__ */ jsx(ChevronUpIcon, { size: iconSize.smallest })
15327
+ }), /* @__PURE__ */ jsx(IconButton, {
15328
+ size: "smallest",
15329
+ "aria-label": t("sort_move_down"),
15330
+ title: t("sort_move_down"),
15331
+ disabled: position === keys.length - 1,
15332
+ onClick: () => moveKey(position, 1),
15333
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: iconSize.smallest })
15334
+ })] }), /* @__PURE__ */ jsx(IconButton, {
15335
+ size: "smallest",
15336
+ "aria-label": t("sort_remove_key"),
15337
+ title: t("sort_remove_key"),
15338
+ onClick: () => removeKey(key),
15339
+ children: /* @__PURE__ */ jsx(XIcon, { size: iconSize.smallest })
15340
+ })]
15240
15341
  })
15241
15342
  ]
15242
15343
  }, key);
15243
15344
  })
15244
15345
  }),
15346
+ keys.length > 0 && inactiveOptions.length > 0 && /* @__PURE__ */ jsx(Typography, {
15347
+ variant: "caption",
15348
+ color: "secondary",
15349
+ className: "font-medium uppercase tracking-wider px-3 pt-2 pb-1",
15350
+ children: t("sort_then_by")
15351
+ }),
15352
+ /* @__PURE__ */ jsx("div", {
15353
+ className: "max-h-[280px] overflow-y-auto flex flex-col",
15354
+ children: inactiveOptions.map(({ key, title, property }) => {
15355
+ const disabled = checkFilterCombination ? !checkFilterCombination(filterValues ?? {}, [...keys, [key, "asc"]]) : false;
15356
+ return /* @__PURE__ */ jsxs("button", {
15357
+ type: "button",
15358
+ "aria-label": keys.length > 0 ? `${t("sort_then_by")} ${title}` : `${t("sort_by")} ${title}`,
15359
+ disabled,
15360
+ onClick: () => onOptionClick(key),
15361
+ className: cls("flex items-center gap-2 px-3 py-1.5 text-sm text-left w-full", disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:bg-surface-accent-100 dark:hover:bg-surface-accent-900"),
15362
+ children: [/* @__PURE__ */ jsx("span", {
15363
+ className: "flex-shrink-0 flex items-center",
15364
+ children: getIconForProperty(property, "smallest")
15365
+ }), /* @__PURE__ */ jsx("span", {
15366
+ className: "flex-grow truncate",
15367
+ children: title
15368
+ })]
15369
+ }, key);
15370
+ })
15371
+ }),
15245
15372
  /* @__PURE__ */ jsx("div", {
15246
15373
  className: "px-3 pt-2",
15247
15374
  children: /* @__PURE__ */ jsx(Button, {
15248
15375
  variant: "text",
15249
15376
  size: "small",
15250
15377
  className: "w-full",
15251
- disabled: !activeOption,
15378
+ disabled: keys.length === 0,
15252
15379
  onClick: () => applySort(void 0),
15253
15380
  children: t("clear_sort")
15254
15381
  })
@@ -15320,7 +15447,7 @@ function CollectionViewStartActions({ collection, relativePath, parentCollection
15320
15447
  })
15321
15448
  })
15322
15449
  }, "filters_tooltip");
15323
- const sortButton = resolvedProperties && (viewMode === "list" || viewMode === "cards") && /* @__PURE__ */ jsx(SortButton, {
15450
+ const sortButton = resolvedProperties && viewMode !== "kanban" && /* @__PURE__ */ jsx(SortButton, {
15324
15451
  tableController,
15325
15452
  properties: resolvedProperties,
15326
15453
  compact: iconOnlyToolbar
@@ -16251,14 +16378,13 @@ function DefaultCollectionEmptyState({ canCreate, onNewClick, isSearching, searc
16251
16378
  }
16252
16379
  /**
16253
16380
  * Inflight count request deduplication map.
16254
- * Keyed by `path|filterKey|sortByProperty|sortDir` so that concurrent
16381
+ * Keyed by `path|filterKey|sort|search` so that concurrent
16255
16382
  * callers (e.g. React StrictMode double-mount) share the same promise.
16256
16383
  */
16257
16384
  var inflightCountRequests = /* @__PURE__ */ new Map();
16258
16385
  function EntitiesCount({ path, collection, filter, sortBy, searchString, onCountChange }) {
16259
16386
  const dataClient = useData();
16260
- const sortByProperty = sortBy ? sortBy[0] : void 0;
16261
- const currentSort = sortBy ? sortBy[1] : void 0;
16387
+ const sortKey = React.useMemo(() => sortBy ? serializeOrderBy(sortBy) ?? "" : "", [sortBy]);
16262
16388
  const dataClientRef = React.useRef(dataClient);
16263
16389
  dataClientRef.current = dataClient;
16264
16390
  const onCountChangeRef = React.useRef(onCountChange);
@@ -16272,8 +16398,8 @@ function EntitiesCount({ path, collection, filter, sortBy, searchString, onCount
16272
16398
  }
16273
16399
  let cancelled = false;
16274
16400
  const whereParams = filter && Object.keys(filter).length > 0 ? filter : void 0;
16275
- const orderByParams = sortByProperty && currentSort ? [String(sortByProperty), currentSort] : void 0;
16276
- const cacheKey = `${path}|${filterKey}|${sortByProperty ?? ""}|${currentSort ?? ""}|${searchString ?? ""}`;
16401
+ const orderByParams = sortBy && sortBy.length > 0 ? sortBy.map(([field, direction]) => [String(field), direction]) : void 0;
16402
+ const cacheKey = `${path}|${filterKey}|${sortKey}|${searchString ?? ""}`;
16277
16403
  let countPromise = inflightCountRequests.get(cacheKey);
16278
16404
  if (!countPromise) {
16279
16405
  countPromise = accessor.count({
@@ -16296,8 +16422,7 @@ function EntitiesCount({ path, collection, filter, sortBy, searchString, onCount
16296
16422
  }, [
16297
16423
  path,
16298
16424
  filterKey,
16299
- sortByProperty,
16300
- currentSort,
16425
+ sortKey,
16301
16426
  searchString
16302
16427
  ]);
16303
16428
  return null;
@@ -24441,4 +24566,4 @@ function getFullIdPath(propertyKey, propertyNamespace) {
24441
24566
  //#endregion
24442
24567
  export { getEntityViewWidth as $, SkeletonPropertyComponent as $n, resolveCollectionPathIds$1 as $t, SelectFieldBinding as A, ArrayOfStringsPreview as An, saveImportedEntities as At, useSelectionDialog as B, useSidePanel as Bn, FieldCaption as Bt, getDefaultFieldId as C, UserPreview as Cn, isReferenceProperty as Cr, convertDataToEntity as Ct, TextFieldBinding as D, KeyValuePreview as Dn, useImportConfig as Dt, VectorFieldBinding as E, DatePreview as En, getInferenceType as Et, MapFieldBinding as F, ArrayOfReferencesPreview as Fn, parseCsvToObjects as Ft, useTopLevelNavigation as G, getEntityPreviewKeys as Gn, mergeEntityActions as Gt, SideDialogs as H, useCollectionRegistryController as Hn, BreadcrumbsProvider as Ht, KeyValueFieldBinding as I, InlineEntityListPreview as In, unflattenObject as It, useBuildCollectionRegistryController as J, ArrayPropertyPreview as Jn, getCollectionPathsCombinations as Jt, useResolvedViews as K, getEntityTitlePropertyKey as Kn, addInitialSlash as Kt, DateTimeFieldBinding as L, ReferencePreview as Ln, ArrayContainer as Lt, ReferenceFieldBinding as M, ArrayEnumPreview as Mn, convertFileToJson as Mt, MultiSelectFieldBinding as N, ArrayOfStorageComponentsPreview as Nn, detectCsvDelimiter as Nt, SwitchFieldBinding as O, MapPropertyPreview as On, ImportSaveInProgress as Ot, MarkdownEditorFieldBinding as P, RelationPreview as Pn, parseCsvRows as Pt, buildSidePanelsFromUrl as Q, StorageThumbnailInternal as Qn, removeTrailingSlash$1 as Qt, BlockFieldBinding as R, EntityPreviewBinding as Rn, PropertyConfigBadge as Rt, getDefaultFieldConfig as S, PropertyPreview as Sn, getResolvedPropertyInPath as Sr, DataNewPropertiesMapping as St, getFieldId as T, BooleanPreview as Tn, processValueMapping as Tt, useBuildUrlController as U, getUserLabel as Un, resolveEntityAction as Ut, SelectionTableBinding as V, CollectionRegistryContext as Vn, useBreadcrumbsController as Vt, useBuildNavigationStateController as W, useResolvedUser as Wn, resolveEntityView as Wt, resolveNavigationFrom as X, EnumValuesChip as Xn, removeInitialAndTrailingSlashes$1 as Xt, useHistory as Y, StringPropertyPreview as Yn, getLastSegment$1 as Yt, useResolvedNavigationFrom as Z, StorageThumbnail as Zn, removeInitialSlash as Zt, useCollectionsConfigController as _, ReadOnlyFieldBinding as _n, getDefaultPropertiesOrder as _r, CollectionViewActions as _t, namespaceToPropertiesPath as a, SelectableTable as an, ImagePreview as ar, removeEmptyContainers as at, PropertyFieldBinding as b, FieldHelperText as bn, getPropertiesWithPropertiesOrder as br, useCollectionEditorDialogsState as bt, buildCollectionGenerationCallback as c, UrlContext as cn, FormSections as cr, copyEntityAction as ct, fromSerializableCollectionConfigs as d, useNavigationStateController as dn, FieldBlock as dr, resetPasswordAction as dt, resolveOpenEntityMode as en, renderSkeletonCaptionText as er, useBuildSidePanel as et, fromSerializableProperties as f, useSideDialogsController as fn, LABEL_ICON_SIZE as fr, CreationResultDialog as ft, toSerializableProperty as g, useClearRestoreValue as gn, getBracketNotation as gr, EntityCardBinding as gt, toSerializableProperties as h, ArrayCustomShapedFieldBinding as hn, PropertyIdCopyTooltip as hr, CollectionCardViewBinding as ht, namespaceToPropertiesOrderPath as i, CollectionTableBinding as in, UrlComponentPreview as ir, getInitialEntityValues as it, RepeatFieldBinding as j, ArrayPropertyEnumPreview as jn, ImportFileUpload as jt, StorageUploadFieldBinding as k, ArrayOneOfPreview as kn, IMPORT_BATCH_SIZE as kt, validateCollectionJson as l, useUrlController as ln, FormRail as lr, deleteEntityAction as lt, toSerializableCollectionConfig as m, SelectableTableContext as mn, spanClass as mr, EntityViewBinding as mt, getFullIdPath as n, useSelectionController as nn, renderSkeletonImageThumbnail as nr, extractTouchedValues as nt, CollectionGenerationApiError as o, CollectionRowActions as on, sanitizeUrl as or, zodToFormErrors as ot, fromSerializableProperty as p, SideDialogsControllerContext as pn, isSelfLabellingProperty as pr, DetailViewBinding as pt, useResolvedCollections as q, getEntityTitlePropertyKeyForEntity as qn, getCollectionBySlugWithin as qt, idToPropertiesPath as r, VirtualTableInput$1 as rn, renderSkeletonText as rr, getChanges as rt, DEFAULT_COLLECTION_GENERATION_ENDPOINT as s, useAdminContext as sn, EmptyValue as sr, CollectionViewBinding as st, getFullId as t, resolveViewMode as tn, renderSkeletonIcon as tr, EditViewBinding as tt, fromSerializableCollectionConfig as u, NavigationStateContext as un, RecordMeta as ur, editEntityAction as ut, EntityFormBinding as v, LabelWithIconAndTooltip as vn, getIconForProperty as vr, useCollectionEditorController as vt, getFieldConfig as w, NumberPropertyPreview as wn, isRelationProperty as wr, flattenEntry as wt, DEFAULT_FIELD_CONFIGS as x, ArrayOfMapsPreview as xn, getPropertyInPath$1 as xr, ImportNewPropertyFieldPreview as xt, EntityForm as y, LabelWithIcon as yn, getIconForWidget as yr, ConfigControllerProvider as yt, ArrayOfReferencesFieldBinding as z, SidePanelControllerContext as zn, SearchIconsView as zt };
24443
24568
 
24444
- //# sourceMappingURL=util-C-D5yD2n.js.map
24569
+ //# sourceMappingURL=util-bL-fk6C6.js.map