@rebasepro/cms 0.20.0 → 0.21.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.
Files changed (43) hide show
  1. package/dist/{CollectionEditorDialog-DQjLtp1W.js → CollectionEditorDialog-C-A_NCKB.js} +15 -14
  2. package/dist/CollectionEditorDialog-C-A_NCKB.js.map +1 -0
  3. package/dist/JsonPreviewBinding-v0w3H0fp.js +35 -0
  4. package/dist/JsonPreviewBinding-v0w3H0fp.js.map +1 -0
  5. package/dist/{PropertyEditView-BMCCD2OI.js → PropertyEditView-Dv7496fx.js} +2 -57
  6. package/dist/{PropertyEditView-BMCCD2OI.js.map → PropertyEditView-Dv7496fx.js.map} +1 -1
  7. package/dist/{RouterCollectionsStudioView-DwsbIAqr.js → RouterCollectionsStudioView-DuDFEYuL.js} +4 -4
  8. package/dist/{RouterCollectionsStudioView-DwsbIAqr.js.map → RouterCollectionsStudioView-DuDFEYuL.js.map} +1 -1
  9. package/dist/collection_editor/ui/collection_editor/templates/blog_template.d.ts +126 -2
  10. package/dist/collection_editor/ui/collection_editor/templates/pages_template.d.ts +201 -2
  11. package/dist/collection_editor/ui/collection_editor/templates/products_template.d.ts +92 -2
  12. package/dist/collection_editor/ui/collection_editor/templates/users_template.d.ts +43 -2
  13. package/dist/collection_editor_ui.js +4 -4
  14. package/dist/components/CollectionViewBinding/CollectionViewStartActions.d.ts +6 -2
  15. package/dist/components/CollectionViewBinding/EntityCardBinding.d.ts +11 -1
  16. package/dist/components/DeleteEntityDialog.d.ts +11 -3
  17. package/dist/data_export/export/ExportCollectionAction.d.ts +1 -1
  18. package/dist/data_export/export/fetch_export_data.d.ts +10 -16
  19. package/dist/editor/editor.d.ts +1 -6
  20. package/dist/editor.js +1 -1
  21. package/dist/editor.js.map +1 -1
  22. package/dist/{export-DJoGYb89.js → export-DtSmLT6u.js} +54 -42
  23. package/dist/export-DtSmLT6u.js.map +1 -0
  24. package/dist/form/EntityForm.d.ts +1 -1
  25. package/dist/{history-BBpsdTrJ.js → history-C6kUDQ3f.js} +2 -2
  26. package/dist/{history-BBpsdTrJ.js.map → history-C6kUDQ3f.js.map} +1 -1
  27. package/dist/{import-C4VQ0rwO.js → import-BciQBH5t.js} +2 -2
  28. package/dist/{import-C4VQ0rwO.js.map → import-BciQBH5t.js.map} +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +12 -12
  31. package/dist/index.js.map +1 -1
  32. package/dist/selection/SelectionMenu.d.ts +27 -0
  33. package/dist/selection/index.d.ts +2 -0
  34. package/dist/selection/resolve_selection.d.ts +62 -0
  35. package/dist/types/components/EntityFormProps.d.ts +13 -0
  36. package/dist/util/entity_pages.d.ts +36 -0
  37. package/dist/util/map_with_concurrency.d.ts +16 -0
  38. package/dist/{util-4oPxPkbp.js → util-h6jhiiBE.js} +674 -195
  39. package/dist/util-h6jhiiBE.js.map +1 -0
  40. package/package.json +9 -9
  41. package/dist/CollectionEditorDialog-DQjLtp1W.js.map +0 -1
  42. package/dist/export-DJoGYb89.js.map +0 -1
  43. package/dist/util-4oPxPkbp.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { n as getThumbnailMeasure, r as sanitizeUrl, t as getPreviewSizeFrom } from "./util-DB31IoJE.js";
2
2
  import * as React$1 from "react";
3
3
  import React, { Suspense, createContext, createElement, forwardRef, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
- import { deepEqual } from "fast-equals";
4
+ import { deepEqual, shallowEqual } from "fast-equals";
5
5
  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, lazyChunk, paperMixin, useDebouncedCallback, useOutsideAlerter, usePortalContainer } from "@rebasepro/ui";
6
6
  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";
7
- 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, useBridgeRegistration, 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
+ 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, useBridgeRegistration, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataTableController, useFetch, useLargeLayout, useNavigationBlocker, usePermissions, useRebaseClient, useRebaseContext, useRelationSelector, useResolvedComponent, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence } from "@rebasepro/app";
8
8
  import { Field, Formex, getIn, setIn, useCreateFormex, useFormex } from "@rebasepro/forms";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { EntityReference, EntityRelation, GeoPoint, MAX_LIST_LIMIT, Vector, getCollectionDataPath, getDataSourceCapabilities, isRelationalCollectionConfig, resourceKeyOf } from "@rebasepro/types";
