@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.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{CollectionEditorDialog-iGgS4rLX.js → CollectionEditorDialog-BO_aQ-Z4.js} +7 -7
  3. package/dist/CollectionEditorDialog-BO_aQ-Z4.js.map +1 -0
  4. package/dist/{PropertyEditView-C9yhLyja.js → PropertyEditView-DKzOpMjR.js} +64 -38
  5. package/dist/PropertyEditView-DKzOpMjR.js.map +1 -0
  6. package/dist/{RouterCollectionsStudioView-Dqx27GD8.js → RouterCollectionsStudioView-BQTIExjA.js} +4 -4
  7. package/dist/{RouterCollectionsStudioView-Dqx27GD8.js.map → RouterCollectionsStudioView-BQTIExjA.js.map} +1 -1
  8. package/dist/collection_editor/_cms_internals.d.ts +1 -1
  9. package/dist/collection_editor/api/generateCollectionApi.d.ts +3 -2
  10. package/dist/collection_editor/serializable_types.d.ts +5 -9
  11. package/dist/collection_editor/serializable_utils.d.ts +2 -2
  12. package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +1 -2
  13. package/dist/collection_editor_ui.js +4 -4
  14. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  15. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  16. package/dist/components/RebaseCMS.d.ts +1 -1
  17. package/dist/components/SelectableTable/SelectionStore.d.ts +12 -21
  18. package/dist/components/field_configs.d.ts +1 -1
  19. package/dist/editor/editor.d.ts +0 -6
  20. package/dist/editor.js +1 -3
  21. package/dist/editor.js.map +1 -1
  22. package/dist/form/components/StorageUploadProgress.d.ts +6 -1
  23. package/dist/form/index.d.ts +0 -1
  24. package/dist/{history-ChHgyyvM.js → history-B3cqBzEV.js} +2 -2
  25. package/dist/{history-ChHgyyvM.js.map → history-B3cqBzEV.js.map} +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +2 -2
  28. package/dist/preview/components/StorageThumbnail.d.ts +3 -1
  29. package/dist/{src-DOX2nOyh.js → src-cxV1ODRG.js} +178 -460
  30. package/dist/src-cxV1ODRG.js.map +1 -0
  31. package/dist/types/components/PropertyPreviewProps.d.ts +2 -1
  32. package/dist/types/fields.d.ts +4 -6
  33. package/package.json +18 -18
  34. package/src/collection_editor/_cms_internals.ts +1 -1
  35. package/src/collection_editor/api/generateCollectionApi.ts +3 -2
  36. package/src/collection_editor/pgColumnToProperty.ts +2 -2
  37. package/src/collection_editor/serializable_types.ts +14 -27
  38. package/src/collection_editor/serializable_utils.ts +25 -37
  39. package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +2 -2
  40. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +41 -24
  41. package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +0 -9
  42. package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +35 -1
  43. package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +1 -1
  44. package/src/collection_editor/ui/collection_editor/utils/supported_fields.tsx +0 -1
  45. package/src/collection_editor/ui/collection_editor/utils/update_property_for_widget.ts +7 -29
  46. package/src/collection_editor/useJsonCollectionsConfigController.tsx +2 -2
  47. package/src/components/DefaultDrawer.tsx +1 -1
  48. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +11 -9
  49. package/src/components/EntityCollectionTable/fields/TableMultipleRelationField.tsx +2 -1
  50. package/src/components/EntityCollectionTable/fields/TableRelationField.tsx +2 -1
  51. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +23 -20
  52. package/src/components/EntityCollectionTable/index.tsx +1 -1
  53. package/src/components/EntityCollectionTable/table_bindings.tsx +24 -44
  54. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +1 -1
  55. package/src/components/EntityCollectionView/EntityCollectionListView.tsx +12 -1
  56. package/src/components/EntityCollectionView/EntityCollectionView.tsx +8 -25
  57. package/src/components/EntityCollectionView/hooks/useCollectionInlineEditor.ts +1 -1
  58. package/src/components/EntityCollectionView/useBoardDataController.tsx +12 -40
  59. package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +3 -3
  60. package/src/components/EntityDetailView.tsx +2 -2
  61. package/src/components/EntityEditView.tsx +2 -2
  62. package/src/components/EntityEditViewFormActions.tsx +3 -3
  63. package/src/components/HomePage/ContentHomePage.tsx +1 -1
  64. package/src/components/RebaseCMS.tsx +2 -3
  65. package/src/components/RebaseNavigation.tsx +3 -2
  66. package/src/components/RelationSelector.tsx +2 -2
  67. package/src/components/SelectableTable/SelectableTable.tsx +11 -3
  68. package/src/components/SelectableTable/SelectionStore.ts +18 -48
  69. package/src/components/common/default_entity_actions.tsx +15 -4
  70. package/src/components/field_configs.tsx +0 -14
  71. package/src/editor/editor.tsx +4 -7
  72. package/src/form/EntityFormActions.tsx +2 -2
  73. package/src/form/EntityFormBinding.tsx +1 -1
  74. package/src/form/components/StorageUploadProgress.tsx +9 -2
  75. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +12 -1
  76. package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +4 -4
  77. package/src/form/field_bindings/RelationFieldBinding.tsx +5 -7
  78. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +19 -15
  79. package/src/form/index.tsx +0 -1
  80. package/src/index.ts +1 -1
  81. package/src/preview/PropertyPreview.tsx +1 -16
  82. package/src/preview/components/StorageThumbnail.tsx +24 -7
  83. package/src/types/components/PropertyPreviewProps.tsx +1 -1
  84. package/src/types/fields.tsx +2 -7
  85. package/src/util/previews.ts +20 -0
  86. package/src/util/propertyConfigMapper.ts +3 -3
  87. package/src/util/property_utils.tsx +1 -2
  88. package/dist/CollectionEditorDialog-iGgS4rLX.js.map +0 -1
  89. package/dist/PropertyEditView-C9yhLyja.js.map +0 -1
  90. package/dist/components/EntityCollectionTable/fields/VirtualTableDateField.d.ts +0 -12
  91. package/dist/components/EntityCollectionTable/fields/VirtualTableInput.d.ts +0 -10
  92. package/dist/components/EntityCollectionTable/fields/VirtualTableNumberInput.d.ts +0 -9
  93. package/dist/components/EntityCollectionTable/fields/VirtualTableSwitch.d.ts +0 -8
  94. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +0 -11
  95. package/dist/src-DOX2nOyh.js.map +0 -1
  96. package/src/components/EntityCollectionTable/fields/VirtualTableDateField.tsx +0 -39
  97. package/src/components/EntityCollectionTable/fields/VirtualTableInput.tsx +0 -90
  98. package/src/components/EntityCollectionTable/fields/VirtualTableNumberInput.tsx +0 -83
  99. package/src/components/EntityCollectionTable/fields/VirtualTableSwitch.tsx +0 -32
  100. 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.slug)}` : undefined;
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.slug,
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.slug,
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, plugins, collectionEditor, navigationGroupMappings }: RebaseCMSConfig) {
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, plugins, collectionEditor, navigationGroupMappings]);
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: registry.cmsConfig?.plugins ?? EMPTY_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.slug,
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
- selectionStore.select(cell);
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
- let selectedCell: SelectedCellProps | undefined = undefined;
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 = ReturnType<typeof createSelectionStore>;
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
- // Memoize a selector that derives a boolean from the store snapshot.
54
- // useSyncExternalStore calls this on every store notification, but
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 { ConfirmationDialog, useSnackbarController, useTranslation, useRebaseClient, useAuthController } from "@rebasepro/core";
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?.urlController!,
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?.urlController!
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
  }
@@ -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, useInjectStyles, TextareaAutosize } from "@rebasepro/ui";
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
- /** @deprecated Use `RichTextEditorTextSize` instead. */
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
- /** @deprecated Use `RichTextEditorProps` instead. */
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
- /** @deprecated Use `RichTextEditor` instead. */
328
- export const RebaseEditor = RichTextEditor;
325
+
329
326
 
330
327
  const cssStyles = `
331
328
  .ProseMirror {
@@ -1,5 +1,5 @@
1
1
 
2
- import type { EntityCollection } from "@rebasepro/types";
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.slug,
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]: `eq.${value}` },
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 storageSource = useStorageSource();
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 storageSource = useStorageSource();
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 { useTranslation, ErrorView } from "@rebasepro/core";
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.driver) : undefined;
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.slug,
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, resolveRelationProperty, normalizeToEntityRelation } from "@rebasepro/common";
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.driver) : undefined;
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.slug,
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(context.collection);
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
- <StorageUpload
107
- value={internalValue}
108
- name={propertyKey}
109
- disabled={disabled ?? false}
110
- autoFocus={autoFocus ?? false}
111
- property={property}
112
- onChange={setValue}
113
- setInternalValue={setInternalValue}
114
- onFilesAdded={onFilesAdded}
115
- onFileUploadComplete={onFileUploadComplete}
116
- storagePathBuilder={storagePathBuilder}
117
- storage={storage}
118
- multipleFilesSupported={multipleFilesSupported}/>
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}
@@ -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, RebaseEditorProps, RebaseEditorTextSize, JSONContent, EditorAIController } from "./editor";
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}