@firecms/core 3.0.0-alpha.81 → 3.0.0-alpha.83

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 (84) hide show
  1. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +4 -3
  2. package/dist/components/EntityCollectionTable/column_utils.d.ts +2 -2
  3. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +5 -1
  4. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +2 -2
  5. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +1 -1
  6. package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
  7. package/dist/contexts/AnalyticsContext.d.ts +3 -0
  8. package/dist/contexts/CustomizationControllerContext.d.ts +3 -0
  9. package/dist/core/field_configs.d.ts +1 -1
  10. package/dist/hooks/index.d.ts +1 -0
  11. package/dist/hooks/useAnalyticsController.d.ts +5 -0
  12. package/dist/hooks/useCustomizationController.d.ts +11 -0
  13. package/dist/hooks/useFireCMSContext.d.ts +0 -1
  14. package/dist/index.es.js +4878 -4856
  15. package/dist/index.es.js.map +1 -1
  16. package/dist/index.umd.js +5 -5
  17. package/dist/index.umd.js.map +1 -1
  18. package/dist/internal/useBuildCustomizationController.d.ts +2 -0
  19. package/dist/types/analytics_controller.d.ts +7 -0
  20. package/dist/types/collections.d.ts +3 -0
  21. package/dist/types/customization_controller.d.ts +47 -0
  22. package/dist/types/entity_callbacks.d.ts +17 -3
  23. package/dist/types/firecms_context.d.ts +6 -46
  24. package/dist/types/index.d.ts +2 -0
  25. package/dist/util/icons.d.ts +4 -1
  26. package/dist/util/useTraceUpdate.d.ts +1 -1
  27. package/package.json +3 -3
  28. package/src/components/DeleteEntityDialog.tsx +9 -3
  29. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +1 -1
  30. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +33 -8
  31. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +5 -5
  32. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
  33. package/src/components/EntityCollectionTable/SimpleEntityCollectionTable.tsx +0 -2
  34. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  35. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +125 -122
  36. package/src/components/EntityCollectionTable/filters/DateTimeFilterField.tsx +2 -2
  37. package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +3 -3
  38. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -3
  39. package/src/components/EntityCollectionView/EntityCollectionView.tsx +77 -48
  40. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -3
  41. package/src/components/EntityPreview.tsx +11 -9
  42. package/src/components/HomePage/DefaultHomePage.tsx +9 -8
  43. package/src/components/HomePage/NavigationCollectionCard.tsx +6 -5
  44. package/src/components/ReferenceSelectionInner.tsx +38 -36
  45. package/src/components/VirtualTable/VirtualTable.tsx +23 -20
  46. package/src/components/VirtualTable/VirtualTableCell.tsx +27 -9
  47. package/src/components/VirtualTable/VirtualTableHeader.tsx +1 -1
  48. package/src/components/VirtualTable/VirtualTableProps.tsx +2 -2
  49. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +2 -2
  50. package/src/contexts/AnalyticsContext.tsx +4 -0
  51. package/src/contexts/CustomizationControllerContext.tsx +4 -0
  52. package/src/core/Drawer.tsx +7 -6
  53. package/src/core/FireCMS.tsx +39 -29
  54. package/src/form/EntityForm.tsx +16 -5
  55. package/src/form/PropertyFieldBinding.tsx +8 -10
  56. package/src/form/components/CustomIdField.tsx +7 -5
  57. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +1 -2
  58. package/src/form/field_bindings/DateTimeFieldBinding.tsx +2 -2
  59. package/src/form/field_bindings/KeyValueFieldBinding.tsx +4 -6
  60. package/src/hooks/data/save.ts +8 -3
  61. package/src/hooks/index.tsx +1 -0
  62. package/src/hooks/useAnalyticsController.tsx +8 -0
  63. package/src/hooks/useCustomizationController.tsx +14 -0
  64. package/src/hooks/useFireCMSContext.tsx +8 -7
  65. package/src/internal/EntityView.tsx +4 -2
  66. package/src/internal/useBuildCustomizationController.tsx +5 -0
  67. package/src/internal/useBuildSideDialogsController.tsx +0 -1
  68. package/src/preview/PropertyPreview.tsx +7 -4
  69. package/src/preview/components/DatePreview.tsx +4 -5
  70. package/src/preview/components/ReferencePreview.tsx +17 -8
  71. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +3 -3
  72. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +3 -3
  73. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +3 -3
  74. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -4
  75. package/src/preview/property_previews/ArrayOneOfPreview.tsx +3 -3
  76. package/src/preview/property_previews/ArrayPropertyPreview.tsx +3 -3
  77. package/src/types/analytics_controller.tsx +10 -0
  78. package/src/types/collections.ts +3 -0
  79. package/src/types/customization_controller.tsx +58 -0
  80. package/src/types/entity_callbacks.ts +22 -3
  81. package/src/types/firecms_context.tsx +6 -51
  82. package/src/types/index.ts +2 -0
  83. package/src/util/icons.tsx +22 -19
  84. package/src/util/useTraceUpdate.tsx +5 -5
