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

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 (201) hide show
  1. package/dist/app/Scaffold.d.ts +4 -0
  2. package/dist/components/ArrayContainer.d.ts +14 -3
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
  6. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  7. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +8 -0
  8. package/dist/components/EntityJsonPreview.d.ts +3 -0
  9. package/dist/components/PropertyIdCopyTooltip.d.ts +1 -1
  10. package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
  11. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +1 -1
  12. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  13. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
  14. package/dist/components/common/default_entity_actions.d.ts +0 -2
  15. package/dist/components/common/index.d.ts +1 -1
  16. package/dist/components/common/useColumnsIds.d.ts +1 -0
  17. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  18. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  19. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  20. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  21. package/dist/core/DefaultAppBar.d.ts +8 -2
  22. package/dist/core/EntityEditView.d.ts +36 -21
  23. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  24. package/dist/core/FireCMS.d.ts +2 -2
  25. package/dist/core/FireCMSRouter.d.ts +4 -0
  26. package/dist/core/NavigationRoutes.d.ts +0 -1
  27. package/dist/core/SideDialogs.d.ts +4 -2
  28. package/dist/core/index.d.ts +2 -1
  29. package/dist/form/EntityForm.d.ts +49 -0
  30. package/dist/form/EntityFormActions.d.ts +17 -0
  31. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  32. package/dist/form/components/FormEntry.d.ts +6 -0
  33. package/dist/form/components/FormLayout.d.ts +5 -0
  34. package/dist/form/components/index.d.ts +2 -0
  35. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  36. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  37. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  38. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  39. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  40. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  41. package/dist/form/index.d.ts +16 -16
  42. package/dist/hooks/data/save.d.ts +1 -1
  43. package/dist/hooks/data/useEntityFetch.d.ts +2 -1
  44. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  45. package/dist/hooks/useBuildNavigationController.d.ts +4 -1
  46. package/dist/hooks/useModeController.d.ts +1 -2
  47. package/dist/index.es.js +15842 -14396
  48. package/dist/index.es.js.map +1 -1
  49. package/dist/index.umd.js +15833 -14389
  50. package/dist/index.umd.js.map +1 -1
  51. package/dist/internal/useBuildDataSource.d.ts +3 -2
  52. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  53. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  54. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  55. package/dist/routes/FireCMSRoute.d.ts +1 -0
  56. package/dist/routes/HomePageRoute.d.ts +3 -0
  57. package/dist/types/collections.d.ts +36 -10
  58. package/dist/types/datasource.d.ts +2 -2
  59. package/dist/types/dialogs_controller.d.ts +7 -3
  60. package/dist/types/entities.d.ts +1 -1
  61. package/dist/types/entity_actions.d.ts +10 -0
  62. package/dist/types/fields.d.ts +13 -3
  63. package/dist/types/firecms.d.ts +1 -1
  64. package/dist/types/navigation.d.ts +33 -12
  65. package/dist/types/plugins.d.ts +2 -0
  66. package/dist/types/properties.d.ts +40 -4
  67. package/dist/types/side_entity_controller.d.ts +6 -1
  68. package/dist/util/builders.d.ts +1 -1
  69. package/dist/util/createFormexStub.d.ts +2 -0
  70. package/dist/util/entity_actions.d.ts +2 -0
  71. package/dist/util/entity_cache.d.ts +23 -0
  72. package/dist/util/index.d.ts +1 -0
  73. package/dist/util/navigation_from_path.d.ts +6 -1
  74. package/dist/util/navigation_utils.d.ts +13 -1
  75. package/dist/util/objects.d.ts +1 -1
  76. package/dist/util/property_utils.d.ts +2 -2
  77. package/dist/util/references.d.ts +2 -2
  78. package/dist/util/resolutions.d.ts +12 -2
  79. package/package.json +20 -21
  80. package/src/app/Scaffold.tsx +13 -3
  81. package/src/components/ArrayContainer.tsx +60 -24
  82. package/src/components/CircularProgressCenter.tsx +1 -1
  83. package/src/components/DeleteEntityDialog.tsx +5 -3
  84. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +46 -23
  85. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
  86. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
  87. package/src/components/EntityCollectionTable/index.tsx +1 -1
  88. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  89. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +10 -13
  90. package/src/components/EntityCollectionView/EntityCollectionView.tsx +105 -47
  91. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -2
  92. package/src/components/EntityJsonPreview.tsx +66 -0
  93. package/src/components/EntityPreview.tsx +10 -3
  94. package/src/components/EntityView.tsx +4 -1
  95. package/src/components/NotFoundPage.tsx +2 -2
  96. package/src/components/PropertyIdCopyTooltip.tsx +2 -3
  97. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +14 -7
  98. package/src/components/SelectableTable/SelectableTable.tsx +18 -3
  99. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +7 -7
  100. package/src/components/UnsavedChangesDialog.tsx +42 -0
  101. package/src/components/VirtualTable/VirtualTable.tsx +48 -3
  102. package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
  103. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
  104. package/src/components/common/default_entity_actions.tsx +37 -37
  105. package/src/components/common/index.ts +1 -1
  106. package/src/components/common/useColumnsIds.tsx +1 -1
  107. package/src/components/common/useDataSourceTableController.tsx +420 -0
  108. package/src/components/common/useDebouncedCallback.tsx +20 -0
  109. package/src/components/common/useScrollRestoration.tsx +68 -0
  110. package/src/contexts/BreacrumbsContext.tsx +38 -0
  111. package/src/contexts/DialogsProvider.tsx +3 -2
  112. package/src/contexts/ModeController.tsx +1 -3
  113. package/src/core/DefaultAppBar.tsx +59 -17
  114. package/src/core/DefaultDrawer.tsx +1 -1
  115. package/src/core/EntityEditView.tsx +354 -1000
  116. package/src/core/EntityEditViewFormActions.tsx +199 -0
  117. package/src/core/EntitySidePanel.tsx +85 -15
  118. package/src/core/FireCMS.tsx +20 -16
  119. package/src/core/FireCMSRouter.tsx +17 -0
  120. package/src/core/NavigationRoutes.tsx +23 -32
  121. package/src/core/SideDialogs.tsx +20 -11
  122. package/src/core/index.tsx +4 -2
  123. package/src/form/EntityForm.tsx +782 -0
  124. package/src/form/EntityFormActions.tsx +169 -0
  125. package/src/form/PropertyFieldBinding.tsx +21 -16
  126. package/src/form/components/FormEntry.tsx +22 -0
  127. package/src/form/components/FormLayout.tsx +16 -0
  128. package/src/form/components/LabelWithIcon.tsx +6 -3
  129. package/src/form/components/StorageUploadProgress.tsx +1 -1
  130. package/src/form/components/index.tsx +2 -0
  131. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +8 -5
  132. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +5 -2
  133. package/src/form/field_bindings/BlockFieldBinding.tsx +5 -3
  134. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -0
  135. package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
  136. package/src/form/field_bindings/MapFieldBinding.tsx +14 -16
  137. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +15 -8
  138. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
  139. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
  140. package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
  141. package/src/form/field_bindings/RepeatFieldBinding.tsx +13 -4
  142. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -2
  143. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +11 -6
  144. package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -0
  145. package/src/form/field_bindings/TextFieldBinding.tsx +1 -0
  146. package/src/form/index.tsx +16 -32
  147. package/src/form/validation.ts +12 -6
  148. package/src/hooks/data/save.ts +25 -30
  149. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  150. package/src/hooks/data/useEntityFetch.tsx +5 -1
  151. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  152. package/src/hooks/useBuildModeController.tsx +15 -28
  153. package/src/hooks/useBuildNavigationController.tsx +81 -24
  154. package/src/hooks/useFireCMSContext.tsx +0 -30
  155. package/src/hooks/useLargeLayout.tsx +0 -35
  156. package/src/hooks/useModeController.tsx +1 -2
  157. package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
  158. package/src/internal/useBuildDataSource.ts +9 -5
  159. package/src/internal/useBuildSideDialogsController.tsx +3 -2
  160. package/src/internal/useBuildSideEntityController.tsx +135 -88
  161. package/src/internal/useUnsavedChangesDialog.tsx +126 -92
  162. package/src/preview/PropertyPreview.tsx +12 -10
  163. package/src/preview/components/UrlComponentPreview.tsx +17 -18
  164. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +4 -3
  165. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
  166. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +4 -2
  167. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
  168. package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -2
  169. package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -2
  170. package/src/preview/property_previews/MapPropertyPreview.tsx +3 -2
  171. package/src/routes/CustomCMSRoute.tsx +21 -0
  172. package/src/routes/FireCMSRoute.tsx +246 -0
  173. package/src/routes/HomePageRoute.tsx +17 -0
  174. package/src/types/collections.ts +45 -13
  175. package/src/types/datasource.ts +2 -1
  176. package/src/types/dialogs_controller.tsx +7 -3
  177. package/src/types/entities.ts +1 -1
  178. package/src/types/entity_actions.tsx +12 -0
  179. package/src/types/fields.tsx +18 -3
  180. package/src/types/firecms.tsx +1 -1
  181. package/src/types/navigation.ts +41 -16
  182. package/src/types/plugins.tsx +2 -0
  183. package/src/types/properties.ts +48 -4
  184. package/src/types/side_dialogs_controller.tsx +2 -0
  185. package/src/types/side_entity_controller.tsx +11 -1
  186. package/src/util/builders.ts +5 -3
  187. package/src/util/createFormexStub.tsx +62 -0
  188. package/src/util/entity_actions.ts +28 -0
  189. package/src/util/entity_cache.ts +204 -0
  190. package/src/util/index.ts +1 -0
  191. package/src/util/join_collections.ts +6 -1
  192. package/src/util/navigation_from_path.ts +18 -7
  193. package/src/util/navigation_utils.ts +115 -14
  194. package/src/util/objects.ts +9 -2
  195. package/src/util/parent_references_from_path.ts +3 -3
  196. package/src/util/property_utils.tsx +7 -3
  197. package/src/util/references.ts +8 -6
  198. package/src/util/resolutions.ts +41 -12
  199. package/src/util/useStorageUploadController.tsx +60 -23
  200. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -236
  201. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