@@ -21,7 +21,6 @@ import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
21
21
  import { SortableContext, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
22
22
  import { CSS } from "@dnd-kit/utilities";
23
23
  import { inferTypeFromValue } from "@rebasepro/inference";
24
- import { Highlight, themes } from "prism-react-renderer";
25
24
  import { generateKeyBetween, generateNKeysBetween } from "fractional-indexing";
26
25
  //#region src/util/property_utils.tsx
27
26
  /**
@@ -962,7 +961,11 @@ function StorageThumbnailInternal({ storeUrl, interactive, storagePathOrDownload
962
961
  if (!storagePathOrDownloadUrl) return null;
963
962
  const filetype = downloadConfig?.metadata ? getFiletype(downloadConfig?.metadata.contentType) : void 0;
964
963
  const previewType = filetype?.startsWith("image") ? "image" : filetype?.startsWith("video") ? "video" : filetype?.startsWith("audio") ? "audio" : "file";
965
- if (downloadConfig?.fileNotFound) return /* @__PURE__ */ jsx(ErrorView, { error: t("file_not_found") });
964
+ if (downloadConfig?.fileNotFound) return /* @__PURE__ */ jsx(FileNotFoundThumbnail, {
965
+ message: t("file_not_found"),
966
+ size,
967
+ fill
968
+ });
966
969
  return downloadConfig?.url ? /* @__PURE__ */ jsx(UrlComponentPreview, {
967
970
  previewType,
968
971
  url: downloadConfig.url,
@@ -972,6 +975,46 @@ function StorageThumbnailInternal({ storeUrl, interactive, storagePathOrDownload
972
975
  hint: storagePathOrDownloadUrl
973
976
  }) : fill ? /* @__PURE__ */ jsx(Skeleton, { className: "w-full h-full" }) : renderSkeletonImageThumbnail(size);
974
977
  }
978
+ /**
979
+ * The placeholder for a path the storage source answered 404 for.
980
+ *
981
+ * It occupies the box the image would have — the card's whole 4:3 thumbnail
982
+ * when `fill`, otherwise the same measure as the skeleton it replaces — and
983
+ * centres its contents in it. This was `ErrorView`, which is a
984
+ * left-aligned block with a red triangle and `m-2` around it: laid over a card
985
+ * thumbnail it sat in the top-left corner with the icon colliding with the
986
+ * text, and a grid of them read as a page of errors rather than as missing
987
+ * pictures. A file that is not there is the same absence `ImagePreview`
988
+ * already draws for an image that fails to load, so it is drawn the same way.
989
+ *
990
+ * At `small` the box is 40px and no label fits, so the message becomes the
991
+ * tooltip instead of being clipped.
992
+ */
993
+ function FileNotFoundThumbnail({ message, size, fill }) {
994
+ const measure = fill ? void 0 : getThumbnailMeasure(size);
995
+ const labelled = fill || size !== "small";
996
+ const body = /* @__PURE__ */ jsxs("div", {
997
+ className: cls("flex flex-col items-center justify-center gap-1 text-center overflow-hidden", "bg-surface-raised rounded-md p-2", fill ? "w-full h-full" : ""),
998
+ style: measure === void 0 ? void 0 : {
999
+ width: measure,
1000
+ height: measure,
1001
+ maxHeight: "100%"
1002
+ },
1003
+ children: [/* @__PURE__ */ jsx(ImageOffIcon, {
1004
+ size: labelled ? iconSize.small : iconSize.smallest,
1005
+ className: "shrink-0 text-surface-400 dark:text-surface-500"
1006
+ }), labelled && /* @__PURE__ */ jsx(Typography, {
1007
+ variant: "caption",
1008
+ color: "secondary",
1009
+ className: "max-w-full line-clamp-2 leading-tight",
1010
+ children: message
1011
+ })]
1012
+ });
1013
+ return labelled ? body : /* @__PURE__ */ jsx(Tooltip, {
1014
+ title: message,
1015
+ children: body
1016
+ });
1017
+ }
975
1018
  function getFiletype(input) {
976
1019
  if (input.startsWith("image")) return "image/*";
977
1020
  else if (input.startsWith("video")) return "video/*";
@@ -1065,7 +1108,7 @@ function StringPropertyPreview({ propertyKey, value, property, size, textOnly, c
1065
1108
  size,
1066
1109
  children: strValue
1067
1110
  }) });
1068
- else if (property.admin?.urlPreview) return /* @__PURE__ */ jsx(UrlComponentPreview, {
1111
+ else if (property.admin?.urlPreview || property.url) return /* @__PURE__ */ jsx(UrlComponentPreview, {
1069
1112
  size,
1070
1113
  url: strValue,
1071
1114
  previewType: typeof property.admin?.urlPreview === "string" ? property.admin?.urlPreview : void 0
@@ -4183,7 +4226,7 @@ function EntityTableCellActions({ showError, disabled, showExpandIcon, selected,
4183
4226
  }
4184
4227
  //#endregion
4185
4228
  //#region src/components/CollectionTableBinding/fields/TableStorageUpload.tsx
4186
- var dropZoneClasses$1 = "max-w-full box-border relative pt-[2px] items-center border border-transparent outline-hidden rounded-md duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] focus:border-primary-solid";
4229
+ var dropZoneClasses$1 = "max-w-full box-border relative pt-[2px] items-center border border-transparent outline-hidden rounded-md duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] focus:border-primary";
4187
4230
  var activeDropClasses$1 = "pt-0 border-2 border-solid";
4188
4231
  var acceptDropClasses$1 = "transition-colors duration-200 ease-[cubic-bezier(0,0,0.2,1)] border-2 border-solid border-green-500 bg-green-50 dark:bg-green-900";
4189
4232
  var rejectDropClasses$1 = "transition-colors duration-200 ease-[cubic-bezier(0,0,0.2,1)] border-2 border-solid border-red-500 bg-red-50 dark:bg-red-900";
@@ -7375,7 +7418,7 @@ var SelectableTable = function SelectableTable({ onValueChange, cellRenderer, on
7375
7418
  children: /* @__PURE__ */ jsx(VirtualTable, {
7376
7419
  data,
7377
7420
  columns,
7378
- cellRenderer: (props) => cellRenderer(props),
7421
+ cellRenderer,
7379
7422
  onRowClick: inlineEditing ? void 0 : onEntityClick ? onRowClick : void 0,
7380
7423
  onEndReached: loadNextPage,
7381
7424
  onResetPagination: resetPagination,
@@ -7434,9 +7477,19 @@ var SelectableTable = function SelectableTable({ onValueChange, cellRenderer, on
7434
7477
  var CollectionTableBinding = function CollectionTableBinding({ className, style, fixedFilter, actionsStart, actions, viewModeToggle, title, tableRowActionsBuilder, uniqueFieldValidator, getPropertyFor, onValueChange, selectionController, highlightedEntities, onEntityClick, onColumnResize, initialScroll, onScroll, onSizeChanged, hoverRow = true, inlineEditing = false, additionalFields, displayedColumnIds, defaultSize, properties, tableController, filterable = true, sortable = true, endAdornment, AddColumnComponent, AdditionalHeaderWidget, additionalIDHeaderWidget, emptyComponent, getIdColumnWidth, enablePopupIcon, openEntityMode = "side_panel", onColumnsOrderChange, hideToolbar = false }) {
7435
7478
  const ref = useRef(null);
7436
7479
  const largeLayout = useLargeLayout();
7437
- const selectedEntities = useMemo(() => {
7438
- return (selectionController?.selectedEntities?.length > 0 ? selectionController?.selectedEntities : highlightedEntities)?.filter(Boolean);
7439
- }, [selectionController?.selectedEntities, highlightedEntities]);
7480
+ const hasSelection = Boolean(selectionController?.hasSelection);
7481
+ const isRowHighlighted = useCallback((entity) => {
7482
+ if (hasSelection) return selectionController.isEntitySelected(entity);
7483
+ return Boolean(highlightedEntities?.some((e) => e && e.id === entity.id && e.path === entity.path));
7484
+ }, [
7485
+ hasSelection,
7486
+ selectionController,
7487
+ highlightedEntities
7488
+ ]);
7489
+ const cellExtraData = useMemo(() => ({
7490
+ selection: selectionController?.selection,
7491
+ highlightedEntityIds: highlightedEntities?.map((e) => e?.id)
7492
+ }), [selectionController?.selection, highlightedEntities]);
7440
7493
  const context = useRebaseContext();
7441
7494
  const [size, setSize] = React.useState(defaultSize ?? "m");
7442
7495
  React.useEffect(() => {
@@ -7646,7 +7699,7 @@ var CollectionTableBinding = function CollectionTableBinding({ className, style,
7646
7699
  inlineEditing,
7647
7700
  cellRenderer,
7648
7701
  onEntityClick,
7649
- highlightedRow: useCallback((entity) => Boolean(selectedEntities?.find((e) => e.id === entity.id && e.path === entity.path)), [selectedEntities]),
7702
+ highlightedRow: isRowHighlighted,
7650
7703
  tableController,
7651
7704
  onValueChange,
7652
7705
  initialScroll,
@@ -7658,46 +7711,290 @@ var CollectionTableBinding = function CollectionTableBinding({ className, style,
7658
7711
  endAdornment,
7659
7712
  AddColumnComponent,
7660
7713
  onColumnsOrderChange,
7661
- extraData: useMemo(() => ({ selectedEntityIds: selectedEntities?.map((e) => e.id) }), [selectedEntities])
7714
+ extraData: cellExtraData
7662
7715
  })]
7663
7716
  });
7664
7717
  };
7665
7718
  //#endregion
7666
7719
  //#region src/components/CollectionViewBinding/useSelectionController.tsx
7720
+ /** Same row: id alone is not unique across a junction-backed tab's two paths. */
7721
+ function sameEntity(a, b) {
7722
+ return a.id === b.id && a.path === b.path;
7723
+ }
7724
+ var EMPTY_SELECTION = {
7725
+ type: "entities",
7726
+ entities: []
7727
+ };
7667
7728
  function useSelectionController(onSelectionChange) {
7668
- const [selectedEntities, setSelectedEntities] = useState([]);
7729
+ const [selection, setSelection] = useState(EMPTY_SELECTION);
7669
7730
  const onSelectionChangeRef = useRef(onSelectionChange);
7670
7731
  onSelectionChangeRef.current = onSelectionChange;
7671
7732
  const toggleEntitySelection = useCallback((entity, newSelectedState) => {
7672
- setSelectedEntities((prev) => {
7673
- const isSelected = Boolean(prev.find((e) => e.id === entity.id && e.path === entity.path));
7674
- const shouldSelect = newSelectedState ?? !isSelected;
7675
- if (shouldSelect && !isSelected) {
7676
- onSelectionChangeRef.current?.(entity, true);
7677
- return [...prev, entity];
7678
- } else if (!shouldSelect && isSelected) {
7679
- onSelectionChangeRef.current?.(entity, false);
7680
- return prev.filter((item) => !(item.id === entity.id && item.path === entity.path));
7733
+ setSelection((prev) => {
7734
+ if (prev.type === "query") {
7735
+ const isExcluded = prev.excluded.some((e) => sameEntity(e, entity));
7736
+ const shouldSelect = newSelectedState ?? isExcluded;
7737
+ if (shouldSelect === !isExcluded) return prev;
7738
+ onSelectionChangeRef.current?.(entity, shouldSelect);
7739
+ return {
7740
+ ...prev,
7741
+ excluded: shouldSelect ? prev.excluded.filter((e) => !sameEntity(e, entity)) : [...prev.excluded, entity]
7742
+ };
7681
7743
  }
7682
- return prev;
7744
+ const isSelected = prev.entities.some((e) => sameEntity(e, entity));
7745
+ const shouldSelect = newSelectedState ?? !isSelected;
7746
+ if (shouldSelect === isSelected) return prev;
7747
+ onSelectionChangeRef.current?.(entity, shouldSelect);
7748
+ return {
7749
+ type: "entities",
7750
+ entities: shouldSelect ? [...prev.entities, entity] : prev.entities.filter((e) => !sameEntity(e, entity))
7751
+ };
7683
7752
  });
7684
7753
  }, []);
7685
7754
  const isEntitySelected = useCallback((entity) => {
7686
- return Boolean(selectedEntities.find((e) => e.id === entity.id && e.path === entity.path));
7687
- }, [selectedEntities]);
7755
+ if (selection.type === "query") return !selection.excluded.some((e) => sameEntity(e, entity));
7756
+ return selection.entities.some((e) => sameEntity(e, entity));
7757
+ }, [selection]);
7758
+ const setSelectedEntities = useCallback((entities) => {
7759
+ setSelection({
7760
+ type: "entities",
7761
+ entities
7762
+ });
7763
+ }, []);
7764
+ const selectAllMatching = useCallback((query, count) => {
7765
+ setSelection({
7766
+ type: "query",
7767
+ query,
7768
+ excluded: [],
7769
+ count
7770
+ });
7771
+ }, []);
7772
+ const clearSelection = useCallback(() => {
7773
+ setSelection(EMPTY_SELECTION);
7774
+ }, []);
7775
+ const selectedCount = useMemo(() => {
7776
+ if (selection.type === "entities") return selection.entities.length;
7777
+ if (selection.count === void 0) return void 0;
7778
+ return Math.max(0, selection.count - selection.excluded.length);
7779
+ }, [selection]);
7780
+ const hasSelection = selection.type === "query" ? selectedCount === void 0 || selectedCount > 0 : selection.entities.length > 0;
7688
7781
  return useMemo(() => ({
7689
- selectedEntities,
7782
+ selection,
7783
+ setSelection,
7784
+ selectedCount,
7785
+ hasSelection,
7690
7786
  setSelectedEntities,
7787
+ selectAllMatching,
7788
+ clearSelection,
7691
7789
  isEntitySelected,
7692
7790
  toggleEntitySelection
7693
7791
  }), [
7694
- selectedEntities,
7792
+ selection,
7793
+ selectedCount,
7794
+ hasSelection,
7695
7795
  setSelectedEntities,
7796
+ selectAllMatching,
7797
+ clearSelection,
7696
7798
  isEntitySelected,
7697
7799
  toggleEntitySelection
7698
7800
  ]);
7699
7801
  }
7700
7802
  //#endregion
7803
+ //#region src/util/entity_pages.ts
7804
+ /**
7805
+ * Read **every** row matching a query, one page at a time.
7806
+ *
7807
+ * `find({})` sends no `limit`, and an absent limit is resolved server-side to
7808
+ * `DEFAULT_LIST_LIMIT` — 50 rows. Anything that means "all of them" and asks
7809
+ * once gets 50 back, with nothing to say that is what happened.
7810
+ *
7811
+ * Termination is driven by the server's `meta.hasMore` where it is reported and
7812
+ * by an empty page otherwise — never by comparing a page's length against the
7813
+ * requested limit, since a final page that happens to be exactly full is
7814
+ * indistinguishable that way and a walk that stops there drops rows.
7815
+ *
7816
+ * @throws When more than `maxRows` rows match. A truncated answer is worse than
7817
+ * no answer: it is indistinguishable from a complete one.
7818
+ */
7819
+ async function walkEntityPages({ accessor, params, pageSize, maxRows, onCeiling, onProgress, signal }) {
7820
+ const rows = [];
7821
+ let offset = 0;
7822
+ for (;;) {
7823
+ if (signal?.aborted) break;
7824
+ const res = await accessor.find({
7825
+ ...params,
7826
+ limit: pageSize,
7827
+ offset
7828
+ });
7829
+ const page = res?.data ?? [];
7830
+ rows.push(...page);
7831
+ onProgress?.(rows.length, res?.meta?.total);
7832
+ if (rows.length > maxRows) throw new Error(onCeiling(maxRows));
7833
+ if (page.length === 0) break;
7834
+ if (res?.meta && !res.meta.hasMore) break;
7835
+ offset += page.length;
7836
+ }
7837
+ return rows;
7838
+ }
7839
+ //#endregion
7840
+ //#region src/selection/resolve_selection.ts
7841
+ /**
7842
+ * Rows asked for per request while resolving a query selection.
7843
+ *
7844
+ * Same ceiling as the export walk, and for the same reason: the server refuses
7845
+ * a `limit` above its `MAX_LIST_LIMIT` rather than clamping it.
7846
+ */
7847
+ var SELECTION_PAGE_SIZE = 500;
7848
+ /**
7849
+ * Most rows one query selection will resolve to ids for.
7850
+ *
7851
+ * The write path is per-row — `deleteMany` loops the single-row delete server
7852
+ * side so hooks and row-level security still run for each — so this is a
7853
+ * genuine ceiling on the work, not just on memory. Going over it **throws**
7854
+ * rather than acting on a prefix, which is the failure that cannot be seen:
7855
+ * "deleted 12,480 rows" reads identically whether it was all of them or the
7856
+ * first page.
7857
+ */
7858
+ var MAX_SELECTION_ROWS = 2e4;
7859
+ /**
7860
+ * A stable string for a selection query, for comparing the query a selection
7861
+ * was made against with the one the view is showing now.
7862
+ *
7863
+ * Filter keys are sorted: `filterValues` is rebuilt on most renders and its key
7864
+ * order follows whatever order the user touched the columns in, so a plain
7865
+ * `JSON.stringify` would report the same filter as a different one and drop a
7866
+ * selection the moment anything re-rendered.
7867
+ */
7868
+ function serializeSelectionQuery(query) {
7869
+ const filter = query.filterValues ? Object.keys(query.filterValues).sort().map((k) => `${k}=${JSON.stringify(query.filterValues[k])}`).join("&") : "";
7870
+ const order = query.sortBy?.map(([field, direction]) => `${String(field)}:${direction}`).join(",") ?? "";
7871
+ return `${query.path}|${filter}|${order}|${query.searchString ?? ""}`;
7872
+ }
7873
+ /** Turn a captured selection query into the `find` params that reproduce it. */
7874
+ function selectionQueryToFindParams(query) {
7875
+ return {
7876
+ where: query.filterValues && Object.keys(query.filterValues).length > 0 ? query.filterValues : void 0,
7877
+ orderBy: query.sortBy && query.sortBy.length > 0 ? query.sortBy.map(([field, direction]) => [String(field), direction]) : void 0,
7878
+ searchString: query.searchString
7879
+ };
7880
+ }
7881
+ /**
7882
+ * The rows a selection covers.
7883
+ *
7884
+ * This is the *only* way to get rows out of a query selection, and it is why
7885
+ * {@link EntitySelection} is a union: a bulk action has to call it, which means
7886
+ * it has to deal with the wait, the ceiling and the failure. The array this
7887
+ * replaced let the same code path read whatever the view had scrolled — 50 rows
7888
+ * of 12,480 — and report success.
7889
+ *
7890
+ * @throws When the query matches more than `maxRows` rows.
7891
+ */
7892
+ async function resolveSelection({ selection, accessor, pageSize = 500, maxRows = MAX_SELECTION_ROWS, onProgress, signal }) {
7893
+ if (selection.type === "entities") {
7894
+ onProgress?.(selection.entities.length, selection.entities.length);
7895
+ return selection.entities;
7896
+ }
7897
+ if (selection.count !== void 0 && selection.count - selection.excluded.length > maxRows) throw new Error(selectionCeilingMessage(maxRows));
7898
+ const rows = await walkEntityPages({
7899
+ accessor,
7900
+ params: selectionQueryToFindParams(selection.query),
7901
+ pageSize,
7902
+ maxRows,
7903
+ onProgress,
7904
+ signal,
7905
+ onCeiling: selectionCeilingMessage
7906
+ });
7907
+ if (selection.excluded.length === 0) return rows;
7908
+ const excluded = new Set(selection.excluded.map((e) => `${e.path}/${e.id}`));
7909
+ return rows.filter((e) => !excluded.has(`${e.path}/${e.id}`));
7910
+ }
7911
+ function selectionCeilingMessage(maxRows) {
7912
+ return `This selection covers more than ${maxRows.toLocaleString()} rows, which is more than can be acted on from the browser in one go. Filter the collection down first.`;
7913
+ }
7914
+ //#endregion
7915
+ //#region src/selection/SelectionMenu.tsx
7916
+ /**
7917
+ * The whole of selecting rows, in one control: a checkbox for the common case
7918
+ * and a caret for everything else.
7919
+ *
7920
+ * The escalation from "the rows on screen" to "every row that matches" lived in
7921
+ * a strip under the toolbar, which was wrong in a way that only shows up in
7922
+ * use: it appears the moment you tick a single checkbox, and the list you were
7923
+ * reading jumps down by its height. A control that punishes the first click is
7924
+ * not worth whatever the strip was explaining.
7925
+ *
7926
+ * So the escalation is a menu item — clicked deliberately, costing no layout,
7927
+ * and in the place you already go to select everything.
7928
+ */
7929
+ function SelectionMenu({ selectionController, loadedEntities, query, collectionEntitiesCount, collectionName }) {
7930
+ const { t } = useTranslation();
7931
+ const { selection, selectedCount, hasSelection, isEntitySelected, setSelectedEntities, selectAllMatching, clearSelection } = selectionController;
7932
+ const selectedLoaded = useMemo(() => loadedEntities.filter((e) => isEntitySelected(e)).length, [loadedEntities, isEntitySelected]);
7933
+ const allLoadedSelected = loadedEntities.length > 0 && selectedLoaded === loadedEntities.length;
7934
+ const someSelected = selectedLoaded > 0 && !allLoadedSelected;
7935
+ const isQuery = selection.type === "query";
7936
+ const moreThanLoaded = collectionEntitiesCount !== void 0 && collectionEntitiesCount > loadedEntities.length;
7937
+ const label = hasSelection ? t("selection_deselect_all") ?? "Deselect all" : t("selection_select_all_loaded") ?? "Select all loaded rows";
7938
+ return /* @__PURE__ */ jsxs("div", {
7939
+ className: cls("flex items-center rounded-md", hasSelection && "bg-surface-hover"),
7940
+ children: [
7941
+ /* @__PURE__ */ jsx(Tooltip, {
7942
+ title: label,
7943
+ children: /* @__PURE__ */ jsx("div", {
7944
+ className: "pl-1.5 pr-0.5 py-1 flex items-center",
7945
+ children: /* @__PURE__ */ jsx(Checkbox, {
7946
+ size: "small",
7947
+ padding: false,
7948
+ "aria-label": label,
7949
+ disabled: loadedEntities.length === 0 && !isQuery,
7950
+ checked: allLoadedSelected || isQuery,
7951
+ indeterminate: someSelected,
7952
+ onCheckedChange: (checked) => {
7953
+ if (checked) setSelectedEntities(loadedEntities);
7954
+ else clearSelection();
7955
+ }
7956
+ })
7957
+ })
7958
+ }),
7959
+ /* @__PURE__ */ jsxs(Menu, {
7960
+ align: "start",
7961
+ trigger: /* @__PURE__ */ jsx("button", {
7962
+ type: "button",
7963
+ "aria-label": t("selection_options") ?? "Selection options",
7964
+ className: cls("pr-1 pl-0 py-1 flex items-center rounded-r-md", "hover:bg-surface-hover focus:outline-none", "text-surface-accent-500 dark:text-surface-accent-300"),
7965
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: iconSize.smallest })
7966
+ }),
7967
+ children: [
7968
+ /* @__PURE__ */ jsx(MenuItem, {
7969
+ dense: true,
7970
+ onClick: () => setSelectedEntities(loadedEntities),
7971
+ children: t("selection_menu_all_loaded") ?? "All on this page"
7972
+ }),
7973
+ moreThanLoaded && /* @__PURE__ */ jsx(MenuItem, {
7974
+ dense: true,
7975
+ onClick: () => selectAllMatching(query, collectionEntitiesCount),
7976
+ children: t("selection_menu_all_matching", {
7977
+ total: collectionEntitiesCount.toLocaleString(),
7978
+ collection: collectionName
7979
+ }) ?? `All ${collectionEntitiesCount.toLocaleString()} ${collectionName}`
7980
+ }),
7981
+ /* @__PURE__ */ jsx(MenuItem, {
7982
+ dense: true,
7983
+ disabled: !hasSelection,
7984
+ onClick: clearSelection,
7985
+ children: t("selection_menu_none") ?? "None"
7986
+ })
7987
+ ]
7988
+ }),
7989
+ hasSelection && /* @__PURE__ */ jsx("span", {
7990
+ "data-testid": "selection-count",
7991
+ className: "pl-1 pr-2 text-xs font-mono tabular-nums text-text-secondary dark:text-text-secondary-dark",
7992
+ children: selectedCount === void 0 ? "…" : selectedCount.toLocaleString()
7993
+ })
7994
+ ]
7995
+ });
7996
+ }
7997
+ //#endregion
7701
7998
  //#region src/components/CollectionViewBinding/hooks/useCollectionInlineEditor.ts
7702
7999
  function useCollectionInlineEditor({ path, collection, dataClient, context }) {
7703
8000
  const uniqueFieldValidator = useCallback(async ({ name, value, property, entityId }) => {
@@ -9594,8 +9891,8 @@ function EditorCollectionAction({ path, parentCollectionSlugs, parentEntityIds,
9594
9891
  }
9595
9892
  //#endregion
9596
9893
  //#region src/components/CollectionViewBinding/CollectionViewActions.tsx
9597
- var ImportCollectionAction = lazyChunk(() => import("./import-C4VQ0rwO.js").then((n) => n.t).then((m) => ({ default: m.ImportCollectionAction })));
9598
- var ExportCollectionAction = lazyChunk(() => import("./export-DJoGYb89.js").then((n) => n.t).then((m) => ({ default: m.ExportCollectionAction })));
9894
+ var ImportCollectionAction = lazyChunk(() => import("./import-BciQBH5t.js").then((n) => n.t).then((m) => ({ default: m.ImportCollectionAction })));
9895
+ var ExportCollectionAction = lazyChunk(() => import("./export-DtSmLT6u.js").then((n) => n.t).then((m) => ({ default: m.ExportCollectionAction })));
9599
9896
  function CollectionViewActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, onNewClick, onAddExistingClick, onMultipleDeleteClick, selectionEnabled, path, selectionController, tableController, collectionEntitiesCount, compact, children, openNewDocument }) {
9600
9897
  const context = useAdminContext();
9601
9898
  const { canCreate, canDelete } = usePermissions();
@@ -9603,7 +9900,7 @@ function CollectionViewActions({ collection, relativePath, parentCollectionSlugs
9603
9900
  const { t } = useTranslation();
9604
9901
  const collectionEditorController = useCollectionEditorController();
9605
9902
  const hasCollectionEditor = Boolean(collectionEditorController?.editCollection);
9606
- const selectedEntities = selectionController.selectedEntities;
9903
+ const { selectedCount, hasSelection } = selectionController;
9607
9904
  const addButton = canCreate(collection, path) && onNewClick && (largeLayout && !compact ? /* @__PURE__ */ jsx(Button, {
9608
9905
  id: `add_entity_${path}`,
9609
9906
  onClick: onNewClick,
@@ -9641,25 +9938,28 @@ function CollectionViewActions({ collection, relativePath, parentCollectionSlugs
9641
9938
  })
9642
9939
  }));
9643
9940
  const multipleDeleteEnabled = canDelete(collection, path, null);
9644
- const hasSelection = Boolean(selectedEntities?.length);
9941
+ const countLabel = selectedCount === void 0 ? "…" : selectedCount.toLocaleString();
9645
9942
  let multipleDeleteButton;
9646
9943
  if (hasSelection) {
9647
9944
  const button = largeLayout && !compact ? /* @__PURE__ */ jsxs(Button, {
9648
9945
  variant: "text",
9649
- disabled: !selectedEntities?.length || !multipleDeleteEnabled,
9650
- startIcon: /* @__PURE__ */ jsx(Trash2Icon, { size: iconSize.small }),
9946
+ disabled: !multipleDeleteEnabled,
9947
+ startIcon: /* @__PURE__ */ jsx(Trash2Icon, {
9948
+ size: iconSize.small,
9949
+ className: "shrink-0"
9950
+ }),
9651
9951
  onClick: onMultipleDeleteClick,
9652
9952
  color: "primary",
9653
- className: "lg:w-20",
9953
+ className: "lg:min-w-20",
9654
9954
  children: [
9655
9955
  "(",
9656
- selectedEntities?.length,
9956
+ countLabel,
9657
9957
  ")"
9658
9958
  ]
9659
9959
  }) : /* @__PURE__ */ jsx(IconButton, {
9660
9960
  size: "small",
9661
9961
  color: "primary",
9662
- disabled: !selectedEntities?.length || !multipleDeleteEnabled,
9962
+ disabled: !multipleDeleteEnabled,
9663
9963
  onClick: onMultipleDeleteClick,
9664
9964
  children: /* @__PURE__ */ jsx(Trash2Icon, { size: iconSize.small })
9665
9965
  });
@@ -10190,7 +10490,7 @@ function EntityCardBinding({ entity, collection, onClick, selected, highlighted,
10190
10490
  path: entity.path,
10191
10491
  entityId: entity.id
10192
10492
  });
10193
- onClick(entity);
10493
+ onClick(entity, e);
10194
10494
  }
10195
10495
  };
10196
10496
  const handleCheckboxClick = (e) => {
@@ -10302,7 +10602,7 @@ function CollectionCardViewBinding({ collection, tableController, onEntityClick,
10302
10602
  const handleSelectionChange = useCallback((entity, selected) => {
10303
10603
  selectionController?.toggleEntitySelection(entity, selected);
10304
10604
  }, [selectionController]);
10305
- const selectedIds = useMemo(() => new Set(selectionController?.selectedEntities.map((e) => e.id)), [selectionController?.selectedEntities]);
10605
+ const selectedIds = useMemo(() => new Set(data.filter((e) => selectionController?.isEntitySelected(e)).map((e) => e.id)), [data, selectionController]);
10306
10606
  const highlightedIds = useMemo(() => new Set(highlightedEntities?.map((e) => e.id)), [highlightedEntities]);
10307
10607
  const handleRowSelectionChange = useCallback((entity, selected) => {
10308
10608
  handleSelectionChange(entity, selected);
@@ -10329,7 +10629,7 @@ function CollectionCardViewBinding({ collection, tableController, onEntityClick,
10329
10629
  entity,
10330
10630
  collection,
10331
10631
  searchString: tableController.searchString,
10332
- onClick,
10632
+ onClick: (_entity, event) => onClick(event),
10333
10633
  selected,
10334
10634
  highlighted,
10335
10635
  onSelectionChange: handleRowSelectionChange,
@@ -10837,7 +11137,7 @@ function CollectionListViewBinding({ collection, tableController, onEntityClick,
10837
11137
  return highlightedEntities?.some((e) => e.id === entity.id && e.path === entity.path) ?? false;
10838
11138
  }, [highlightedEntities]);
10839
11139
  const rowClasses = getRowClasses(size);
10840
- const selectedIds = useMemo(() => new Set(selectionController?.selectedEntities.map((e) => e.id)), [selectionController?.selectedEntities]);
11140
+ const selectedIds = useMemo(() => new Set(data.filter((e) => selectionController?.isEntitySelected(e)).map((e) => e.id)), [data, selectionController]);
10841
11141
  const highlightedIds = useMemo(() => new Set(highlightedEntities?.map((e) => e.id)), [highlightedEntities]);
10842
11142
  const handleRowSelectionChange = useCallback((entity, selected) => {
10843
11143
  handleSelectionChange(entity, selected);
@@ -11070,7 +11370,7 @@ var ListRow = React.memo(function ListRow({ entity, collection, onClick, selecte
11070
11370
  children: slots.title?.value !== void 0 ? /* @__PURE__ */ jsx(Typography, {
11071
11371
  component: "div",
11072
11372
  variant: "body2",
11073
- className: "font-semibold text-surface-900 dark:text-surface-50 truncate transition-colors group-hover:text-primary-600 dark:group-hover:text-primary-400",
11373
+ className: "font-semibold text-surface-900 dark:text-surface-50 truncate transition-colors group-hover:text-primary dark:group-hover:text-primary-light",
11074
11374
  children: terms.length > 0 && typeof slots.title?.value === "string" ? /* @__PURE__ */ jsx(Highlighted, {
11075
11375
  text: slots.title.value,
11076
11376
  terms
@@ -11081,7 +11381,7 @@ var ListRow = React.memo(function ListRow({ entity, collection, onClick, selecte
11081
11381
  }) : /* @__PURE__ */ jsx(Typography, {
11082
11382
  component: "div",
11083
11383
  variant: "body2",
11084
- className: "font-semibold text-surface-500 dark:text-surface-400 font-mono text-xs transition-colors group-hover:text-primary-600 dark:group-hover:text-primary-400",
11384
+ className: "font-semibold text-surface-500 dark:text-surface-400 font-mono text-xs transition-colors group-hover:text-primary dark:group-hover:text-primary-light",
11085
11385
  children: entity.id
11086
11386
  })
11087
11387
  }),
@@ -11527,21 +11827,21 @@ function EntityViewBinding({ entity, collection, className, asPage = false, open
11527
11827
  return next;
11528
11828
  });
11529
11829
  }, []);
11830
+ const rebaseContext = useRebaseContext();
11530
11831
  const renderField = useCallback((field) => {
11531
11832
  if (field.additional) {
11532
11833
  const additionalField = collection.additionalFields?.find((f) => f.key === field.key);
11533
11834
  if (!additionalField || !formContext) return null;
11534
11835
  const AdditionalFieldBuilder = additionalField.Builder;
11535
- const additionalFieldContext = formContext;
11536
11836
  const child = AdditionalFieldBuilder ? /* @__PURE__ */ jsx(AdditionalFieldBuilder, {
11537
11837
  entity,
11538
- context: additionalFieldContext
11838
+ context: rebaseContext
11539
11839
  }) : /* @__PURE__ */ jsx("div", {
11540
11840
  className: "w-full",
11541
11841
  children: /* @__PURE__ */ jsx(AdditionalFieldValue, {
11542
11842
  field: additionalField,
11543
11843
  entity,
11544
- context: additionalFieldContext
11844
+ context: rebaseContext
11545
11845
  })
11546
11846
  });
11547
11847
  return /* @__PURE__ */ jsx("div", {
@@ -11583,7 +11883,8 @@ function EntityViewBinding({ entity, collection, className, asPage = false, open
11583
11883
  }, [
11584
11884
  collection,
11585
11885
  entity,
11586
- formContext
11886
+ formContext,
11887
+ rebaseContext
11587
11888
  ]);
11588
11889
  /**
11589
11890
  * One field of a `readVariant: "summary"` section: a label/value row rather
@@ -11926,36 +12227,18 @@ function SplitListShowButton({ onClick }) {
11926
12227
  });
11927
12228
  }
11928
12229
  //#endregion
11929
- //#region src/components/JsonPreviewBinding.tsx
11930
- function JsonPreviewBinding({ values }) {
11931
- const code = JSON.stringify(values, null, " ");
11932
- const { mode } = useModeController();
11933
- const preRef = useRef(null);
11934
- return /* @__PURE__ */ jsx(Highlight, {
11935
- theme: mode === "dark" ? themes.vsDark : themes.github,
11936
- code,
11937
- language: "json",
11938
- children: ({ style, tokens, getLineProps, getTokenProps }) => /* @__PURE__ */ jsx("pre", {
11939
- ref: preRef,
11940
- style: {
11941
- ...style,
11942
- backgroundColor: "inherit"
11943
- },
11944
- className: "max-w-6xl mx-auto p-8 rounded-xs text-sm",
11945
- children: tokens.map((line, i) => /* @__PURE__ */ jsx("div", {
11946
- ...getLineProps({ line }),
11947
- className: "text-wrap",
11948
- children: line.map((token, key) => /* @__PURE__ */ jsx("span", {
11949
- ...getTokenProps({ token }),
11950
- className: "word-break"
11951
- }, key))
11952
- }, i))
11953
- })
11954
- });
11955
- }
11956
- //#endregion
11957
12230
  //#region src/components/EntityInspector.tsx
11958
- var EntityHistoryView = lazyChunk(() => import("./history-BBpsdTrJ.js").then((m) => ({ default: m.EntityHistoryView })));
12231
+ var EntityHistoryView = lazyChunk(() => import("./history-C6kUDQ3f.js").then((m) => ({ default: m.EntityHistoryView })));
12232
+ /**
12233
+ * The JSON tab pulls `prism-react-renderer` — 85 kB, and it was EAGER.
12234
+ *
12235
+ * A static import here put the syntax highlighter in the set a browser
12236
+ * downloads before the login screen paints, for a preview that cannot appear
12237
+ * until somebody has signed in, opened a record, and chosen this tab. Its
12238
+ * neighbour `EntityHistoryView` is already loaded this way, behind the same
12239
+ * `Suspense`, for the same reason.
12240
+ */
12241
+ var JsonPreviewBinding = lazyChunk(() => import("./JsonPreviewBinding-v0w3H0fp.js").then((m) => ({ default: m.JsonPreviewBinding })));
11959
12242
  /**
11960
12243
  * Raw values and revision history, as an inspector rather than as tabs.
11961
12244
  *
@@ -12017,7 +12300,10 @@ function EntityInspector({ tab, onTabChange, onClose, collection, entity, formCo
12017
12300
  ]
12018
12301
  }), /* @__PURE__ */ jsx("div", {
12019
12302
  className: "flex-1 min-h-0 overflow-auto",
12020
- children: /* @__PURE__ */ jsxs(ErrorBoundary, { children: [tab === "json" && /* @__PURE__ */ jsx(JsonPreviewBinding, { values: values ?? entity?.values ?? {} }), tab === "history" && includeHistory && formContext && /* @__PURE__ */ jsx(Suspense, {
12303
+ children: /* @__PURE__ */ jsxs(ErrorBoundary, { children: [tab === "json" && /* @__PURE__ */ jsx(Suspense, {
12304
+ fallback: /* @__PURE__ */ jsx(CircularProgressCenter, {}),
12305
+ children: /* @__PURE__ */ jsx(JsonPreviewBinding, { values: values ?? entity?.values ?? {} })
12306
+ }), tab === "history" && includeHistory && formContext && /* @__PURE__ */ jsx(Suspense, {
12021
12307
  fallback: /* @__PURE__ */ jsx(CircularProgressCenter, {}),
12022
12308
  children: /* @__PURE__ */ jsx(EntityHistoryView, {
12023
12309
  collection,
@@ -14189,18 +14475,91 @@ function ViewModeToggle({ viewMode = "table", onViewModeChange, enabledViews = A
14189
14475
  });
14190
14476
  }
14191
14477
  //#endregion
14478
+ //#region src/util/map_with_concurrency.ts
14479
+ /**
14480
+ * Run `task` over every item, at most `concurrency` at a time, and return the
14481
+ * results in the order the items were given.
14482
+ *
14483
+ * The bulk delete used to be `Promise.all(entities.map(performDelete))`. That is
14484
+ * fine for the handful of rows anyone could tick by hand, and it is the reason
14485
+ * a "select all matching" over a real collection could not simply be poured
14486
+ * into the existing dialog: twelve thousand entries in that array is twelve
14487
+ * thousand simultaneous requests, which the browser queues and the server takes
14488
+ * as a burst.
14489
+ *
14490
+ * Rejections are not swallowed and do not cancel: every task is run, and the
14491
+ * first rejection is thrown once they have all settled, so a bulk action cannot
14492
+ * half-apply and then report the failure as if nothing had happened.
14493
+ */
14494
+ async function mapWithConcurrency(items, concurrency, task, onSettled) {
14495
+ const results = new Array(items.length);
14496
+ const limit = Math.max(1, Math.floor(concurrency));
14497
+ let next = 0;
14498
+ let completed = 0;
14499
+ let firstError;
14500
+ let failed = false;
14501
+ async function worker() {
14502
+ for (;;) {
14503
+ const index = next++;
14504
+ if (index >= items.length) return;
14505
+ try {
14506
+ results[index] = await task(items[index], index);
14507
+ } catch (e) {
14508
+ if (!failed) {
14509
+ failed = true;
14510
+ firstError = e;
14511
+ }
14512
+ }
14513
+ onSettled?.(++completed, items.length);
14514
+ }
14515
+ }
14516
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
14517
+ if (failed) throw firstError;
14518
+ return results;
14519
+ }
14520
+ //#endregion
14192
14521
  //#region src/components/DeleteEntityDialog.tsx
14193
- function DeleteEntityDialog({ entityOrEntitiesToDelete, collection, onClose, open, onEntityDelete, onMultipleEntitiesDelete, path, variant = "delete" }) {
14194
- useAuthController();
14522
+ /**
14523
+ * Deletes in flight at once.
14524
+ *
14525
+ * The bulk delete used to be `Promise.all(entities.map(performDelete))`, which
14526
+ * is unremarkable for the handful of rows anyone could tick by hand and
14527
+ * untenable the moment "select all 12,480 matching" can reach it.
14528
+ */
14529
+ var DELETE_CONCURRENCY = 8;
14530
+ function isSelection(target) {
14531
+ return target.type === "entities" || target.type === "query";
14532
+ }
14533
+ function DeleteEntityDialog({ target, collection, onClose, open, onEntityDelete, onMultipleEntitiesDelete, path, variant = "delete" }) {
14195
14534
  const dataClient = useData();
14196
- useCustomizationController();
14197
14535
  const snackbarController = useSnackbarController();
14198
14536
  const { t } = useTranslation();
14199
14537
  const [loading, setLoading] = useState(false);
14538
+ const [progress, setProgress] = useState(void 0);
14539
+ const cancelledRef = useRef({ aborted: false });
14200
14540
  const context = useAdminContext();
14201
- const entityOrEntities = Array.isArray(entityOrEntitiesToDelete) && entityOrEntitiesToDelete.length === 1 ? entityOrEntitiesToDelete[0] : entityOrEntitiesToDelete;
14202
- const multipleEntities = Array.isArray(entityOrEntities);
14541
+ const { singleEntity, selection } = useMemo(() => {
14542
+ if (!target) return {
14543
+ singleEntity: void 0,
14544
+ selection: void 0
14545
+ };
14546
+ if (!isSelection(target)) return {
14547
+ singleEntity: target,
14548
+ selection: void 0
14549
+ };
14550
+ if (target.type === "entities" && target.entities.length === 1) return {
14551
+ singleEntity: target.entities[0],
14552
+ selection: void 0
14553
+ };
14554
+ return {
14555
+ singleEntity: void 0,
14556
+ selection: target
14557
+ };
14558
+ }, [target]);
14559
+ /** How many rows this will delete; `undefined` when only the server knows. */
14560
+ const selectionCount = selection === void 0 ? 1 : selection.type === "entities" ? selection.entities.length : selection.count === void 0 ? void 0 : Math.max(0, selection.count - selection.excluded.length);
14203
14561
  const handleCancel = useCallback(() => {
14562
+ cancelledRef.current.aborted = true;
14204
14563
  onClose();
14205
14564
  }, [onClose]);
14206
14565
  const onDeleteSuccess = useCallback((entity) => {
@@ -14230,70 +14589,104 @@ function DeleteEntityDialog({ entityOrEntitiesToDelete, collection, onClose, ope
14230
14589
  context
14231
14590
  ]);
14232
14591
  const handleOk = useCallback(async () => {
14233
- if (entityOrEntities) {
14592
+ if (singleEntity) {
14234
14593
  setLoading(true);
14235
- if (multipleEntities) Promise.all(entityOrEntities.map(performDelete)).then((results) => {
14236
- setLoading(false);
14237
- if (onMultipleEntitiesDelete && entityOrEntities) onMultipleEntitiesDelete(path, entityOrEntities);
14238
- if (results.every(Boolean)) snackbarController.open({
14594
+ const success = await performDelete(singleEntity);
14595
+ setLoading(false);
14596
+ if (success) {
14597
+ onEntityDelete?.(path, singleEntity);
14598
+ snackbarController.open({
14239
14599
  type: "success",
14240
- message: t("multiple_deleted", { collection: collection.name })
14241
- });
14242
- else if (results.some(Boolean)) snackbarController.open({
14243
- type: "warning",
14244
- message: t("some_entities_deleted", { collection: collection.name })
14245
- });
14246
- else snackbarController.open({
14247
- type: "error",
14248
- message: t("error_deleting_entities", { collection: collection.name })
14600
+ message: t("deleted", { name: collection.singularName ?? collection.name })
14249
14601
  });
14250
14602
  onClose();
14603
+ }
14604
+ return;
14605
+ }
14606
+ if (!selection) return;
14607
+ setLoading(true);
14608
+ cancelledRef.current = { aborted: false };
14609
+ let entities;
14610
+ try {
14611
+ entities = await resolveSelection({
14612
+ selection,
14613
+ accessor: dataClient.collection(path),
14614
+ signal: cancelledRef.current,
14615
+ onProgress: (loaded, total) => setProgress(total === void 0 ? t("selection_reading_rows_unknown", { loaded: loaded.toLocaleString() }) ?? `Reading rows… ${loaded}` : t("selection_reading_rows", {
14616
+ loaded: loaded.toLocaleString(),
14617
+ total: total.toLocaleString()
14618
+ }) ?? `Reading rows… ${loaded} of ${total}`)
14251
14619
  });
14252
- else performDelete(entityOrEntities).then((success) => {
14253
- setLoading(false);
14254
- if (success) {
14255
- if (onEntityDelete && entityOrEntities) onEntityDelete(path, entityOrEntities);
14256
- snackbarController.open({
14257
- type: "success",
14258
- message: t("deleted", { name: collection.singularName ?? collection.name })
14259
- });
14260
- onClose();
14261
- }
14620
+ } catch (e) {
14621
+ setLoading(false);
14622
+ setProgress(void 0);
14623
+ snackbarController.open({
14624
+ type: "error",
14625
+ title: t("error_deleting_entities", { collection: collection.name }),
14626
+ message: e?.message
14262
14627
  });
14628
+ return;
14263
14629
  }
14630
+ if (cancelledRef.current.aborted) {
14631
+ setLoading(false);
14632
+ setProgress(void 0);
14633
+ return;
14634
+ }
14635
+ let results = [];
14636
+ try {
14637
+ results = await mapWithConcurrency(entities, DELETE_CONCURRENCY, (entity) => cancelledRef.current.aborted ? Promise.resolve(false) : performDelete(entity), (completed, total) => setProgress(t("selection_deleting_progress", {
14638
+ completed: completed.toLocaleString(),
14639
+ total: total.toLocaleString()
14640
+ }) ?? `Deleted ${completed} of ${total}`));
14641
+ } catch (e) {
14642
+ console.error("Error deleting entities", e);
14643
+ }
14644
+ setLoading(false);
14645
+ setProgress(void 0);
14646
+ onMultipleEntitiesDelete?.(path, entities.filter((_, i) => results[i]));
14647
+ const deleted = results.filter(Boolean).length;
14648
+ if (deleted === entities.length && entities.length > 0) snackbarController.open({
14649
+ type: "success",
14650
+ message: t("multiple_deleted", { collection: collection.name })
14651
+ });
14652
+ else if (deleted > 0) snackbarController.open({
14653
+ type: "warning",
14654
+ message: t("some_entities_deleted", { collection: collection.name })
14655
+ });
14656
+ else snackbarController.open({
14657
+ type: "error",
14658
+ message: t("error_deleting_entities", { collection: collection.name })
14659
+ });
14660
+ onClose();
14264
14661
  }, [
14265
- entityOrEntities,
14266
- multipleEntities,
14662
+ singleEntity,
14663
+ selection,
14267
14664
  performDelete,
14268
- onMultipleEntitiesDelete,
14665
+ dataClient,
14269
14666
  path,
14667
+ onEntityDelete,
14668
+ onMultipleEntitiesDelete,
14270
14669
  onClose,
14271
14670
  snackbarController,
14272
14671
  collection.name,
14273
- onEntityDelete
14672
+ collection.singularName,
14673
+ t
14274
14674
  ]);
14275
- let content;
14276
- if (entityOrEntities && multipleEntities) content = /* @__PURE__ */ jsx(Fragment, { children: t("multiple_entities") });
14277
- else {
14278
- const entity = entityOrEntities;
14279
- content = entity ? /* @__PURE__ */ jsx(EntityViewBinding, {
14280
- entity,
14281
- collection,
14282
- path
14283
- }) : /* @__PURE__ */ jsx(Fragment, {});
14284
- }
14285
14675
  const entityName = collection.singularName ?? collection.name;
14286
- const dialogTitle = variant === "unlink" ? multipleEntities ? /* @__PURE__ */ jsxs(Fragment, { children: [
14676
+ let dialogTitle;
14677
+ if (singleEntity) dialogTitle = variant === "unlink" ? t("unlink_entity_confirm_title", { entityName }) ?? `Remove this ${entityName} from this record?` : t("delete_entity_confirm_title", { entityName });
14678
+ else if (variant === "unlink") dialogTitle = /* @__PURE__ */ jsxs(Fragment, { children: [
14287
14679
  /* @__PURE__ */ jsx("b", { children: collection.name }),
14288
14680
  ": ",
14289
14681
  t("confirm_multiple_unlink") ?? "Remove these from this record?"
14290
- ] }) : t("unlink_entity_confirm_title", { entityName }) ?? `Remove this ${entityName} from this record?` : multipleEntities ? /* @__PURE__ */ jsxs(Fragment, { children: [
14291
- /* @__PURE__ */ jsx("b", { children: collection.name }),
14292
- ": ",
14293
- t("confirm_multiple_delete")
14294
- ] }) : t("delete_entity_confirm_title", { entityName });
14682
+ ] });
14683
+ else if (selectionCount === void 0) dialogTitle = t("confirm_delete_selection_unknown") ?? "Delete every row matching the current filter?";
14684
+ else dialogTitle = t("confirm_delete_selection", {
14685
+ total: selectionCount.toLocaleString(),
14686
+ collection: collection.name
14687
+ }) ?? `Delete ${selectionCount.toLocaleString()} ${collection.name}?`;
14295
14688
  return /* @__PURE__ */ jsxs(Dialog, {
14296
- maxWidth: multipleEntities ? "lg" : "2xl",
14689
+ maxWidth: singleEntity ? "2xl" : "lg",
14297
14690
  "aria-labelledby": "delete-dialog",
14298
14691
  open,
14299
14692
  onOpenChange: (open) => !open ? onClose() : void 0,
@@ -14304,23 +14697,42 @@ function DeleteEntityDialog({ entityOrEntitiesToDelete, collection, onClose, ope
14304
14697
  }),
14305
14698
  /* @__PURE__ */ jsxs(DialogContent, {
14306
14699
  fullHeight: true,
14307
- children: [variant === "unlink" && /* @__PURE__ */ jsx("div", {
14308
- className: "px-4 pt-4",
14309
- children: /* @__PURE__ */ jsx(Typography, {
14310
- variant: "body2",
14311
- color: "secondary",
14312
- children: t("unlink_entity_confirm_body", { collectionName: collection.name }) ?? `It stays in ${collection.name} and remains available to other records.`
14700
+ children: [
14701
+ variant === "unlink" && /* @__PURE__ */ jsx("div", {
14702
+ className: "px-4 pt-4",
14703
+ children: /* @__PURE__ */ jsx(Typography, {
14704
+ variant: "body2",
14705
+ color: "secondary",
14706
+ children: t("unlink_entity_confirm_body", { collectionName: collection.name }) ?? `It stays in ${collection.name} and remains available to other records.`
14707
+ })
14708
+ }),
14709
+ singleEntity && /* @__PURE__ */ jsx("div", {
14710
+ className: "p-4",
14711
+ children: /* @__PURE__ */ jsx(EntityViewBinding, {
14712
+ entity: singleEntity,
14713
+ collection,
14714
+ path
14715
+ })
14716
+ }),
14717
+ !singleEntity && variant === "delete" && /* @__PURE__ */ jsx("div", {
14718
+ className: "px-4 pt-4",
14719
+ children: /* @__PURE__ */ jsx(Typography, {
14720
+ variant: "body2",
14721
+ color: "secondary",
14722
+ children: t("confirm_delete_selection_body") ?? "This cannot be undone. Each row is deleted individually, so this may take a while."
14723
+ })
14313
14724
  })
14314
- }), !multipleEntities && /* @__PURE__ */ jsx("div", {
14315
- className: "p-4",
14316
- children: content
14317
- })]
14725
+ ]
14318
14726
  }),
14319
14727
  /* @__PURE__ */ jsxs(DialogActions, { children: [
14320
14728
  loading && /* @__PURE__ */ jsx(CircularProgress, { size: "smallest" }),
14729
+ progress && /* @__PURE__ */ jsx(Typography, {
14730
+ variant: "caption",
14731
+ color: "secondary",
14732
+ children: progress
14733
+ }),
14321
14734
  /* @__PURE__ */ jsx(Button, {
14322
14735
  onClick: handleCancel,
14323
- disabled: loading,
14324
14736
  variant: "text",
14325
14737
  children: t("cancel")
14326
14738
  }),
@@ -14549,14 +14961,14 @@ function buildRemoveEntityAction({ key, name, icon, variant }) {
14549
14961
  Component: ({ open }) => {
14550
14962
  if (!collection || !path) throw new Error(`${key}EntityAction: Collection is undefined`);
14551
14963
  return /* @__PURE__ */ jsx(DeleteEntityDialog, {
14552
- entityOrEntitiesToDelete: entity,
14964
+ target: entity,
14553
14965
  path,
14554
14966
  collection,
14555
14967
  variant,
14556
14968
  open,
14557
14969
  onEntityDelete: () => {
14558
14970
  context?.analyticsController?.onAnalyticsEvent?.("single_entity_deleted", { path });
14559
- selectionController?.setSelectedEntities(selectionController.selectedEntities.filter((e) => e.id !== entity.id));
14971
+ selectionController?.toggleEntitySelection(entity, false);
14560
14972
  onCollectionChange?.();
14561
14973
  if (openEntityMode === "full_screen" && context?.urlController) {
14562
14974
  const collectionUrl = context.urlController.buildUrlCollectionPath(path);
@@ -15683,7 +16095,7 @@ function SortButton({ tableController, properties, compact }) {
15683
16095
  }
15684
16096
  //#endregion
15685
16097
  //#region src/components/CollectionViewBinding/CollectionViewStartActions.tsx
15686
- function CollectionViewStartActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, path, selectionController, tableController, collectionEntitiesCount, entitiesCount, resolvedProperties, viewMode, compact, openNewDocument }) {
16098
+ function CollectionViewStartActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, path, selectionController, tableController, collectionEntitiesCount, entitiesCount, resolvedProperties, viewMode, selectionEnabled, selectionQuery, compact, openNewDocument }) {
15687
16099
  const context = useAdminContext();
15688
16100
  const largeLayout = useLargeLayout();
15689
16101
  const { t } = useTranslation();
@@ -15764,6 +16176,16 @@ function CollectionViewStartActions({ collection, relativePath, parentCollection
15764
16176
  return /* @__PURE__ */ jsxs(Fragment, { children: [
15765
16177
  [
15766
16178
  backButton,
16179
+ selectionEnabled && selectionQuery ? /* @__PURE__ */ jsx("div", {
16180
+ className: "mr-1 flex items-center",
16181
+ children: /* @__PURE__ */ jsx(SelectionMenu, {
16182
+ selectionController,
16183
+ loadedEntities: tableController.data,
16184
+ query: selectionQuery,
16185
+ collectionEntitiesCount,
16186
+ collectionName: collection.name
16187
+ })
16188
+ }, "select_all") : null,
15767
16189
  filtersButton,
15768
16190
  sortButton,
15769
16191
  /* @__PURE__ */ jsx(ClearFilterSortButton, {
@@ -15869,12 +16291,15 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
15869
16291
  const linkedTargetSlug = isLinkedChildView && childViewSource.kind === "relation" ? childViewSource.targetSlug : void 0;
15870
16292
  const containerRef = React.useRef(null);
15871
16293
  const scrollRestoration = useScrollRestoration();
16294
+ const collectionPropRef = React.useRef(collectionProp);
16295
+ if (!shallowEqual(collectionPropRef.current, collectionProp)) collectionPropRef.current = collectionProp;
16296
+ const stableCollectionProp = collectionPropRef.current;
15872
16297
  const collection = useMemo(() => {
15873
- const registryCollection = collectionRegistry.getCollection(path) || collectionProp;
16298
+ const registryCollection = collectionRegistry.getCollection(path) || stableCollectionProp;
15874
16299
  const userOverride = userConfigPersistence?.getCollectionConfig(path);
15875
16300
  return userOverride ? mergeDeep(registryCollection, userOverride) : registryCollection;
15876
16301
  }, [
15877
- collectionProp,
16302
+ stableCollectionProp,
15878
16303
  path,
15879
16304
  userConfigPersistence,
15880
16305
  collectionRegistry
@@ -15891,7 +16316,7 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
15891
16316
  const [docsCount, setDocsCount] = useState(null);
15892
16317
  const [localPropertiesOrder, setLocalPropertiesOrder] = useState(collection.propertiesOrder);
15893
16318
  useEffect(() => {
15894
- setLocalPropertiesOrder(collection.propertiesOrder);
16319
+ setLocalPropertiesOrder((previous) => deepEqual(previous, collection.propertiesOrder) ? previous : collection.propertiesOrder);
15895
16320
  }, [collection.propertiesOrder]);
15896
16321
  const unselectNavigatedEntity = useCallback(() => {
15897
16322
  const currentSelection = highlightedEntity;
@@ -15968,7 +16393,7 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
15968
16393
  const [customViewSize, setCustomViewSize] = useState(collection.defaultSize ?? "m");
15969
16394
  const selectionController = useSelectionController();
15970
16395
  const usedSelectionController = collection.selectionController ?? selectionController;
15971
- const { selectedEntities, setSelectedEntities } = usedSelectionController;
16396
+ const { selection, clearSelection, toggleEntitySelection } = usedSelectionController;
15972
16397
  const tableController = useDataTableController({
15973
16398
  path,
15974
16399
  collection,
@@ -16083,17 +16508,17 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
16083
16508
  ]);
16084
16509
  const onMultipleDeleteClick = () => {
16085
16510
  analyticsController.onAnalyticsEvent?.("multiple_delete_dialog_open", { path });
16086
- setDeleteEntityClicked(selectedEntities);
16511
+ setDeleteEntityClicked(selection);
16087
16512
  };
16088
16513
  const internalOnEntityDelete = (_path, entity) => {
16089
16514
  analyticsController.onAnalyticsEvent?.("single_entity_deleted", { path });
16090
- setSelectedEntities((selectedEntities) => selectedEntities.filter((e) => e.id !== entity.id));
16515
+ toggleEntitySelection(entity, false);
16091
16516
  setDeletedEntities((prev) => [...prev, entity]);
16092
16517
  setLastDeleteTimestamp(Date.now());
16093
16518
  };
16094
16519
  const internalOnMultipleEntitiesDelete = (_path, entities) => {
16095
16520
  analyticsController.onAnalyticsEvent?.("multiple_entities_deleted", { path });
16096
- setSelectedEntities([]);
16521
+ clearSelection();
16097
16522
  setDeleteEntityClicked(void 0);
16098
16523
  setDeletedEntities((prev) => [...prev, ...entities]);
16099
16524
  setLastDeleteTimestamp(Date.now());
@@ -16321,7 +16746,7 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
16321
16746
  return (largeLayout ? 80 + actionsWidth : 70 + actionsWidth) + (collapsedActions.length > 0 ? largeLayout ? 40 : 30 : 0);
16322
16747
  }, [getActionsForEntity, largeLayout]);
16323
16748
  const tableRowActionsBuilder = useCallback(({ entity, size, width, frozen }) => {
16324
- const isSelected = Boolean(usedSelectionController.selectedEntities.find((e) => e.id == entity.id && e.path == entity.path));
16749
+ const isSelected = usedSelectionController.isEntitySelected(entity);
16325
16750
  const customEntityActions = (collection.entityActions ?? EMPTY_ARRAY).map((action) => resolveEntityAction(action, customizationController.entityActions)).filter(Boolean);
16326
16751
  const actions = getActionsForEntity({
16327
16752
  entity,
@@ -16353,6 +16778,26 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
16353
16778
  searchString: tableController.searchString,
16354
16779
  onCountChange: setDocsCount
16355
16780
  });
16781
+ const liveSelectionQuery = useMemo(() => ({
16782
+ path,
16783
+ filterValues: tableController.filterValues,
16784
+ searchString: tableController.searchString,
16785
+ sortBy: tableController.sortBy
16786
+ }), [
16787
+ path,
16788
+ tableController.filterValues,
16789
+ tableController.searchString,
16790
+ tableController.sortBy
16791
+ ]);
16792
+ const selectionQueryKey = selection.type === "query" ? serializeSelectionQuery(selection.query) : void 0;
16793
+ const liveQueryKey = serializeSelectionQuery(liveSelectionQuery);
16794
+ useEffect(() => {
16795
+ if (selectionQueryKey !== void 0 && selectionQueryKey !== liveQueryKey) clearSelection();
16796
+ }, [
16797
+ selectionQueryKey,
16798
+ liveQueryKey,
16799
+ clearSelection
16800
+ ]);
16356
16801
  const { resolvedSlots } = customizationController;
16357
16802
  const headerActionContributions = useMemo(() => resolvedSlots.filter((s) => s.slot === "collection.header.action").sort((a, b) => (a.order ?? 50) - (b.order ?? 50)), [resolvedSlots]);
16358
16803
  const buildAdditionalHeaderWidget = useCallback(({ property, propertyKey, onHover }) => {
@@ -16460,6 +16905,8 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
16460
16905
  collectionEntitiesCount: docsCount ?? void 0,
16461
16906
  resolvedProperties: resolvedCollection.properties,
16462
16907
  viewMode,
16908
+ selectionEnabled: activeSelectionEnabled,
16909
+ selectionQuery: liveSelectionQuery,
16463
16910
  entitiesCount: docsCount,
16464
16911
  openNewDocument,
16465
16912
  compact: isCompact
@@ -16654,7 +17101,7 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
16654
17101
  container: containerRef.current
16655
17102
  }, `popup_form_${popupCell?.propertyKey}_${popupCell?.entityId}`),
16656
17103
  deleteEntityClicked && /* @__PURE__ */ jsx(DeleteEntityDialog, {
16657
- entityOrEntitiesToDelete: deleteEntityClicked,
17104
+ target: deleteEntityClicked,
16658
17105
  path,
16659
17106
  collection,
16660
17107
  open: Boolean(deleteEntityClicked),
@@ -17404,13 +17851,26 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
17404
17851
  const mainViewVisible = activeTab === "__main_##Q$SC^#S6" || Boolean(selectedSecondaryForm);
17405
17852
  const mountedTabsRef = useRef(/* @__PURE__ */ new Set());
17406
17853
  if (activeTab) mountedTabsRef.current.add(activeTab);
17854
+ /**
17855
+ * The record as a context with no form behind it: what a view is handed
17856
+ * where nothing can edit the record — the read-only rendering for a user
17857
+ * without permission, and the entity-view tabs until the form has mounted
17858
+ * and published its own. Its writes throw, so it says `disabled`: that is
17859
+ * the flag every control already honours, and a stand-in reporting
17860
+ * `disabled: false` invited the very write it was about to refuse.
17861
+ *
17862
+ * Not dropped once the form's context arrives. The form mounts even where
17863
+ * it is hidden — for a user who cannot edit, the read-only view is shown
17864
+ * beside a hidden form — so its context always arrives, and the read-only
17865
+ * view lost its context the moment it did: a `formView` flipped to the
17866
+ * default rendering, and `additionalFields` vanished from the default one.
17867
+ */
17407
17868
  const readOnlyFormContext = useMemo(() => {
17408
- if (formContext) return void 0;
17409
17869
  if (!entityId) return void 0;
17410
17870
  const formexStub = createFormexStub(usedEntity?.values ?? {});
17411
17871
  return {
17412
17872
  entityId,
17413
- disabled: false,
17873
+ disabled: true,
17414
17874
  readOnly: true,
17415
17875
  openEntityMode: layout,
17416
17876
  status,
@@ -17431,7 +17891,6 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
17431
17891
  formex: formexStub
17432
17892
  };
17433
17893
  }, [
17434
- formContext,
17435
17894
  entityId,
17436
17895
  layout,
17437
17896
  status,
@@ -17594,8 +18053,32 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
17594
18053
  collection
17595
18054
  ]);
17596
18055
  const formViewConfig = collection.formView;
17597
- const FormViewBuilder = formViewConfig?.Builder ? resolveComponentRef(formViewConfig.Builder) : null;
17598
- formViewConfig?.includeActions;
18056
+ const FormViewBuilder = resolveComponentRef(formViewConfig?.Builder);
18057
+ const formViewIncludeActions = formViewConfig?.includeActions !== false;
18058
+ /**
18059
+ * What the record form renders in place of its generated fields.
18060
+ *
18061
+ * A `formView` Builder is rendered *by the form*, never instead of it. It
18062
+ * used to be drawn here in the form's place, and the form is the only
18063
+ * thing that can hand it a working context — the real one arrives through
18064
+ * `onFormContextReady`, and with no form mounted it never did. Every
18065
+ * Builder got the read-only stand-in above: each keystroke threw inside
18066
+ * `setFieldValue`, and Save and Discard, which wait on that same context,
18067
+ * never appeared. A new record was worse: with no id there is not even a
18068
+ * stand-in, and the screen stayed blank.
18069
+ *
18070
+ * An entity view declared with `includeActions` is a second form over the
18071
+ * same record, and takes the place while its tab is open.
18072
+ */
18073
+ const FormBuilder = selectedSecondaryForm ? resolveComponentRef(selectedSecondaryForm.Builder) : FormViewBuilder;
18074
+ /**
18075
+ * `formView.includeActions: false` leaves saving to the Builder — it holds
18076
+ * `formContext.submit` and `save` — so the bar offers no Save or Discard of
18077
+ * its own. Only for the collection's form: an entity view that declared
18078
+ * `includeActions` is on screen because it asked for them. Delete is a
18079
+ * record action, in the bar's overflow menu, and is not affected.
18080
+ */
18081
+ const formActionsContext = canEdit && formContext && (!FormViewBuilder || formViewIncludeActions || selectedSecondaryForm) ? formContext : void 0;
17599
18082
  const entityReadOnlyView = !canEdit && entity ? FormViewBuilder && readOnlyFormContext ? /* @__PURE__ */ jsx("div", {
17600
18083
  className: cls("@container/form flex-1 flex flex-row w-full overflow-y-auto justify-center", canEdit || !mainViewVisible || selectedSecondaryForm ? "hidden" : ""),
17601
18084
  children: /* @__PURE__ */ jsx("div", {
@@ -17621,20 +18104,7 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
17621
18104
  openEntityMode: layout,
17622
18105
  formContext: readOnlyFormContext
17623
18106
  }) : null;
17624
- const entityView = FormViewBuilder ? /* @__PURE__ */ jsx("div", {
17625
- className: cls("relative flex-1 w-full h-full overflow-auto", (!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : ""),
17626
- children: /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(Suspense, {
17627
- fallback: /* @__PURE__ */ jsx(CircularProgressCenter, {}),
17628
- children: (formContext ?? readOnlyFormContext) && /* @__PURE__ */ jsx(FormViewBuilder, {
17629
- collection,
17630
- parentCollectionSlugs,
17631
- parentEntityIds,
17632
- entity: usedEntity,
17633
- modifiedValues: (formContext ?? readOnlyFormContext)?.formex?.values ?? usedEntity?.values,
17634
- formContext: formContext ?? readOnlyFormContext
17635
- })
17636
- }) })
17637
- }) : /* @__PURE__ */ jsx(ResolvedEntityForm, {
18107
+ const entityView = /* @__PURE__ */ jsx(ResolvedEntityForm, {
17638
18108
  collection,
17639
18109
  path,
17640
18110
  entityId: entityId ?? usedEntity?.id,
@@ -17674,7 +18144,9 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
17674
18144
  setTimeout(() => onCloseRequest?.(), 0);
17675
18145
  }
17676
18146
  },
17677
- Builder: resolveComponentRef(selectedSecondaryForm?.Builder)
18147
+ Builder: FormBuilder,
18148
+ parentCollectionSlugs,
18149
+ parentEntityIds
17678
18150
  });
17679
18151
  const subcollectionTabs = subcollections && subcollections.map((subcollection) => {
17680
18152
  const icon = getIcon(subcollection.icon, void 0, void 0, "smallest");
@@ -17736,25 +18208,25 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
17736
18208
  hasErrors: hasFormErrors,
17737
18209
  saveDisabled: !canEdit || saveDisabled,
17738
18210
  onBack: layout === "full_screen" && !onShowList ? () => navigate(withListState(urlController.buildUrlCollectionPath(path))) : void 0,
17739
- onSave: canEdit && formContext ? () => {
18211
+ onSave: formActionsContext ? () => {
17740
18212
  sideDialogContext.setPendingClose?.(false);
17741
18213
  pendingCloseRef.current = false;
17742
- formContext.submit();
18214
+ formActionsContext.submit();
17743
18215
  } : void 0,
17744
- onSaveAndClose: canEdit && formContext && canCloseAfterSave ? () => {
18216
+ onSaveAndClose: formActionsContext && canCloseAfterSave ? () => {
17745
18217
  if (layout === "split") {
17746
18218
  pendingCloseRef.current = true;
17747
- Promise.resolve(formContext.submit()).finally(() => {
18219
+ Promise.resolve(formActionsContext.submit()).finally(() => {
17748
18220
  pendingCloseRef.current = false;
17749
18221
  });
17750
18222
  return;
17751
18223
  }
17752
18224
  sideDialogContext.setPendingClose?.(true);
17753
- Promise.resolve(formContext.submit()).finally(() => sideDialogContext.setPendingClose?.(false));
18225
+ Promise.resolve(formActionsContext.submit()).finally(() => sideDialogContext.setPendingClose?.(false));
17754
18226
  } : void 0,
17755
18227
  saveAndClosePlacement: layout === "split" ? "menu" : "button",
17756
18228
  onClose: onCloseRequest,
17757
- onDiscard: canEdit && formContext ? () => discard(formContext.formex, status) : void 0,
18229
+ onDiscard: formActionsContext ? () => discard(formActionsContext.formex, status) : void 0,
17758
18230
  onInspect: includeJsonView ? () => setInspectorTab("json") : void 0,
17759
18231
  onViewHistory: includeHistoryView ? () => setInspectorTab("history") : void 0,
17760
18232
  externalLink: usedEntity ? customizationController?.entityLinkBuilder?.({ entity: usedEntity }) : void 0,
@@ -19611,6 +20083,8 @@ function SideDialogView({ offsetPosition, panel, panelIndex, isTopPanel }) {
19611
20083
  }
19612
20084
  //#endregion
19613
20085
  //#region src/components/ReferenceTable/SelectionTableBinding.tsx
20086
+ /** Stable identity, so the effect that propagates the picks does not re-fire. */
20087
+ var EMPTY_ENTITIES = [];
19614
20088
  /**
19615
20089
  * This component allows to select entities from a given collection.
19616
20090
  * You probably want to open this dialog as a side view using {@link useSelectionTableBinding}
@@ -19632,11 +20106,12 @@ function SelectionTableBindingInternal({ onSingleEntitySelected, onMultipleEntit
19632
20106
  const dataClient = useData();
19633
20107
  const [entitiesDisplayedFirst, setEntitiesDisplayedFirst] = useState([]);
19634
20108
  const selectionController = useSelectionController();
20109
+ const selectedEntities = selectionController.selection.type === "entities" ? selectionController.selection.entities : EMPTY_ENTITIES;
19635
20110
  const selectionInitializedRef = useRef(false);
19636
20111
  useEffect(() => {
19637
20112
  if (!selectionInitializedRef.current) return;
19638
- if (onMultipleEntitiesSelected) onMultipleEntitiesSelected(selectionController.selectedEntities);
19639
- }, [selectionController.selectedEntities]);
20113
+ if (onMultipleEntitiesSelected) onMultipleEntitiesSelected(selectedEntities);
20114
+ }, [selectedEntities]);
19640
20115
  /**
19641
20116
  * Fetch initially selected ids
19642
20117
  */
@@ -19684,7 +20159,6 @@ function SelectionTableBindingInternal({ onSingleEntitySelected, onMultipleEntit
19684
20159
  path,
19685
20160
  entityId: entity.id
19686
20161
  });
19687
- const selectedEntities = selectionController.selectedEntities;
19688
20162
  if (selectedEntities.map((e) => e.id).indexOf(entity.id) > -1) selectionController.setSelectedEntities(selectedEntities.filter((item) => item.id !== entity.id));
19689
20163
  else {
19690
20164
  if (maxSelection && selectedEntities.length >= maxSelection) return;
@@ -19698,6 +20172,7 @@ function SelectionTableBindingInternal({ onSingleEntitySelected, onMultipleEntit
19698
20172
  path,
19699
20173
  sideDialogContext,
19700
20174
  selectionController,
20175
+ selectedEntities,
19701
20176
  maxSelection
19702
20177
  ]);
19703
20178
  const onNewClick = () => {
@@ -19714,13 +20189,12 @@ function SelectionTableBindingInternal({ onSingleEntitySelected, onMultipleEntit
19714
20189
  });
19715
20190
  };
19716
20191
  const tableRowActionsBuilder = ({ entity, size, width, frozen }) => {
19717
- const selectedEntities = selectionController.selectedEntities;
19718
20192
  return /* @__PURE__ */ jsx(CollectionRowActions, {
19719
20193
  width,
19720
20194
  frozen,
19721
20195
  entity,
19722
20196
  size,
19723
- isSelected: selectedEntities && selectedEntities.map((e) => e.id).indexOf(entity.id) > -1,
20197
+ isSelected: selectionController.isEntitySelected(entity),
19724
20198
  selectionEnabled: multiselect,
19725
20199
  hideId: collection?.hideIdFromCollection,
19726
20200
  path,
@@ -21222,7 +21696,7 @@ function StorageItemPreview({ name, property, value, onRemove, disabled, size, p
21222
21696
  }
21223
21697
  //#endregion
21224
21698
  //#region src/form/field_bindings/StorageUploadFieldBinding.tsx
21225
- var dropZoneClasses = "box-border relative pt-[2px] h-[172px] items-center border border-transparent outline-none rounded-md duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] focus:border-primary-solid";
21699
+ var dropZoneClasses = "box-border relative pt-[2px] h-[172px] items-center border border-transparent outline-none rounded-md duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] focus:border-primary";
21226
21700
  var disabledClasses = fieldBackgroundDisabledMixin;
21227
21701
  var nonActiveDropClasses = fieldBackgroundHoverMixin;
21228
21702
  var activeDropClasses = "pt-0 border-2 border-solid";
@@ -21526,7 +22000,7 @@ function TextFieldBinding({ propertyKey, value, setValue, error, showError, disa
21526
22000
  let url;
21527
22001
  if (property.type === "string") {
21528
22002
  multiline = property.admin?.multiline;
21529
- url = property.admin?.urlPreview;
22003
+ url = property.admin?.urlPreview ?? property.url;
21530
22004
  }
21531
22005
  useClearRestoreValue({
21532
22006
  property,
@@ -21552,7 +22026,7 @@ function TextFieldBinding({ propertyKey, value, setValue, error, showError, disa
21552
22026
  if (property.type === "number") inputType = "number";
21553
22027
  else if (property.type === "string") {
21554
22028
  if (property.email) inputType = "email";
21555
- else if (property.admin?.urlPreview) inputType = "url";
22029
+ else if (property.admin?.urlPreview || property.url) inputType = "url";
21556
22030
  }
21557
22031
  const label = hideLabel ? void 0 : /* @__PURE__ */ jsx(LabelWithIcon, {
21558
22032
  icon: getIconForProperty(property, "small"),
@@ -21721,7 +22195,7 @@ function VectorFieldBinding({ propertyKey, value, setValue, error, showError, di
21721
22195
  ]
21722
22196
  })]
21723
22197
  }), showValues && isPopulated && /* @__PURE__ */ jsx("div", {
21724
- className: "mt-1 p-3 rounded-lg bg-surface-well border border-hairline max-h-36 overflow-y-auto font-mono text-[11px] leading-relaxed text-text-secondary dark:text-text-secondary-dark break-all selection:bg-primary-100 dark:selection:bg-primary-900/40",
22198
+ className: "mt-1 p-3 rounded-lg bg-surface-well border border-hairline max-h-36 overflow-y-auto font-mono text-[11px] leading-relaxed text-text-secondary dark:text-text-secondary-dark break-all selection:bg-primary/20",
21725
22199
  children: arrayValue.join(", ")
21726
22200
  })]
21727
22201
  }) : /* @__PURE__ */ jsxs("div", {
@@ -23399,7 +23873,7 @@ function LocalChangesMenu({ cachedData, formex, onClearLocalChanges, cacheKey, p
23399
23873
  *
23400
23874
  * @group Components
23401
23875
  */
23402
- function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified, onIdChange, onSaved, entity, initialDirtyValues, onFormContextReady, initialStatus, className, onStatusChange, onEntityChange, openEntityMode = "full_screen", formex: formexProp, disabled: disabledProp, Builder, EntityFormActionsComponent = EntityFormActions, showDefaultActions = true, navigateBack: navigateBackProp, children, onSubmit: onSubmitProp, onValuesChangeDeferred: onValuesChangeDeferredProp, onReset: onResetProp, uniqueFieldValidator: uniqueFieldValidatorProp, beforeFields, afterFields, pluginActions: pluginActionsProp, computedInitialValues, hasLocalChanges: hasLocalChangesProp, localChangesData, manualApplyLocalChanges, localChangesCacheKey, onClearLocalChanges }) {
23876
+ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified, onIdChange, onSaved, entity, initialDirtyValues, onFormContextReady, initialStatus, className, onStatusChange, onEntityChange, openEntityMode = "full_screen", formex: formexProp, disabled: disabledProp, Builder, parentCollectionSlugs, parentEntityIds, EntityFormActionsComponent = EntityFormActions, showDefaultActions = true, navigateBack: navigateBackProp, children, onSubmit: onSubmitProp, onValuesChangeDeferred: onValuesChangeDeferredProp, onReset: onResetProp, uniqueFieldValidator: uniqueFieldValidatorProp, beforeFields, afterFields, pluginActions: pluginActionsProp, computedInitialValues, hasLocalChanges: hasLocalChangesProp, localChangesData, manualApplyLocalChanges, localChangesCacheKey, onClearLocalChanges }) {
23403
23877
  const navigateBack = useCallback(() => {
23404
23878
  if (navigateBackProp) navigateBackProp();
23405
23879
  }, [navigateBackProp]);
@@ -23547,6 +24021,7 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
23547
24021
  });
23548
24022
  };
23549
24023
  const disabled = formex.isSubmitting || Boolean(disabledProp);
24024
+ const rebaseContext = useRebaseContext();
23550
24025
  const formContext = {
23551
24026
  setFieldValue: useCallback((key, value) => formex.setFieldValue(key, value), []),
23552
24027
  values: formex.values,
@@ -23654,16 +24129,15 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
23654
24129
  if (!additionalField || !entity) return null;
23655
24130
  const AdditionalFieldBuilder = additionalField.Builder;
23656
24131
  if (!AdditionalFieldBuilder && !additionalField.value) throw new Error("When using additional fields you need to provide a Builder or a value");
23657
- const additionalFieldContext = formContext;
23658
24132
  const child = AdditionalFieldBuilder ? /* @__PURE__ */ jsx(AdditionalFieldBuilder, {
23659
24133
  entity,
23660
- context: additionalFieldContext
24134
+ context: rebaseContext
23661
24135
  }) : /* @__PURE__ */ jsx("div", {
23662
24136
  className: "w-full",
23663
24137
  children: /* @__PURE__ */ jsx(AdditionalFieldValue, {
23664
24138
  field: additionalField,
23665
24139
  entity,
23666
- context: additionalFieldContext
24140
+ context: rebaseContext
23667
24141
  })
23668
24142
  });
23669
24143
  return /* @__PURE__ */ jsx("div", {
@@ -23712,11 +24186,16 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
23712
24186
  }, `field_${field.key}`);
23713
24187
  };
23714
24188
  const formFields = () => {
23715
- if (Builder) return /* @__PURE__ */ jsx(Builder, {
23716
- collection,
23717
- entity,
23718
- modifiedValues: formex.values,
23719
- formContext
24189
+ if (Builder) return /* @__PURE__ */ jsx(Suspense, {
24190
+ fallback: /* @__PURE__ */ jsx(CircularProgressCenter, {}),
24191
+ children: /* @__PURE__ */ jsx(Builder, {
24192
+ collection,
24193
+ parentCollectionSlugs,
24194
+ parentEntityIds,
24195
+ entity,
24196
+ modifiedValues: formex.values,
24197
+ formContext
24198
+ })
23720
24199
  });
23721
24200
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(FormSections, {
23722
24201
  sections: showRail || !layout.sidebar.length ? layout.sections : [...layout.sections, {
@@ -25090,6 +25569,6 @@ function getFullIdPath(propertyKey, propertyNamespace) {
25090
25569
  return idToPropertiesPath(propertyNamespace ? `${propertyNamespace}.${propertyKey}` : propertyKey);
25091
25570
  }
25092
25571
  //#endregion
25093
- export { NAVIGATION_DEFAULT_GROUP_NAME as $, getEntityTitlePropertyKeyForEntity as $n, getCollectionBySlugWithin as $t, getFieldId as A, NumberPropertyPreview as An, convertDataToEntity as At, MapFieldBinding as B, RelationPreview as Bn, detectCsvDelimiter as Bt, EntityFormBinding as C, ReadOnlyFieldBinding as Cn, getIconForProperty as Cr, EntityCardBinding as Ct, getDefaultFieldConfig as D, ArrayOfMapsPreview as Dn, getResolvedPropertyInPath as Dr, useCollectionEditorDialogsState as Dt, DEFAULT_FIELD_CONFIGS as E, FieldHelperText as En, getPropertyInPath$1 as Er, ConfigControllerProvider as Et, SelectFieldBinding as F, ArrayOneOfPreview as Fn, ImportSaveInProgress as Ft, useSelectionDialog as G, SidePanelControllerContext as Gn, SearchIconsView as Gt, DateTimeFieldBinding as H, InlineEntityListPreview as Hn, parseCsvToObjects as Ht, RepeatFieldBinding as I, ArrayOfStringsPreview as In, IMPORT_BATCH_SIZE as It, useBuildUrlController as J, useCollectionRegistryController as Jn, BreadcrumbsProvider as Jt, SelectionTableBinding as K, useSidePanel as Kn, FieldCaption as Kt, ReferenceFieldBinding as L, ArrayPropertyEnumPreview as Ln, saveImportedEntities as Lt, TextFieldBinding as M, DatePreview as Mn, processValueMapping as Mt, SwitchFieldBinding as N, KeyValuePreview as Nn, getInferenceType as Nt, getDefaultFieldId as O, PropertyPreview as On, isReferenceProperty as Or, ImportNewPropertyFieldPreview as Ot, StorageUploadFieldBinding as P, MapPropertyPreview as Pn, useImportConfig as Pt, useResolvedCollections as Q, getEntityTitlePropertyKey as Qn, addInitialSlash as Qt, MultiSelectFieldBinding as R, ArrayEnumPreview as Rn, ImportFileUpload as Rt, isSchemaChangeCancelled as S, useClearRestoreValue as Sn, getDefaultPropertiesOrder as Sr, CollectionCardViewBinding as St, PropertyFieldBinding as T, LabelWithIcon as Tn, getPropertiesWithPropertiesOrder as Tr, useCollectionEditorController as Tt, BlockFieldBinding as U, ReferencePreview as Un, ArrayContainer as Ut, KeyValueFieldBinding as V, ArrayOfReferencesPreview as Vn, parseCsvRows as Vt, ArrayOfReferencesFieldBinding as W, EntityPreviewBinding as Wn, PropertyConfigBadge as Wt, useTopLevelNavigation as X, useResolvedUser as Xn, resolveEntityView as Xt, useBuildNavigationStateController as Y, getUserLabel as Yn, resolveEntityAction as Yt, useResolvedViews as Z, getEntityPreviewKeys as Zn, mergeEntityActions as Zt, useCollectionsConfigController as _, useNavigationStateController as _n, LABEL_ICON_SIZE as _r, editEntityAction as _t, namespaceToPropertiesPath as a, resolveCollectionPathIds$1 as an, SkeletonPropertyComponent as ar, getEntityViewWidth as at, asUnavailable as b, SelectableTableContext as bn, PropertyIdCopyTooltip as br, DetailViewBinding as bt, buildCollectionGenerationCallback as c, useSelectionController as cn, renderSkeletonImageThumbnail as cr, useSafeSnackbarController as ct, fromSerializableCollectionConfigs as d, SelectableTable as dn, ImagePreview as dr, getInitialEntityValues as dt, getCollectionPathsCombinations as en, ArrayPropertyPreview as er, useBuildCollectionRegistryController as et, fromSerializableProperties as f, CollectionRowActions as fn, EmptyValue as fr, removeEmptyContainers as ft, toSerializableProperty as g, NavigationStateContext as gn, FieldBlock as gr, deleteEntityAction as gt, toSerializableProperties as h, useUrlController as hn, RecordMeta as hr, copyEntityAction as ht, namespaceToPropertiesOrderPath as i, removeTrailingSlash$1 as in, StorageThumbnailInternal as ir, buildSidePanelsFromUrl as it, VectorFieldBinding as j, BooleanPreview as jn, flattenEntry as jt, getFieldConfig as k, UserPreview as kn, isRelationProperty as kr, DataNewPropertiesMapping as kt, validateCollectionJson as l, VirtualTableInput$1 as ln, renderSkeletonText as lr, extractTouchedValues as lt, toSerializableCollectionConfig as m, UrlContext as mn, FormRail as mr, CollectionViewBinding as mt, getFullIdPath as n, removeInitialAndTrailingSlashes$1 as nn, EnumValuesChip as nr, resolveNavigationFrom as nt, CollectionGenerationApiError as o, resolveOpenEntityMode as on, renderSkeletonCaptionText as or, useBuildSidePanel as ot, fromSerializableProperty as p, useAdminContext as pn, FormSections as pr, zodToFormErrors as pt, SideDialogs as q, CollectionRegistryContext as qn, useBreadcrumbsController as qt, idToPropertiesPath as r, removeInitialSlash as rn, StorageThumbnail as rr, useResolvedNavigationFrom as rt, DEFAULT_COLLECTION_GENERATION_ENDPOINT as s, resolveViewMode as sn, renderSkeletonIcon as sr, EditViewBinding as st, getFullId as t, getLastSegment$1 as tn, StringPropertyPreview as tr, useHistory as tt, fromSerializableCollectionConfig as u, CollectionTableBinding as un, UrlComponentPreview as ur, getChanges as ut, LiveSchemaError as v, useSideDialogsController as vn, isSelfLabellingProperty as vr, resetPasswordAction as vt, EntityForm as w, LabelWithIconAndTooltip as wn, getIconForWidget as wr, CollectionViewActions as wt, createLiveSchemaClient as x, ArrayCustomShapedFieldBinding as xn, getBracketNotation as xr, EntityViewBinding as xt, SchemaChangeCancelled as y, SideDialogsControllerContext as yn, spanClass as yr, CreationResultDialog as yt, MarkdownEditorFieldBinding as z, ArrayOfStorageComponentsPreview as zn, convertFileToJson as zt };
25572
+ export { NAVIGATION_DEFAULT_GROUP_NAME as $, useSidePanel as $n, getCollectionBySlugWithin as $t, getFieldId as A, ReadOnlyFieldBinding as An, getIconForProperty as Ar, convertDataToEntity as At, MapFieldBinding as B, KeyValuePreview as Bn, detectCsvDelimiter as Bt, EntityFormBinding as C, NavigationStateContext as Cn, FieldBlock as Cr, EntityCardBinding as Ct, getDefaultFieldConfig as D, SelectableTableContext as Dn, PropertyIdCopyTooltip as Dr, useCollectionEditorDialogsState as Dt, DEFAULT_FIELD_CONFIGS as E, SideDialogsControllerContext as En, spanClass as Er, ConfigControllerProvider as Et, SelectFieldBinding as F, PropertyPreview as Fn, isReferenceProperty as Fr, ImportSaveInProgress as Ft, useSelectionDialog as G, ArrayEnumPreview as Gn, SearchIconsView as Gt, DateTimeFieldBinding as H, ArrayOneOfPreview as Hn, parseCsvToObjects as Ht, RepeatFieldBinding as I, UserPreview as In, isRelationProperty as Ir, IMPORT_BATCH_SIZE as It, useBuildUrlController as J, ArrayOfReferencesPreview as Jn, BreadcrumbsProvider as Jt, SelectionTableBinding as K, ArrayOfStorageComponentsPreview as Kn, FieldCaption as Kt, ReferenceFieldBinding as L, NumberPropertyPreview as Ln, saveImportedEntities as Lt, TextFieldBinding as M, LabelWithIcon as Mn, getPropertiesWithPropertiesOrder as Mr, processValueMapping as Mt, SwitchFieldBinding as N, FieldHelperText as Nn, getPropertyInPath$1 as Nr, getInferenceType as Nt, getDefaultFieldId as O, ArrayCustomShapedFieldBinding as On, getBracketNotation as Or, ImportNewPropertyFieldPreview as Ot, StorageUploadFieldBinding as P, ArrayOfMapsPreview as Pn, getResolvedPropertyInPath as Pr, useImportConfig as Pt, useResolvedCollections as Q, SidePanelControllerContext as Qn, addInitialSlash as Qt, MultiSelectFieldBinding as R, BooleanPreview as Rn, ImportFileUpload as Rt, isSchemaChangeCancelled as S, useUrlController as Sn, RecordMeta as Sr, CollectionCardViewBinding as St, PropertyFieldBinding as T, useSideDialogsController as Tn, isSelfLabellingProperty as Tr, useCollectionEditorController as Tt, BlockFieldBinding as U, ArrayOfStringsPreview as Un, ArrayContainer as Ut, KeyValueFieldBinding as V, MapPropertyPreview as Vn, parseCsvRows as Vt, ArrayOfReferencesFieldBinding as W, ArrayPropertyEnumPreview as Wn, PropertyConfigBadge as Wt, useTopLevelNavigation as X, ReferencePreview as Xn, resolveEntityView as Xt, useBuildNavigationStateController as Y, InlineEntityListPreview as Yn, resolveEntityAction as Yt, useResolvedViews as Z, EntityPreviewBinding as Zn, mergeEntityActions as Zt, useCollectionsConfigController as _, CollectionTableBinding as _n, UrlComponentPreview as _r, editEntityAction as _t, namespaceToPropertiesPath as a, resolveCollectionPathIds$1 as an, getEntityTitlePropertyKey as ar, getEntityViewWidth as at, asUnavailable as b, useAdminContext as bn, FormSections as br, DetailViewBinding as bt, buildCollectionGenerationCallback as c, SelectionMenu as cn, StringPropertyPreview as cr, useSafeSnackbarController as ct, fromSerializableCollectionConfigs as d, resolveSelection as dn, StorageThumbnailInternal as dr, getInitialEntityValues as dt, getCollectionPathsCombinations as en, CollectionRegistryContext as er, useBuildCollectionRegistryController as et, fromSerializableProperties as f, selectionQueryToFindParams as fn, SkeletonPropertyComponent as fr, removeEmptyContainers as ft, toSerializableProperty as g, VirtualTableInput$1 as gn, renderSkeletonText as gr, deleteEntityAction as gt, toSerializableProperties as h, useSelectionController as hn, renderSkeletonImageThumbnail as hr, copyEntityAction as ht, namespaceToPropertiesOrderPath as i, removeTrailingSlash$1 as in, getEntityPreviewKeys as ir, buildSidePanelsFromUrl as it, VectorFieldBinding as j, LabelWithIconAndTooltip as jn, getIconForWidget as jr, flattenEntry as jt, getFieldConfig as k, useClearRestoreValue as kn, getDefaultPropertiesOrder as kr, DataNewPropertiesMapping as kt, validateCollectionJson as l, MAX_SELECTION_ROWS as ln, EnumValuesChip as lr, extractTouchedValues as lt, toSerializableCollectionConfig as m, walkEntityPages as mn, renderSkeletonIcon as mr, CollectionViewBinding as mt, getFullIdPath as n, removeInitialAndTrailingSlashes$1 as nn, getUserLabel as nr, resolveNavigationFrom as nt, CollectionGenerationApiError as o, resolveOpenEntityMode as on, getEntityTitlePropertyKeyForEntity as or, useBuildSidePanel as ot, fromSerializableProperty as p, serializeSelectionQuery as pn, renderSkeletonCaptionText as pr, zodToFormErrors as pt, SideDialogs as q, RelationPreview as qn, useBreadcrumbsController as qt, idToPropertiesPath as r, removeInitialSlash as rn, useResolvedUser as rr, useResolvedNavigationFrom as rt, DEFAULT_COLLECTION_GENERATION_ENDPOINT as s, resolveViewMode as sn, ArrayPropertyPreview as sr, EditViewBinding as st, getFullId as t, getLastSegment$1 as tn, useCollectionRegistryController as tr, useHistory as tt, fromSerializableCollectionConfig as u, SELECTION_PAGE_SIZE as un, StorageThumbnail as ur, getChanges as ut, LiveSchemaError as v, SelectableTable as vn, ImagePreview as vr, resetPasswordAction as vt, EntityForm as w, useNavigationStateController as wn, LABEL_ICON_SIZE as wr, CollectionViewActions as wt, createLiveSchemaClient as x, UrlContext as xn, FormRail as xr, EntityViewBinding as xt, SchemaChangeCancelled as y, CollectionRowActions as yn, EmptyValue as yr, CreationResultDialog as yt, MarkdownEditorFieldBinding as z, DatePreview as zn, convertFileToJson as zt };
25094
25573
 
25095
- //# sourceMappingURL=util-4oPxPkbp.js.map
25574
+ //# sourceMappingURL=util-h6jhiiBE.js.map