@rebasepro/admin 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CollectionEditorDialog-Dxx0ZaDk.js → CollectionEditorDialog-D2Vf4C3-.js} +7 -7
- package/dist/{CollectionEditorDialog-Dxx0ZaDk.js.map → CollectionEditorDialog-D2Vf4C3-.js.map} +1 -1
- package/dist/{PropertyEditView-D6xNTm0d.js → PropertyEditView-D3NBsK9_.js} +4 -13
- package/dist/PropertyEditView-D3NBsK9_.js.map +1 -0
- package/dist/{RouterCollectionsStudioView-C4vSn9Pu.js → RouterCollectionsStudioView-hPB54l3Q.js} +4 -4
- package/dist/{RouterCollectionsStudioView-C4vSn9Pu.js.map → RouterCollectionsStudioView-hPB54l3Q.js.map} +1 -1
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/CollectionViewBinding/CollectionViewStartActions.d.ts +9 -1
- package/dist/components/RelationSelector.d.ts +8 -0
- package/dist/{export-BZi1MzQ5.js → export-oauWlxuu.js} +2 -2
- package/dist/{export-BZi1MzQ5.js.map → export-oauWlxuu.js.map} +1 -1
- package/dist/form/useUndoableDiscard.d.ts +15 -0
- package/dist/{history-B5pkfldE.js → history-C2cqc0bw.js} +2 -2
- package/dist/{history-B5pkfldE.js.map → history-C2cqc0bw.js.map} +1 -1
- package/dist/{import-CXpuYo-T.js → import-Bxv5JPm-.js} +2 -2
- package/dist/{import-CXpuYo-T.js.map → import-Bxv5JPm-.js.map} +1 -1
- package/dist/index.js +17 -25
- package/dist/index.js.map +1 -1
- package/dist/{util-BKtHPLe4.js → util-C4qsI1QV.js} +163 -55
- package/dist/util-C4qsI1QV.js.map +1 -0
- package/package.json +12 -9
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +4 -4
- package/src/components/CollectionPanel.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +3 -11
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +32 -2
- package/src/components/CollectionViewBinding/EntityCardBinding.tsx +10 -8
- package/src/components/DefaultAppBar.tsx +0 -10
- package/src/components/DetailViewBinding.tsx +18 -1
- package/src/components/EditViewBinding.tsx +26 -2
- package/src/components/ReferenceTable/SelectionTableBinding.tsx +10 -1
- package/src/components/RelationSelector.tsx +115 -3
- package/src/contexts/BreacrumbsContext.tsx +4 -21
- package/src/form/EntityForm.tsx +8 -3
- package/src/form/components/FormRail.tsx +15 -4
- package/src/form/useUndoableDiscard.ts +39 -0
- package/src/preview/components/StorageThumbnail.tsx +35 -1
- package/src/routes/RebaseRoute.tsx +2 -10
- package/dist/PropertyEditView-D6xNTm0d.js.map +0 -1
- package/dist/util-BKtHPLe4.js.map +0 -1
|
@@ -374,7 +374,7 @@ function isPromise(value) {
|
|
|
374
374
|
function FormRail({ fields, showRecordMeta, entity, renderField }) {
|
|
375
375
|
if (!fields.length && !showRecordMeta) return null;
|
|
376
376
|
return /* @__PURE__ */ jsxs("aside", {
|
|
377
|
-
className: cls("flex flex-col gap-6 shrink-0 w-76 border-l overflow-y-auto", "px-5 py-6 bg-surface-50 dark:bg-surface-900", defaultBorderMixin),
|
|
377
|
+
className: cls("flex flex-col gap-6 shrink-0 w-76 @7xl/form:w-84 border-l overflow-y-auto", "px-5 py-6 bg-surface-50 dark:bg-surface-900", defaultBorderMixin),
|
|
378
378
|
children: [fields.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
379
379
|
className: "flex flex-col gap-5",
|
|
380
380
|
children: fields.map(renderField)
|
|
@@ -931,6 +931,33 @@ function areEqual$1(prevProps, nextProps) {
|
|
|
931
931
|
return prevProps.size === nextProps.size && prevProps.storagePathOrDownloadUrl === nextProps.storagePathOrDownloadUrl && prevProps.storeUrl === nextProps.storeUrl && prevProps.interactive === nextProps.interactive && prevProps.fill === nextProps.fill && prevProps.storageSourceKey === nextProps.storageSourceKey;
|
|
932
932
|
}
|
|
933
933
|
var URL_CACHE = {};
|
|
934
|
+
/**
|
|
935
|
+
* The signed-URL request currently in flight for a given cache key.
|
|
936
|
+
*
|
|
937
|
+
* A collection view draws one thumbnail per row and rows share images far more
|
|
938
|
+
* often than not — the demo's 200 blog posts are illustrated by 20 hero images.
|
|
939
|
+
* Every one of those thumbnails mounts in the same tick, and each used to call
|
|
940
|
+
* `getSignedUrl` on its own: {@link URL_CACHE} is only written when a response
|
|
941
|
+
* *lands*, so it is still empty while the burst goes out and dedupes nothing.
|
|
942
|
+
* The result was five or more identical requests per distinct file, and minting
|
|
943
|
+
* a download token is a real request — `/api/storage/metadata/<path>` — against
|
|
944
|
+
* a rate-limited surface. One page view was enough to exhaust the budget and
|
|
945
|
+
* every thumbnail on the screen then failed with a 429.
|
|
946
|
+
*
|
|
947
|
+
* Sharing the promise collapses that burst to one request per file. It
|
|
948
|
+
* deliberately does not extend how long anything is cached: `DownloadConfig.url`
|
|
949
|
+
* is temporal, so a later mount still refetches exactly as it did before.
|
|
950
|
+
*/
|
|
951
|
+
var IN_FLIGHT = /* @__PURE__ */ new Map();
|
|
952
|
+
function getSignedUrlOnce(storage, path, cacheKey) {
|
|
953
|
+
const existing = IN_FLIGHT.get(cacheKey);
|
|
954
|
+
if (existing) return existing;
|
|
955
|
+
const request = storage.getSignedUrl(path).finally(() => {
|
|
956
|
+
IN_FLIGHT.delete(cacheKey);
|
|
957
|
+
});
|
|
958
|
+
IN_FLIGHT.set(cacheKey, request);
|
|
959
|
+
return request;
|
|
960
|
+
}
|
|
934
961
|
function StorageThumbnailInternal({ storeUrl, interactive, storagePathOrDownloadUrl, size, fill, storageSourceKey }) {
|
|
935
962
|
const { t } = useTranslation();
|
|
936
963
|
const [error, setError] = React.useState(void 0);
|
|
@@ -945,7 +972,7 @@ function StorageThumbnailInternal({ storeUrl, interactive, storagePathOrDownload
|
|
|
945
972
|
useEffect(() => {
|
|
946
973
|
if (!storagePathOrDownloadUrl) return;
|
|
947
974
|
let unmounted = false;
|
|
948
|
-
storage
|
|
975
|
+
getSignedUrlOnce(storage, storagePathOrDownloadUrl, cacheKey).then(function(downloadConfig) {
|
|
949
976
|
if (!unmounted) {
|
|
950
977
|
setDownloadConfig(downloadConfig);
|
|
951
978
|
URL_CACHE[cacheKey] = downloadConfig;
|
|
@@ -4598,10 +4625,13 @@ function relationCardinality$2(relation) {
|
|
|
4598
4625
|
if (relation.kind === "belongsTo" || relation.kind === "hasOne") return "one";
|
|
4599
4626
|
return relation.cardinality;
|
|
4600
4627
|
}
|
|
4601
|
-
var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChange, invisible, disabled, placeholder, useChips = true, className, relation, multiple: multipleOverride, fixedFilter, pageSize, emptyPlaceholder, searchPlaceholder = "Search...", noResultsText = "No matches.", emptyText = "Select…", emptyCollectionText, loadingText = "Loading..." }, ref) => {
|
|
4628
|
+
var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChange, invisible, disabled, placeholder, useChips = true, className, relation, multiple: multipleOverride, fixedFilter, pageSize, emptyPlaceholder, searchPlaceholder = "Search...", noResultsText = "No matches.", emptyText = "Select…", emptyCollectionText, loadingText = "Loading...", allowCreate = true }, ref) => {
|
|
4602
4629
|
const collection = relation.target();
|
|
4630
|
+
const dataPath = getCollectionDataPath(collection);
|
|
4603
4631
|
const dataClient = useData();
|
|
4604
4632
|
const sidePanelController = useSidePanel();
|
|
4633
|
+
const { canCreate } = usePermissions();
|
|
4634
|
+
const { t } = useTranslation();
|
|
4605
4635
|
const contextPortalContainer = usePortalContainer();
|
|
4606
4636
|
const multiple = multipleOverride ?? relationCardinality$2(relation) === "many";
|
|
4607
4637
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
@@ -4613,7 +4643,7 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4613
4643
|
const localSelectionIdsRef = useRef(null);
|
|
4614
4644
|
const pinnedIdsRef = useRef(null);
|
|
4615
4645
|
const { items: availableItems, isLoading, hasMore, search, loadMore, entityToRelationItem } = useRelationSelector({
|
|
4616
|
-
path:
|
|
4646
|
+
path: dataPath,
|
|
4617
4647
|
collection,
|
|
4618
4648
|
fixedFilter,
|
|
4619
4649
|
pageSize,
|
|
@@ -4770,6 +4800,8 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4770
4800
|
if (multiple) onValueChange?.(selected.length ? selected.map((i) => i.relation) : void 0);
|
|
4771
4801
|
else onValueChange?.(selected[0]?.relation);
|
|
4772
4802
|
}, [onValueChange, multiple]);
|
|
4803
|
+
const emitValueChangeRef = useRef(emitValueChange);
|
|
4804
|
+
emitValueChangeRef.current = emitValueChange;
|
|
4773
4805
|
const computeSelectionFingerprint = useCallback((items) => {
|
|
4774
4806
|
return items.map((i) => String(i.id)).sort().join(",");
|
|
4775
4807
|
}, []);
|
|
@@ -4846,6 +4878,45 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4846
4878
|
isPopoverOpenRef.current = false;
|
|
4847
4879
|
pinnedIdsRef.current = null;
|
|
4848
4880
|
}, []);
|
|
4881
|
+
const canCreateTarget = allowCreate && canCreate(collection, dataPath);
|
|
4882
|
+
const buildDefaultValues = useCallback((searchText) => {
|
|
4883
|
+
const trimmed = searchText.trim();
|
|
4884
|
+
if (!trimmed) return void 0;
|
|
4885
|
+
const titleKey = getTitlePropertyKey(collection);
|
|
4886
|
+
if (!titleKey || titleKey.includes(".")) return void 0;
|
|
4887
|
+
const titleProperty = collection.properties?.[titleKey];
|
|
4888
|
+
if (!titleProperty || typeof titleProperty === "function") return void 0;
|
|
4889
|
+
if (titleProperty.type !== "string" || titleProperty.enum) return void 0;
|
|
4890
|
+
return { [titleKey]: trimmed };
|
|
4891
|
+
}, [collection]);
|
|
4892
|
+
const handleCreateNew = useCallback(() => {
|
|
4893
|
+
const defaultValues = buildDefaultValues(searchString);
|
|
4894
|
+
closePopover();
|
|
4895
|
+
sidePanelController.open({
|
|
4896
|
+
path: dataPath,
|
|
4897
|
+
collection,
|
|
4898
|
+
updateUrl: false,
|
|
4899
|
+
closeOnSave: true,
|
|
4900
|
+
defaultValues,
|
|
4901
|
+
onUpdate: ({ entity }) => {
|
|
4902
|
+
if (!entity) return;
|
|
4903
|
+
const item = entityToRelationItemRef.current(entity, new EntityRelation(entity.id, dataPath));
|
|
4904
|
+
const current = selectedItemsRef.current;
|
|
4905
|
+
const newSelected = multiple ? [...current.filter((i) => String(i.id) !== String(item.id)), item] : [item];
|
|
4906
|
+
setSelectedItems(newSelected);
|
|
4907
|
+
localSelectionIdsRef.current = newSelected.map((i) => String(i.id)).sort().join(",");
|
|
4908
|
+
emitValueChangeRef.current(newSelected);
|
|
4909
|
+
}
|
|
4910
|
+
});
|
|
4911
|
+
}, [
|
|
4912
|
+
buildDefaultValues,
|
|
4913
|
+
searchString,
|
|
4914
|
+
closePopover,
|
|
4915
|
+
sidePanelController,
|
|
4916
|
+
dataPath,
|
|
4917
|
+
collection,
|
|
4918
|
+
multiple
|
|
4919
|
+
]);
|
|
4849
4920
|
const resolvedPlaceholder = placeholder || emptyPlaceholder || emptyText;
|
|
4850
4921
|
const portalContainer = contextPortalContainer ?? (typeof document !== "undefined" ? document.body : void 0) ?? void 0;
|
|
4851
4922
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(PopoverPrimitive.Root, {
|
|
@@ -5069,7 +5140,24 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
5069
5140
|
})
|
|
5070
5141
|
] })
|
|
5071
5142
|
]
|
|
5072
|
-
})
|
|
5143
|
+
}),
|
|
5144
|
+
canCreateTarget && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Separator, {
|
|
5145
|
+
orientation: "horizontal",
|
|
5146
|
+
className: "my-0"
|
|
5147
|
+
}), /* @__PURE__ */ jsxs("button", {
|
|
5148
|
+
type: "button",
|
|
5149
|
+
"data-relation-selector-create": true,
|
|
5150
|
+
onMouseDown: (e) => {
|
|
5151
|
+
e.preventDefault();
|
|
5152
|
+
e.stopPropagation();
|
|
5153
|
+
},
|
|
5154
|
+
onClick: handleCreateNew,
|
|
5155
|
+
className: cls("w-full flex flex-row items-center gap-2 px-4 py-3 text-sm text-left", "text-primary hover:bg-surface-accent-50 dark:hover:bg-surface-800"),
|
|
5156
|
+
children: [/* @__PURE__ */ jsx(PlusIcon, { size: iconSize.smallest }), /* @__PURE__ */ jsx("span", {
|
|
5157
|
+
className: "truncate",
|
|
5158
|
+
children: searchString.trim() ? t("add_named", { name: searchString.trim() }) : t("add_specific", { name: collection.singularName ?? collection.name })
|
|
5159
|
+
})]
|
|
5160
|
+
})] })
|
|
5073
5161
|
]
|
|
5074
5162
|
})
|
|
5075
5163
|
})
|
|
@@ -7757,40 +7845,21 @@ function resolvedSelectedEntityView(customViews, customizationController, select
|
|
|
7757
7845
|
}
|
|
7758
7846
|
var BreadcrumbContext = React.createContext({
|
|
7759
7847
|
breadcrumbs: [],
|
|
7760
|
-
set: () => {}
|
|
7761
|
-
updateCount: () => {}
|
|
7848
|
+
set: () => {}
|
|
7762
7849
|
});
|
|
7763
7850
|
var BreadcrumbsProvider = ({ children }) => {
|
|
7764
7851
|
const [breadcrumbs, setBreadcrumbs] = useState([]);
|
|
7765
7852
|
const set = useCallback((props) => {
|
|
7766
7853
|
setBreadcrumbs((prev) => {
|
|
7767
|
-
const next = props.breadcrumbs
|
|
7768
|
-
|
|
7769
|
-
if (prevEntry && newEntry.count === null && typeof prevEntry.count === "number") return {
|
|
7770
|
-
...newEntry,
|
|
7771
|
-
count: prevEntry.count
|
|
7772
|
-
};
|
|
7773
|
-
return newEntry;
|
|
7774
|
-
});
|
|
7775
|
-
if (prev.length === next.length && prev.every((p, i) => p.title === next[i].title && p.url === next[i].url && p.id === next[i].id && p.count === next[i].count)) return prev;
|
|
7854
|
+
const next = props.breadcrumbs;
|
|
7855
|
+
if (prev.length === next.length && prev.every((p, i) => p.title === next[i].title && p.url === next[i].url && p.id === next[i].id)) return prev;
|
|
7776
7856
|
return next;
|
|
7777
7857
|
});
|
|
7778
7858
|
}, []);
|
|
7779
|
-
const updateCount = useCallback((id, count) => {
|
|
7780
|
-
setBreadcrumbs((prev) => prev.map((entry) => entry.id === id ? {
|
|
7781
|
-
...entry,
|
|
7782
|
-
count
|
|
7783
|
-
} : entry));
|
|
7784
|
-
}, []);
|
|
7785
7859
|
const value = useMemo(() => ({
|
|
7786
7860
|
breadcrumbs,
|
|
7787
|
-
set
|
|
7788
|
-
|
|
7789
|
-
}), [
|
|
7790
|
-
breadcrumbs,
|
|
7791
|
-
set,
|
|
7792
|
-
updateCount
|
|
7793
|
-
]);
|
|
7861
|
+
set
|
|
7862
|
+
}), [breadcrumbs, set]);
|
|
7794
7863
|
return /* @__PURE__ */ jsx(BreadcrumbContext.Provider, {
|
|
7795
7864
|
value,
|
|
7796
7865
|
children
|
|
@@ -9358,8 +9427,8 @@ function EditorCollectionAction({ path, parentCollectionSlugs, parentEntityIds,
|
|
|
9358
9427
|
}
|
|
9359
9428
|
//#endregion
|
|
9360
9429
|
//#region src/components/CollectionViewBinding/CollectionViewActions.tsx
|
|
9361
|
-
var ImportCollectionAction = lazyChunk(() => import("./import-
|
|
9362
|
-
var ExportCollectionAction = lazyChunk(() => import("./export-
|
|
9430
|
+
var ImportCollectionAction = lazyChunk(() => import("./import-Bxv5JPm-.js").then((n) => n.t).then((m) => ({ default: m.ImportCollectionAction })));
|
|
9431
|
+
var ExportCollectionAction = lazyChunk(() => import("./export-oauWlxuu.js").then((n) => n.t).then((m) => ({ default: m.ExportCollectionAction })));
|
|
9363
9432
|
function CollectionViewActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, onNewClick, onAddExistingClick, onMultipleDeleteClick, selectionEnabled, path, selectionController, tableController, collectionEntitiesCount, compact, children, openNewDocument }) {
|
|
9364
9433
|
const context = useAdminContext();
|
|
9365
9434
|
const { canCreate, canDelete } = usePermissions();
|
|
@@ -9996,12 +10065,6 @@ function EntityCardBinding({ entity, collection, onClick, selected, highlighted,
|
|
|
9996
10065
|
}), /* @__PURE__ */ jsxs("div", {
|
|
9997
10066
|
className: "p-3",
|
|
9998
10067
|
children: [
|
|
9999
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
10000
|
-
variant: "caption",
|
|
10001
|
-
color: "disabled",
|
|
10002
|
-
className: "font-mono truncate block",
|
|
10003
|
-
children: entity.id
|
|
10004
|
-
}),
|
|
10005
10068
|
/* @__PURE__ */ jsx("div", {
|
|
10006
10069
|
className: "truncate my-1 text-sm font-medium min-h-[20px]",
|
|
10007
10070
|
children: slots.title ? terms.length > 0 && typeof slots.title.value === "string" ? /* @__PURE__ */ jsx(Highlighted, {
|
|
@@ -11038,7 +11101,7 @@ function ListHeaderLabel({ column, direction, position, onSort }) {
|
|
|
11038
11101
|
className: "flex-shrink-0"
|
|
11039
11102
|
}),
|
|
11040
11103
|
direction && position !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
11041
|
-
className: "flex-shrink-0 text-[9px] font-
|
|
11104
|
+
className: "flex-shrink-0 text-[9px] font-semibold leading-none tabular-nums",
|
|
11042
11105
|
children: position + 1
|
|
11043
11106
|
})
|
|
11044
11107
|
] });
|
|
@@ -11713,7 +11776,7 @@ function JsonPreviewBinding({ values }) {
|
|
|
11713
11776
|
}
|
|
11714
11777
|
//#endregion
|
|
11715
11778
|
//#region src/components/EntityInspector.tsx
|
|
11716
|
-
var EntityHistoryView = lazyChunk(() => import("./history-
|
|
11779
|
+
var EntityHistoryView = lazyChunk(() => import("./history-C2cqc0bw.js").then((m) => ({ default: m.EntityHistoryView })));
|
|
11717
11780
|
/**
|
|
11718
11781
|
* Raw values and revision history, as an inspector rather than as tabs.
|
|
11719
11782
|
*
|
|
@@ -12173,6 +12236,7 @@ function DetailViewBindingInner({ path, entityId, selectedTab: selectedTabProp,
|
|
|
12173
12236
|
onSideTabClick(value);
|
|
12174
12237
|
},
|
|
12175
12238
|
children: [
|
|
12239
|
+
customViewTabsStart,
|
|
12176
12240
|
/* @__PURE__ */ jsx(Tab, {
|
|
12177
12241
|
value: "__main_##Q$SC^#S6",
|
|
12178
12242
|
className: cls("h-full data-[state=active]:bg-white dark:data-[state=active]:bg-surface-800", "text-sm min-w-[90px]"),
|
|
@@ -12181,7 +12245,6 @@ function DetailViewBindingInner({ path, entityId, selectedTab: selectedTabProp,
|
|
|
12181
12245
|
children: [getIcon(collection.icon, void 0, void 0, "smallest"), collection.singularName ?? collection.name]
|
|
12182
12246
|
})
|
|
12183
12247
|
}),
|
|
12184
|
-
customViewTabsStart,
|
|
12185
12248
|
customViewTabsEnd,
|
|
12186
12249
|
subcollectionTabs
|
|
12187
12250
|
]
|
|
@@ -15444,7 +15507,7 @@ function SortButton({ tableController, properties, compact }) {
|
|
|
15444
15507
|
}
|
|
15445
15508
|
//#endregion
|
|
15446
15509
|
//#region src/components/CollectionViewBinding/CollectionViewStartActions.tsx
|
|
15447
|
-
function CollectionViewStartActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, path, selectionController, tableController, collectionEntitiesCount, resolvedProperties, viewMode, compact, openNewDocument }) {
|
|
15510
|
+
function CollectionViewStartActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, path, selectionController, tableController, collectionEntitiesCount, entitiesCount, resolvedProperties, viewMode, compact, openNewDocument }) {
|
|
15448
15511
|
const context = useAdminContext();
|
|
15449
15512
|
const largeLayout = useLargeLayout();
|
|
15450
15513
|
const { t } = useTranslation();
|
|
@@ -15515,6 +15578,13 @@ function CollectionViewStartActions({ collection, relativePath, parentCollection
|
|
|
15515
15578
|
tableController,
|
|
15516
15579
|
compact
|
|
15517
15580
|
}, "filter_presets") : null;
|
|
15581
|
+
const countBadge = !iconOnlyToolbar && entitiesCount !== void 0 ? /* @__PURE__ */ jsx(Tooltip, {
|
|
15582
|
+
title: t("records_in_view"),
|
|
15583
|
+
children: entitiesCount === null ? /* @__PURE__ */ jsx(Skeleton, { className: "w-8 h-4 rounded-md mx-1" }) : /* @__PURE__ */ jsx("span", {
|
|
15584
|
+
className: "mx-1 text-xs text-surface-accent-600 dark:text-surface-accent-400 bg-surface-100 dark:bg-surface-800 px-1.5 py-0.5 rounded tabular-nums",
|
|
15585
|
+
children: entitiesCount.toLocaleString()
|
|
15586
|
+
})
|
|
15587
|
+
}, "entities_count") : null;
|
|
15518
15588
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15519
15589
|
[
|
|
15520
15590
|
backButton,
|
|
@@ -15525,7 +15595,8 @@ function CollectionViewStartActions({ collection, relativePath, parentCollection
|
|
|
15525
15595
|
compact: iconOnlyToolbar,
|
|
15526
15596
|
enabled: !collection.fixedFilter
|
|
15527
15597
|
}, "clear_filter"),
|
|
15528
|
-
filterPresetsButton
|
|
15598
|
+
filterPresetsButton,
|
|
15599
|
+
countBadge
|
|
15529
15600
|
],
|
|
15530
15601
|
useSlot("collection.actions.start", actionProps),
|
|
15531
15602
|
resolvedProperties && tableController.setFilterValues && /* @__PURE__ */ jsx(FiltersDialog, {
|
|
@@ -15604,7 +15675,6 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
|
|
|
15604
15675
|
const context = useAdminContext();
|
|
15605
15676
|
const collectionRegistry = useCollectionRegistryController();
|
|
15606
15677
|
const urlController = useUrlController();
|
|
15607
|
-
const breadcrumbs = useBreadcrumbsController();
|
|
15608
15678
|
const path = pathProp ?? getCollectionDataPath(collectionProp);
|
|
15609
15679
|
const dataClient = useData();
|
|
15610
15680
|
const sidePanelController = useSidePanel();
|
|
@@ -16099,11 +16169,6 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
|
|
|
16099
16169
|
openEntityMode
|
|
16100
16170
|
});
|
|
16101
16171
|
}, [updateLastDeleteTimestamp, usedSelectionController]);
|
|
16102
|
-
const updateCountRef = React.useRef(breadcrumbs.updateCount);
|
|
16103
|
-
updateCountRef.current = breadcrumbs.updateCount;
|
|
16104
|
-
useEffect(() => {
|
|
16105
|
-
updateCountRef.current(path, docsCount);
|
|
16106
|
-
}, [docsCount, path]);
|
|
16107
16172
|
const countFetcher = /* @__PURE__ */ jsx(EntitiesCount, {
|
|
16108
16173
|
path,
|
|
16109
16174
|
collection,
|
|
@@ -16219,6 +16284,7 @@ var CollectionViewBindingInner = React.memo(function CollectionViewBindingInner(
|
|
|
16219
16284
|
collectionEntitiesCount: docsCount ?? void 0,
|
|
16220
16285
|
resolvedProperties: resolvedCollection.properties,
|
|
16221
16286
|
viewMode,
|
|
16287
|
+
entitiesCount: docsCount,
|
|
16222
16288
|
openNewDocument,
|
|
16223
16289
|
compact: isCompact
|
|
16224
16290
|
}),
|
|
@@ -16931,6 +16997,46 @@ function EntityActionButton({ action, enabled, props }) {
|
|
|
16931
16997
|
});
|
|
16932
16998
|
}
|
|
16933
16999
|
//#endregion
|
|
17000
|
+
//#region src/collection_editor/useSafeSnackbarController.ts
|
|
17001
|
+
function useSafeSnackbarController() {
|
|
17002
|
+
try {
|
|
17003
|
+
return useSnackbarController();
|
|
17004
|
+
} catch {
|
|
17005
|
+
return;
|
|
17006
|
+
}
|
|
17007
|
+
}
|
|
17008
|
+
//#endregion
|
|
17009
|
+
//#region src/form/useUndoableDiscard.ts
|
|
17010
|
+
/**
|
|
17011
|
+
* Throw away everything the user has typed, reversibly.
|
|
17012
|
+
*
|
|
17013
|
+
* "Discard" and "Clear" sit next to Save, take one click, and undo everything
|
|
17014
|
+
* the user has done since they opened the record — the identity bar's version
|
|
17015
|
+
* does not even stop to ask. So the reset goes *through* the undo history
|
|
17016
|
+
* rather than around it (see `undoable` in `FormexResetProps`), and the
|
|
17017
|
+
* confirmation carries the Undo, which is the only place it can be: the form
|
|
17018
|
+
* has no undo button, only the ⌘Z the user has no reason to guess at.
|
|
17019
|
+
*
|
|
17020
|
+
* Pass `values` to reset to something other than the form's current baseline.
|
|
17021
|
+
*/
|
|
17022
|
+
function useUndoableDiscard() {
|
|
17023
|
+
const snackbarController = useSafeSnackbarController();
|
|
17024
|
+
return useCallback((formex, status, values) => {
|
|
17025
|
+
formex.resetForm(values !== void 0 ? {
|
|
17026
|
+
values,
|
|
17027
|
+
undoable: true
|
|
17028
|
+
} : { undoable: true });
|
|
17029
|
+
snackbarController?.open({
|
|
17030
|
+
type: "info",
|
|
17031
|
+
message: status === "existing" ? "Changes discarded" : "Form cleared",
|
|
17032
|
+
action: {
|
|
17033
|
+
label: "Undo",
|
|
17034
|
+
onClick: () => formex.undo()
|
|
17035
|
+
}
|
|
17036
|
+
});
|
|
17037
|
+
}, [snackbarController]);
|
|
17038
|
+
}
|
|
17039
|
+
//#endregion
|
|
16934
17040
|
//#region src/hooks/useRecordActions.tsx
|
|
16935
17041
|
/**
|
|
16936
17042
|
* The actions you can perform *on* a record — copy, delete, and whatever the
|
|
@@ -17040,6 +17146,7 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
|
|
|
17040
17146
|
}, [entity]);
|
|
17041
17147
|
const [formContext, setFormContext] = useState(void 0);
|
|
17042
17148
|
useLargeLayout();
|
|
17149
|
+
const discard = useUndoableDiscard();
|
|
17043
17150
|
const customizationController = useCustomizationController();
|
|
17044
17151
|
const plugins = customizationController.plugins;
|
|
17045
17152
|
const formActionTopProps = useMemo(() => ({
|
|
@@ -17454,7 +17561,7 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
|
|
|
17454
17561
|
} : void 0,
|
|
17455
17562
|
saveAndClosePlacement: layout === "split" ? "menu" : "button",
|
|
17456
17563
|
onClose: onCloseRequest,
|
|
17457
|
-
onDiscard: canEdit && formContext ? () => formContext.formex
|
|
17564
|
+
onDiscard: canEdit && formContext ? () => discard(formContext.formex, status) : void 0,
|
|
17458
17565
|
onInspect: includeJsonView ? () => setInspectorTab("json") : void 0,
|
|
17459
17566
|
onViewHistory: includeHistoryView ? () => setInspectorTab("history") : void 0,
|
|
17460
17567
|
externalLink: usedEntity ? customizationController?.entityLinkBuilder?.({ entity: usedEntity }) : void 0,
|
|
@@ -17492,6 +17599,7 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
|
|
|
17492
17599
|
onSideTabClick(value);
|
|
17493
17600
|
},
|
|
17494
17601
|
children: [
|
|
17602
|
+
customViewTabsStart,
|
|
17495
17603
|
/* @__PURE__ */ jsx(Tab, {
|
|
17496
17604
|
value: "__main_##Q$SC^#S6",
|
|
17497
17605
|
className: cls("h-full data-[state=active]:bg-white dark:data-[state=active]:bg-surface-800", "text-sm min-w-[90px]"),
|
|
@@ -17500,7 +17608,6 @@ function EditViewBindingInner({ path, entityId, selectedTab: selectedTabProp, co
|
|
|
17500
17608
|
children: [getIcon(collection.icon, void 0, void 0, "smallest"), collection.singularName ?? collection.name]
|
|
17501
17609
|
})
|
|
17502
17610
|
}),
|
|
17503
|
-
customViewTabsStart,
|
|
17504
17611
|
customViewTabsEnd,
|
|
17505
17612
|
subcollectionTabs
|
|
17506
17613
|
]
|
|
@@ -19399,7 +19506,7 @@ function SelectionTableBindingInternal({ onSingleEntitySelected, onMultipleEntit
|
|
|
19399
19506
|
sidePanelController.open({
|
|
19400
19507
|
path,
|
|
19401
19508
|
collection,
|
|
19402
|
-
updateUrl:
|
|
19509
|
+
updateUrl: false,
|
|
19403
19510
|
onUpdate: ({ entity }) => {
|
|
19404
19511
|
setEntitiesDisplayedFirst([entity, ...entitiesDisplayedFirst]);
|
|
19405
19512
|
onEntityClick(entity);
|
|
@@ -23114,6 +23221,7 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
23114
23221
|
const [savingError, setSavingError] = useState();
|
|
23115
23222
|
const [isSavingAutoSave, setIsSavingAutoSave] = useState(false);
|
|
23116
23223
|
const [discardDialogOpen, setDiscardDialogOpen] = useState(false);
|
|
23224
|
+
const discard = useUndoableDiscard();
|
|
23117
23225
|
const autoSave = collection.formAutoSave;
|
|
23118
23226
|
const baseInitialValues = useMemo(() => {
|
|
23119
23227
|
if (computedInitialValues !== void 0) return computedInitialValues;
|
|
@@ -23457,7 +23565,7 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
23457
23565
|
maxWidth: "sm",
|
|
23458
23566
|
children: [
|
|
23459
23567
|
/* @__PURE__ */ jsx(DialogTitle, { children: status === "existing" ? "Discard changes?" : "Clear form?" }),
|
|
23460
|
-
/* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */
|
|
23568
|
+
/* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(Typography, { children: [status === "existing" ? "All unsaved changes will be lost. " : "All entered values will be cleared. ", "You can bring them back with Undo (⌘Z / Ctrl+Z), or from the notification, for as long as this form stays open."] }) }),
|
|
23461
23569
|
/* @__PURE__ */ jsxs(DialogActions, { children: [/* @__PURE__ */ jsx(Button, {
|
|
23462
23570
|
variant: "text",
|
|
23463
23571
|
onClick: () => setDiscardDialogOpen(false),
|
|
@@ -23467,7 +23575,7 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
23467
23575
|
color: "error",
|
|
23468
23576
|
onClick: () => {
|
|
23469
23577
|
setDiscardDialogOpen(false);
|
|
23470
|
-
formex
|
|
23578
|
+
discard(formex, status, baseInitialValues);
|
|
23471
23579
|
},
|
|
23472
23580
|
children: status === "existing" ? "Discard" : "Clear"
|
|
23473
23581
|
})] })
|
|
@@ -24653,6 +24761,6 @@ function getFullIdPath(propertyKey, propertyNamespace) {
|
|
|
24653
24761
|
return idToPropertiesPath(propertyNamespace ? `${propertyNamespace}.${propertyKey}` : propertyKey);
|
|
24654
24762
|
}
|
|
24655
24763
|
//#endregion
|
|
24656
|
-
export { getEntityViewWidth as $,
|
|
24764
|
+
export { getEntityViewWidth as $, StorageThumbnailInternal as $n, removeTrailingSlash$1 as $t, SelectFieldBinding as A, ArrayOneOfPreview as An, IMPORT_BATCH_SIZE as At, useSelectionDialog as B, SidePanelControllerContext as Bn, SearchIconsView as Bt, getDefaultFieldId as C, PropertyPreview as Cn, getResolvedPropertyInPath as Cr, DataNewPropertiesMapping as Ct, TextFieldBinding as D, DatePreview as Dn, getInferenceType as Dt, VectorFieldBinding as E, BooleanPreview as En, processValueMapping as Et, MapFieldBinding as F, RelationPreview as Fn, parseCsvRows as Ft, useTopLevelNavigation as G, useResolvedUser as Gn, resolveEntityView as Gt, SideDialogs as H, CollectionRegistryContext as Hn, useBreadcrumbsController as Ht, KeyValueFieldBinding as I, ArrayOfReferencesPreview as In, parseCsvToObjects as It, useBuildCollectionRegistryController as J, getEntityTitlePropertyKeyForEntity as Jn, getCollectionBySlugWithin as Jt, useResolvedViews as K, getEntityPreviewKeys as Kn, mergeEntityActions as Kt, DateTimeFieldBinding as L, InlineEntityListPreview as Ln, unflattenObject as Lt, ReferenceFieldBinding as M, ArrayPropertyEnumPreview as Mn, ImportFileUpload as Mt, MultiSelectFieldBinding as N, ArrayEnumPreview as Nn, convertFileToJson as Nt, SwitchFieldBinding as O, KeyValuePreview as On, useImportConfig as Ot, MarkdownEditorFieldBinding as P, ArrayOfStorageComponentsPreview as Pn, detectCsvDelimiter as Pt, buildSidePanelsFromUrl as Q, StorageThumbnail as Qn, removeInitialSlash as Qt, BlockFieldBinding as R, ReferencePreview as Rn, ArrayContainer as Rt, getDefaultFieldConfig as S, ArrayOfMapsPreview as Sn, getPropertyInPath$1 as Sr, ImportNewPropertyFieldPreview as St, getFieldId as T, NumberPropertyPreview as Tn, isRelationProperty as Tr, flattenEntry as Tt, useBuildUrlController as U, useCollectionRegistryController as Un, BreadcrumbsProvider as Ut, SelectionTableBinding as V, useSidePanel as Vn, FieldCaption as Vt, useBuildNavigationStateController as W, getUserLabel as Wn, resolveEntityAction as Wt, resolveNavigationFrom as X, StringPropertyPreview as Xn, getLastSegment$1 as Xt, useHistory as Y, ArrayPropertyPreview as Yn, getCollectionPathsCombinations as Yt, useResolvedNavigationFrom as Z, EnumValuesChip as Zn, removeInitialAndTrailingSlashes$1 as Zt, useCollectionsConfigController as _, useClearRestoreValue as _n, getBracketNotation as _r, EntityCardBinding as _t, namespaceToPropertiesPath as a, CollectionTableBinding as an, UrlComponentPreview as ar, getInitialEntityValues as at, PropertyFieldBinding as b, LabelWithIcon as bn, getIconForWidget as br, ConfigControllerProvider as bt, buildCollectionGenerationCallback as c, useAdminContext as cn, EmptyValue as cr, CollectionViewBinding as ct, fromSerializableCollectionConfigs as d, NavigationStateContext as dn, RecordMeta as dr, editEntityAction as dt, resolveCollectionPathIds$1 as en, SkeletonPropertyComponent as er, useBuildSidePanel as et, fromSerializableProperties as f, useNavigationStateController as fn, FieldBlock as fr, resetPasswordAction as ft, toSerializableProperty as g, ArrayCustomShapedFieldBinding as gn, PropertyIdCopyTooltip as gr, CollectionCardViewBinding as gt, toSerializableProperties as h, SelectableTableContext as hn, spanClass as hr, EntityViewBinding as ht, namespaceToPropertiesOrderPath as i, VirtualTableInput$1 as in, renderSkeletonText as ir, getChanges as it, RepeatFieldBinding as j, ArrayOfStringsPreview as jn, saveImportedEntities as jt, StorageUploadFieldBinding as k, MapPropertyPreview as kn, ImportSaveInProgress as kt, validateCollectionJson as l, UrlContext as ln, FormSections as lr, copyEntityAction as lt, toSerializableCollectionConfig as m, SideDialogsControllerContext as mn, isSelfLabellingProperty as mr, DetailViewBinding as mt, getFullIdPath as n, resolveViewMode as nn, renderSkeletonIcon as nr, useSafeSnackbarController as nt, CollectionGenerationApiError as o, SelectableTable as on, ImagePreview as or, removeEmptyContainers as ot, fromSerializableProperty as p, useSideDialogsController as pn, LABEL_ICON_SIZE as pr, CreationResultDialog as pt, useResolvedCollections as q, getEntityTitlePropertyKey as qn, addInitialSlash as qt, idToPropertiesPath as r, useSelectionController as rn, renderSkeletonImageThumbnail as rr, extractTouchedValues as rt, DEFAULT_COLLECTION_GENERATION_ENDPOINT as s, CollectionRowActions as sn, sanitizeUrl as sr, zodToFormErrors as st, getFullId as t, resolveOpenEntityMode as tn, renderSkeletonCaptionText as tr, EditViewBinding as tt, fromSerializableCollectionConfig as u, useUrlController as un, FormRail as ur, deleteEntityAction as ut, EntityFormBinding as v, ReadOnlyFieldBinding as vn, getDefaultPropertiesOrder as vr, CollectionViewActions as vt, getFieldConfig as w, UserPreview as wn, isReferenceProperty as wr, convertDataToEntity as wt, DEFAULT_FIELD_CONFIGS as x, FieldHelperText as xn, getPropertiesWithPropertiesOrder as xr, useCollectionEditorDialogsState as xt, EntityForm as y, LabelWithIconAndTooltip as yn, getIconForProperty as yr, useCollectionEditorController as yt, ArrayOfReferencesFieldBinding as z, EntityPreviewBinding as zn, PropertyConfigBadge as zt };
|
|
24657
24765
|
|
|
24658
|
-
//# sourceMappingURL=util-
|
|
24766
|
+
//# sourceMappingURL=util-C4qsI1QV.js.map
|