@firecms/core 3.0.0-canary.235 → 3.0.0-canary.237

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 (37) hide show
  1. package/dist/components/EntityPreview.d.ts +4 -2
  2. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  3. package/dist/components/index.d.ts +1 -0
  4. package/dist/hooks/useBuildNavigationController.d.ts +2 -9
  5. package/dist/index.es.js +266 -138
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +266 -138
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/types/collections.d.ts +13 -0
  10. package/dist/types/plugins.d.ts +12 -0
  11. package/dist/types/side_entity_controller.d.ts +4 -0
  12. package/dist/util/callbacks.d.ts +2 -0
  13. package/dist/util/index.d.ts +1 -0
  14. package/package.json +5 -5
  15. package/src/components/ConfirmationDialog.tsx +9 -9
  16. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
  17. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
  18. package/src/components/EntityPreview.tsx +18 -14
  19. package/src/components/ErrorView.tsx +1 -1
  20. package/src/components/HomePage/DefaultHomePage.tsx +1 -1
  21. package/src/components/SelectableTable/SelectableTable.tsx +140 -143
  22. package/src/components/VirtualTable/VirtualTable.tsx +7 -4
  23. package/src/components/index.tsx +2 -0
  24. package/src/core/EntityEditView.tsx +25 -14
  25. package/src/core/EntitySidePanel.tsx +19 -12
  26. package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -2
  27. package/src/hooks/useBuildNavigationController.tsx +29 -16
  28. package/src/internal/useBuildSideEntityController.tsx +1 -1
  29. package/src/preview/components/ReferencePreview.tsx +1 -1
  30. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  31. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
  32. package/src/types/collections.ts +16 -0
  33. package/src/types/firecms.tsx +0 -1
  34. package/src/types/plugins.tsx +16 -0
  35. package/src/types/side_entity_controller.tsx +5 -5
  36. package/src/util/callbacks.ts +119 -0
  37. package/src/util/index.ts +1 -0
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useState } from "react";
2
- import { Entity, EntityCollection, EntityStatus, FireCMSPlugin, FormContext, User } from "../types";
2
+ import { Entity, EntityCollection, EntityCustomView, EntityStatus, FireCMSPlugin, FormContext, User } from "../types";
3
3
 
4
4
  import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary } from "../components";
