@firecms/core 3.0.0-beta.13 → 3.0.0-beta.15

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 (124) hide show
  1. package/dist/components/ArrayContainer.d.ts +7 -12
  2. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  3. package/dist/components/EntityPreview.d.ts +4 -2
  4. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  5. package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
  6. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  7. package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
  8. package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
  9. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  10. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  11. package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
  12. package/dist/components/index.d.ts +1 -0
  13. package/dist/core/EntityEditView.d.ts +3 -0
  14. package/dist/core/EntityEditViewFormActions.d.ts +1 -1
  15. package/dist/core/field_configs.d.ts +1 -1
  16. package/dist/form/EntityForm.d.ts +2 -1
  17. package/dist/form/EntityFormActions.d.ts +6 -2
  18. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  19. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  20. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
  21. package/dist/form/index.d.ts +1 -0
  22. package/dist/hooks/useBuildNavigationController.d.ts +50 -8
  23. package/dist/index.es.js +3259 -1649
  24. package/dist/index.es.js.map +1 -1
  25. package/dist/index.umd.js +3265 -1658
  26. package/dist/index.umd.js.map +1 -1
  27. package/dist/types/analytics.d.ts +1 -1
  28. package/dist/types/collections.d.ts +28 -4
  29. package/dist/types/customization_controller.d.ts +8 -0
  30. package/dist/types/entity_actions.d.ts +46 -6
  31. package/dist/types/firecms.d.ts +10 -1
  32. package/dist/types/navigation.d.ts +26 -5
  33. package/dist/types/plugins.d.ts +32 -1
  34. package/dist/types/properties.d.ts +7 -0
  35. package/dist/types/property_config.d.ts +1 -1
  36. package/dist/types/side_entity_controller.d.ts +8 -0
  37. package/dist/util/callbacks.d.ts +2 -0
  38. package/dist/util/icons.d.ts +2 -2
  39. package/dist/util/index.d.ts +1 -0
  40. package/dist/util/navigation_from_path.d.ts +4 -0
  41. package/dist/util/resolutions.d.ts +2 -1
  42. package/package.json +8 -6
  43. package/src/components/ArrayContainer.tsx +409 -294
  44. package/src/components/ClearFilterSortButton.tsx +1 -1
  45. package/src/components/ConfirmationDialog.tsx +10 -9
  46. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
  47. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +26 -4
  48. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  49. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
  50. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
  51. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  52. package/src/components/EntityCollectionView/EntityCollectionView.tsx +11 -4
  53. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
  54. package/src/components/EntityPreview.tsx +18 -14
  55. package/src/components/ErrorView.tsx +1 -1
  56. package/src/components/FireCMSLogo.tsx +7 -51
  57. package/src/components/HomePage/DefaultHomePage.tsx +491 -156
  58. package/src/components/HomePage/FavouritesView.tsx +3 -3
  59. package/src/components/HomePage/HomePageDnD.tsx +599 -0
  60. package/src/components/HomePage/NavigationCard.tsx +47 -38
  61. package/src/components/HomePage/NavigationCardBinding.tsx +13 -7
  62. package/src/components/HomePage/NavigationGroup.tsx +63 -29
  63. package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
  64. package/src/components/SelectableTable/SelectableTable.tsx +140 -143
  65. package/src/components/UnsavedChangesDialog.tsx +6 -2
  66. package/src/components/VirtualTable/VirtualTable.tsx +8 -30
  67. package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
  68. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
  69. package/src/components/common/default_entity_actions.tsx +25 -9
  70. package/src/components/common/useDataSourceTableController.tsx +2 -2
  71. package/src/components/index.tsx +2 -0
  72. package/src/core/DefaultDrawer.tsx +8 -8
  73. package/src/core/DrawerNavigationItem.tsx +1 -1
  74. package/src/core/EntityEditView.tsx +65 -18
  75. package/src/core/EntityEditViewFormActions.tsx +154 -29
  76. package/src/core/EntitySidePanel.tsx +19 -21
  77. package/src/core/FireCMS.tsx +9 -1
  78. package/src/core/field_configs.tsx +15 -1
  79. package/src/form/EntityForm.tsx +37 -5
  80. package/src/form/EntityFormActions.tsx +51 -9
  81. package/src/form/PropertyFieldBinding.tsx +0 -1
  82. package/src/form/components/CustomIdField.tsx +3 -1
  83. package/src/form/components/LabelWithIcon.tsx +1 -1
  84. package/src/form/components/StorageItemPreview.tsx +1 -1
  85. package/src/form/components/StorageUploadProgress.tsx +3 -3
  86. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
  87. package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
  88. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
  89. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
  90. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  91. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
  92. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +216 -153
  93. package/src/form/index.tsx +1 -0
  94. package/src/hooks/data/save.ts +0 -6
  95. package/src/hooks/useBuildNavigationController.tsx +299 -96
  96. package/src/internal/useBuildSideEntityController.tsx +8 -5
  97. package/src/preview/PropertyPreview.tsx +14 -0
  98. package/src/preview/components/ImagePreview.tsx +2 -2
  99. package/src/preview/components/ReferencePreview.tsx +1 -1
  100. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  101. package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
  102. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
  103. package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
  104. package/src/routes/FireCMSRoute.tsx +3 -3
  105. package/src/types/analytics.ts +3 -0
  106. package/src/types/collections.ts +32 -4
  107. package/src/types/customization_controller.tsx +9 -0
  108. package/src/types/entity_actions.tsx +57 -6
  109. package/src/types/firecms.tsx +11 -2
  110. package/src/types/navigation.ts +34 -6
  111. package/src/types/plugins.tsx +40 -1
  112. package/src/types/properties.ts +8 -0
  113. package/src/types/property_config.tsx +1 -0
  114. package/src/types/side_entity_controller.tsx +10 -5
  115. package/src/util/callbacks.ts +119 -0
  116. package/src/util/icons.tsx +22 -7
  117. package/src/util/index.ts +1 -0
  118. package/src/util/join_collections.ts +3 -1
  119. package/src/util/navigation_from_path.ts +15 -5
  120. package/src/util/navigation_utils.ts +72 -57
  121. package/src/util/objects.ts +53 -20
  122. package/src/util/resolutions.ts +13 -1
  123. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  124. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
