@firecms/core 3.0.0-alpha.80 → 3.0.0-alpha.82

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 (81) hide show
  1. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +3 -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 +1 -1
  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 +4614 -4592
  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/EntityCollectionTable.tsx +28 -5
  30. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +4 -5
  31. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  32. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +125 -122
  33. package/src/components/EntityCollectionTable/filters/DateTimeFilterField.tsx +2 -2
  34. package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +3 -3
  35. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -3
  36. package/src/components/EntityCollectionView/EntityCollectionView.tsx +74 -50
  37. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -3
  38. package/src/components/EntityPreview.tsx +11 -7
  39. package/src/components/HomePage/DefaultHomePage.tsx +9 -8
  40. package/src/components/HomePage/NavigationCollectionCard.tsx +6 -5
  41. package/src/components/ReferenceSelectionInner.tsx +13 -9
  42. package/src/components/VirtualTable/VirtualTable.tsx +23 -20
  43. package/src/components/VirtualTable/VirtualTableCell.tsx +27 -9
  44. package/src/components/VirtualTable/VirtualTableHeader.tsx +1 -1
  45. package/src/components/VirtualTable/VirtualTableProps.tsx +2 -2
  46. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +2 -2
  47. package/src/contexts/AnalyticsContext.tsx +4 -0
  48. package/src/contexts/CustomizationControllerContext.tsx +4 -0
  49. package/src/core/Drawer.tsx +7 -6
  50. package/src/core/FireCMS.tsx +39 -29
  51. package/src/form/EntityForm.tsx +16 -5
  52. package/src/form/PropertyFieldBinding.tsx +8 -10
  53. package/src/form/components/CustomIdField.tsx +7 -5
  54. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +1 -2
  55. package/src/form/field_bindings/DateTimeFieldBinding.tsx +2 -2
  56. package/src/form/field_bindings/KeyValueFieldBinding.tsx +4 -6
  57. package/src/hooks/data/save.ts +8 -3
  58. package/src/hooks/index.tsx +1 -0
  59. package/src/hooks/useAnalyticsController.tsx +8 -0
  60. package/src/hooks/useCustomizationController.tsx +14 -0
  61. package/src/hooks/useFireCMSContext.tsx +8 -7
  62. package/src/internal/EntityView.tsx +4 -2
  63. package/src/internal/useBuildCustomizationController.tsx +5 -0
  64. package/src/internal/useBuildSideDialogsController.tsx +0 -1
  65. package/src/preview/PropertyPreview.tsx +4 -3
  66. package/src/preview/components/DatePreview.tsx +4 -5
  67. package/src/preview/components/ReferencePreview.tsx +17 -8
  68. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +3 -3
  69. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +3 -3
  70. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +3 -3
  71. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -4
  72. package/src/preview/property_previews/ArrayOneOfPreview.tsx +3 -3
  73. package/src/preview/property_previews/ArrayPropertyPreview.tsx +3 -3
  74. package/src/types/analytics_controller.tsx +10 -0
  75. package/src/types/collections.ts +3 -0
  76. package/src/types/customization_controller.tsx +58 -0
  77. package/src/types/entity_callbacks.ts +22 -3
  78. package/src/types/firecms_context.tsx +6 -51
  79. package/src/types/index.ts +2 -0
  80. package/src/util/icons.tsx +22 -19
  81. package/src/util/useTraceUpdate.tsx +5 -5