5
5
  import {
@@ -182,11 +182,13 @@ export function EntityEditViewInner<M extends Record<string, any>>({
182
182
 
183
183
  const subcollections = (collection.subcollections ?? []).filter(c => !c.hideFromNavigation);
184
184
  const subcollectionsCount = subcollections?.length ?? 0;
185
- const customViews = collection.entityViews;
185
+ const customViews = collection.entityViews ?? [];
186
186
  const customViewsCount = customViews?.length ?? 0;
187
187
  const includeJsonView = collection.includeJsonView === undefined ? true : collection.includeJsonView;
188
188
  const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0 || includeJsonView;
189
189
 
190
+ const plugins = customizationController.plugins;
191
+
190
192
  const {
191
193
  resolvedEntityViews,
192
194
  selectedEntityView,
@@ -384,14 +386,24 @@ export function EntityEditViewInner<M extends Record<string, any>>({
384
386
  </Tab>
385
387
  );
386
388
 
387
- const customViewTabs = resolvedEntityViews.map((view) =>
388
- <Tab
389
- className="text-sm min-w-[120px]"
390
- value={view.key}
391
- key={`entity_detail_collection_tab_${view.name}`}>
392
- {view.name}
393
- </Tab>
394
- );
389
+ const customViewTabsStart = resolvedEntityViews.filter(view => view.position === "start")
390
+ .map((view) =>
391
+ <Tab
392
+ className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
393
+ value={view.key}
394
+ key={`entity_detail_collection_tab_${view.name}`}>
395
+ {view.tabComponent ?? view.name}
396
+ </Tab>
397
+ );
398
+ const customViewTabsEnd = resolvedEntityViews.filter(view => !view.position || view.position === "end")
399
+ .map((view) =>
400
+ <Tab
401
+ className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
402
+ value={view.key}
403
+ key={`entity_detail_collection_tab_${view.name}`}>
404
+ {view.tabComponent ?? view.name}
405
+ </Tab>
406
+ );
395
407
 
396
408
  const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
397
409
 
@@ -417,11 +429,12 @@ export function EntityEditViewInner<M extends Record<string, any>>({
417
429
  {includeJsonView && <Tab
418
430
  disabled={!hasAdditionalViews}
419
431
  value={JSON_TAB_VALUE}
420
- innerClassName={"block"}
421
432
  className={"text-sm"}>
422
433
  <CodeIcon size={"small"}/>
423
434
  </Tab>}
424
435
 
436
+ {customViewTabsStart}
437
+
425
438
  <Tab
426
439
  disabled={!hasAdditionalViews}
427
440
  value={MAIN_TAB_VALUE}
@@ -430,7 +443,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
430
443
  </Tab>
431
444
 
432
445
 
433
- {customViewTabs}
446
+ {customViewTabsEnd}
434
447
 
435
448
  {subcollectionTabs}
436
449
  </Tabs>}
@@ -453,8 +466,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
453
466
 
454
467
  </div>;
455
468
 
456
- const plugins = customizationController.plugins;
457
-
458
469
  if (plugins) {
459
470
  plugins.forEach((plugin: FireCMSPlugin) => {
460
471
  if (plugin.form?.provider) {
@@ -19,11 +19,18 @@ import { useLocation, useNavigate } from "react-router-dom";
19
19
  */
20
20
  export function EntitySidePanel(props: EntitySidePanelProps) {
21
21
 
22
+ const {
23
+ allowFullScreen = true,
24
+ path,
25
+ entityId,
26
+ formProps,
27
+ } = props;
28
+
22
29
  const {
23
30
  blocked,
24
31
  setBlocked,
25
32
  setBlockedNavigationMessage,
26
- close
33
+ close,
27
34
  } = useSideDialogContext();
28
35
 
29
36
  const navigate = useNavigate();
@@ -43,8 +50,8 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
43
50
  }
44
51
 
45
52
  const onUpdate = (params: OnUpdateParams) => {
46
- if (props.onUpdate) {
47
- props.onUpdate(params);
53
+ if (onUpdate) {
54
+ onUpdate(params);
48
55
  }
49
56
  if (params.status !== "existing") {
50
57
  sideEntityController.replace({
@@ -64,21 +71,21 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
64
71
  }
65
72
 
66
73
  const parentCollectionIds = useMemo(() => {
67
- return navigationController.getParentCollectionIds(props.path);
68
- }, [navigationController, props.path]);
74
+ return navigationController.getParentCollectionIds(path);
75
+ }, [navigationController, path]);
69
76
 
70
77
  const collection = useMemo(() => {
71
78
  if (props.collection) {
72
79
  return props.collection;
73
80
  }
74
81
 
75
- const registryCollection = navigationController.getCollection(props.path);
82
+ const registryCollection = navigationController.getCollection(path);
76
83
  if (registryCollection) {
77
84
  return registryCollection;
78
85
  }
79
86
 
80
- console.error("ERROR: No collection found in path `", props.path, "`. Entity id: ", props.entityId);
81
- throw Error("ERROR: No collection found in path `" + props.path + "`. Make sure you have defined a collection for this path in the root navigation.");
87
+ console.error("ERROR: No collection found in path `", path, "`. Entity id: ", entityId);
88
+ throw Error("ERROR: No collection found in path `" + path + "`. Make sure you have defined a collection for this path in the root navigation.");
82
89
  }, [navigationController, props.collection]);
83
90
 
84
91
  useEffect(() => {
@@ -126,16 +133,16 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
126
133
  onClick={onClose}>
127
134
  <CloseIcon size={"small"}/>
128
135
  </IconButton>
129
- <IconButton
136
+ {allowFullScreen && <IconButton
130
137
  className="self-center"
131
138
  onClick={() => {
132
- if (props.entityId)
139
+ if (entityId)
133
140
  navigate(location.pathname);
134
141
  else
135
142
  navigate(location.pathname + "#new");
136
143
  }}>
137
144
  <OpenInFullIcon size={"small"}/>
138
- </IconButton>
145
+ </IconButton>}
139
146
  </>}
140
147
  onTabChange={({
141
148
  path,
@@ -151,7 +158,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
151
158
  collection,
152
159
  });
153
160
  }}
154
- formProps={props.formProps}
161
+ formProps={formProps}
155
162
  />
156
163
  </ErrorBoundary>
157
164
 
@@ -69,8 +69,6 @@ export function KeyValueFieldBinding({
69
69
  title={property.name}
70
70
  className={"text-text-secondary dark:text-text-secondary-dark"}/>;
71
71
 
72
-
73
- console.log("minimalistView", propertyKey, minimalistView)
74
72
  return (
75
73
  <>
76
74
 
@@ -9,6 +9,7 @@ import {
9
9
  EntityCollection,
10
10
  EntityCollectionsBuilder,
11
11
  EntityReference,
12
+ FireCMSPlugin,
12
13
  NavigationBlocker,
13
14
  NavigationController,
14
15
  PermissionsBuilder,
@@ -43,15 +44,7 @@ export type BuildNavigationContextProps<EC extends EntityCollection, USER extend
43
44
  viewsOrder?: string[];
44
45
  userConfigPersistence?: UserConfigurationPersistence;
45
46
  dataSourceDelegate: DataSourceDelegate;
46
- /**
47
- * Use this method to inject collections to the CMS.
48
- * You receive the current collections as a parameter, and you can return
49
- * a new list of collections.
50
- * @see {@link joinCollectionLists}
51
- * @param collections
52
- */
53
- injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
54
-
47
+ plugins?: FireCMSPlugin[];
55
48
  /**
56
49
  * If true, the navigation logic will not be updated until this flag is false
57
50
  */
@@ -68,9 +61,9 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
68
61
  views: viewsProp,
69
62
  adminViews: adminViewsProp,
70
63
  viewsOrder,
64
+ plugins,
71
65
  userConfigPersistence,
72
66
  dataSourceDelegate,
73
- injectCollections,
74
67
  disabled
75
68
  } = props;
76
69
 
@@ -203,7 +196,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
203
196
  try {
204
197
 
205
198
  const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
206
- resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, injectCollections),
199
+ resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, plugins),
207
200
  resolveCMSViews(viewsProp, authController, dataSourceDelegate),
208
201
  resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)
209
202
  ]
@@ -250,8 +243,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
250
243
  disabled,
251
244
  viewsProp,
252
245
  adminViewsProp,
253
- computeTopNavigation,
254
- injectCollections
246
+ computeTopNavigation
255
247
  ]);
256
248
 
257
249
  useEffect(() => {
@@ -447,11 +439,24 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
447
439
  });
448
440
  }
449
441
 
442
+ function applyPluginModifyCollection(resolvedCollections: EntityCollection[], modifyCollection: (collection: EntityCollection) => EntityCollection) {
443
+ return resolvedCollections.map((collection: EntityCollection): EntityCollection => {
444
+ const modifiedCollection = modifyCollection(collection);
445
+ if (modifiedCollection.subcollections) {
446
+ return {
447
+ ...modifiedCollection,
448
+ subcollections: applyPluginModifyCollection(modifiedCollection.subcollections, modifyCollection)
449
+ } satisfies EntityCollection;
450
+ }
451
+ return modifiedCollection;
452
+ });
453
+ }
454
+
450
455
  async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder<any>,
451
456
  collectionPermissions: PermissionsBuilder | undefined,
452
457
  authController: AuthController,
453
458
  dataSource: DataSourceDelegate,
454
- injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
459
+ plugins: FireCMSPlugin[] | undefined): Promise<EntityCollection[]> {
455
460
  let resolvedCollections: EntityCollection[] = [];
456
461
  if (typeof collections === "function") {
457
462
  resolvedCollections = await collections({
@@ -463,8 +468,16 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
463
468
  resolvedCollections = collections;
464
469
  }
465
470
 
466
- if (injectCollections) {
467
- resolvedCollections = injectCollections(resolvedCollections ?? []);
471
+ if (plugins) {
472
+ for (const plugin of plugins) {
473
+ if (plugin.collection?.modifyCollection) {
474
+ resolvedCollections = applyPluginModifyCollection(resolvedCollections, plugin.collection.modifyCollection);
475
+ }
476
+
477
+ if (plugin.collection?.injectCollections) {
478
+ resolvedCollections = plugin.collection.injectCollections(resolvedCollections ?? []);
479
+ }
480
+ }
468
481
  }
469
482
 
470
483
  resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
@@ -33,7 +33,7 @@ export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: bool
33
33
  selectedSecondaryForm
34
34
  } = resolvedSelectedEntityView(props.collection?.entityViews, customizationController, props.selectedTab);
35
35
 
36
- const shouldUseSmallLayout = !props.selectedTab || props.selectedTab === JSON_TAB_VALUE || Boolean(selectedSecondaryForm);
36
+ const shouldUseSmallLayout = !props.selectedTab || props.selectedTab === JSON_TAB_VALUE || props.selectedTab === "__history" || Boolean(selectedSecondaryForm);
37
37
 
38
38
  let resolvedWidth: string | undefined;
39
39
  if (props.width) {
@@ -141,7 +141,7 @@ function ReferencePreviewExisting<M extends Record<string, any> = any>({
141
141
  );
142
142
  }
143
143
  return <EntityPreview size={size}
144
- previewProperties={previewProperties}
144
+ previewKeys={previewProperties}
145
145
  disabled={disabled}
146
146
  entity={usedEntity}
147
147
  collection={collection}
@@ -37,7 +37,7 @@ export function ArrayOfMapsPreview({
37
37
  throw Error(`You need to specify a 'properties' prop (or specify a custom field) in your map property ${propertyKey}`);
38
38
  }
39
39
  const values = value;
40
- const previewProperties: string[] | undefined = mapProperty.previewProperties;
40
+ const previewProperties: string[] | undefined = mapProperty.previewKeys;
41
41
 
42
42
  if (!values) return null;
43
43
 
@@ -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();
@@ -337,6 +337,12 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
337
337
  * If set to true, a tab including the JSON representation of the entity will be included.
338
338
  */
339
339
  includeJsonView?: boolean;
340
+
341
+ /**
342
+ * If set to true, changes to the entity will be saved in a subcollection.
343
+ * This prop has no effect if the history plugin is not enabled
344
+ */
345
+ history?: boolean;
340
346
  }
341
347
 
342
348
  /**
@@ -519,6 +525,11 @@ export type EntityCustomView<M extends Record<string, any> = any> =
519
525
  */
520
526
  name: string;
521
527
 
528
+ /**
529
+ * Render this custom view in the tab of the entity view, instead of the name
530
+ */
531
+ tabComponent?: React.ReactNode;
532
+
522
533
  /**
523
534
  * If set to true, the actions of the entity (save, discard,delete) will be
524
535
  * included in the view. By default the actions are located in the right or bottom,
@@ -531,6 +542,11 @@ export type EntityCustomView<M extends Record<string, any> = any> =
531
542
  * Builder for rendering the custom view
532
543
  */
533
544
  Builder?: React.ComponentType<EntityCustomViewParams<M>>;
545
+
546
+ /**
547
+ * Position of this tab in the entity view. Defaults to `end`.
548
+ */
549
+ position?: "start" | "end";
534
550
  };
535
551
 
536
552
  /**
@@ -92,7 +92,6 @@ export type FireCMSProps<USER extends User> = {
92
92
  *
93
93
  * You can also define an entity view from the UI.
94
94
  */
95
-
96
95
  entityViews?: EntityCustomView[];
97
96
 
98
97
  /**
@@ -158,6 +158,22 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
158
158
  fieldBuilderEnabled?: <T extends CMSType = CMSType>(props: PluginFieldBuilderParams<T>) => boolean;
159
159
  }
160
160
 
161
+ collection?: {
162
+
163
+ /**
164
+ * Use this method to modify a single collection before it is rendered.
165
+ * @param collection
166
+ */
167
+ modifyCollection?: (collection: EntityCollection) => EntityCollection;
168
+
169
+ /**
170
+ * Use this method to modify, add or remove collections.
171
+ * @param collections
172
+ */
173
+ injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
174
+
175
+ }
176
+
161
177
  }
162
178
 
163
179
  /**
@@ -20,11 +20,6 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
20
20
  */
21
21
  entityId?: string;
22
22
 
23
- // /**
24
- // * Navigation path with ids of the entity
25
- // */
26
- // fullIdPath: string;
27
-
28
23
  /**
29
24
  * Set this flag to true if you want to make a copy of an existing entity
30
25
  */
@@ -76,6 +71,11 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
76
71
  * Override some form properties
77
72
  */
78
73
  formProps?: Partial<EntityFormProps<M>>;
74
+
75
+ /**
76
+ * Allow the user to open the entity fullscreen
77
+ */
78
+ allowFullScreen?: boolean;
79
79
  }
80
80
 
81
81
  /**
@@ -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
+ };
package/src/util/index.ts CHANGED
@@ -23,3 +23,4 @@ export * from "./join_collections";
23
23
  export * from "./builders";
24
24
  export * from "./useTraceUpdate";
25
25
  export * from "./storage";
26
+ export * from "./callbacks";