@@ -128,7 +128,7 @@ export function KeyValuePreview({ value }: { value: any }) {
128
128
  </div>
129
129
  <div
130
130
  className="flex-grow max-w-[75%]">
131
- {childValue && <Typography>
131
+ {childValue && typeof childValue !== "object" && <Typography>
132
132
  <ErrorBoundary>
133
133
  {childValue.toString()}
134
134
  </ErrorBoundary>
@@ -46,7 +46,7 @@ export function SkeletonPropertyComponent({
46
46
  content = <>{arrayProperty.of.map((p, i) => renderGenericArrayCell(p, i))} </>;
47
47
  } else {
48
48
  if (arrayProperty.of.dataType === "map" && arrayProperty.of.properties) {
49
- content = renderArrayOfMaps(arrayProperty.of.properties, size, arrayProperty.of.previewProperties);
49
+ content = renderArrayOfMaps(arrayProperty.of.properties, size, arrayProperty.of.previewKeys);
50
50
  } else if (arrayProperty.of.dataType === "string") {
51
51
  if (arrayProperty.of.enumValues) {
52
52
  content = renderArrayEnumTableCell();
@@ -45,7 +45,7 @@ export function StringPropertyPreview({
45
45
  if (!value) return <></>;
46
46
  const lines = value.split("\n");
47
47
  return value && value.includes("\n")
48
- ? <div className={cls("overflow-x-scroll", size === "small" ? "text-sm" : "")}>
48
+ ? <div className={cls("overflow-x-scroll overflow-hidden", size === "small" ? "text-sm" : "")}>
49
49
  {lines.map((str, index) =>
50
50
  <React.Fragment key={`string_preview_${index}`}>
51
51
  <span>{str}</span>
@@ -79,7 +79,8 @@ export function FireCMSRoute() {
79
79
  key={`collection_view_${collection.id ?? collection.path}`}
80
80
  isSubCollection={false}
81
81
  parentCollectionIds={[]}
82
- fullPath={collection.id}
82
+ fullPath={collection.path}
83
+ fullIdPath={collection.id}
83
84
  updateUrl={true}
84
85
  {...collection}
85
86
  Actions={toArray(collection.Actions)}/>
@@ -100,7 +101,7 @@ export function FireCMSRoute() {
100
101
  fullIdPath={collection.id}
101
102
  isSubCollection={false}
102
103
  parentCollectionIds={[]}
103
- fullPath={collection.id}
104
+ fullPath={collection.path}
104
105
  updateUrl={true}
105
106
  {...collection}
106
107
  Actions={toArray(collection.Actions)}/>;
@@ -200,7 +201,6 @@ function EntityFullScreenRoute({
200
201
  const collection = isNew ? lastCollectionEntry!.collection : lastEntityEntry!.parentCollection;
201
202
  const fullIdPath = isNew ? lastCollectionEntry!.path : lastEntityEntry!.path;
202
203
  const collectionPath = navigation.resolveIdsFrom(fullIdPath);
203
-
204
204
  return <>
205
205
  <EntityEditView
206
206
  key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
@@ -28,6 +28,9 @@ export type CMSAnalyticsEvent =
28
28
  | "home_navigate_to_view"
29
29
  | "home_navigate_to_admin_view"
30
30
  | "home_favorite_navigate_to_view"
31
+ | "home_move_card"
32
+ | "home_move_group"
33
+ | "home_drop_new_group"
31
34
 
32
35
  | "collection_inline_editing"
33
36
 
@@ -70,12 +70,15 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
70
70
  * e.g. 'account_tree' or 'person'.
71
71
  * Find all the icons in https://firecms.co/docs/icons
72
72
  */
73
- icon?: string;
73
+ icon?: string | React.ReactNode;
74
74
 
75
75
  /**
76
76
  * Optional field used to group top level navigation entries under a~
77
77
  * navigation view. If you set this value in a subcollection it has no
78
78
  * effect.
79
+ * @deprecated This prop is deprecated and will be removed in the future.
80
+ * You can apply grouping by using the `navigationGroupMappings` prop in the
81
+ * {@link useBuildNavigationController} hook instead.
79
82
  */
80
83
  group?: string;
81
84
 
@@ -112,7 +115,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
112
115
  * `hidden` in the property definition, will be ignored.
113
116
  * `propertiesOrder` has precedence over `hidden`.
114
117
  * - For properties use the property key.
115
- * - For additional columns use the column id.
118
+ * - For additional fields use the field key.
116
119
  * - If you have subcollections, you get a column for each subcollection,
117
120
  * with the path (or alias) as the subcollection, prefixed with
118
121
  * `subcollection:`. e.g. `subcollection:orders`.
@@ -127,7 +130,8 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
127
130
 
128
131
  /**
129
132
  * If enabled, content is loaded in batches. If `false` all entities in the
130
- * collection are loaded.
133
+ * collection are loaded. This means that when reaching the end of the
134
+ * collection, the CMS will load more entities.
131
135
  * You can specify a number to specify the pagination size (50 by default)
132
136
  * Defaults to `true`
133
137
  */
@@ -192,8 +196,11 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
192
196
  * }
193
197
  * }
194
198
  * ```
199
+ *
200
+ * You can also pass the action as a string that represents the `key`, in which case it will
201
+ * use the action defined in the main configuration under `entityActions`.
195
202
  */
196
- entityActions?: EntityAction<M, USER>[];
203
+ entityActions?: (EntityAction<M, USER> | string)[];
197
204
 
198
205
  /**
199
206
  * Pass your own selection controller if you want to control selected
@@ -337,6 +344,12 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
337
344
  * If set to true, a tab including the JSON representation of the entity will be included.
338
345
  */
339
346
  includeJsonView?: boolean;
347
+
348
+ /**
349
+ * If set to true, changes to the entity will be saved in a subcollection.
350
+ * This prop has no effect if the history plugin is not enabled
351
+ */
352
+ history?: boolean;
340
353
  }
341
354
 
342
355
  /**
@@ -519,6 +532,11 @@ export type EntityCustomView<M extends Record<string, any> = any> =
519
532
  */
520
533
  name: string;
521
534
 
535
+ /**
536
+ * Render this custom view in the tab of the entity view, instead of the name
537
+ */
538
+ tabComponent?: React.ReactNode;
539
+
522
540
  /**
523
541
  * If set to true, the actions of the entity (save, discard,delete) will be
524
542
  * included in the view. By default the actions are located in the right or bottom,
@@ -531,6 +549,11 @@ export type EntityCustomView<M extends Record<string, any> = any> =
531
549
  * Builder for rendering the custom view
532
550
  */
533
551
  Builder?: React.ComponentType<EntityCustomViewParams<M>>;
552
+
553
+ /**
554
+ * Position of this tab in the entity view. Defaults to `end`.
555
+ */
556
+ position?: "start" | "end";
534
557
  };
535
558
 
536
559
  /**
@@ -561,6 +584,11 @@ export interface EntityCustomViewParams<M extends Record<string, any> = any> {
561
584
  * Use the form context to access the form state and methods
562
585
  */
563
586
  formContext: FormContext;
587
+
588
+ /**
589
+ * If this is a subcollection, this is the path of the parent collections
590
+ */
591
+ parentCollectionIds?: string[];
564
592
  }
565
593
 
566
594
  export type InferCollectionType<S extends EntityCollection> = S extends EntityCollection<infer M> ? M : never;
@@ -4,6 +4,7 @@ import { FireCMSPlugin } from "./plugins";
4
4
  import { EntityCustomView } from "./collections";
5
5
  import { Locale } from "./locales";
6
6
  import { PropertyConfig } from "./property_config";
7
+ import { EntityAction } from "./entity_actions";
7
8
 
8
9
  export type CustomizationController = {
9
10
 
@@ -26,6 +27,14 @@ export type CustomizationController = {
26
27
  */
27
28
  entityViews?: EntityCustomView[];
28
29
 
30
+ /**
31
+ * List of actions that can be performed on entities.
32
+ * These actions are displayed in the entity view and in the collection view.
33
+ * You can later reuse these actions in the `entityActions` prop of a collection,
34
+ * by specifying the `key` of the action.
35
+ */
36
+ entityActions?: EntityAction<any, any>[];
37
+
29
38
  /**
30
39
  * Format of the dates in the CMS.
31
40
  * Defaults to 'MMMM dd, yyyy, HH:mm:ss'
@@ -4,6 +4,7 @@ import { Entity } from "./entities";
4
4
  import { EntityCollection, SelectionController } from "./collections";
5
5
  import { User } from "./user";
6
6
  import { SideEntityController } from "./side_entity_controller";
7
+ import { FormContext } from "./fields";
7
8
 
8
9
  /**
9
10
  * An entity action is a custom action that can be performed on an entity.
@@ -17,7 +18,10 @@ export type EntityAction<M extends object = any, USER extends User = User> = {
17
18
 
18
19
  /**
19
20
  * Key of the action. You only need to provide this if you want to
20
- * override the default actions.
21
+ * override the default actions, or if you are not passing the action
22
+ * directly to the `entityActions` prop of a collection.
23
+ * You can define your actions at the app level, in which case you
24
+ * must provide a key.
21
25
  * The default actions are:
22
26
  * - edit
23
27
  * - delete
@@ -36,6 +40,12 @@ export type EntityAction<M extends object = any, USER extends User = User> = {
36
40
  */
37
41
  onClick: (props: EntityActionClickProps<M, USER>) => Promise<void> | void;
38
42
 
43
+ /**
44
+ * Optional callback in case you want to disable the action
45
+ * @param props
46
+ */
47
+ isEnabled?: (props: EntityActionClickProps<M, USER>) => boolean;
48
+
39
49
  /**
40
50
  * Show this action collapsed in the menu of the collection view.
41
51
  * Defaults to true
@@ -51,18 +61,59 @@ export type EntityAction<M extends object = any, USER extends User = User> = {
51
61
  }
52
62
 
53
63
  export type EntityActionClickProps<M extends object, USER extends User = User> = {
54
- entity: Entity<M>;
64
+ entity?: Entity<M>;
55
65
  context: FireCMSContext<USER>;
66
+
56
67
  fullPath?: string;
68
+ fullIdPath?: string;
57
69
  collection?: EntityCollection<M>;
70
+
71
+ /**
72
+ * Optional form context, present if the action is being called from a form.
73
+ * This allows you to access the form state and methods, including modifying the form values.
74
+ */
75
+ formContext?: FormContext;
76
+
77
+ /**
78
+ * Present if this actions is being called from a side dialog only
79
+ */
80
+ sideEntityController?: SideEntityController;
81
+
82
+ /**
83
+ * Is the action being called from the collection view or from the entity form view?
84
+ */
85
+ view: "collection" | "form";
86
+
87
+ /**
88
+ * If the action is rendered in the form, is it open in a side panel or full screen?
89
+ */
90
+ openEntityMode: "side_panel" | "full_screen";
91
+
92
+ /**
93
+ * Optional selection controller, present if the action is being called from a collection view
94
+ */
58
95
  selectionController?: SelectionController;
96
+
97
+ /**
98
+ * Optional highlight function to highlight the entity in the collection view
99
+ * @param entity
100
+ */
59
101
  highlightEntity?: (entity: Entity<any>) => void;
102
+
103
+ /**
104
+ * Optional unhighlight function to remove the highlight from the entity in the collection view
105
+ * @param entity
106
+ */
60
107
  unhighlightEntity?: (entity: Entity<any>) => void;
61
- onCollectionChange?: () => void;
108
+
62
109
  /**
63
- * If this actions is being called from a side dialog
110
+ * Optional function to navigate back (e.g. when deleting an entity or navigating from a form)
64
111
  */
65
- sideEntityController?: SideEntityController;
112
+ navigateBack?: () => void;
113
+
114
+ /**
115
+ * Callback to be called when the collection changes, e.g. after an entity is deleted or created.
116
+ */
117
+ onCollectionChange?: () => void;
66
118
 
67
- openEntityMode: "side_panel" | "full_screen";
68
119
  };
@@ -12,6 +12,7 @@ import { EntityLinkBuilder } from "./entity_link_builder";
12
12
  import { UserConfigurationPersistence } from "./local_config_persistence";
13
13
  import { FireCMSPlugin } from "./plugins";
14
14
  import { CMSAnalyticsEvent } from "./analytics";
15
+ import { EntityAction } from "./entity_actions";
15
16
 
16
17
  /**
17
18
  * Use this callback to build entity collections dynamically.
@@ -92,9 +93,16 @@ export type FireCMSProps<USER extends User> = {
92
93
  *
93
94
  * You can also define an entity view from the UI.
94
95
  */
95
-
96
96
  entityViews?: EntityCustomView[];
97
97
 
98
+ /**
99
+ * List of actions that can be performed on entities.
100
+ * These actions are displayed in the entity view and in the collection view.
101
+ * You can later reuse these actions in the `entityActions` prop of a collection,
102
+ * by specifying the `key` of the action.
103
+ */
104
+ entityActions?: EntityAction[];
105
+
98
106
  /**
99
107
  * Format of the dates in the CMS.
100
108
  * Defaults to 'MMMM dd, yyyy, HH:mm:ss'
@@ -129,7 +137,8 @@ export type FireCMSProps<USER extends User> = {
129
137
 
130
138
  /**
131
139
  * Use plugins to modify the behaviour of the CMS.
132
- * Currently, in ALPHA, and likely subject to change.
140
+ * DEPRECATED: use the `plugins` prop in the `useBuildNavigationController` instead.
141
+ * This prop will work as a fallback for the `plugins` prop in the `useBuildNavigationController`.
133
142
  */
134
143
  plugins?: FireCMSPlugin<any, any, any>[];
135
144
 
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { EntityCollection } from "./collections";
3
3
  import { EntityReference } from "./entities";
4
+ import { FireCMSPlugin } from "./plugins";
4
5
 
5
6
  /**
6
7
  * Controller that includes the resolved navigation and utility methods and
@@ -35,7 +36,7 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
35
36
  * level of the navigation (e.g. in the home page or the navigation
36
37
  * drawer)
37
38
  */
38
- topLevelNavigation?: TopNavigationResult;
39
+ topLevelNavigation?: NavigationResult;
39
40
 
40
41
  /**
41
42
  * Is the navigation loading (the configuration persistence has not
@@ -152,6 +153,11 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
152
153
  */
153
154
  navigate: (to: string, options?: NavigateOptions) => void;
154
155
 
156
+ /**
157
+ * Plugin system allowing to extend the CMS functionality.
158
+ */
159
+ plugins?: FireCMSPlugin<any, any, any>[];
160
+
155
161
  }
156
162
 
157
163
  export interface NavigateOptions {
@@ -200,7 +206,7 @@ export interface CMSView {
200
206
  * e.g. 'account_tree' or 'person'
201
207
  * Find all the icons in https://firecms.co/docs/icons
202
208
  */
203
- icon?: string;
209
+ icon?: string | React.ReactNode;
204
210
 
205
211
  /**
206
212
  * Should this view be hidden from the main navigation panel.
@@ -223,7 +229,22 @@ export interface CMSView {
223
229
 
224
230
  }
225
231
 
226
- export interface TopNavigationEntry {
232
+ /**
233
+ * Used to group navigation entries in the main navigation.
234
+ */
235
+ export interface NavigationGroupMapping {
236
+ /**
237
+ * Name of the group, used to display the group header in the UI
238
+ */
239
+ name: string;
240
+ /**
241
+ * List of collection ids or view paths that belong to this group.
242
+ */
243
+ entries: string[];
244
+ }
245
+
246
+ export interface NavigationEntry {
247
+ id: string;
227
248
  url: string;
228
249
  name: string;
229
250
  path: string;
@@ -234,7 +255,14 @@ export interface TopNavigationEntry {
234
255
  group: string;
235
256
  }
236
257
 
237
- export type TopNavigationResult = {
238
- navigationEntries: TopNavigationEntry[],
239
- groups: string[]
258
+ export type NavigationResult = {
259
+
260
+ allowDragAndDrop: boolean;
261
+
262
+ navigationEntries: NavigationEntry[],
263
+
264
+ groups: string[],
265
+
266
+ onNavigationEntriesUpdate: (entries: NavigationGroupMapping[]) => void;
240
267
  };
268
+
@@ -7,6 +7,7 @@ import { FieldProps, FormContext } from "./fields";
7
7
  import { CMSType, Property } from "./properties";
8
8
  import { EntityStatus } from "./entities";
9
9
  import { ResolvedProperty } from "./resolved_entities";
10
+ import { NavigationGroupMapping } from "./navigation";
10
11
 
11
12
  /**
12
13
  * Interface used to define plugins for FireCMS.
@@ -84,6 +85,20 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
84
85
  children: React.ReactNode;
85
86
  }
86
87
 
88
+ /**
89
+ * Allow reordering with drag and drop of the collections in the home page.
90
+ */
91
+ allowDragAndDrop?: boolean;
92
+
93
+ navigationEntries?: NavigationGroupMapping[];
94
+
95
+ /**
96
+ * This method will be called when the entries are updated in the home page.
97
+ * group => navigationEntriesOrder (path)
98
+ * @param entries
99
+ */
100
+ onNavigationEntriesUpdate?: (entries: NavigationGroupMapping[]) => void;
101
+
87
102
  }
88
103
 
89
104
  collectionView?: {
@@ -151,13 +166,37 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
151
166
  props?: FORM_PROPS;
152
167
  }
153
168
 
169
+ /**
170
+ * Add custom actions to the default ones ("Save", "Discard"...)
171
+ */
154
172
  Actions?: React.ComponentType<PluginFormActionProps<any, EC>>;
155
173
 
174
+ /**
175
+ * Add custom actions to the top of the form
176
+ */
177
+ ActionsTop?: React.ComponentType<PluginFormActionProps<any, EC>>;
178
+
156
179
  fieldBuilder?: <T extends CMSType = CMSType>(props: PluginFieldBuilderParams<T, any, EC>) => React.ComponentType<FieldProps<T>> | null;
157
180
 
158
181
  fieldBuilderEnabled?: <T extends CMSType = CMSType>(props: PluginFieldBuilderParams<T>) => boolean;
159
182
  }
160
183
 
184
+ collection?: {
185
+
186
+ /**
187
+ * Use this method to modify a single collection before it is rendered.
188
+ * @param collection
189
+ */
190
+ modifyCollection?: (collection: EntityCollection) => EntityCollection;
191
+
192
+ /**
193
+ * Use this method to modify, add or remove collections.
194
+ * @param collections
195
+ */
196
+ injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
197
+
198
+ }
199
+
161
200
  }
162
201
 
163
202
  /**
@@ -190,12 +229,12 @@ export interface PluginHomePageActionsProps<EP extends object = object, M extend
190
229
  export interface PluginFormActionProps<USER extends User = User, EC extends EntityCollection = EntityCollection> {
191
230
  entityId?: string;
192
231
  path: string;
232
+ parentCollectionIds: string[];
193
233
  status: EntityStatus;
194
234
  collection: EC;
195
235
  disabled: boolean;
196
236
  formContext?: FormContext<any>;
197
237
  context: FireCMSContext<USER>;
198
- currentEntityId?: string;
199
238
  openEntityMode: "side_panel" | "full_screen";
200
239
  }
201
240
 
@@ -413,6 +413,14 @@ export interface StringProperty extends BaseProperty<string> {
413
413
  * Add an icon to clear the value and set it to `null`. Defaults to `false`
414
414
  */
415
415
  clearable?: boolean;
416
+
417
+ /**
418
+ * You can use this property (a string) to behave as a reference to another
419
+ * collection. The stored value is the ID of the entity in the
420
+ * collection, and the `path` prop is used to
421
+ * define the collection this reference points to.
422
+ */
423
+ reference?: ReferenceProperty;
416
424
  }
417
425
 
418
426
  /**
@@ -62,6 +62,7 @@ export type PropertyConfigId =
62
62
  "group" |
63
63
  "key_value" |
64
64
  "reference" |
65
+ "reference_as_string" |
65
66
  "multi_references" |
66
67
  "switch" |
67
68
  "date_time" |
@@ -15,16 +15,16 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
15
15
  */
16
16
  path: string;
17
17
 
18
+ /**
19
+ * Full CMS path of the entity, including the collection and sub-collections.
20
+ */
21
+ fullIdPath?: string;
22
+
18
23
  /**
19
24
  * ID of the entity, if not set, it means we are creating a new entity
20
25
  */
21
26
  entityId?: string;
22
27
 
23
- // /**
24
- // * Navigation path with ids of the entity
25
- // */
26
- // fullIdPath: string;
27
-
28
28
  /**
29
29
  * Set this flag to true if you want to make a copy of an existing entity
30
30
  */
@@ -76,6 +76,11 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
76
76
  * Override some form properties
77
77
  */
78
78
  formProps?: Partial<EntityFormProps<M>>;
79
+
80
+ /**
81
+ * Allow the user to open the entity fullscreen
82
+ */
83
+ allowFullScreen?: boolean;
79
84
  }
80
85
 
81
86
  /**
@@ -0,0 +1,119 @@
1
+ import { EntityCallbacks } from "../types";
2
+
3
+ export const mergeCallbacks = (
4
+ baseCallbacks: EntityCallbacks = {},
5
+ pluginCallbacks: EntityCallbacks = {}
6
+ ): EntityCallbacks | undefined => {
7
+
8
+ if (!baseCallbacks && !pluginCallbacks) {
9
+ return undefined;
10
+ }
11
+
12
+ const mergedCallbacks: EntityCallbacks = {};
13
+
14
+ // Handle onFetch - returns Entity<M> or Promise<Entity<M>>
15
+ if (baseCallbacks.onFetch || pluginCallbacks.onFetch) {
16
+ mergedCallbacks.onFetch = async (props) => {
17
+ let entity = props.entity;
18
+ if (baseCallbacks.onFetch) {
19
+ entity = await Promise.resolve(baseCallbacks.onFetch(props));
20
+ }
21
+ if (pluginCallbacks.onFetch) {
22
+ entity = await Promise.resolve(pluginCallbacks.onFetch({
23
+ ...props,
24
+ entity
25
+ }));
26
+ }
27
+ return entity;
28
+ };
29
+ }
30
+
31
+ // Handle onSaveSuccess - returns void or Promise<void>
32
+ if (baseCallbacks.onSaveSuccess || pluginCallbacks.onSaveSuccess) {
33
+ mergedCallbacks.onSaveSuccess = async (props) => {
34
+ if (baseCallbacks.onSaveSuccess) {
35
+ await Promise.resolve(baseCallbacks.onSaveSuccess(props));
36
+ }
37
+ if (pluginCallbacks.onSaveSuccess) {
38
+ await Promise.resolve(pluginCallbacks.onSaveSuccess(props));
39
+ }
40
+ };
41
+ }
42
+
43
+ // Handle onSaveFailure - returns void or Promise<void>
44
+ if (baseCallbacks.onSaveFailure || pluginCallbacks.onSaveFailure) {
45
+ mergedCallbacks.onSaveFailure = async (props) => {
46
+ if (baseCallbacks.onSaveFailure) {
47
+ await Promise.resolve(baseCallbacks.onSaveFailure(props));
48
+ }
49
+ if (pluginCallbacks.onSaveFailure) {
50
+ await Promise.resolve(pluginCallbacks.onSaveFailure(props));
51
+ }
52
+ };
53
+ }
54
+
55
+ // Handle onPreSave - returns Partial<EntityValues<M>> or Promise<Partial<EntityValues<M>>>
56
+ if (baseCallbacks.onPreSave || pluginCallbacks.onPreSave) {
57
+ mergedCallbacks.onPreSave = async (props) => {
58
+ let values = { ...props.values };
59
+ if (baseCallbacks.onPreSave) {
60
+ const baseValues = await Promise.resolve(baseCallbacks.onPreSave(props));
61
+ values = { ...values, ...baseValues };
62
+ }
63
+ if (pluginCallbacks.onPreSave) {
64
+ const pluginValues = await Promise.resolve(pluginCallbacks.onPreSave({
65
+ ...props,
66
+ values
67
+ }));
68
+ values = { ...values, ...pluginValues };
69
+ }
70
+ return values;
71
+ };
72
+ }
73
+
74
+ // Handle onPreDelete - returns void
75
+ if (baseCallbacks.onPreDelete || pluginCallbacks.onPreDelete) {
76
+ mergedCallbacks.onPreDelete = (props) => {
77
+ if (baseCallbacks.onPreDelete) {
78
+ baseCallbacks.onPreDelete(props);
79
+ }
80
+ if (pluginCallbacks.onPreDelete) {
81
+ pluginCallbacks.onPreDelete(props);
82
+ }
83
+ };
84
+ }
85
+
86
+ // Handle onDelete - returns void
87
+ if (baseCallbacks.onDelete || pluginCallbacks.onDelete) {
88
+ mergedCallbacks.onDelete = (props) => {
89
+ if (baseCallbacks.onDelete) {
90
+ baseCallbacks.onDelete(props);
91
+ }
92
+ if (pluginCallbacks.onDelete) {
93
+ pluginCallbacks.onDelete(props);
94
+ }
95
+ };
96
+ }
97
+
98
+ // Handle onIdUpdate - returns string or Promise<string>
99
+ if (baseCallbacks.onIdUpdate || pluginCallbacks.onIdUpdate) {
100
+ mergedCallbacks.onIdUpdate = async (props) => {
101
+ let id = props.entityId || "";
102
+
103
+ if (baseCallbacks.onIdUpdate) {
104
+ id = await Promise.resolve(baseCallbacks.onIdUpdate(props));
105
+ }
106
+
107
+ if (pluginCallbacks.onIdUpdate) {
108
+ id = await Promise.resolve(pluginCallbacks.onIdUpdate({
109
+ ...props,
110
+ entityId: id
111
+ }));
112
+ }
113
+
114
+ return id;
115
+ };
116
+ }
117
+
118
+ return Object.keys(mergedCallbacks).length > 0 ? mergedCallbacks : undefined;
119
+ };