@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
@@ -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 => {
@@ -17,7 +17,7 @@ import {
17
17
  import { ReadOnlyFieldBinding } from "./field_bindings/ReadOnlyFieldBinding";
18
18
 
19
19
  import { isHidden, isPropertyBuilder, isReadOnly, resolveProperty } from "../util";
20
- import { useFireCMSContext } from "../hooks";
20
+ import { useCustomizationController } from "../hooks";
21
21
  import { Typography } from "@firecms/ui";
22
22
  import { getFieldConfig, getFieldId } from "../core";
23
23
  import { ErrorBoundary } from "../components";
@@ -81,9 +81,9 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, CustomProps =
81
81
  autoFocus,
82
82
  }: PropertyFieldBindingProps<T, M>): ReactElement<PropertyFieldBindingProps<T, M>> {
83
83
 
84
- const fireCMSContext = useFireCMSContext();
84
+ const customizationController = useCustomizationController();
85
85
 
86
- const shouldAlwaysRerender = shouldPropertyReRender(property, fireCMSContext.plugins);
86
+ const shouldAlwaysRerender = shouldPropertyReRender(property, customizationController.plugins);
87
87
  // we use the standard Field for user defined fields, since it rebuilds
88
88
  // when there are changes in other values, in contrast to FastField
89
89
  const FieldComponent = shouldAlwaysRerender ? Field : FastField;
@@ -102,7 +102,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, CustomProps =
102
102
  values: fieldProps.form.values,
103
103
  path: context.path,
104
104
  entityId: context.entityId,
105
- fields: fireCMSContext.propertyConfigs
105
+ fields: customizationController.propertyConfigs
106
106
  });
107
107
 
108
108
  if (resolvedProperty === null || isHidden(resolvedProperty)) {
@@ -114,9 +114,9 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, CustomProps =
114
114
  Component = resolvedProperty.Field as ComponentType<FieldProps<any>>;
115
115
  }
116
116
  } else {
117
- const propertyConfig = getFieldConfig(resolvedProperty, fireCMSContext.propertyConfigs);
117
+ const propertyConfig = getFieldConfig(resolvedProperty, customizationController.propertyConfigs);
118
118
  if (!propertyConfig) {
119
- console.log("INTERNAL: Could not find field config for property", { propertyKey, resolvedProperty, fields: fireCMSContext.propertyConfigs, propertyConfig });
119
+ console.log("INTERNAL: Could not find field config for property", { propertyKey, resolvedProperty, fields: customizationController.propertyConfigs, propertyConfig });
120
120
  throw new Error(`INTERNAL: Could not find field config for property ${propertyKey}`);
121
121
  }
122
122
  const configProperty = resolveProperty({
@@ -125,7 +125,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, CustomProps =
125
125
  values: fieldProps.form.values,
126
126
  path: context.path,
127
127
  entityId: context.entityId,
128
- fields: fireCMSContext.propertyConfigs
128
+ fields: customizationController.propertyConfigs
129
129
  });
130
130
  Component = configProperty.Field as ComponentType<FieldProps<T>>;
131
131
  }
@@ -189,7 +189,7 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
189
189
  fieldProps: FormikFieldProps<T>
