@rebasepro/admin 0.7.0 → 0.8.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/LICENSE +21 -0
- package/dist/{CollectionEditorDialog-iGgS4rLX.js → CollectionEditorDialog-BO_aQ-Z4.js} +7 -7
- package/dist/CollectionEditorDialog-BO_aQ-Z4.js.map +1 -0
- package/dist/{PropertyEditView-C9yhLyja.js → PropertyEditView-DKzOpMjR.js} +64 -38
- package/dist/PropertyEditView-DKzOpMjR.js.map +1 -0
- package/dist/{RouterCollectionsStudioView-Dqx27GD8.js → RouterCollectionsStudioView-BQTIExjA.js} +4 -4
- package/dist/{RouterCollectionsStudioView-Dqx27GD8.js.map → RouterCollectionsStudioView-BQTIExjA.js.map} +1 -1
- package/dist/collection_editor/_cms_internals.d.ts +1 -1
- package/dist/collection_editor/api/generateCollectionApi.d.ts +3 -2
- package/dist/collection_editor/serializable_types.d.ts +5 -9
- package/dist/collection_editor/serializable_utils.d.ts +2 -2
- package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +1 -2
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
- package/dist/components/EntityCollectionTable/index.d.ts +1 -1
- package/dist/components/RebaseCMS.d.ts +1 -1
- package/dist/components/SelectableTable/SelectionStore.d.ts +12 -21
- package/dist/components/field_configs.d.ts +1 -1
- package/dist/editor/editor.d.ts +0 -6
- package/dist/editor.js +1 -3
- package/dist/editor.js.map +1 -1
- package/dist/form/components/StorageUploadProgress.d.ts +6 -1
- package/dist/form/index.d.ts +0 -1
- package/dist/{history-ChHgyyvM.js → history-B3cqBzEV.js} +2 -2
- package/dist/{history-ChHgyyvM.js.map → history-B3cqBzEV.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/preview/components/StorageThumbnail.d.ts +3 -1
- package/dist/{src-DOX2nOyh.js → src-cxV1ODRG.js} +178 -460
- package/dist/src-cxV1ODRG.js.map +1 -0
- package/dist/types/components/PropertyPreviewProps.d.ts +2 -1
- package/dist/types/fields.d.ts +4 -6
- package/package.json +18 -18
- package/src/collection_editor/_cms_internals.ts +1 -1
- package/src/collection_editor/api/generateCollectionApi.ts +3 -2
- package/src/collection_editor/pgColumnToProperty.ts +2 -2
- package/src/collection_editor/serializable_types.ts +14 -27
- package/src/collection_editor/serializable_utils.ts +25 -37
- package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +2 -2
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +41 -24
- package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +0 -9
- package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +35 -1
- package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +1 -1
- package/src/collection_editor/ui/collection_editor/utils/supported_fields.tsx +0 -1
- package/src/collection_editor/ui/collection_editor/utils/update_property_for_widget.ts +7 -29
- package/src/collection_editor/useJsonCollectionsConfigController.tsx +2 -2
- package/src/components/DefaultDrawer.tsx +1 -1
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +11 -9
- package/src/components/EntityCollectionTable/fields/TableMultipleRelationField.tsx +2 -1
- package/src/components/EntityCollectionTable/fields/TableRelationField.tsx +2 -1
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +23 -20
- package/src/components/EntityCollectionTable/index.tsx +1 -1
- package/src/components/EntityCollectionTable/table_bindings.tsx +24 -44
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +1 -1
- package/src/components/EntityCollectionView/EntityCollectionListView.tsx +12 -1
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +8 -25
- package/src/components/EntityCollectionView/hooks/useCollectionInlineEditor.ts +1 -1
- package/src/components/EntityCollectionView/useBoardDataController.tsx +12 -40
- package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +3 -3
- package/src/components/EntityDetailView.tsx +2 -2
- package/src/components/EntityEditView.tsx +2 -2
- package/src/components/EntityEditViewFormActions.tsx +3 -3
- package/src/components/HomePage/ContentHomePage.tsx +1 -1
- package/src/components/RebaseCMS.tsx +2 -3
- package/src/components/RebaseNavigation.tsx +3 -2
- package/src/components/RelationSelector.tsx +2 -2
- package/src/components/SelectableTable/SelectableTable.tsx +11 -3
- package/src/components/SelectableTable/SelectionStore.ts +18 -48
- package/src/components/common/default_entity_actions.tsx +15 -4
- package/src/components/field_configs.tsx +0 -14
- package/src/editor/editor.tsx +4 -7
- package/src/form/EntityFormActions.tsx +2 -2
- package/src/form/EntityFormBinding.tsx +1 -1
- package/src/form/components/StorageUploadProgress.tsx +9 -2
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +12 -1
- package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +4 -4
- package/src/form/field_bindings/RelationFieldBinding.tsx +5 -7
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +19 -15
- package/src/form/index.tsx +0 -1
- package/src/index.ts +1 -1
- package/src/preview/PropertyPreview.tsx +1 -16
- package/src/preview/components/StorageThumbnail.tsx +24 -7
- package/src/types/components/PropertyPreviewProps.tsx +1 -1
- package/src/types/fields.tsx +2 -7
- package/src/util/previews.ts +20 -0
- package/src/util/propertyConfigMapper.ts +3 -3
- package/src/util/property_utils.tsx +1 -2
- package/dist/CollectionEditorDialog-iGgS4rLX.js.map +0 -1
- package/dist/PropertyEditView-C9yhLyja.js.map +0 -1
- package/dist/components/EntityCollectionTable/fields/VirtualTableDateField.d.ts +0 -12
- package/dist/components/EntityCollectionTable/fields/VirtualTableInput.d.ts +0 -10
- package/dist/components/EntityCollectionTable/fields/VirtualTableNumberInput.d.ts +0 -9
- package/dist/components/EntityCollectionTable/fields/VirtualTableSwitch.d.ts +0 -8
- package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +0 -11
- package/dist/src-DOX2nOyh.js.map +0 -1
- package/src/components/EntityCollectionTable/fields/VirtualTableDateField.tsx +0 -39
- package/src/components/EntityCollectionTable/fields/VirtualTableInput.tsx +0 -90
- package/src/components/EntityCollectionTable/fields/VirtualTableNumberInput.tsx +0 -83
- package/src/components/EntityCollectionTable/fields/VirtualTableSwitch.tsx +0 -32
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +0 -141
|
@@ -2,7 +2,7 @@ import type { ComponentRef, EntityCollection, EntityCustomViewParams, FormViewCo
|
|
|
2
2
|
import type { FormContext } from "../types/fields";
|
|
3
3
|
import type { PluginFormActionProps } from "@rebasepro/types";
|
|
4
4
|
import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { Entity, EntityStatus } from "@rebasepro/types";
|
|
5
|
+
import { Entity, EntityStatus, getCollectionDataPath } from "@rebasepro/types";
|
|
6
6
|
import { PluginProviderStack, resolveComponentRef, useComponentOverride, CollectionComponentOverrideProvider } from "@rebasepro/core";
|
|
7
7
|
|
|
8
8
|
import { EntityCollectionView, EntityView } from "../components";
|
|
@@ -416,7 +416,7 @@ parentEntityIds,
|
|
|
416
416
|
|
|
417
417
|
const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
|
|
418
418
|
const subcollectionId = subcollection.slug;
|
|
419
|
-
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection
|
|
419
|
+
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(getCollectionDataPath(subcollection))}` : undefined;
|
|
420
420
|
|
|
421
421
|
if (activeTab !== subcollectionId) return null;
|
|
422
422
|
return (
|
|
@@ -3,7 +3,7 @@ import type { EntityCollection } from "@rebasepro/types";
|
|
|
3
3
|
import type { FormContext } from "../types/fields";
|
|
4
4
|
import type { EntityAction, EntityActionClickProps, SideEntityController } from "@rebasepro/types";
|
|
5
5
|
import React, { useMemo } from "react";
|
|
6
|
-
import { Entity, RebaseContext } from "@rebasepro/types";
|
|
6
|
+
import { Entity, getCollectionDataPath, RebaseContext } from "@rebasepro/types";
|
|
7
7
|
import type { EntityFormActionsProps } from "../types/components/EntityFormActionsProps";
|
|
8
8
|
import { copyEntityAction, deleteEntityAction } from "../components";
|
|
9
9
|
import { mergeEntityActions } from "../util/entity_actions";
|
|
@@ -168,7 +168,7 @@ function buildBottomActions<M extends Record<string, unknown>>({
|
|
|
168
168
|
const props = {
|
|
169
169
|
view: "form",
|
|
170
170
|
entity,
|
|
171
|
-
path: collection
|
|
171
|
+
path: getCollectionDataPath(collection),
|
|
172
172
|
collection: collection,
|
|
173
173
|
context,
|
|
174
174
|
sideEntityController,
|
|
@@ -292,7 +292,7 @@ function buildSideActions<M extends Record<string, unknown>>({
|
|
|
292
292
|
const props = {
|
|
293
293
|
view: "form",
|
|
294
294
|
entity,
|
|
295
|
-
path: collection
|
|
295
|
+
path: getCollectionDataPath(collection),
|
|
296
296
|
collection: collection,
|
|
297
297
|
context,
|
|
298
298
|
sideEntityController,
|
|
@@ -76,7 +76,7 @@ export function ContentHomePage({
|
|
|
76
76
|
// Content mode shows collections, CMS custom views, and admin entries — but not Studio dev views.
|
|
77
77
|
const rawNavigationEntries = useMemo(() => {
|
|
78
78
|
if (adminModeController.mode === "studio") {
|
|
79
|
-
return unFilteredNavigationEntries.filter(e => e.type === "view" || e.type === "admin");
|
|
79
|
+
return unFilteredNavigationEntries.filter(e => (e.type === "view" && studioViewSlugs.has(e.slug)) || e.type === "admin");
|
|
80
80
|
}
|
|
81
81
|
return unFilteredNavigationEntries.filter(e => e.type !== "view" || !studioViewSlugs.has(e.slug));
|
|
82
82
|
}, [unFilteredNavigationEntries, adminModeController.mode, studioViewSlugs]);
|
|
@@ -10,7 +10,7 @@ import type { RebaseCMSConfig } from "@rebasepro/types";
|
|
|
10
10
|
* is auto-wired as a native feature (slots, provider, Studio view) without
|
|
11
11
|
* needing any external plugin.
|
|
12
12
|
*/
|
|
13
|
-
export function RebaseCMS({ collections, views, homePage, entityViews, entityActions,
|
|
13
|
+
export function RebaseCMS({ collections, views, homePage, entityViews, entityActions, collectionEditor, navigationGroupMappings }: RebaseCMSConfig) {
|
|
14
14
|
const dispatch = useRebaseRegistryDispatch();
|
|
15
15
|
|
|
16
16
|
useLayoutEffect(() => {
|
|
@@ -19,11 +19,10 @@ views,
|
|
|
19
19
|
homePage,
|
|
20
20
|
entityViews,
|
|
21
21
|
entityActions,
|
|
22
|
-
plugins,
|
|
23
22
|
collectionEditor,
|
|
24
23
|
navigationGroupMappings });
|
|
25
24
|
return () => dispatch.unregisterCMS();
|
|
26
|
-
}, [dispatch, collections, views, homePage, entityViews, entityActions,
|
|
25
|
+
}, [dispatch, collections, views, homePage, entityViews, entityActions, collectionEditor, navigationGroupMappings]);
|
|
27
26
|
|
|
28
27
|
return null;
|
|
29
28
|
}
|
|
@@ -195,8 +195,10 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
|
|
|
195
195
|
return [...cmsViews, ...devViews];
|
|
196
196
|
}, [cmsViews, devViews]);
|
|
197
197
|
|
|
198
|
+
const parentCustomizationController = useCustomizationController();
|
|
199
|
+
|
|
198
200
|
const navigationStateController = useBuildNavigationStateController({
|
|
199
|
-
plugins:
|
|
201
|
+
plugins: parentCustomizationController.plugins ?? EMPTY_PLUGINS,
|
|
200
202
|
collections: collectionsBuilder,
|
|
201
203
|
views: mergedViews,
|
|
202
204
|
navigationGroupMappings: registry.cmsConfig?.navigationGroupMappings,
|
|
@@ -213,7 +215,6 @@ export function RebaseNavigation({ children }: RebaseNavigationProps) {
|
|
|
213
215
|
// automatically merged. We re-provide an enriched controller here so that
|
|
214
216
|
// downstream consumers (EntityEditView, side panels, etc.) can resolve
|
|
215
217
|
// string-keyed entity views like "blog_preview".
|
|
216
|
-
const parentCustomizationController = useCustomizationController();
|
|
217
218
|
const enrichedCustomizationController = useMemo(() => {
|
|
218
219
|
const cmsEntityViews = (registry.cmsConfig?.entityViews ?? []) as EntityCustomView[];
|
|
219
220
|
const cmsEntityActions = (registry.cmsConfig?.entityActions ?? []) as EntityAction[];
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
import * as React from "react";
|
|
25
25
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
26
26
|
import { Command as CommandPrimitive } from "cmdk";
|
|
27
|
-
import { Entity, EntityRelation, FilterValues, Relation } from "@rebasepro/types";
|
|
27
|
+
import { Entity, EntityRelation, FilterValues, Relation, getCollectionDataPath } from "@rebasepro/types";
|
|
28
28
|
import { EntityPreviewData } from "./EntityPreview";
|
|
29
29
|
import { useData, useRelationSelector } from "@rebasepro/core";
|
|
30
30
|
import { useSideEntityController } from "../hooks/useSideEntityController";
|
|
@@ -115,7 +115,7 @@ export const RelationSelector = React.forwardRef<
|
|
|
115
115
|
loadMore,
|
|
116
116
|
entityToRelationItem
|
|
117
117
|
} = useRelationSelector({
|
|
118
|
-
path: collection
|
|
118
|
+
path: getCollectionDataPath(collection),
|
|
119
119
|
collection,
|
|
120
120
|
fixedFilter,
|
|
121
121
|
pageSize
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSelectionStore } from "./SelectionStore";
|
|
1
|
+
import { createSelectionStore, AdminSelectedCell } from "./SelectionStore";
|
|
2
2
|
import type { Property } from "@rebasepro/types";
|
|
3
3
|
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
4
4
|
import { CollectionSize, Entity, EntityRelation, EntityTableController, FilterValues, SelectedCellProps } from "@rebasepro/types";
|
|
@@ -192,7 +192,15 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
|
|
|
192
192
|
|
|
193
193
|
const select = useCallback((cell?: SelectedCellProps<M>) => {
|
|
194
194
|
setLocalSelectedCell(cell);
|
|
195
|
-
|
|
195
|
+
if (cell) {
|
|
196
|
+
selectionStore.select({
|
|
197
|
+
...cell,
|
|
198
|
+
columnKey: cell.propertyKey,
|
|
199
|
+
rowId: cell.entityId
|
|
200
|
+
} as AdminSelectedCell);
|
|
201
|
+
} else {
|
|
202
|
+
selectionStore.select(undefined);
|
|
203
|
+
}
|
|
196
204
|
}, [selectionStore]);
|
|
197
205
|
|
|
198
206
|
const unselect = useCallback(() => {
|
|
@@ -255,7 +263,7 @@ export const SelectableTable = function SelectableTable<M extends Record<string,
|
|
|
255
263
|
onColumnResize={onColumnResize}
|
|
256
264
|
rowHeight={getRowHeight(size)}
|
|
257
265
|
loading={dataLoading}
|
|
258
|
-
filter={filterValues}
|
|
266
|
+
filter={filterValues as any}
|
|
259
267
|
onFilterUpdate={setFilterValues ? onFilterUpdate : undefined}
|
|
260
268
|
sortBy={sortBy}
|
|
261
269
|
onSortByUpdate={setSortBy as ((sortBy?: [string, "asc" | "desc"]) => void)}
|
|
@@ -1,64 +1,34 @@
|
|
|
1
1
|
import { useSyncExternalStore, useCallback, useRef } from "react";
|
|
2
2
|
import { SelectedCellProps } from "@rebasepro/types";
|
|
3
|
+
import {
|
|
4
|
+
createVirtualTableSelectionStore,
|
|
5
|
+
VirtualTableSelectionStore,
|
|
6
|
+
SelectedCell
|
|
7
|
+
} from "@rebasepro/ui";
|
|
8
|
+
|
|
9
|
+
export interface AdminSelectedCell extends SelectedCell {
|
|
10
|
+
entityPath: string;
|
|
11
|
+
entityId: string | number;
|
|
12
|
+
propertyKey: string;
|
|
13
|
+
cellRect: DOMRect;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
3
17
|
|
|
4
|
-
/**
|
|
5
|
-
* A ref-based selection store that avoids React context re-renders.
|
|
6
|
-
*
|
|
7
|
-
* The problem with putting `selectedCell` in a React context value is that
|
|
8
|
-
* ANY cell selection change triggers a context value change, which forces
|
|
9
|
-
* ALL consumers (every PropertyTableCell in the table) to re-render.
|
|
10
|
-
* This causes the DOM to be replaced between mousedown and click events,
|
|
11
|
-
* breaking `alwaysInteractive` cells (like relation selectors) where the
|
|
12
|
-
* user needs to click a button on the very first interaction.
|
|
13
|
-
*
|
|
14
|
-
* This store uses `useSyncExternalStore` so only cells whose `selected`
|
|
15
|
-
* derivation actually changed will re-render.
|
|
16
|
-
*/
|
|
17
18
|
export function createSelectionStore() {
|
|
18
|
-
|
|
19
|
-
const listeners = new Set<() => void>();
|
|
20
|
-
|
|
21
|
-
function getSnapshot(): SelectedCellProps | undefined {
|
|
22
|
-
return selectedCell;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function subscribe(listener: () => void): () => void {
|
|
26
|
-
listeners.add(listener);
|
|
27
|
-
return () => listeners.delete(listener);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function select(cell: SelectedCellProps | undefined) {
|
|
31
|
-
selectedCell = cell;
|
|
32
|
-
listeners.forEach(l => l());
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return { getSnapshot,
|
|
36
|
-
subscribe,
|
|
37
|
-
select };
|
|
19
|
+
return createVirtualTableSelectionStore<AdminSelectedCell>();
|
|
38
20
|
}
|
|
39
21
|
|
|
40
|
-
export type SelectionStore =
|
|
22
|
+
export type SelectionStore = VirtualTableSelectionStore<AdminSelectedCell>;
|
|
41
23
|
|
|
42
|
-
/**
|
|
43
|
-
* Hook that subscribes a cell to the selection store and returns
|
|
44
|
-
* whether THIS cell is selected. Only re-renders when the cell's
|
|
45
|
-
* `selected` boolean actually changes (not on every store update).
|
|
46
|
-
*/
|
|
47
24
|
export function useCellSelected(
|
|
48
25
|
store: { getSnapshot: () => SelectedCellProps | undefined; subscribe: (listener: () => void) => () => void },
|
|
49
26
|
propertyKey: string,
|
|
50
27
|
entityPath: string,
|
|
51
28
|
entityId: string | number
|
|
52
29
|
): boolean {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// React only re-renders when the returned value !== the previous one.
|
|
56
|
-
const selectorRef = useRef({ propertyKey,
|
|
57
|
-
entityPath,
|
|
58
|
-
entityId });
|
|
59
|
-
selectorRef.current = { propertyKey,
|
|
60
|
-
entityPath,
|
|
61
|
-
entityId };
|
|
30
|
+
const selectorRef = useRef({ propertyKey, entityPath, entityId });
|
|
31
|
+
selectorRef.current = { propertyKey, entityPath, entityId };
|
|
62
32
|
|
|
63
33
|
const getSnapshot = useCallback(() => {
|
|
64
34
|
const cell = store.getSnapshot();
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { CopyIcon, iconSize, KeyRoundIcon, PencilIcon, Trash2Icon } from "@rebasepro/ui";
|
|
2
2
|
import type { EntityAction, User, UserCreationResult } from "@rebasepro/types";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ConfirmationDialog,
|
|
5
|
+
useAuthController,
|
|
6
|
+
useRebaseClient,
|
|
7
|
+
useSnackbarController,
|
|
8
|
+
useTranslation
|
|
9
|
+
} from "@rebasepro/core";
|
|
4
10
|
import { DeleteEntityDialog } from "../DeleteEntityDialog";
|
|
5
11
|
import { addRecentId } from "../EntityCollectionView/utils";
|
|
6
12
|
import { navigateToEntity } from "../../util/navigation_utils";
|
|
@@ -8,7 +14,6 @@ import { resolveDefaultSelectedView } from "@rebasepro/common";
|
|
|
8
14
|
import { CreationResultDialog } from "../admin/CreationResultDialog";
|
|
9
15
|
import React, { useState } from "react";
|
|
10
16
|
|
|
11
|
-
|
|
12
17
|
export const editEntityAction: EntityAction = {
|
|
13
18
|
icon: <PencilIcon size={iconSize.smallest}/>,
|
|
14
19
|
key: "edit",
|
|
@@ -55,6 +60,9 @@ export const editEntityAction: EntityAction = {
|
|
|
55
60
|
entityId: entity.id
|
|
56
61
|
}
|
|
57
62
|
);
|
|
63
|
+
if (!context?.urlController) {
|
|
64
|
+
throw new Error("INTERNAL: editEntityAction: urlController is undefined");
|
|
65
|
+
}
|
|
58
66
|
navigateToEntity({
|
|
59
67
|
openEntityMode,
|
|
60
68
|
collection,
|
|
@@ -62,7 +70,7 @@ export const editEntityAction: EntityAction = {
|
|
|
62
70
|
path: newFullIdPath,
|
|
63
71
|
sideEntityController,
|
|
64
72
|
onClose: () => unhighlightEntity?.(entity),
|
|
65
|
-
navigation: context
|
|
73
|
+
navigation: context.urlController,
|
|
66
74
|
selectedTab: defaultSelectedView
|
|
67
75
|
});
|
|
68
76
|
|
|
@@ -98,6 +106,9 @@ export const copyEntityAction: EntityAction = {
|
|
|
98
106
|
});
|
|
99
107
|
|
|
100
108
|
const usedPath = path ?? collection?.slug ?? entity.path;
|
|
109
|
+
if (!context?.urlController) {
|
|
110
|
+
throw new Error("INTERNAL: copyEntityAction: urlController is undefined");
|
|
111
|
+
}
|
|
101
112
|
navigateToEntity({
|
|
102
113
|
openEntityMode,
|
|
103
114
|
collection,
|
|
@@ -106,7 +117,7 @@ export const copyEntityAction: EntityAction = {
|
|
|
106
117
|
copy: true,
|
|
107
118
|
sideEntityController,
|
|
108
119
|
onClose: () => unhighlightEntity?.(entity),
|
|
109
|
-
navigation: context
|
|
120
|
+
navigation: context.urlController
|
|
110
121
|
});
|
|
111
122
|
|
|
112
123
|
return Promise.resolve(undefined);
|
|
@@ -9,7 +9,6 @@ import { KeyValueFieldBinding } from "../form/field_bindings/KeyValueFieldBindin
|
|
|
9
9
|
import { MapFieldBinding } from "../form/field_bindings/MapFieldBinding";
|
|
10
10
|
import { MarkdownEditorFieldBinding } from "../form/field_bindings/MarkdownEditorFieldBinding";
|
|
11
11
|
import { MultiSelectFieldBinding } from "../form/field_bindings/MultiSelectFieldBinding";
|
|
12
|
-
import { ReferenceAsStringFieldBinding } from "../form/field_bindings/ReferenceAsStringFieldBinding";
|
|
13
12
|
import { ReferenceFieldBinding } from "../form/field_bindings/ReferenceFieldBinding";
|
|
14
13
|
import { RepeatFieldBinding } from "../form/field_bindings/RepeatFieldBinding";
|
|
15
14
|
import { SelectFieldBinding } from "../form/field_bindings/SelectFieldBinding";
|
|
@@ -220,17 +219,6 @@ Field: TextFieldBinding }
|
|
|
220
219
|
ui: { Field: StorageUploadFieldBinding }
|
|
221
220
|
}
|
|
222
221
|
},
|
|
223
|
-
reference_as_string: {
|
|
224
|
-
key: "reference_as_string",
|
|
225
|
-
name: "Reference (as string)",
|
|
226
|
-
description: "The value refers to a different collection (it is saved as a string)",
|
|
227
|
-
Icon: LinkIcon,
|
|
228
|
-
color: "#154fb3",
|
|
229
|
-
property: {
|
|
230
|
-
type: "string",
|
|
231
|
-
ui: { Field: ReferenceAsStringFieldBinding }
|
|
232
|
-
}
|
|
233
|
-
},
|
|
234
222
|
reference: {
|
|
235
223
|
key: "reference",
|
|
236
224
|
name: "Reference",
|
|
@@ -393,8 +381,6 @@ export function getDefaultFieldId(property: Property) {
|
|
|
393
381
|
return "select";
|
|
394
382
|
} else if (property.userSelect) {
|
|
395
383
|
return "user_select";
|
|
396
|
-
} else if ((property as StringProperty).reference) {
|
|
397
|
-
return "reference_as_string";
|
|
398
384
|
} else {
|
|
399
385
|
return "text_field";
|
|
400
386
|
}
|
package/src/editor/editor.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import React, { useEffect, useState, useRef, useMemo } from "react";
|
|
3
3
|
import { EditorState, Selection } from "prosemirror-state";
|
|
4
|
-
import { cls, defaultBorderMixin, Separator,
|
|
4
|
+
import { cls, defaultBorderMixin, Separator, TextareaAutosize, useInjectStyles } from "@rebasepro/ui";
|
|
5
5
|
import { useTranslation } from "@rebasepro/core";
|
|
6
6
|
import { EditorBubble, ImageBubble, SlashCommandMenu, TableBubble, type JSONContent } from "./components";
|
|
7
7
|
import { NodeSelector } from "./selectors/node-selector";
|
|
@@ -26,8 +26,7 @@ export interface MarkdownEditorConfig {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export type RichTextEditorTextSize = "sm" | "base" | "lg";
|
|
29
|
-
|
|
30
|
-
export type RebaseEditorTextSize = RichTextEditorTextSize;
|
|
29
|
+
|
|
31
30
|
|
|
32
31
|
export type RichTextEditorProps = {
|
|
33
32
|
content?: JSONContent | string,
|
|
@@ -43,8 +42,7 @@ export type RichTextEditorProps = {
|
|
|
43
42
|
disabled?: boolean;
|
|
44
43
|
markdownConfig?: MarkdownEditorConfig;
|
|
45
44
|
};
|
|
46
|
-
|
|
47
|
-
export type RebaseEditorProps = RichTextEditorProps;
|
|
45
|
+
|
|
48
46
|
|
|
49
47
|
const proseClasses = {
|
|
50
48
|
"sm": "prose-sm",
|
|
@@ -324,8 +322,7 @@ view }), [state, view])}>
|
|
|
324
322
|
</div>
|
|
325
323
|
);
|
|
326
324
|
};
|
|
327
|
-
|
|
328
|
-
export const RebaseEditor = RichTextEditor;
|
|
325
|
+
|
|
329
326
|
|
|
330
327
|
const cssStyles = `
|
|
331
328
|
.ProseMirror {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import type
|
|
2
|
+
import { type EntityCollection, getCollectionDataPath } from "@rebasepro/types";
|
|
3
3
|
import type { FormContext } from "../types/fields";
|
|
4
4
|
import type { EntityAction } from "@rebasepro/types";
|
|
5
5
|
import React from "react";
|
|
@@ -127,7 +127,7 @@ function buildBottomActions<M extends Record<string, unknown>>({
|
|
|
127
127
|
action.onClick({
|
|
128
128
|
view: "form",
|
|
129
129
|
entity,
|
|
130
|
-
path: path ?? collection
|
|
130
|
+
path: path ?? getCollectionDataPath(collection),
|
|
131
131
|
collection: collection,
|
|
132
132
|
context: undefined,
|
|
133
133
|
sideEntityController: undefined,
|
|
@@ -254,7 +254,7 @@ export function EntityFormBinding<M extends Record<string, unknown>>({
|
|
|
254
254
|
try {
|
|
255
255
|
const accessor = dataClient.collection(path);
|
|
256
256
|
const { data } = await accessor.find({
|
|
257
|
-
where: { [name]:
|
|
257
|
+
where: { [name]: ["==", value] },
|
|
258
258
|
limit: 2
|
|
259
259
|
});
|
|
260
260
|
const otherEntities = entityId ? data.filter(e => e.id !== entityId) : data;
|
|
@@ -3,6 +3,7 @@ import React, { useCallback } from "react";
|
|
|
3
3
|
import { useSnackbarController, useStorageSource } from "@rebasepro/core";
|
|
4
4
|
import { StorageFieldItem } from "@rebasepro/core";
|
|
5
5
|
import { ErrorView } from "@rebasepro/core";
|
|
6
|
+
import type { StorageSource } from "@rebasepro/types";
|
|
6
7
|
import { cls, paperMixin, Skeleton } from "@rebasepro/ui";
|
|
7
8
|
|
|
8
9
|
export interface StorageUploadItemProps {
|
|
@@ -15,6 +16,10 @@ export interface StorageUploadItemProps {
|
|
|
15
16
|
uploadedUrl?: string) => Promise<void>;
|
|
16
17
|
imageSize: number;
|
|
17
18
|
simple: boolean;
|
|
19
|
+
/** Override the storage source for this upload. When provided, this source
|
|
20
|
+
* is used instead of the default from context — enabling per-property
|
|
21
|
+
* multi-backend uploads. */
|
|
22
|
+
storageSource?: StorageSource;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export function StorageUploadProgress({
|
|
@@ -23,10 +28,12 @@ export function StorageUploadProgress({
|
|
|
23
28
|
metadata,
|
|
24
29
|
onFileUploadComplete,
|
|
25
30
|
imageSize,
|
|
26
|
-
simple
|
|
31
|
+
simple,
|
|
32
|
+
storageSource: storageSourceProp
|
|
27
33
|
}: StorageUploadItemProps) {
|
|
28
34
|
|
|
29
|
-
const
|
|
35
|
+
const defaultStorageSource = useStorageSource();
|
|
36
|
+
const storageSource = storageSourceProp ?? defaultStorageSource;
|
|
30
37
|
|
|
31
38
|
const snackbarController = useSnackbarController();
|
|
32
39
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
3
3
|
import { useAuthController, useStorageSource } from "@rebasepro/core";
|
|
4
|
+
import { useStorageSources } from "@rebasepro/core";
|
|
5
|
+
import { resolveStorageSource } from "@rebasepro/common";
|
|
4
6
|
import { getIconForProperty } from "../../util/property_utils";
|
|
5
7
|
import type { FieldProps } from "../../types/fields";
|
|
6
8
|
import type { ArrayProperty, StringProperty } from "@rebasepro/types";
|
|
@@ -84,8 +86,17 @@ export function MarkdownEditorFieldBinding({
|
|
|
84
86
|
const disabled = disabledProp || isSubmitting;
|
|
85
87
|
const highlight = customProps?.highlight;
|
|
86
88
|
const editorProps = customProps?.editorProps;
|
|
87
|
-
const
|
|
89
|
+
const defaultStorageSource = useStorageSource();
|
|
88
90
|
const storage = property.storage;
|
|
91
|
+
const storageSources = useStorageSources();
|
|
92
|
+
|
|
93
|
+
// Resolve the correct storage source for this property.
|
|
94
|
+
// Mirrors the resolution in useStorageUploadController.
|
|
95
|
+
const storageSource = useMemo(() => resolveStorageSource({
|
|
96
|
+
sourceKey: storage?.storageSource,
|
|
97
|
+
sources: storageSources.sources,
|
|
98
|
+
defaultSource: defaultStorageSource
|
|
99
|
+
}), [storage?.storageSource, storageSources.sources, defaultStorageSource]);
|
|
89
100
|
|
|
90
101
|
const entityValues = context.values;
|
|
91
102
|
const entityId = context.entityId;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useEntitySelectionDialog } from "../../hooks/useEntitySelectionDialog";
|
|
2
2
|
import type { FieldProps } from "../../types/fields";
|
|
3
3
|
import type { RelationProperty } from "@rebasepro/types";
|
|
4
|
+
import { Entity, getCollectionDataPath, getDataSourceCapabilities } from "@rebasepro/types";
|
|
4
5
|
import React, { useCallback } from "react";
|
|
5
|
-
import { Entity, getDataSourceCapabilities } from "@rebasepro/types";
|
|
6
6
|
import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
7
7
|
import { ArrayContainer, ArrayEntryParams } from "../../components/ArrayContainer";
|
|
8
8
|
import { getIconForProperty } from "../../util/property_utils";
|
|
9
9
|
import { getRelationFrom, resolveRelationProperty } from "@rebasepro/common";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { ErrorView, useTranslation } from "@rebasepro/core";
|
|
12
12
|
import { Button, cls, ExpandablePanel, fieldBackgroundMixin, PencilIcon, Typography } from "@rebasepro/ui";
|
|
13
13
|
import { RelationPreview } from "../../preview";
|
|
14
14
|
|
|
@@ -41,7 +41,7 @@ export function MultipleRelationFieldBinding({
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const parentCollection = context.collection;
|
|
44
|
-
const capabilities = parentCollection ? getDataSourceCapabilities(parentCollection.
|
|
44
|
+
const capabilities = parentCollection ? getDataSourceCapabilities(parentCollection.engine) : undefined;
|
|
45
45
|
if (!parentCollection || !capabilities?.supportsRelations || !("relations" in parentCollection) || !parentCollection.relations) {
|
|
46
46
|
throw Error("RelationFieldBinding expected a collection with relations support");
|
|
47
47
|
}
|
|
@@ -61,7 +61,7 @@ export function MultipleRelationFieldBinding({
|
|
|
61
61
|
|
|
62
62
|
const referenceDialogController = useEntitySelectionDialog({
|
|
63
63
|
multiselect: true,
|
|
64
|
-
path: collection
|
|
64
|
+
path: getCollectionDataPath(collection),
|
|
65
65
|
collection,
|
|
66
66
|
onMultipleEntitiesSelected,
|
|
67
67
|
selectedEntityIds,
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { useEntitySelectionDialog } from "../../hooks/useEntitySelectionDialog";
|
|
2
2
|
import type { FieldProps } from "../../types/fields";
|
|
3
3
|
import type { RelationProperty } from "@rebasepro/types";
|
|
4
|
+
import { Entity, getCollectionDataPath, getDataSourceCapabilities, Relation } from "@rebasepro/types";
|
|
4
5
|
import React, { useCallback } from "react";
|
|
5
|
-
import { Entity, EntityRelation, getDataSourceCapabilities, Relation } from "@rebasepro/types";
|
|
6
6
|
import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
7
7
|
import { EntityPreviewContainer } from "../../components/EntityPreview";
|
|
8
|
-
import { IconForView } from "@rebasepro/core";
|
|
8
|
+
import { ErrorView, IconForView } from "@rebasepro/core";
|
|
9
9
|
import { getIconForProperty } from "../../util/property_utils";
|
|
10
|
-
import { getRelationFrom,
|
|
11
|
-
|
|
12
|
-
import { ErrorView } from "@rebasepro/core";
|
|
10
|
+
import { getRelationFrom, normalizeToEntityRelation, resolveRelationProperty } from "@rebasepro/common";
|
|
13
11
|
import { cls } from "@rebasepro/ui";
|
|
14
12
|
import { RelationPreview } from "../../preview";
|
|
15
13
|
import { RelationSelector } from "../../components/RelationSelector";
|
|
@@ -22,7 +20,7 @@ export function RelationFieldBinding(props: FieldProps<RelationProperty>) {
|
|
|
22
20
|
throw Error("RelationFieldBinding expected a property containing a relation");
|
|
23
21
|
}
|
|
24
22
|
const collection = context.collection;
|
|
25
|
-
const capabilities = collection ? getDataSourceCapabilities(collection.
|
|
23
|
+
const capabilities = collection ? getDataSourceCapabilities(collection.engine) : undefined;
|
|
26
24
|
if (!collection || !capabilities?.supportsRelations || !("relations" in collection) || !collection.relations) {
|
|
27
25
|
throw Error("RelationFieldBinding expected a collection with relations support");
|
|
28
26
|
}
|
|
@@ -124,7 +122,7 @@ function SingleRelationFieldBinding({
|
|
|
124
122
|
|
|
125
123
|
const referenceDialogController = useEntitySelectionDialog({
|
|
126
124
|
multiselect: false,
|
|
127
|
-
path: collection
|
|
125
|
+
path: getCollectionDataPath(collection),
|
|
128
126
|
collection,
|
|
129
127
|
onSingleEntitySelected,
|
|
130
128
|
selectedEntityIds: validValue && normalizedValue ? [normalizedValue.id] : undefined,
|
|
@@ -10,6 +10,7 @@ import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
|
10
10
|
import { isReadOnly } from "@rebasepro/common";
|
|
11
11
|
import { getIconForProperty } from "../../util/property_utils";
|
|
12
12
|
import { useAuthController, useSnackbarController, useStorageSource, useTranslation } from "@rebasepro/core";
|
|
13
|
+
import { StorageSourceContext } from "@rebasepro/core";
|
|
13
14
|
import {
|
|
14
15
|
closestCenter,
|
|
15
16
|
DndContext,
|
|
@@ -62,7 +63,7 @@ export function StorageUploadFieldBinding({
|
|
|
62
63
|
|
|
63
64
|
const authController = useAuthController();
|
|
64
65
|
|
|
65
|
-
const storageSource = useStorageSource(
|
|
66
|
+
const storageSource = useStorageSource();
|
|
66
67
|
const disabled = isReadOnly(property) || !!property.ui?.disabled || isSubmitting || context.disabled;
|
|
67
68
|
|
|
68
69
|
const {
|
|
@@ -72,7 +73,8 @@ export function StorageUploadFieldBinding({
|
|
|
72
73
|
storage,
|
|
73
74
|
onFileUploadComplete,
|
|
74
75
|
storagePathBuilder,
|
|
75
|
-
multipleFilesSupported
|
|
76
|
+
multipleFilesSupported,
|
|
77
|
+
resolvedStorageSource
|
|
76
78
|
} = useStorageUploadController({
|
|
77
79
|
entityValues: context.values,
|
|
78
80
|
entityId: context.entityId,
|
|
@@ -103,19 +105,21 @@ export function StorageUploadFieldBinding({
|
|
|
103
105
|
title={property.name ?? propertyKey}
|
|
104
106
|
className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
|
|
105
107
|
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
<StorageSourceContext.Provider value={resolvedStorageSource}>
|
|
109
|
+
<StorageUpload
|
|
110
|
+
value={internalValue}
|
|
111
|
+
name={propertyKey}
|
|
112
|
+
disabled={disabled ?? false}
|
|
113
|
+
autoFocus={autoFocus ?? false}
|
|
114
|
+
property={property}
|
|
115
|
+
onChange={setValue}
|
|
116
|
+
setInternalValue={setInternalValue}
|
|
117
|
+
onFilesAdded={onFilesAdded}
|
|
118
|
+
onFileUploadComplete={onFileUploadComplete}
|
|
119
|
+
storagePathBuilder={storagePathBuilder}
|
|
120
|
+
storage={storage}
|
|
121
|
+
multipleFilesSupported={multipleFilesSupported}/>
|
|
122
|
+
</StorageSourceContext.Provider>
|
|
119
123
|
|
|
120
124
|
<FieldHelperText includeDescription={includeDescription}
|
|
121
125
|
showError={showError}
|
package/src/form/index.tsx
CHANGED
|
@@ -12,7 +12,6 @@ export { TextFieldBinding } from "./field_bindings/TextFieldBinding";
|
|
|
12
12
|
export { SwitchFieldBinding } from "./field_bindings/SwitchFieldBinding";
|
|
13
13
|
export { DateTimeFieldBinding } from "./field_bindings/DateTimeFieldBinding";
|
|
14
14
|
export { ReferenceFieldBinding } from "./field_bindings/ReferenceFieldBinding";
|
|
15
|
-
export { ReferenceAsStringFieldBinding } from "./field_bindings/ReferenceAsStringFieldBinding";
|
|
16
15
|
export { MapFieldBinding } from "./field_bindings/MapFieldBinding";
|
|
17
16
|
export { KeyValueFieldBinding } from "./field_bindings/KeyValueFieldBinding";
|
|
18
17
|
export { RepeatFieldBinding } from "./field_bindings/RepeatFieldBinding";
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export * from "./types";
|
|
3
3
|
// Editor types only — the full ProseMirror editor (RichTextEditor) is a heavy import (~300 KB)
|
|
4
4
|
// and is available as a separate entry point: @rebasepro/admin/editor
|
|
5
|
-
export type { RichTextEditorProps, RichTextEditorTextSize,
|
|
5
|
+
export type { RichTextEditorProps, RichTextEditorTextSize, JSONContent, EditorAIController } from "./editor";
|
|
6
6
|
export * from "./form";
|
|
7
7
|
export * from "./preview";
|
|
8
8
|
export * from "./routes";
|
|
@@ -82,6 +82,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<P extends Pro
|
|
|
82
82
|
content = <StorageThumbnail
|
|
83
83
|
interactive={interactive}
|
|
84
84
|
storeUrl={property.storage?.storeUrl ?? false}
|
|
85
|
+
storageSourceKey={stringProperty.storage.storageSource}
|
|
85
86
|
size={props.size}
|
|
86
87
|
fill={fill}
|
|
87
88
|
storagePathOrDownloadUrl={filePath}/>;
|
|
@@ -107,22 +108,6 @@ export const PropertyPreview = React.memo(function PropertyPreview<P extends Pro
|
|
|
107
108
|
propertyKey={propertyKey}
|
|
108
109
|
size={props.size}
|
|
109
110
|
/>;
|
|
110
|
-
} else if (stringProperty.reference) {
|
|
111
|
-
if (typeof stringProperty.reference.path === "string") {
|
|
112
|
-
content = <ReferencePreview
|
|
113
|
-
disabled={!stringProperty.reference.path}
|
|
114
|
-
previewProperties={stringProperty.reference?.ui?.previewProperties}
|
|
115
|
-
includeId={stringProperty.reference.includeId}
|
|
116
|
-
includeEntityLink={stringProperty.reference.includeEntityLink}
|
|
117
|
-
size={props.size}
|
|
118
|
-
reference={new EntityReference({ id: value,
|
|
119
|
-
path: stringProperty.reference.path })}
|
|
120
|
-
textOnly={props.textOnly}
|
|
121
|
-
/>;
|
|
122
|
-
} else {
|
|
123
|
-
content = <EmptyValue/>;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
111
|
} else {
|
|
127
112
|
content = <StringPropertyPreview {...props}
|
|
128
113
|
property={stringProperty}
|