@@ -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
@@ -29,10 +31,10 @@ export function EntityPreview<M extends Record<string, any>>(
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();
37
+ const customizationController: CustomizationController = useCustomizationController();
36
38
 
37
39
  const properties: ResolvedProperties = resolvedCollection.properties;
38
40
 
@@ -43,10 +45,11 @@ export function EntityPreview<M extends Record<string, any>>(
43
45
  <div className="flex items-center w-1/4">
44
46
  <span className="pl-2 text-sm text-gray-600">Id</span>
45
47
  </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">
48
+ <div
49
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
47
50
  <span className="flex-grow mr-2">{entity.id}</span>
48
- {appConfig?.entityLinkBuilder &&
49
- <a href={appConfig.entityLinkBuilder({ entity })}
51
+ {customizationController?.entityLinkBuilder &&
52
+ <a href={customizationController.entityLinkBuilder({ entity })}
50
53
  rel="noopener noreferrer"
51
54
  target="_blank">
52
55
  <IconButton>
@@ -66,7 +69,8 @@ export function EntityPreview<M extends Record<string, any>>(
66
69
  <div className="flex items-center w-1/4">
67
70
  <span className="pl-2 text-sm text-gray-600">{property.name}</span>
68
71
  </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">
72
+ <div
73
+ className="flex-grow p-2 ml-2 w-3/4 text-gray-900 dark:text-white min-h-[56px] flex items-center">
70
74
  <PropertyPreview
71
75
  propertyKey={key}
72
76
  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
 
@@ -142,7 +146,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
142
146
  }, [dataSource, fullPath, selectedEntityIdsProp, collection, selectionController.setSelectedEntities]);
143
147
 
144
148
  const onClear = () => {
145
- context.onAnalyticsEvent?.("reference_selection_clear", {
149
+ analyticsController.onAnalyticsEvent?.("reference_selection_clear", {
146
150
  path: fullPath
147
151
  });
148
152
  selectionController.setSelectedEntities([]);
@@ -158,7 +162,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
158
162
  let newValue;
159
163
  const selectedEntities = selectionController.selectedEntities;
160
164
 
161
- context.onAnalyticsEvent?.("reference_selection_toggle", {
165
+ analyticsController.onAnalyticsEvent?.("reference_selection_toggle", {
162
166
  path: fullPath,
163
167
  entityId: entity.id
164
168
  });
@@ -182,7 +186,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
182
186
  console.debug("ReferenceSelectionInner onEntityClick", entity);
183
187
 
184
188
  if (!multiselect && onSingleEntitySelected) {
185
- context.onAnalyticsEvent?.("reference_selected_single", {
189
+ analyticsController.onAnalyticsEvent?.("reference_selected_single", {
186
190
  path: fullPath,
187
191
  entityId: entity.id
188
192
  });
@@ -195,7 +199,7 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
195
199
 
196
200
  // create a new entity from within the reference dialog
197
201
  const onNewClick = () => {
198
- context.onAnalyticsEvent?.("reference_selection_new_entity", {
202
+ analyticsController.onAnalyticsEvent?.("reference_selection_new_entity", {
199
203
  path: fullPath
200
204
  });
201
205
  sideEntityController.open({
@@ -251,8 +255,8 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
251
255
  collection: collection,
252
256
  path: fullPath,
253
257
  values: {},
254
- fields: context.propertyConfigs
255
- }), [collection, context.propertyConfigs, fullPath]);
258
+ fields: customizationController.propertyConfigs
259
+ }), [collection, customizationController.propertyConfigs, fullPath]);
256
260
 
257
261
  // eslint-disable-next-line react-hooks/rules-of-hooks
258
262
  const displayedColumnIds = useColumnIds(resolvedCollection, false);
@@ -257,31 +257,34 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
257
257
  const empty = !loading && (data?.length ?? 0) === 0;
258
258
  const customView = error ? buildErrorView() : (empty ? buildEmptyView() : undefined);
259
259
 
260
+ const virtualListController = {
261
+ data,
262
+ size,
263
+ cellRenderer,
264
+ columns,
265
+ currentSort,
266
+ onRowClick,
267
+ customView,
268
+ onColumnResize: onColumnResizeInternal,
269
+ onColumnResizeEnd: onColumnResizeEndInternal,
270
+ filter: filterRef.current,
271
+ onColumnSort,
272
+ onFilterUpdate: onFilterUpdateInternal,
273
+ sortByProperty,
274
+ hoverRow: hoverRow ?? false,
275
+ createFilterField,
276
+ rowClassName,
277
+ endAdornment,
278
+ AddColumnComponent
279
+ };
280
+
281
+ // useTraceUpdate(virtualListController);
260
282
  return (
261
283
  <div
262
284
  ref={measureRef}
263
285
  className={cn("h-full w-full", className)}>
264
286
  <VirtualListContext.Provider
265
- value={{
266
- data,
267
- size,
268
- cellRenderer,
269
- columns,
270
- currentSort,
271
- onRowClick,
272
- customView,
273
- onColumnResize: onColumnResizeInternal,
274
- onColumnResizeEnd: onColumnResizeEndInternal,
275
- filter: filterRef.current,
276
- onColumnSort,
277
- onFilterUpdate: onFilterUpdateInternal,
278
- sortByProperty,
279
- hoverRow: hoverRow ?? false,
280
- createFilterField,
281
- rowClassName,
282
- endAdornment,
283
- AddColumnComponent
284
- }}>
287
+ value={virtualListController}>
285
288
 
286
289
  <MemoizedList
287
290
  outerRef={tableRef}
@@ -17,16 +17,34 @@ type VirtualTableCellProps<T extends any> = {
17
17
 
18
18
  export const VirtualTableCell = React.memo<VirtualTableCellProps<any>>(
19
19
  function VirtualTableCell<T extends any>(props: VirtualTableCellProps<T>) {
20
- return props.rowData && <props.cellRenderer
21
- rowData={props.rowData}
22
- rowIndex={props.rowIndex}
23
- isScrolling={false}
24
- column={props.column}
25
- columns={props.columns}
26
- columnIndex={props.columnIndex}
27
- width={props.column.width}/>
20
+ // @ts-ignore
21
+ return props.rowData && props.cellRenderer(
22
+ {
23
+ cellData: props.cellData,
24
+ rowData: props.rowData,
25
+ rowIndex: props.rowIndex,
26
+ isScrolling: false,
27
+ column: props.column,
28
+ columns: props.columns,
29
+ columnIndex: props.columnIndex,
30
+ width: props.column.width
31
+ } as CellRendererParams<T>
32
+ );
33
+ // return props.rowData && <props.cellRenderer
34
+ // rowData={props.rowData}
35
+ // rowIndex={props.rowIndex}
36
+ // isScrolling={false}
37
+ // column={props.column}
38
+ // columns={props.columns}
39
+ // columnIndex={props.columnIndex}
40
+ // width={props.column.width}/>
28
41
  },
29
42
  (a, b) => {
30
- return equal(a, b);
43
+ return equal(a.rowData, b.rowData) &&
44
+ equal(a.column, b.column) &&
45
+ equal(a.cellData, b.cellData) &&
46
+ equal(a.rowIndex, b.rowIndex) &&
47
+ equal(a.cellRenderer, b.cellRenderer) &&
48
+ equal(a.columnIndex, b.columnIndex)
31
49
  }
32
50
  );
@@ -106,7 +106,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
106
106
  <div
107
107
  className={`flex items-center justify-${column.headerAlign} flex-row`}>
108
108
 
109
- {column.icon && column.icon(onHover || openFilter)}
109
+ {column.icon}
110
110
 
111
111
  <div
112
112
  className="truncate -webkit-box w-full mx-1 overflow-hidden"
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { ResolvedProperty, WhereFilterOp } from "../../types";
2
+ import { WhereFilterOp } from "../../types";
3
3
  import { FilterFormFieldProps } from "./VirtualTableHeader";
4
4
 
5
5
  export type OnRowClickParams<T extends Record<string, any>> = {
@@ -188,7 +188,7 @@ export interface VirtualTableColumn<CustomProps extends any = any> {
188
188
  /**
189
189
  * Icon displayed in the header
190
190
  */
191
- icon?: (hoverOrOpen: boolean) => React.ReactNode;
191
+ icon?: React.ReactNode;
192
192
 
193
193
  /**
194
194
  *
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { DateTimeField } from "@firecms/ui";
3
- import { useFireCMSContext } from "../../../hooks";
3
+ import { useCustomizationController } from "../../../hooks";
4
4
 
5
5
  export function VirtualTableDateField(props: {
6
6
  name: string;
@@ -13,7 +13,7 @@ export function VirtualTableDateField(props: {
13
13
  onBlur?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
14
14
  }) {
15
15
 
16
- const { locale } = useFireCMSContext();
16
+ const { locale } = useCustomizationController();
17
17
  const {
18
18
  disabled,
19
19
  error,
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import { AnalyticsController } from "../types";
3
+
4
+ export const AnalyticsContext = React.createContext<AnalyticsController>({} as AnalyticsController);
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import { CustomizationController } from "../types/customization_controller";
3
+
4
+ export const CustomizationControllerContext = React.createContext<CustomizationController>({} as CustomizationController);
@@ -1,11 +1,12 @@
1
1
  import React, { useCallback } from "react";
2
2
 
3
- import { useFireCMSContext, useLargeLayout, useNavigationController } from "../hooks";
3
+ import { useLargeLayout, useNavigationController } from "../hooks";
4
4
 
5
5
  import { NavLink } from "react-router-dom";
6
6
  import { CMSAnalyticsEvent, TopNavigationEntry, TopNavigationResult } from "../types";
7
- import { getIconForView } from "../util";
7
+ import { IconForView } from "../util";
8
8
  import { cn, Tooltip, Typography } from "@firecms/ui";
9
+ import { useAnalyticsController } from "../hooks/useAnalyticsController";
9
10
 
10
11
  /**
11
12
  * Props used in case you need to override the default drawer
@@ -27,7 +28,7 @@ export function Drawer({
27
28
  closeDrawer
28
29
  }: DrawerProps) {
29
30
 
30
- const context = useFireCMSContext();
31
+ const analyticsController = useAnalyticsController();
31
32
  const navigation = useNavigationController();
32
33
 
33
34
  const tooltipsOpen = hovered && !drawerOpen;
@@ -60,7 +61,7 @@ export function Drawer({
60
61
  const eventName: CMSAnalyticsEvent = view.type === "collection"
61
62
  ? "drawer_navigate_to_collection"
62
63
  : (view.type === "view" ? "drawer_navigate_to_view" : "unmapped_event");
63
- context.onAnalyticsEvent?.(eventName, { url: view.url });
64
+ analyticsController.onAnalyticsEvent?.(eventName, { url: view.url });
64
65
  if (!largeLayout)
65
66
  closeDrawer();
66
67
  };
@@ -77,7 +78,7 @@ export function Drawer({
77
78
  .map((view, index) =>
78
79
  <DrawerNavigationItem
79
80
  key={`navigation_${index}`}
80
- icon={getIconForView(view.collection ?? view.view)}
81
+ icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
81
82
  tooltipsOpen={tooltipsOpen}
82
83
  drawerOpen={drawerOpen}
83
84
  onClick={() => onClick(view)}
@@ -92,7 +93,7 @@ export function Drawer({
92
93
 
93
94
  return <DrawerNavigationItem
94
95
  key={`navigation_${index}`}
95
- icon={getIconForView(view.collection ?? view.view)}
96
+ icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
96
97
  tooltipsOpen={tooltipsOpen}
97
98
  onClick={() => onClick(view)}
98
99
  drawerOpen={drawerOpen}
@@ -3,7 +3,7 @@ import React, { useMemo } from "react";
3
3
  import { EntityCollection, FireCMSContext, FireCMSPlugin, FireCMSProps, User } from "../types";
4
4
  import { AuthControllerContext, ModeControllerContext } from "../contexts";
5
5
  import { useBuildSideEntityController } from "../internal/useBuildSideEntityController";
6
- import { FireCMSContextInstance, useFireCMSContext, useModeController } from "../hooks";
6
+ import { useCustomizationController, useFireCMSContext, useModeController } from "../hooks";
7
7
  import { useBuildSideDialogsController } from "../internal/useBuildSideDialogsController";
8
8
  import { ErrorView } from "../components";
9
9
  import { StorageSourceContext } from "../contexts/StorageSourceContext";
@@ -17,6 +17,9 @@ import { CenteredView } from "@firecms/ui";
17
17
  import { DialogsProvider } from "../contexts/DialogsProvider";
18
18
  import { useBuildNavigationController } from "../internal/useBuildNavigationController";
19
19
  import { useBuildDataSource } from "../internal/useBuildDataSource";
20
+ import { useBuildCustomizationController } from "../internal/useBuildCustomizationController";
21
+ import { CustomizationControllerContext } from "../contexts/CustomizationControllerContext";
22
+ import { AnalyticsContext } from "../contexts/AnalyticsContext";
20
23
 
21
24
  /**
22
25
  * If you are using independent components of the CMS
@@ -82,16 +85,19 @@ export function FireCMS<UserType extends User, EC extends EntityCollection>(prop
82
85
 
83
86
  const loading = authController.initialLoading || navigationController.loading || pluginsLoading;
84
87
 
85
- const context: Partial<FireCMSContext> = useMemo(() => ({
86
- entityLinkBuilder,
88
+ const customizationController = useBuildCustomizationController({
87
89
  dateTimeFormat,
88
90
  locale,
91
+ entityLinkBuilder,
89
92
  plugins,
90
- onAnalyticsEvent,
91
93
  entityViews: entityViews ?? [],
92
94
  propertyConfigs: propertyConfigs ?? {},
93
95
  components
94
- }), [dateTimeFormat, locale, plugins, entityViews, propertyConfigs, components]);
96
+ })
97
+
98
+ const analyticsController = useMemo(() => ({
99
+ onAnalyticsEvent
100
+ }), []);
95
101
 
96
102
  if (navigationController.navigationLoadingError) {
97
103
  return (
@@ -115,35 +121,37 @@ export function FireCMS<UserType extends User, EC extends EntityCollection>(prop
115
121
 
116
122
  return (
117
123
  <ModeControllerContext.Provider value={modeController}>
118
- <FireCMSContextInstance.Provider value={context}>
119
- <UserConfigurationPersistenceContext.Provider
120
- value={userConfigPersistence}>
121
- <StorageSourceContext.Provider
122
- value={storageSource}>
123
- <DataSourceContext.Provider
124
- value={dataSource}>
125
- <AuthControllerContext.Provider
126
- value={authController}>
127
- <SideDialogsControllerContext.Provider
128
- value={sideDialogsController}>
129
- <SideEntityControllerContext.Provider
130
- value={sideEntityController}>
131
- <NavigationContext.Provider
132
- value={navigationController}>
124
+ <AnalyticsContext.Provider value={analyticsController}>
125
+ <CustomizationControllerContext.Provider value={customizationController}>
126
+ <UserConfigurationPersistenceContext.Provider
127
+ value={userConfigPersistence}>
128
+ <StorageSourceContext.Provider
129
+ value={storageSource}>
130
+ <DataSourceContext.Provider
131
+ value={dataSource}>
132
+ <AuthControllerContext.Provider
133
+ value={authController}>
134
+ <SideDialogsControllerContext.Provider
135
+ value={sideDialogsController}>
136
+ <SideEntityControllerContext.Provider
137
+ value={sideEntityController}>
138
+ <NavigationContext.Provider
139
+ value={navigationController}>
133
140
  <DialogsProvider>
134
141
  <FireCMSInternal
135
142
  loading={loading}>
136
143
  {children}
137
144
  </FireCMSInternal>
138
145
  </DialogsProvider>
139
- </NavigationContext.Provider>
140
- </SideEntityControllerContext.Provider>
141
- </SideDialogsControllerContext.Provider>
142
- </AuthControllerContext.Provider>
143
- </DataSourceContext.Provider>
144
- </StorageSourceContext.Provider>
145
- </UserConfigurationPersistenceContext.Provider>
146
- </FireCMSContextInstance.Provider>
146
+ </NavigationContext.Provider>
147
+ </SideEntityControllerContext.Provider>
148
+ </SideDialogsControllerContext.Provider>
149
+ </AuthControllerContext.Provider>
150
+ </DataSourceContext.Provider>
151
+ </StorageSourceContext.Provider>
152
+ </UserConfigurationPersistenceContext.Provider>
153
+ </CustomizationControllerContext.Provider>
154
+ </AnalyticsContext.Provider>
147
155
  </ModeControllerContext.Provider>
148
156
  );
149
157
 
@@ -161,12 +169,14 @@ function FireCMSInternal({
161
169
  }) {
162
170
 
163
171
  const context = useFireCMSContext();
172
+ const customizationController = useCustomizationController();
173
+
164
174
  let childrenResult = children({
165
175
  context,
166
176
  loading
167
177
  });
168
178
 
169
- const plugins = context.plugins;
179
+ const plugins = customizationController.plugins;
170
180
  if (!loading && plugins) {
171
181
  plugins.forEach((plugin: FireCMSPlugin) => {
172
182
  if (plugin.provider) {
@@ -25,7 +25,13 @@ import {
25
25
  isReadOnly,
26
26
  resolveCollection
27
27
  } from "../util";
28
- import { useAuthController, useDataSource, useFireCMSContext, useSideEntityController } from "../hooks";
28
+ import {
29
+ useAuthController,
30
+ useCustomizationController,
31
+ useDataSource,
32
+ useFireCMSContext,
33
+ useSideEntityController
34
+ } from "../hooks";
29
35
  import { ErrorFocus } from "./components/ErrorFocus";
30
36
  import { CustomIdField } from "./components/CustomIdField";
31
37
  import { Alert, Button, cn, DialogActions, IconButton, Typography } from "@firecms/ui";
@@ -34,6 +40,7 @@ import {
34
40
  copyEntityAction,
35
41
  deleteEntityAction
36
42
  } from "../components/EntityCollectionTable/internal/default_entity_actions";
43
+ import { useAnalyticsController } from "../hooks/useAnalyticsController";
37
44
 
38
45
  /**
39
46
  * @group Components
@@ -148,15 +155,19 @@ function EntityFormInternal<M extends Record<string, any>>({
148
155
  onIdUpdateError
149
156
  }: EntityFormProps<M>) {
150
157
 
158
+ const analyticsController = useAnalyticsController();
159
+
160
+ const customizationController = useCustomizationController();
161
+
151
162
  const context = useFireCMSContext();
152
163
  const dataSource = useDataSource();
153
- const plugins = context.plugins;
164
+ const plugins = customizationController.plugins;
154
165
 
155
166
  const initialResolvedCollection = useMemo(() => resolveCollection({
156
167
  collection: inputCollection,
157
168
  path,
158
169
  values: entity?.values,
159
- fields: context.propertyConfigs
170
+ fields: customizationController.propertyConfigs
160
171
  }), [entity?.values, path]);
161
172
 
162
173
  const mustSetCustomId: boolean = (status === "new" || status === "copy") &&
@@ -219,7 +230,7 @@ function EntityFormInternal<M extends Record<string, any>>({
219
230
  entityId,
220
231
  values: internalValues,
221
232
  previousValues: initialValues,
222
- fields: context.propertyConfigs
233
+ fields: customizationController.propertyConfigs
223
234
  });
224
235
 
225
236
  const onIdUpdate = inputCollection.callbacks?.onIdUpdate;
@@ -274,7 +285,7 @@ function EntityFormInternal<M extends Record<string, any>>({
274
285
  const eventName: CMSAnalyticsEvent = status === "new"
275
286
  ? "new_entity_saved"
276
287
  : (status === "copy" ? "entity_copied" : (status === "existing" ? "entity_edited" : "unmapped_event"));
277
- context.onAnalyticsEvent?.(eventName, { path });
288
+ analyticsController.onAnalyticsEvent?.(eventName, { path });
278
289
  initialValuesRef.current = values;
279
290
  })
280
291
  .catch(e => {