190
190
  }) {
191
191
 
192
- const { plugins } = useFireCMSContext();
192
+ const { plugins } = useCustomizationController();
193
193
 
194
194
  const customFieldProps: any = property.customProps;
195
195
  const value = fieldProps.field.value;
@@ -197,8 +197,6 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
197
197
  const error = getIn(fieldProps.form.errors, propertyKey);
198
198
  const touched = getIn(fieldProps.form.touched, propertyKey);
199
199
 
200
- console.log("error", propertyKey, error);
201
-
202
200
  const showError: boolean = error &&
203
201
  (fieldProps.form.submitCount > 0 || property.validation?.unique) &&
204
202
  (!Array.isArray(error) || !!error.filter((e: any) => !!e).length);
@@ -2,12 +2,14 @@ import React, { useMemo } from "react";
2
2
 
3
3
  import { ErrorMessage } from "formik";
4
4
 
5
- import { Entity, EntityStatus, EnumValueConfig, EnumValues, FireCMSContext } from "../../types";
5
+ import { Entity, EntityStatus, EnumValueConfig, EnumValues } from "../../types";
6
6
 
7
- import { useClipboard, useFireCMSContext, useSnackbarController } from "../../hooks";
7
+ import { useClipboard, useSnackbarController } from "../../hooks";
8
8
  import { enumToObjectEntries } from "../../util";
9
9
  import { ContentCopyIcon, IconButton, OpenInNewIcon, Select, SelectItem, TextField, Tooltip } from "@firecms/ui";
10
10
  import { EnumValuesChip } from "../../preview";
11
+ import { useCustomizationController } from "../../hooks/useCustomizationController";
12
+ import { CustomizationController } from "../../types/customization_controller";
11
13
 
12
14
  export function CustomIdField<M extends Record<string, any>>({
13
15
  customId,
@@ -42,7 +44,7 @@ export function CustomIdField<M extends Record<string, any>>({
42
44
  })
43
45
  });
44
46
 
45
- const appConfig: FireCMSContext | undefined = useFireCMSContext();
47
+ const customizationController: CustomizationController = useCustomizationController();
46
48
 
47
49
  const fieldProps = {
48
50
  label: idSetAutomatically ? "ID is set automatically" : "ID",
@@ -60,10 +62,10 @@ export function CustomIdField<M extends Record<string, any>>({
60
62
  </IconButton>
61
63
  </Tooltip>
62
64
 
63
- {appConfig?.entityLinkBuilder &&
65
+ {customizationController?.entityLinkBuilder &&
64
66
  <Tooltip title={"Open in the console"}>
65
67
  <IconButton component={"a"}
66
- href={appConfig.entityLinkBuilder({ entity })}
68
+ href={customizationController.entityLinkBuilder({ entity })}
67
69
  rel="noopener noreferrer"
68
70
  target="_blank"
69
71
  onClick={(e) => e.stopPropagation()}
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import { FieldProps } from "../../types";
3
3
  import { FieldHelperText } from "../components";
4
4
  import { PropertyFieldBinding } from "../PropertyFieldBinding";
5
- import { useClearRestoreValue, useFireCMSContext } from "../../hooks";
5
+ import { useClearRestoreValue } from "../../hooks";
6
6
  import { ExpandablePanel } from "@firecms/ui";
7
7
  import { getIconForProperty } from "../../util";
8
8
  import { LabelWithIcon } from "../../components";
@@ -29,7 +29,6 @@ export function ArrayCustomShapedFieldBinding<T extends Array<any>>({
29
29
  disabled
30
30
  }: FieldProps<T, any, any>) {
31
31
 
32
- const { propertyConfigs } = useFireCMSContext();
33
32
  if (!Array.isArray(property.resolvedProperties))
34
33
  throw Error("ArrayCustomShapedFieldBinding misconfiguration. Property `of` not set");
35
34
 
@@ -4,7 +4,7 @@ import { FieldProps } from "../../types";
4
4
 
5
5
  import { FieldHelperText } from "../components";
6
6
  import { LabelWithIcon } from "../../components";
7
- import { useClearRestoreValue, useFireCMSContext } from "../../hooks";
7
+ import { useClearRestoreValue, useCustomizationController } from "../../hooks";
8
8
  import { getIconForProperty } from "../../util";
9
9
  import { DateTimeField } from "@firecms/ui";
10
10
 
@@ -30,7 +30,7 @@ export function DateTimeFieldBinding({
30
30
  includeDescription
31
31
  }: DateTimeFieldProps) {
32
32
 
33
- const { locale } = useFireCMSContext();
33
+ const { locale } = useCustomizationController();
34
34
  const internalValue = value || null;
35
35
 
36
36
  useClearRestoreValue({
@@ -1,10 +1,8 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
  import { DataType, EntityReference, FieldProps, GeoPoint } from "../../types";
3
3
 
4
- import { ArrayContainer } from "../../components";
4
+ import { ArrayContainer, LabelWithIcon } from "../../components";
5
5
  import { FieldHelperText } from "../components";
6
-
7
- import { LabelWithIcon } from "../../components";
8
6
  import {
9
7
  AddIcon,
10
8
  ArrowDropDownIcon,
@@ -22,7 +20,7 @@ import {
22
20
  Typography
23
21
  } from "@firecms/ui";
24
22
  import { getDefaultValueForDataType, getIconForProperty } from "../../util";
25
- import { useFireCMSContext } from "../../hooks";
23
+ import { useCustomizationController } from "../../hooks";
26
24
 
27
25
  type MapEditViewRowState = [number, {
28
26
  key: string,
@@ -257,7 +255,7 @@ function MapKeyValueRow<T extends Record<string, any>>({
257
255
  updateDataType: (rowId: number, dataType: DataType) => void
258
256
  }) {
259
257
 
260
- const { locale } = useFireCMSContext();
258
+ const { locale } = useCustomizationController();
261
259
 
262
260
  function buildInput(entryValue: any, fieldKey: string, dataType: DataType) {
263
261
  if (dataType === "string" || dataType === "number") {
@@ -439,7 +437,7 @@ function ArrayKeyValueRow<T>({
439
437
  disabled?: boolean,
440
438
  }) {
441
439
 
442
- const { locale } = useFireCMSContext();
440
+ const { locale } = useCustomizationController();
443
441
  const [selectedDataType, setSelectedDataType] = useState<DataType>(getDataType(value) ?? "string");
444
442
 
445
443
  function doUpdateDataType(dataType: DataType) {
@@ -70,8 +70,13 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
70
70
  }
71
71
  ): Promise<void> {
72
72
 
73
+ if (status !== "new" && !entityId) {
74
+ throw new Error("Entity id must be specified when updating an existing entity");
75
+ }
73
76
  let updatedValues: Partial<EntityValues<M>>;
74
77
 
78
+ const customizationController = context.customizationController;
79
+
75
80
  const resolvedPath = context.navigation.resolveAliasesFrom(path);
76
81
 
77
82
  const callbacks = collection.callbacks;
@@ -82,7 +87,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
82
87
  path,
83
88
  values: previousValues as EntityValues<M>,
84
89
  entityId,
85
- fields: context.propertyConfigs
90
+ fields: customizationController.propertyConfigs
86
91
  });
87
92
  updatedValues = await callbacks.onPreSave({
88
93
  collection: resolvedCollection,
@@ -119,7 +124,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
119
124
  path,
120
125
  values: updatedValues as EntityValues<M>,
121
126
  entityId,
122
- fields: context.propertyConfigs
127
+ fields: customizationController.propertyConfigs
123
128
  });
124
129
  callbacks.onSaveSuccess({
125
130
  collection: resolvedCollection,
@@ -147,7 +152,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
147
152
  path,
148
153
  values: updatedValues as EntityValues<M>,
149
154
  entityId,
150
- fields: context.propertyConfigs
155
+ fields: customizationController.propertyConfigs
151
156
  });
152
157
  callbacks.onSaveFailure({
153
158
  collection: resolvedCollection,
@@ -21,3 +21,4 @@ export * from "./useLargeLayout";
21
21
 
22
22
  export * from "./useReferenceDialog";
23
23
  export * from "./useBrowserTitleAndIcon";
24
+ export * from "./useCustomizationController";
@@ -0,0 +1,8 @@
1
+ import { useContext } from "react";
2
+ import { AnalyticsController } from "../types";
3
+ import { AnalyticsContext } from "../contexts/AnalyticsContext";
4
+
5
+ /**
6
+ * @group Hooks and utilities
7
+ */
8
+ export const useAnalyticsController = (): AnalyticsController => useContext(AnalyticsContext);
@@ -0,0 +1,14 @@
1
+ import { useContext } from "react";
2
+ import { CustomizationController } from "../types/customization_controller";
3
+ import { CustomizationControllerContext } from "../contexts/CustomizationControllerContext";
4
+
5
+ /**
6
+ * Use this hook to retrieve the customization controller.
7
+ * This hook includes all the customization options that can be used
8
+ * to customize the CMS.
9
+ *
10
+ * You will likely not need to use this hook directly.
11
+ *
12
+ * @group Hooks and utilities
13
+ */
14
+ export const useCustomizationController = (): CustomizationController => useContext(CustomizationControllerContext);