@@ -38,6 +38,7 @@ import { ReferencePreview } from "../../preview";
38
38
  import {
39
39
  saveEntityWithCallbacks,
40
40
  useAuthController,
41
+ useCustomizationController,
41
42
  useDataSource,
42
43
  useFireCMSContext,
43
44
  useLargeLayout,
@@ -46,7 +47,9 @@ import {
46
47
  } from "../../hooks";
47
48
  import { useUserConfigurationPersistence } from "../../hooks/useUserConfigurationPersistence";
48
49
  import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
49
- import { useDataSourceEntityCollectionTableController } from "../EntityCollectionTable/useDataSourceEntityCollectionTableController";
50
+ import {
51
+ useDataSourceEntityCollectionTableController
52
+ } from "../EntityCollectionTable/useDataSourceEntityCollectionTableController";
50
53
  import {
51
54
  AddIcon,
52
55
  Button,
@@ -72,6 +75,7 @@ import {
72
75
  editEntityAction
73
76
  } from "../EntityCollectionTable/internal/default_entity_actions";
74
77
  import { DeleteEntityDialog } from "../DeleteEntityDialog";
78
+ import { useAnalyticsController } from "../../hooks/useAnalyticsController";
75
79
 
76
80
  const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
77
81
 
@@ -124,6 +128,8 @@ export const EntityCollectionView = React.memo(
124
128
  const sideEntityController = useSideEntityController();
125
129
  const authController = useAuthController();
126
130
  const userConfigPersistence = useUserConfigurationPersistence();
131
+ const analyticsController = useAnalyticsController();
132
+ const customizationController = useCustomizationController();
127
133
  const context = useFireCMSContext();
128
134
 
129
135
  const containerRef = React.useRef<HTMLDivElement>(null);
@@ -133,6 +139,11 @@ export const EntityCollectionView = React.memo(
133
139
  return (userOverride ? mergeDeep(collectionProp, userOverride) : collectionProp) as EntityCollection<M>;
134
140
  }, [collectionProp, fullPath, userConfigPersistence?.getCollectionConfig]);
135
141
 
142
+ const collectionRef = React.useRef(collection);
143
+ useEffect(() => {
144
+ collectionRef.current = collection;
145
+ }, [collection]);
146
+
136
147
  const canCreateEntities = canCreateEntity(collection, authController, fullPathToCollectionSegments(fullPath), null);
137
148
  const [selectedNavigationEntity, setSelectedNavigationEntity] = useState<Entity<M> | undefined>(undefined);
138
149
  const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
@@ -152,11 +163,12 @@ export const EntityCollectionView = React.memo(
152
163
  }, [selectedNavigationEntity]);
153
164
 
154
165
  const checkInlineEditing = useCallback((entity?: Entity<any>): boolean => {
166
+ const collection = collectionRef.current;
155
167
  if (!canEditEntity(collection, authController, fullPathToCollectionSegments(fullPath), entity ?? null)) {
156
168
  return false;
157
169
  }
158
170
  return collection.inlineEditing === undefined || collection.inlineEditing;
159
- }, [collection, authController, fullPath]);
171
+ }, [authController, fullPath]);
160
172
 
161
173
  const selectionEnabled = collection.selectionEnabled === undefined || collection.selectionEnabled;
162
174
  const hoverRow = !checkInlineEditing();
@@ -178,7 +190,7 @@ export const EntityCollectionView = React.memo(
178
190
 
179
191
  const tableController = useDataSourceEntityCollectionTableController<M>({
180
192
  fullPath,
181
- collection,
193
+ collection: collectionRef.current,
182
194
  entitiesDisplayedFirst: [],
183
195
  lastDeleteTimestamp
184
196
  });
@@ -191,8 +203,9 @@ export const EntityCollectionView = React.memo(
191
203
  }, [tableController.setPopupCell]);
192
204
 
193
205
  const onEntityClick = useCallback((clickedEntity: Entity<M>) => {
206
+ const collection = collectionRef.current;
194
207
  setSelectedNavigationEntity(clickedEntity);
195
- context.onAnalyticsEvent?.("edit_entity_clicked", {
208
+ analyticsController.onAnalyticsEvent?.("edit_entity_clicked", {
196
209
  path: clickedEntity.path,
197
210
  entityId: clickedEntity.id
198
211
  });
@@ -203,10 +216,12 @@ export const EntityCollectionView = React.memo(
203
216
  updateUrl: true,
204
217
  onClose: unselectNavigatedEntity
205
218
  });
206
- }, [fullPath, collection, sideEntityController]);
219
+ }, [unselectNavigatedEntity]);
207
220
 