- import { EntityCollection, PropertiesOrBuilders, PropertyConfig, PropertyOrBuilder, ResolvedProperties, ResolvedProperty } from "../types";
3
- export declare function isReferenceProperty(propertyOrBuilder: PropertyOrBuilder, fields: Record<string, PropertyConfig>): boolean | null;
2
+ import { AuthController, EntityCollection, PropertiesOrBuilders, PropertyConfig, PropertyOrBuilder, ResolvedProperties, ResolvedProperty } from "../types";
3
+ export declare function isReferenceProperty(authController: AuthController, propertyOrBuilder: PropertyOrBuilder, fields: Record<string, PropertyConfig>): boolean | null;
4
4
  export declare function getIdIcon(size: "small" | "medium" | "large"): React.ReactNode;
5
5
  export declare function getIconForWidget(widget: PropertyConfig | undefined, size: "small" | "medium" | "large"): import("react/jsx-runtime").JSX.Element;
6
6
  export declare function getIconForProperty(property: PropertyOrBuilder<any> | ResolvedProperty<any>, size?: "small" | "medium" | "large", fields?: Record<string, PropertyConfig>): React.ReactNode;
@@ -1,4 +1,4 @@
1
- import { EntityCollection, PropertyConfig, ResolvedEntityCollection } from "../types";
2
- export declare function getEntityPreviewKeys(targetCollection: EntityCollection<any>, fields: Record<string, PropertyConfig>, previewProperties?: string[], limit?: number): string[];
1
+ import { AuthController, EntityCollection, PropertyConfig, ResolvedEntityCollection } from "../types";
2
+ export declare function getEntityPreviewKeys(authController: AuthController, targetCollection: EntityCollection<any>, fields: Record<string, PropertyConfig>, previewProperties?: string[], limit?: number): string[];
3
3
  export declare function getEntityTitlePropertyKey<M extends Record<string, any>>(collection: EntityCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): string | undefined;