208
221
  const onNewClick = useCallback(() => {
209
- context.onAnalyticsEvent?.("new_entity_click", {
222
+
223
+ const collection = collectionRef.current;
224
+ analyticsController.onAnalyticsEvent?.("new_entity_click", {
210
225
  path: fullPath
211
226
  });
212
227
  sideEntityController.open({
@@ -215,38 +230,31 @@ export const EntityCollectionView = React.memo(
215
230
  updateUrl: true,
216
231
  onClose: unselectNavigatedEntity
217
232
  });
218
- }, [fullPath, collection, sideEntityController]);
219
-
220
- const onSingleDeleteClick = useCallback((selectedEntity: Entity<any>) => {
221
- context.onAnalyticsEvent?.("single_delete_dialog_open", {
222
- path: fullPath
223
- });
224
- setDeleteEntityClicked(selectedEntity);
225
- }, [context, fullPath]);
233
+ }, [fullPath]);
226
234
 
227
- const onMultipleDeleteClick = useCallback(() => {
228
- context.onAnalyticsEvent?.("multiple_delete_dialog_open", {
235
+ const onMultipleDeleteClick = () => {
236
+ analyticsController.onAnalyticsEvent?.("multiple_delete_dialog_open", {
229
237
  path: fullPath
230
238
  });
231
239
  setDeleteEntityClicked(selectedEntities);
232
- }, [context, fullPath, selectedEntities]);
240
+ };
233
241
 
234
- const internalOnEntityDelete = useCallback((_path: string, entity: Entity<M>) => {
235
- context.onAnalyticsEvent?.("single_entity_deleted", {
242
+ const internalOnEntityDelete = (_path: string, entity: Entity<M>) => {
243
+ analyticsController.onAnalyticsEvent?.("single_entity_deleted", {
236
244
  path: fullPath
237
245
  });
238
246
  setSelectedEntities((selectedEntities) => selectedEntities.filter((e) => e.id !== entity.id));
239
247
  setLastDeleteTimestamp(Date.now());
240
- }, [context, fullPath, setSelectedEntities]);
248
+ };
241
249
 
242
- const internalOnMultipleEntitiesDelete = useCallback((_path: string, entities: Entity<M>[]) => {
243
- context.onAnalyticsEvent?.("multiple_entities_deleted", {
250
+ const internalOnMultipleEntitiesDelete = (_path: string, entities: Entity<M>[]) => {
251
+ analyticsController.onAnalyticsEvent?.("multiple_entities_deleted", {
244
252
  path: fullPath
245
253
  });
246
254
  setSelectedEntities([]);
247
255
  setDeleteEntityClicked(undefined);
248
256
  setLastDeleteTimestamp(Date.now());
249
- }, [setSelectedEntities]);
257
+ };
250
258
 
251
259
  let AddColumnComponent: React.ComponentType<{
252
260
  fullPath: string,
@@ -255,8 +263,8 @@ export const EntityCollectionView = React.memo(
255
263
  }> | undefined
256
264
 
257
265
  // we are only using the first plugin that implements this
258
- if (context?.plugins) {
259
- AddColumnComponent = context.plugins.find(plugin => plugin.collectionView?.AddColumnComponent)?.collectionView?.AddColumnComponent;
266
+ if (customizationController?.plugins) {
267
+ AddColumnComponent = customizationController.plugins.find(plugin => plugin.collectionView?.AddColumnComponent)?.collectionView?.AddColumnComponent;
260
268
  }
261
269
 
262
270
  const onCollectionModifiedForUser = useCallback((path: string, partialCollection: PartialEntityCollection<M>) => {
@@ -271,11 +279,13 @@ export const EntityCollectionView = React.memo(
271
279
  width,
272
280
  key
273
281
  }: OnColumnResizeParams) => {
282
+
283
+ const collection = collectionRef.current;
274
284
  // Only for property columns
275
285
  if (!getPropertyInPath(collection.properties, key)) return;
276
286
  const localCollection = buildPropertyWidthOverwrite(key, width);
277
287
  onCollectionModifiedForUser(fullPath, localCollection);
278
- }, [collection, onCollectionModifiedForUser, fullPath]);
288
+ }, [onCollectionModifiedForUser, fullPath]);
279
289
 
280
290
  const onSizeChanged = useCallback((size: CollectionSize) => {
281
291
  if (userConfigPersistence)
@@ -334,7 +344,7 @@ export const EntityCollectionView = React.memo(
334
344
  const resolvedCollection = useMemo(() => resolveCollection<M>({
335
345
  collection,
336
346
  path: fullPath,
337
- fields: context.propertyConfigs
347
+ fields: customizationController.propertyConfigs
338
348
  }), [collection, fullPath]);
339
349
 
340
350
  const getPropertyFor = useCallback(({
@@ -357,9 +367,9 @@ export const EntityCollectionView = React.memo(
357
367
  propertyValue,
358
368
  values: entity.values,
359
369
  entityId: entity.id,
360
- fields: context.propertyConfigs
370
+ fields: customizationController.propertyConfigs
361
371
  });
362
- }, [collection.properties, context.propertyConfigs, fullPath, resolvedCollection.properties]);
372
+ }, [collection.properties, customizationController.propertyConfigs, fullPath, resolvedCollection.properties]);
363
373
 
364
374
  const displayedColumnIds = useColumnIds(resolvedCollection, true);
365
375
 
@@ -448,7 +458,7 @@ export const EntityCollectionView = React.memo(
448
458
  const uncollapsedActions = entityActions.filter(a => a.collapsed === false);
449
459
  const actionsWidth = uncollapsedActions.length * (largeLayout ? 40 : 30);
450
460
  return (largeLayout ? (80 + actionsWidth) : (70 + actionsWidth)) + (collapsedActions.length > 0 ? (largeLayout ? 40 : 30) : 0);
451
- }, [largeLayout, getActionsForEntity]);
461
+ }, [largeLayout]);
452
462
 
453
463
  const tableRowActionsBuilder = useCallback(({
454
464
  entity,
@@ -500,8 +510,7 @@ export const EntityCollectionView = React.memo(
500
510
  setPopOverOpen(true);
501
511
  e.stopPropagation();
502
512
  }
503
- : undefined}
504
- >
513
+ : undefined}>
505
514
  {`${collection.name}`}
506
515
  </Typography>
507
516
 
@@ -522,19 +531,20 @@ export const EntityCollectionView = React.memo(
522
531
 
523
532
  </Popover>;
524
533
 
525
- function buildAdditionalHeaderWidget({
526
- property,
527
- propertyKey,
528
- onHover
529
- }: {
534
+ const buildAdditionalHeaderWidget = useCallback(({
535
+ property,
536
+ propertyKey,
537
+ onHover
538
+ }: {
530
539
  property: ResolvedProperty,
531
540
  propertyKey: string,
532
541
  onHover: boolean
533
- }) {
534
- if (!context.plugins)
542
+ }) => {
543
+ const collection = collectionRef.current;
544
+ if (!customizationController.plugins)
535
545
  return null;
536
546
  return <>
537
- {context.plugins.filter(plugin => plugin.collectionView?.HeaderAction)
547
+ {customizationController.plugins.filter(plugin => plugin.collectionView?.HeaderAction)
538
548
  .map((plugin, i) => {
539
549
  const HeaderAction = plugin.collectionView!.HeaderAction!;
540
550
  return <HeaderAction
@@ -547,7 +557,7 @@ export const EntityCollectionView = React.memo(
547
557
  parentCollectionIds={parentCollectionIds ?? []}/>;
548
558
  })}
549
559
  </>;
550
- }
560
+ }, [customizationController.plugins, fullPath, parentCollectionIds]);
551
561
 
552
562
  const addColumnComponentInternal = AddColumnComponent
553
563
  ? function () {
@@ -562,13 +572,13 @@ export const EntityCollectionView = React.memo(
562
572
  const [textSearchInitialised, setTextSearchInitialised] = useState<boolean>(false);
563
573
  let onTextSearchClick: (() => void) | undefined;
564
574
  let textSearchEnabled = Boolean(collection.textSearchEnabled);
565
- if (context?.plugins) {
566
- const addTextSearchClickListener = context.plugins?.find(p => Boolean(p.collectionView?.onTextSearchClick));
575
+ if (customizationController?.plugins) {
576
+ const addTextSearchClickListener = customizationController.plugins?.find(p => Boolean(p.collectionView?.onTextSearchClick));
567
577
 
568
578
  onTextSearchClick = addTextSearchClickListener
569
579
  ? () => {
570
580
  setTextSearchLoading(true);
571
- Promise.all(context.plugins?.map(p => {
581
+ Promise.all(customizationController.plugins?.map(p => {
572
582
  if (p.collectionView?.onTextSearchClick)
573
583
  return p.collectionView.onTextSearchClick({ context, path: resolvedFullPath, collection, parentCollectionIds });
574
584
  return Promise.resolve(true);
@@ -580,7 +590,7 @@ export const EntityCollectionView = React.memo(
580
590
  }
581
591
  : undefined;
582
592
 
583
- context.plugins?.forEach(p => {
593
+ customizationController.plugins?.forEach(p => {
584
594
  if (!textSearchEnabled)
585
595
  if (p.collectionView?.showTextSearchBar) {
586
596
  textSearchEnabled = p.collectionView.showTextSearchBar({ context, path: resolvedFullPath, collection, parentCollectionIds });
@@ -675,17 +685,36 @@ export const EntityCollectionView = React.memo(
675
685
 
676
686
  </div>
677
687
  );
678
- }, equal) as React.FunctionComponent<EntityCollectionViewProps<any>>
679
-
680
- export function useSelectionController<M extends Record<string, any>>(): SelectionController<M> {
688
+ }, (a, b) => {
689
+ return equal(a.fullPath, b.fullPath) &&
690
+ equal(a.parentCollectionIds, b.parentCollectionIds) &&
691
+ equal(a.isSubCollection, b.isSubCollection) &&
692
+ equal(a.className, b.className) &&
693
+ equal(a.properties, b.properties) &&
694
+ equal(a.hideIdFromCollection, b.hideIdFromCollection) &&
695
+ equal(a.inlineEditing, b.inlineEditing) &&
696
+ equal(a.selectionEnabled, b.selectionEnabled) &&
697
+ equal(a.selectionController, b.selectionController) &&
698
+ equal(a.Actions, b.Actions) &&
699
+ equal(a.defaultSize, b.defaultSize) &&
700
+ equal(a.textSearchEnabled, b.textSearchEnabled) &&
701
+ equal(a.additionalFields, b.additionalFields) &&
702
+ equal(a.forceFilter, b.forceFilter);
703
+ }) as React.FunctionComponent<EntityCollectionViewProps<any>>
704
+
705
+ export function useSelectionController<M extends Record<string, any> = any>(
706
+ onSelectionChange?: (entity: Entity<M>, selected: boolean) => void
707
+ ): SelectionController<M> {
681
708
 
682
709
  const [selectedEntities, setSelectedEntities] = useState<Entity<M>[]>([]);
683
710
 
684
711
  const toggleEntitySelection = useCallback((entity: Entity<M>) => {
685
712
  let newValue;
686
713
  if (selectedEntities.map(e => e.id).includes(entity.id)) {
714
+ onSelectionChange?.(entity, false);
687
715
  newValue = selectedEntities.filter((item: Entity<M>) => item.id !== entity.id);
688
716
  } else {
717
+ onSelectionChange?.(entity, true);
689
718
  newValue = [...selectedEntities, entity];
690
719
  }
691
720
  setSelectedEntities(newValue);
@@ -1,8 +1,8 @@
1
1
  import React from "react";
2
2
 
3
3
  import { canCreateEntity, canDeleteEntity, fullPathToCollectionSegments } from "../../util";
4
- import { useAuthController, useFireCMSContext, useLargeLayout } from "../../hooks";
5
- import { CollectionActionsProps, EntityCollection, SelectionController, EntityTableController } from "../../types";
4
+ import { useAuthController, useCustomizationController, useFireCMSContext, useLargeLayout } from "../../hooks";
5
+ import { CollectionActionsProps, EntityCollection, EntityTableController, SelectionController } from "../../types";
6
6
  import { AddIcon, Button, DeleteIcon, IconButton, Tooltip } from "@firecms/ui";
7
7
  import { toArray } from "../../util/arrays";
8
8
  import { ErrorBoundary } from "../ErrorBoundary";
@@ -34,7 +34,9 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
34
34
  }: EntityCollectionViewActionsProps<M>) {
35
35
 
36
36
  const context = useFireCMSContext();
37
- const plugins = context.plugins ?? [];
37
+
38
+ const customizationController = useCustomizationController();
39
+ const plugins = customizationController.plugins ?? [];
38
40
 
39
41
  const authController = useAuthController();
40
42
 
@@ -1,9 +1,11 @@
1
1
  import React, { useMemo } from "react";
2
2
  import { PropertyPreview } from "../preview";
3
- import { Entity, EntityCollection, FireCMSContext, ResolvedEntityCollection, ResolvedProperties } from "../types";
3
+ import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
4
4
  import { resolveCollection } from "../util";
5
5
  import { useFireCMSContext } from "../hooks";
6
6
  import { cn, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
7
+ import { CustomizationController } from "../types/customization_controller";
8
+ import { useCustomizationController } from "../hooks/useCustomizationController";
7
9
 
8
10
  /**
9
11
  * @group Components
@@ -23,17 +25,15 @@ export function EntityPreview<M extends Record<string, any>>(
23
25
  className
24
26
  }: EntityPreviewProps<M>) {
25
27
 
26
- const context = useFireCMSContext();
28
+ const customizationController: CustomizationController = useCustomizationController();
27
29
  const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
28
30
  collection,
29
31
  path,
30
32
  entityId: entity.id,
31
33
  values: entity.values,
32
- fields: context.propertyConfigs
34
+ fields: customizationController.propertyConfigs
33
35
  }), [collection, path, entity]);
34
36
 
35
- const appConfig: FireCMSContext | undefined = useFireCMSContext();
36
-
37
37
  const properties: ResolvedProperties = resolvedCollection.properties;
38
38
 
39
39
  return (
@@ -43,10 +43,11 @@ export function EntityPreview<M extends Record<string, any>>(
43
43
  <div className="flex items-center w-1/4">
44
44
  <span className="pl-2 text-sm text-gray-600">Id</span>
45
45
  </div>
46
- <div className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
46
+ <div
47
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
47
48
  <span className="flex-grow mr-2">{entity.id}</span>
48
- {appConfig?.entityLinkBuilder &&
49
- <a href={appConfig.entityLinkBuilder({ entity })}
49
+ {customizationController?.entityLinkBuilder &&
50
+ <a href={customizationController.entityLinkBuilder({ entity })}
50
51
  rel="noopener noreferrer"
51
52
  target="_blank">
52
53
  <IconButton>
@@ -66,7 +67,8 @@ export function EntityPreview<M extends Record<string, any>>(
66
67
  <div className="flex items-center w-1/4">
67
68
  <span className="pl-2 text-sm text-gray-600">{property.name}</span>
68
69
  </div>
69
- <div className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
70
+ <div
71
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
70
72
  <PropertyPreview
71
73
  propertyKey={key}
72
74
  value={value}
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useEffect, useState } from "react";
2
2
 
3
- import { useFireCMSContext, useNavigationController } from "../../hooks";
3
+ import { useCustomizationController, useFireCMSContext, useNavigationController } from "../../hooks";
4
4
  import { PluginGenericProps, PluginHomePageAdditionalCardsProps } from "../../types";
5
5
 
6
6
  import { toArray } from "../../util/arrays";
@@ -46,6 +46,7 @@ export function DefaultHomePage({
46
46
  }) {
47
47
 
48
48
  const context = useFireCMSContext();
49
+ const customizationController = useCustomizationController();
49
50
  const navigationController = useNavigationController();
50
51
 
51
52
  if (!navigationController.topLevelNavigation)
@@ -90,12 +91,12 @@ export function DefaultHomePage({
90
91
  let additionalPluginChildrenStart: React.ReactNode | undefined;
91
92
  let additionalPluginChildrenEnd: React.ReactNode | undefined;
92
93
  let additionalPluginSections: React.ReactNode | undefined;
93
- if (context.plugins) {
94
+ if (customizationController.plugins) {
94
95
  const sectionProps: PluginGenericProps = {
95
96
  context
96
97
  };
97
98
  additionalPluginSections = <>
98
- {context.plugins.filter(plugin => plugin.homePage?.includeSection)
99
+ {customizationController.plugins.filter(plugin => plugin.homePage?.includeSection)
99
100
  .map((plugin, i) => {
100
101
  const section = plugin.homePage!.includeSection!(sectionProps)
101
102
  return (
@@ -108,14 +109,14 @@ export function DefaultHomePage({
108
109
  })}
109
110
  </>;
110
111
  additionalPluginChildrenStart = <div className={"flex flex-col gap-2"}>
111
- {context.plugins.filter(plugin => plugin.homePage?.additionalChildrenStart)
112
+ {customizationController.plugins.filter(plugin => plugin.homePage?.additionalChildrenStart)
112
113
  .map((plugin, i) => {
113
114
  return <div key={`plugin_children_start_${i}`}>{plugin.homePage!.additionalChildrenStart}</div>;
114
115
  })}
115
116
  </div>;
116
117
 
117
118
  additionalPluginChildrenEnd = <div className={"flex flex-col gap-2"}>
118
- {context.plugins.filter(plugin => plugin.homePage?.additionalChildrenEnd)
119
+ {customizationController.plugins.filter(plugin => plugin.homePage?.additionalChildrenEnd)
119
120
  .map((plugin, i) => {
120
121
  return <div key={`plugin_children_start_${i}`}>{plugin.homePage!.additionalChildrenEnd}</div>;
121
122
  })}
@@ -152,8 +153,8 @@ export function DefaultHomePage({
152
153
  context
153
154
  };
154
155
 
155
- if (context.plugins) {
156
- context.plugins.forEach(plugin => {
156
+ if (customizationController.plugins) {
157
+ customizationController.plugins.forEach(plugin => {
157
158
  if (plugin.homePage?.AdditionalCards) {
158
159
  AdditionalCards.push(...toArray(plugin.homePage?.AdditionalCards));
159
160
  }
@@ -181,7 +182,7 @@ export function DefaultHomePage({
181
182
  : entry.type === "view"
182
183
  ? "home_navigate_to_view"
183
184
  : "unmapped_event";
184
- context.onAnalyticsEvent?.(event, { path: entry.path });
185
+ context.analyticsController?.onAnalyticsEvent?.(event, { path: entry.path });
185
186
  }}
186
187
  />
187
188
  </div>
@@ -1,8 +1,8 @@
1
1
  import { useNavigate } from "react-router-dom";
2
2
 
3
- import { useFireCMSContext } from "../../hooks";
3
+ import { useCustomizationController, useFireCMSContext } from "../../hooks";
4
4
  import { PluginHomePageActionsProps, TopNavigationEntry } from "../../types";
5
- import { getIconForView } from "../../util";
5
+ import { IconForView } from "../../util";
6
6
  import { useUserConfigurationPersistence } from "../../hooks/useUserConfigurationPersistence";
7
7
  import { ArrowForwardIcon, Card, cn, IconButton, Markdown, StarBorderIcon, StarIcon, Typography } from "@firecms/ui";
8
8
 
@@ -32,22 +32,23 @@ export function NavigationCollectionCard({
32
32
  }) {
33
33
 
34
34
  const userConfigurationPersistence = useUserConfigurationPersistence();
35
- const collectionIcon = getIconForView(collection ?? view);
35
+ const collectionIcon = <IconForView collectionOrView={collection ?? view}/>;
36
36
 
37
37
  const navigate = useNavigate();
38
38
  const context = useFireCMSContext();
39
+ const customizationController = useCustomizationController();
39
40
 
40
41
  const favourite = (userConfigurationPersistence?.favouritePaths ?? []).includes(path);
41
42
 
42
43
  let actions: React.ReactNode | undefined;
43
- if (context.plugins && collection) {
44
+ if (customizationController.plugins && collection) {
44
45
  const actionProps: PluginHomePageActionsProps = {
45
46
  path,
46
47
  collection,
47
48
  context
48
49
  };
49
50
  actions = <>
50
- {context.plugins.map((plugin, i) => (
51
+ {customizationController.plugins.map((plugin, i) => (
51
52
  plugin.homePage?.CollectionActions
52
53
  ? <plugin.homePage.CollectionActions
53
54
  key={`actions_${i}`}
@@ -5,8 +5,8 @@ import { EntityCollectionTable } from "./EntityCollectionTable";
5
5
  import { EntityCollectionRowActions } from "./EntityCollectionTable/EntityCollectionRowActions";
6
6
  import {
7
7
  useAuthController,
8
+ useCustomizationController,
8
9
  useDataSource,
9
- useFireCMSContext,
10
10
  useLargeLayout,
11
11
  useNavigationController,
12
12
  useSideEntityController
@@ -15,9 +15,12 @@ import { ErrorView } from "./ErrorView";
15
15
  import { AddIcon, Button, DialogActions, Typography } from "@firecms/ui";
16
16
  import { canCreateEntity, fullPathToCollectionSegments, resolveCollection } from "../util";
17
17
  import { useSelectionController } from "./EntityCollectionView/EntityCollectionView";
18
- import { useDataSourceEntityCollectionTableController } from "./EntityCollectionTable/useDataSourceEntityCollectionTableController";
18
+ import {
19
+ useDataSourceEntityCollectionTableController
20
+ } from "./EntityCollectionTable/useDataSourceEntityCollectionTableController";
19
21
  import { useColumnIds } from "./EntityCollectionView/useColumnsIds";
20
22
  import { useSideDialogContext } from "../core";
23
+ import { useAnalyticsController } from "../hooks/useAnalyticsController";
21
24
 
22
25
  /**
23
26
  * @group Components
@@ -101,7 +104,8 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
101
104
  const sideDialogContext = useSideDialogContext();
102
105
  const sideEntityController = useSideEntityController();
103
106
  const navigation = useNavigationController();
104
- const context = useFireCMSContext();
107
+ const analyticsController = useAnalyticsController();
108
+ const customizationController = useCustomizationController();
105
109
 
106
110
  const fullPath = navigation.resolveAliasesFrom(pathInput);
107
111
 
@@ -109,7 +113,31 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
109
113
 
110
114
  const [entitiesDisplayedFirst, setEntitiesDisplayedFirst] = useState<Entity<any>[]>([]);
111
115
 
112
- const selectionController = useSelectionController();
116
+ const toggleEntitySelection = (entity: Entity<any>) => {
117
+ let newValue;
118
+ const selectedEntities = selectionController.selectedEntities;
119
+
120
+ analyticsController.onAnalyticsEvent?.("reference_selection_toggle", {
121
+ path: fullPath,
122
+ entityId: entity.id
123
+ });
124
+ if (selectedEntities) {
125
+
126
+ if (selectedEntities.map((e) => e.id).indexOf(entity.id) > -1) {
127
+ newValue = selectedEntities.filter((item: Entity<any>) => item.id !== entity.id);
128
+ } else {
129
+ if (maxSelection && selectedEntities.length >= maxSelection)
130
+ return;
131
+ newValue = [...selectedEntities, entity];
132
+ }
133
+ selectionController.setSelectedEntities(newValue);
134
+
135
+ if (onMultipleEntitiesSelected)
136
+ onMultipleEntitiesSelected(newValue);
137
+ }
138
+ };
139
+
140
+ const selectionController = useSelectionController(toggleEntitySelection);
113
141
 
114
142
  /**
115
143
  * Fetch initially selected ids
@@ -142,7 +170,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
142
170
  }, [dataSource, fullPath, selectedEntityIdsProp, collection, selectionController.setSelectedEntities]);
143
171
 
144
172
  const onClear = () => {
145
- context.onAnalyticsEvent?.("reference_selection_clear", {
173
+ analyticsController.onAnalyticsEvent?.("reference_selection_clear", {
146
174
  path: fullPath
147
175
  });
148
176
  selectionController.setSelectedEntities([]);
@@ -153,36 +181,9 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
153
181
  }
154
182
  };
155
183
 
156
- const toggleEntitySelection = (entity: Entity<any>) => {
157
- console.debug("ReferenceSelectionInner toggleEntitySelection", entity);
158
- let newValue;
159
- const selectedEntities = selectionController.selectedEntities;
160
-
161
- context.onAnalyticsEvent?.("reference_selection_toggle", {
162
- path: fullPath,
163
- entityId: entity.id
164
- });
165
- if (selectedEntities) {
166
-
167
- if (selectedEntities.map((e) => e.id).indexOf(entity.id) > -1) {
168
- newValue = selectedEntities.filter((item: Entity<any>) => item.id !== entity.id);
169
- } else {
170
- if (maxSelection && selectedEntities.length >= maxSelection)
171
- return;
172
- newValue = [...selectedEntities, entity];
173
- }
174
- selectionController.setSelectedEntities(newValue);
175
-
176
- if (onMultipleEntitiesSelected)
177
- onMultipleEntitiesSelected(newValue);
178
- }
179
- };
180
-
181
184
  const onEntityClick = (entity: Entity<any>) => {
182
- console.debug("ReferenceSelectionInner onEntityClick", entity);
183
-
184
185
  if (!multiselect && onSingleEntitySelected) {
185
- context.onAnalyticsEvent?.("reference_selected_single", {
186
+ analyticsController.onAnalyticsEvent?.("reference_selected_single", {
186
187
  path: fullPath,
187
188
  entityId: entity.id
188
189
  });
@@ -195,7 +196,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
195
196
 
196
197
  // create a new entity from within the reference dialog
197
198
  const onNewClick = () => {
198
- context.onAnalyticsEvent?.("reference_selection_new_entity", {
199
+ analyticsController.onAnalyticsEvent?.("reference_selection_new_entity", {
199
200
  path: fullPath
200
201
  });
201
202
  sideEntityController.open({
@@ -251,8 +252,8 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
251
252
  collection: collection,
252
253
  path: fullPath,
253
254
  values: {},
254
- fields: context.propertyConfigs
255
- }), [collection, context.propertyConfigs, fullPath]);
255
+ fields: customizationController.propertyConfigs
256
+ }), [collection, customizationController.propertyConfigs, fullPath]);
256
257
 
257
258
  // eslint-disable-next-line react-hooks/rules-of-hooks
258
259
  const displayedColumnIds = useColumnIds(resolvedCollection, false);
@@ -272,6 +273,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
272
273
  <div className="flex-grow">
273
274
  {entitiesDisplayedFirst &&
274
275
  <EntityCollectionTable
276
+ debugKey={"reference_selection_table"}
275
277
  displayedColumnIds={displayedColumnIds}
276
278
  onEntityClick={onEntityClick}
277
279
  tableController={tableController}