4
4
  export declare function getEntityImagePreviewPropertyKey<M extends object>(collection: ResolvedEntityCollection<M>): string | undefined;
@@ -1,5 +1,5 @@
1
- import { ArrayProperty, CMSType, EntityCollection, EntityCustomView, EntityValues, EnumValueConfig, EnumValues, NumberProperty, PropertiesOrBuilders, PropertyConfig, PropertyOrBuilder, ResolvedArrayProperty, ResolvedEntityCollection, ResolvedNumberProperty, ResolvedProperties, ResolvedProperty, ResolvedStringProperty, StringProperty, UserConfigurationPersistence } from "../types";
2
- export declare const resolveCollection: <M extends Record<string, any>>({ collection, path, entityId, values, previousValues, userConfigPersistence, propertyConfigs, ignoreMissingFields }: {
1
+ import { ArrayProperty, AuthController, CMSType, CustomizationController, EntityCollection, EntityCustomView, EntityValues, EnumValueConfig, EnumValues, NumberProperty, PropertiesOrBuilders, PropertyConfig, PropertyOrBuilder, ResolvedArrayProperty, ResolvedEntityCollection, ResolvedNumberProperty, ResolvedProperties, ResolvedProperty, ResolvedStringProperty, StringProperty, UserConfigurationPersistence } from "../types";
2
+ export declare const resolveCollection: <M extends Record<string, any>>({ collection, path, entityId, values, previousValues, userConfigPersistence, propertyConfigs, ignoreMissingFields, authController }: {
3
3
  collection: EntityCollection<M> | ResolvedEntityCollection<M>;
4
4
  path: string;
5
5
  entityId?: string;
@@ -8,6 +8,7 @@ export declare const resolveCollection: <M extends Record<string, any>>({ collec
8
8
  userConfigPersistence?: UserConfigurationPersistence;
9
9
  propertyConfigs?: Record<string, PropertyConfig>;
10
10
  ignoreMissingFields?: boolean;
11
+ authController: AuthController;
11
12
  }) => ResolvedEntityCollection<M>;
12
13
  /**
13
14
  * Resolve property builders, enums and arrays.
@@ -25,6 +26,7 @@ export declare function resolveProperty<T extends CMSType = CMSType, M extends R
25
26
  fromBuilder?: boolean;
26
27
  propertyConfigs?: Record<string, PropertyConfig<any>>;
27
28
  ignoreMissingFields?: boolean;
29
+ authController: AuthController;
28
30
  }): ResolvedProperty<T> | null;
29
31
  export declare function getArrayResolvedProperties<M>({ propertyKey, propertyValue, property, ...props }: {
30
32
  propertyValue: any;
@@ -38,6 +40,7 @@ export declare function getArrayResolvedProperties<M>({ propertyKey, propertyVal
38
40
  index?: number;
39
41
  fromBuilder?: boolean;
40
42
  propertyConfigs?: Record<string, PropertyConfig>;
43
+ authController: AuthController;
41
44
  }): ResolvedProperty[];
42
45
  export declare function resolveArrayProperty<T extends any[], M>({ propertyKey, property, ignoreMissingFields, ...props }: {
43
46
  propertyKey?: string;
@@ -50,6 +53,7 @@ export declare function resolveArrayProperty<T extends any[], M>({ propertyKey,
50
53
  fromBuilder?: boolean;
51
54
  propertyConfigs?: Record<string, PropertyConfig>;
52
55
  ignoreMissingFields?: boolean;
56
+ authController: AuthController;
53
57
  }): ResolvedArrayProperty;
54
58
  /**
55
59
  * Resolve enums and arrays for properties
@@ -67,6 +71,7 @@ export declare function resolveProperties<M extends Record<string, any>>({ prope
67
71
  fromBuilder?: boolean;
68
72
  propertyConfigs?: Record<string, PropertyConfig>;
69
73
  ignoreMissingFields?: boolean;
74
+ authController: AuthController;
70
75
  }): ResolvedProperties<M>;
71
76
  /**
72
77
  * Resolve enum aliases for a string or number property
@@ -76,3 +81,8 @@ export declare function resolveProperties<M extends Record<string, any>>({ prope
76
81
  export declare function resolvePropertyEnum(property: StringProperty | NumberProperty, fromBuilder?: boolean): ResolvedStringProperty | ResolvedNumberProperty;
77
82
  export declare function resolveEnumValues(input: EnumValues): EnumValueConfig[] | undefined;
78
83
  export declare function resolveEntityView(entityView: string | EntityCustomView<any>, contextEntityViews?: EntityCustomView<any>[]): EntityCustomView<any> | undefined;
84
+ export declare function resolvedSelectedEntityView<M extends Record<string, any>>(customViews: (string | EntityCustomView<M>)[] | undefined, customizationController: CustomizationController, selectedTab?: string, canEdit?: boolean): {
85
+ resolvedEntityViews: EntityCustomView<M>[];
86
+ selectedEntityView: EntityCustomView<M> | undefined;
87
+ selectedSecondaryForm: EntityCustomView<M> | undefined;
88
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/core",
3
3
  "type": "module",
4
- "version": "3.0.0-beta.11",
4
+ "version": "3.0.0-beta.13",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -50,63 +50,62 @@
50
50
  "./package.json": "./package.json"
51
51
  },
52
52
  "dependencies": {
53
- "@firecms/editor": "^3.0.0-beta.11",
54
- "@firecms/formex": "^3.0.0-beta.11",
55
- "@firecms/ui": "^3.0.0-beta.11",
53
+ "@firecms/editor": "^3.0.0-beta.13",
54
+ "@firecms/formex": "^3.0.0-beta.13",
55
+ "@firecms/ui": "^3.0.0-beta.13",
56
56
  "@hello-pangea/dnd": "^17.0.0",
57
- "@radix-ui/react-portal": "^1.1.2",
57
+ "@radix-ui/react-portal": "^1.1.3",
58
58
  "clsx": "^2.1.1",
59
59
  "date-fns": "^3.6.0",
60
60
  "fuse.js": "^7.0.0",
61
61
  "history": "^5.3.0",
62
62
  "markdown-it": "^14.1.0",
63
- "notistack": "^3.0.1",
63
+ "notistack": "^3.0.2",
64
64
  "object-hash": "^3.0.0",
65
+ "prism-react-renderer": "^2.4.1",
65
66
  "react-dropzone": "^14.3.5",
66
67
  "react-fast-compare": "^3.2.2",
67
68
  "react-image-file-resizer": "^0.4.8",
68
69
  "react-transition-group": "^4.4.5",
69
70
  "react-use-measure": "^2.1.1",
70
- "react-window": "^1.8.10",
71
+ "react-window": "^1.8.11",
71
72
  "yup": "^0.32.11"
72
73
  },
73
74
  "peerDependencies": {
74
- "firebase": "^10.5.2",
75
- "react": "^18.3.1",
76
- "react-dom": "^18.3.1",
75
+ "react": ">=18.0.0",
76
+ "react-dom": ">=18.0.0",
77
77
  "react-router": "^6.28.0",
78
78
  "react-router-dom": "^6.28.0"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@jest/globals": "^29.7.0",
82
- "@testing-library/react": "^16.0.1",
83
- "@testing-library/user-event": "^14.5.2",
82
+ "@testing-library/react": "^16.2.0",
83
+ "@testing-library/user-event": "^14.6.1",
84
84
  "@types/jest": "^29.5.14",
85
- "@types/node": "^20.17.9",
85
+ "@types/node": "^20.17.14",
86
86
  "@types/object-hash": "^3.0.6",
87
- "@types/react": "^18.3.11",
88
- "@types/react-dom": "^18.3.1",
87
+ "@types/react": "^18.3.18",
88
+ "@types/react-dom": "^18.3.0",
89
89
  "@types/react-measure": "^2.0.12",
90
90
  "@vitejs/plugin-react": "^4.3.4",
91
91
  "babel-plugin-react-compiler": "beta",
92
92
  "cross-env": "^7.0.3",
93
93
  "eslint-plugin-react-compiler": "beta",
94
- "firebase": "^10.14.1",
95
94
  "jest": "^29.7.0",
96
95
  "npm-run-all": "^4.1.5",
97
- "react-router": "^6.28.0",
98
- "react-router-dom": "^6.28.0",
96
+ "react-router": "^6.28.2",
97
+ "react-router-dom": "^6.28.2",
99
98
  "ts-jest": "^29.2.5",
100
99
  "ts-node": "^10.9.2",
101
100
  "tsd": "^0.31.2",
102
- "typescript": "^5.7.2",
103
- "vite": "^5.4.11"
101
+ "typescript": "^5.7.3",
102
+ "vite": "^5.4.14"
104
103
  },
105
104
  "files": [
106
105
  "dist",
107
106
  "src"
108
107
  ],
109
- "gitHead": "cb6d419faf2664cfd6cb2603a834e4bf499a92bb",
108
+ "gitHead": "26d5e1f33a7ac00c78e45cd3cb3c397f4ba00821",
110
109
  "publishConfig": {
111
110
  "access": "public"
112
111
  },
@@ -23,6 +23,11 @@ export interface ScaffoldProps {
23
23
  */
24
24
  logo?: string;
25
25
 
26
+ /**
27
+ * If true, the main content will be padded in large layouts. Defaults to true.
28
+ */
29
+ padding?: boolean;
30
+
26
31
  className?: string;
27
32
 
28
33
  style?: React.CSSProperties;
@@ -47,7 +52,8 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
47
52
  autoOpenDrawer,
48
53
  logo,
49
54
  className,
50
- style
55
+ style,
56
+ padding = true
51
57
  } = props;
52
58
 
53
59
  const drawerChildren = React.Children.toArray(children).filter((child: any) => child.type.componentType === "Drawer");
@@ -116,10 +122,14 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
116
122
 
117
123
  <main
118
124
  className="flex flex-col flex-grow overflow-auto">
125
+
119
126
  {hasAppBar && <DrawerHeader/>}
127
+
120
128
  <div
121
- className={cls(defaultBorderMixin, "flex-grow overflow-auto lg:m-0 lg:mx-4 lg:mb-4 lg:rounded-lg lg:border lg:border-solid m-0 mt-1", {
122
- "lg:mt-4": !hasAppBar
129
+ className={cls(defaultBorderMixin, "flex-grow overflow-auto m-0 ", {
130
+ "lg:mt-4": !hasAppBar,
131
+ "mt-1 lg:m-0 lg:mx-4 lg:mb-4 lg:rounded-lg lg:border lg:border-solid": padding,
132
+ "border-t": hasAppBar && !padding,
123
133
  })}>
124
134
 
125
135
  <ErrorBoundary>
@@ -37,10 +37,17 @@ export interface ArrayContainerProps<T> {
37
37
  disabled?: boolean;
38
38
  size?: "small" | "medium";
39
39
  onInternalIdAdded?: (id: number) => void;
40
+ /**
41
+ * @deprecated Use `canAddElements` instead
42
+ */
40
43
  includeAddButton?: boolean;
44
+ canAddElements?: boolean;
45
+ sortable?: boolean;
41
46
  newDefaultEntry: T;
42
47
  onValueChange: (value: T[]) => void,
43
48
  className?: string;
49
+ min?: number;
50
+ max?: number;
44
51
  }
45
52
 
46
53
  const buildIdsMap = (value: any[]) =>
@@ -64,12 +71,19 @@ export function ArrayContainer<T>({
64
71
  buildEntry,
65
72
  size = "medium",
66
73
  onInternalIdAdded,
67
- includeAddButton,
74
+ includeAddButton: deprecatedIncludeAddButton,
75
+ canAddElements: canAddElementsProp = true,
76
+ sortable = true,
68
77
  newDefaultEntry,
69
78
  onValueChange,
70
- className
79
+ className,
80
+ min = 0,
81
+ max = Infinity
71
82
  }: ArrayContainerProps<T>) {
72
83
 
84
+ const canAddElements = (canAddElementsProp || canAddElementsProp === undefined)
85
+ && (deprecatedIncludeAddButton === undefined || deprecatedIncludeAddButton);
86
+
73
87
  const hasValue = value && Array.isArray(value) && value.length > 0;
74
88
 
75
89
  // Used to track the ids that have displayed the initial show animation
@@ -78,7 +92,8 @@ export function ArrayContainer<T>({
78
92
  const [internalIds, setInternalIds] = useState<number[]>(
79
93
  hasValue
80
94
  ? Object.values(internalIdsRef.current)
81
- : []);
95
+ : []
96
+ );
82
97
 
83
98
  const itemCustomPropsRef = useRef<Record<number, object>>({});
84
99
 
@@ -104,7 +119,7 @@ export function ArrayContainer<T>({
104
119
 
105
120
  const insertInEnd = (e: React.SyntheticEvent) => {
106
121
  e.preventDefault();
107
- if (disabled) return;
122
+ if (disabled || (value ?? []).length >= max) return;
108
123
  const id = getRandomId();
109
124
  const newIds: number[] = [...internalIds, id];
110
125
  if (onInternalIdAdded)
@@ -114,6 +129,7 @@ export function ArrayContainer<T>({
114
129
  };
115
130
 
116
131
  const remove = (index: number) => {
132
+ if ((value ?? []).length <= min) return;
117
133
  const newIds = [...internalIds];
118
134
  newIds.splice(index, 1);
119
135
  setInternalIds(newIds);
@@ -121,12 +137,14 @@ export function ArrayContainer<T>({
121
137
  };
122
138
 
123
139
  const copy = (index: number) => {
140
+ if ((value ?? []).length >= max) return;
124
141
  const id = getRandomId();
125
142
  const copyingItem = value[index];
126
143
  const newIds: number[] = [
127
- ...internalIds.splice(0, index + 1),
144
+ ...internalIds.slice(0, index + 1),
128
145
  id,
129
- ...internalIds.splice(index + 1, internalIds.length - index - 1)];
146
+ ...internalIds.slice(index + 1)
147
+ ];
130
148
  if (onInternalIdAdded)
131
149
  onInternalIdAdded(id);
132
150
  setInternalIds(newIds);
@@ -135,11 +153,13 @@ export function ArrayContainer<T>({
135
153
  };
136
154
 
137
155
  const addInIndex = (index: number) => {
156
+ if ((value ?? []).length >= max) return;
138
157
  const id = getRandomId();
139
158
  const newIds: number[] = [
140
- ...internalIds.splice(0, index),
159
+ ...internalIds.slice(0, index),
141
160
  id,
142
- ...internalIds.slice(index)];
161
+ ...internalIds.slice(index)
162
+ ];
143
163
  if (onInternalIdAdded)
144
164
  onInternalIdAdded(id);
145
165
  setInternalIds(newIds);
@@ -183,6 +203,8 @@ export function ArrayContainer<T>({
183
203
  storedProps={itemCustomPropsRef.current[internalId]}
184
204
  updateItemCustomProps={updateItemCustomProps}
185
205
  addInIndex={addInIndex}
206
+ canAddElements={canAddElements}
207
+ sortable={sortable}
186
208
  />
187
209
  );
188
210
  }}
@@ -197,7 +219,7 @@ export function ArrayContainer<T>({
197
219
  <Draggable
198
220
  key={`array_field_${internalId}`}
199
221
  draggableId={`array_field_${internalId}`}
200
- isDragDisabled={disabled}
222
+ isDragDisabled={disabled || !sortable}
201
223
  index={index}>
202
224
  {(provided, snapshot) => (
203
225
  <ArrayContainerItem
@@ -213,6 +235,8 @@ export function ArrayContainer<T>({
213
235
  storedProps={itemCustomPropsRef.current[internalId]}
214
236
  updateItemCustomProps={updateItemCustomProps}
215
237
  addInIndex={addInIndex}
238
+ canAddElements={canAddElements}
239
+ sortable={sortable}
216
240
  />
217
241
  )}
218
242
  </Draggable>
@@ -221,17 +245,19 @@ export function ArrayContainer<T>({
221
245
 
222
246
  {droppableProvided.placeholder}
223
247
 
224
- {includeAddButton && <div className="py-4 justify-center text-left">
225
- <Button
226
- variant={"text"}
227
- size={size === "small" ? "small" : "medium"}
228
- color="primary"
229
- disabled={disabled}
230
- startIcon={<AddIcon/>}
231
- onClick={insertInEnd}>
232
- {addLabel ?? "Add"}
233
- </Button>
234
- </div>}
248
+ {canAddElements && (
249
+ <div className="my-4 justify-center text-left">
250
+ <Button
251
+ variant={"text"}
252
+ size={size === "small" ? "small" : "medium"}
253
+ color="primary"
254
+ disabled={disabled || value?.length >= max}
255
+ startIcon={<AddIcon/>}
256
+ onClick={insertInEnd}>
257
+ {addLabel ?? "Add"}
258
+ </Button>
259
+ </div>
260
+ )}
235
261
  </div>
236
262
  )}
237
263
  </Droppable>
@@ -249,6 +275,8 @@ type ArrayContainerItemProps = {
249
275
  remove: (index: number) => void,
250
276
  copy: (index: number) => void,
251
277
  addInIndex?: (index: number) => void,
278
+ canAddElements?: boolean,
279
+ sortable: boolean,
252
280
  isDragging: boolean,
253
281
  storedProps?: object,
254
282
  updateItemCustomProps: (internalId: number, props: object) => void
@@ -263,6 +291,8 @@ export function ArrayContainerItem({
263
291
  buildEntry,
264
292
  remove,
265
293
  addInIndex,
294
+ canAddElements,
295
+ sortable,
266
296
  copy,
267
297
  isDragging,
268
298
  storedProps,
@@ -273,7 +303,7 @@ export function ArrayContainerItem({
273
303
  ref={provided.innerRef}
274
304
  {...provided.draggableProps}
275
305
  style={provided.draggableProps.style}
276
- className={`${
306
+ className={`relative ${
277
307
  !isDragging ? "hover:bg-surface-accent-50 dark:hover:bg-surface-800 dark:hover:bg-opacity-20" : ""
278
308
  } rounded-md opacity-100`}
279
309
  >
@@ -295,6 +325,8 @@ export function ArrayContainerItem({
295
325
  index={index}
296
326
  provided={provided}
297
327
  addInIndex={addInIndex}
328
+ canAddElements={canAddElements}
329
+ sortable={sortable}
298
330
  copy={copy}/>
299
331
  </div>
300
332
  </div>;
@@ -307,6 +339,8 @@ export function ArrayItemOptions({
307
339
  index,
308
340
  provided,
309
341
  copy,
342
+ canAddElements,
343
+ sortable,
310
344
  addInIndex
311
345
  }: {
312
346
  direction?: "row" | "column",
@@ -315,6 +349,8 @@ export function ArrayItemOptions({
315
349
  index: number,
316
350
  provided: any,
317
351
  copy: (index: number) => void,
352
+ sortable: boolean,
353
+ canAddElements?: boolean,
318
354
  addInIndex?: (index: number) => void
319
355
  }) {
320
356
 
@@ -330,10 +366,10 @@ export function ArrayItemOptions({
330
366
  delayDuration={400}
331
367
  open={menuOpen ? false : undefined}
332
368
  side={direction === "column" ? "left" : undefined}
333
- title="Drag to move. Click for more options">
369
+ title={!disabled && sortable ? "Drag to move. Click for more options" : undefined}>
334
370
  <IconButton
335
371
  size="small"
336
- disabled={disabled}
372
+ disabled={disabled || !canAddElements}
337
373
  onClick={(e) => {
338
374
  e.preventDefault();
339
375
  setMenuOpen(true);
@@ -341,7 +377,7 @@ export function ArrayItemOptions({
341
377
  onDragStart={(e: any) => {
342
378
  setMenuOpen(false);
343
379
  }}
344
- className={`cursor-${disabled ? "inherit" : "grab"}`}>
380
+ className={disabled || !sortable ? "cursor-inherit" : "cursor-grab"}>
345
381
  <HandleIcon/>
346
382
  </IconButton>
347
383
 
@@ -13,7 +13,7 @@ export function CircularProgressCenter({ text, ...props }: CircularProgressProps
13
13
  }) {
14
14
  return (
15
15
  <div
16
- className="flex w-full h-screen max-h-full max-w-full bg-surface-50 dark:bg-surface-900 gap-4">
16
+ className="flex w-full h-screen max-h-full max-w-full gap-4">
17
17
  <div className="m-auto flex flex-col gap-2 items-center">
18
18
  <CircularProgress {...props}/>
19
19
  {text && <Typography
@@ -3,6 +3,7 @@ import React, { useCallback, useMemo, useState } from "react";
3
3
  import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle } from "@firecms/ui";
4
4
  import {
5
5
  deleteEntityWithCallbacks,
6
+ useAuthController,
6
7
  useCustomizationController,
7
8
  useDataSource,
8
9
  useFireCMSContext,
@@ -34,7 +35,7 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
34
35
  onMultipleEntitiesDelete,
35
36
  path
36
37
  }: DeleteEntityDialogProps<M>) {
37
-
38
+ const authController = useAuthController();
38
39
  const dataSource = useDataSource(collection);
39
40
  const customizationController = useCustomizationController();
40
41
  const snackbarController = useSnackbarController();
@@ -50,7 +51,8 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
50
51
  const resolvedCollection = useMemo(() => resolveCollection<M>({
51
52
  collection,
52
53
  path,
53
- propertyConfigs: customizationController.propertyConfigs
54
+ propertyConfigs: customizationController.propertyConfigs,
55
+ authController
54
56
  }), [collection, path]);
55
57
 
56
58
  const handleCancel = useCallback(() => {
@@ -181,7 +183,7 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
181
183
  </DialogContent>
182
184
  <DialogActions>
183
185
 
184
- {loading && <CircularProgress size={"small"}/>}
186
+ {loading && <CircularProgress size={"smallest"}/>}
185
187
 
186
188
  <Button onClick={handleCancel}
187
189
  disabled={loading}
@@ -1,8 +1,20 @@
1
1
  import React, { MouseEvent, useCallback } from "react";
2
2
 
3
3
  import { CollectionSize, Entity, EntityAction, EntityCollection, SelectionController } from "../../types";
4
- import { Checkbox, cls, IconButton, Menu, MenuItem, MoreVertIcon, Skeleton, Tooltip } from "@firecms/ui";
4
+ import {
5
+ Checkbox,
6
+ Chip,
7
+ cls,
8
+ EditIcon,
9
+ IconButton,
10
+ Menu,
11
+ MenuItem,
12
+ MoreVertIcon,
13
+ Skeleton,
14
+ Tooltip
15
+ } from "@firecms/ui";
5
16
  import { useFireCMSContext, useLargeLayout } from "../../hooks";
17
+ import { hasEntityInCache } from "../../util/entity_cache";
6
18
 
7
19
  /**
8
20
  *
@@ -32,23 +44,25 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
32
44
  actions = [],
33
45
  hideId,
34
46
  selectionController,
47
+ openEntityMode
35
48
  }:
36
- {
37
- entity: Entity<any>,
38
- collection?: EntityCollection<any>,
39
- fullPath?: string,
40
- width: number,
41
- frozen?: boolean,
42
- size: CollectionSize,
43
- isSelected?: boolean,
44
- selectionEnabled?: boolean,
45
- actions?: EntityAction[],
46
- hideId?: boolean,
47
- onCollectionChange?: () => void,
48
- selectionController?: SelectionController;
49
- highlightEntity?: (entity: Entity<any>) => void;
50
- unhighlightEntity?: (entity: Entity<any>) => void;
51
- }) {
49
+ {
50
+ entity: Entity<any>,
51
+ collection?: EntityCollection<any>,
52
+ fullPath?: string,
53
+ width: number,
54
+ frozen?: boolean,
55
+ size: CollectionSize,
56
+ isSelected?: boolean,
57
+ selectionEnabled?: boolean,
58
+ actions?: EntityAction[],
59
+ hideId?: boolean,
60
+ onCollectionChange?: () => void,
61
+ selectionController?: SelectionController;
62
+ highlightEntity?: (entity: Entity<any>) => void;
63
+ unhighlightEntity?: (entity: Entity<any>) => void;
64
+ openEntityMode: "side_panel" | "full_screen";
65
+ }) {
52
66
 
53
67
  const largeLayout = useLargeLayout();
54
68
 
@@ -63,6 +77,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
63
77
 
64
78
  const collapsedActions = actions.filter(a => a.collapsed || a.collapsed === undefined);
65
79
  const uncollapsedActions = actions.filter(a => a.collapsed === false);
80
+ const hasDraft = hasEntityInCache(fullPath + "/" + entity.id);
66
81
  return (
67
82
  <div
68
83
  className={cls(
@@ -98,6 +113,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
98
113
  highlightEntity,
99
114
  unhighlightEntity,
100
115
  onCollectionChange,
116
+ openEntityMode: openEntityMode ?? collection?.openEntityMode
101
117
  });
102
118
  }}
103
119
  size={largeLayout ? "medium" : "small"}>
@@ -126,6 +142,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
126
142
  highlightEntity,
127
143
  unhighlightEntity,
128
144
  onCollectionChange,
145
+ openEntityMode: openEntityMode ?? collection?.openEntityMode
129
146
  });
130
147
  }}>
131
148
  {action.icon}
@@ -148,15 +165,21 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
148
165
 
149
166
  {!hideId && size !== "xs" && (
150
167
  <div
151
- className="w-[138px] text-center overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2"
168
+ className="w-[138px] overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2 align-center justify-center flex items-center gap-1"
152
169
  onClick={(event) => {
153
170
  event.stopPropagation();
154
171
  }}>
155
-
156
- {entity
157
- ? entity.id
158
- : <Skeleton/>
159
- }
172
+ {hasDraft && <Tooltip title={"Local unsaved changes"} className={"inline"}>
173
+ <Chip colorScheme={"orangeDarker"} className={"p-0.5"}>
174
+ <EditIcon size={12}/>
175
+ </Chip>
176
+ </Tooltip>}
177
+ <span className="min-w-0 truncate text-center">
178
+ {entity
179
+ ? entity.id
180
+ : <Skeleton/>
181
+ }
182
+ </span>
160
183
  </div>
161
184
  )}
162
185
 
@@ -34,14 +34,14 @@ import { getRowHeight } from "../common/table_height";
34
34
  *
35
35
  * The data displayed in the table is managed by a {@link EntityTableController}.
36
36
  * You can build the default, bound to a path in the datasource, by using the hook
37
- * {@link useDataSourceEntityCollectionTableController}
37
+ * {@link useDataSourceTableController}
38
38
  *
39
39
  * @see EntityCollectionTableProps
40
40
  * @see EntityCollectionView
41
41
  * @see VirtualTable
42
42
  * @group Components
43
43
  */
44
- export const EntityCollectionTable = function EntityCollectionTable<M extends Record<string, any>, USER extends User>
44
+ export const EntityCollectionTable = function EntityCollectionTable<M extends Record<string, any> = any, USER extends User = any>
45
45
  ({
46
46
  className,
47
47
  style,
@@ -57,6 +57,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
57
57
  highlightedEntities,
58
58
  onEntityClick,
59
59
  onColumnResize,
60
+ initialScroll,
61
+ onScroll,
60
62
  onSizeChanged,
61
63
  textSearchEnabled = false,
62
64
  hoverRow = true,
@@ -76,7 +78,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
76
78
  getIdColumnWidth,
77
79
  onTextSearchClick,
78
80
  textSearchLoading,
79
- enablePopupIcon
81
+ enablePopupIcon,
82
+ openEntityMode = "side_panel"
80
83
  }: EntityCollectionTableProps<M>) {
81
84
 
82
85
  const ref = useRef<HTMLDivElement>(null);
@@ -265,7 +268,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
265
268
  width={column.width}
266
269
  frozen={column.frozen}
267
270
  isSelected={false}
268
- size={size}/>;
271
+ size={size}
272
+ openEntityMode={openEntityMode}/>;
269
273
  } else if (additionalFieldsMap[columnKey]) {
270
274
  return additionalCellRenderer(props);
271
275
  } else if (props.columnIndex < columns.length + 1) {
@@ -313,6 +317,8 @@ export const EntityCollectionTable = function EntityCollectionTable<M extends Re
313
317
  highlightedRow={(entity: Entity<M>) => Boolean(selectedEntities?.find(e => e.id === entity.id && e.path === entity.path))}
314
318
  tableController={tableController}
315
319
  onValueChange={onValueChange}
320
+ initialScroll={initialScroll}
321
+ onScroll={onScroll}
316
322
  onColumnResize={onColumnResize}
317
323
  hoverRow={hoverRow}
318
324
  filterable={filterable}