@firecms/collection_editor 3.0.0-3.0.0-beta.4.pre.1.0

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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/ConfigControllerProvider.d.ts +37 -0
  4. package/dist/index.d.ts +11 -0
  5. package/dist/index.es.js +5454 -0
  6. package/dist/index.es.js.map +1 -0
  7. package/dist/index.umd.js +4 -0
  8. package/dist/index.umd.js.map +1 -0
  9. package/dist/types/collection_editor_controller.d.ts +36 -0
  10. package/dist/types/collection_inference.d.ts +2 -0
  11. package/dist/types/config_controller.d.ts +51 -0
  12. package/dist/types/config_permissions.d.ts +19 -0
  13. package/dist/types/persisted_collection.d.ts +6 -0
  14. package/dist/ui/CollectionViewHeaderAction.d.ts +10 -0
  15. package/dist/ui/EditorCollectionAction.d.ts +2 -0
  16. package/dist/ui/HomePageEditorCollectionAction.d.ts +2 -0
  17. package/dist/ui/MissingReferenceWidget.d.ts +3 -0
  18. package/dist/ui/NewCollectionButton.d.ts +1 -0
  19. package/dist/ui/NewCollectionCard.d.ts +2 -0
  20. package/dist/ui/PropertyAddColumnComponent.d.ts +6 -0
  21. package/dist/ui/RootCollectionSuggestions.d.ts +3 -0
  22. package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +10 -0
  23. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +36 -0
  24. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +15 -0
  25. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +19 -0
  26. package/dist/ui/collection_editor/CollectionYupValidation.d.ts +14 -0
  27. package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
  28. package/dist/ui/collection_editor/EnumForm.d.ts +12 -0
  29. package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
  30. package/dist/ui/collection_editor/PropertyEditView.d.ts +40 -0
  31. package/dist/ui/collection_editor/PropertyFieldPreview.d.ts +15 -0
  32. package/dist/ui/collection_editor/PropertySelectItem.d.ts +8 -0
  33. package/dist/ui/collection_editor/PropertyTree.d.ts +33 -0
  34. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +12 -0
  35. package/dist/ui/collection_editor/SwitchControl.d.ts +8 -0
  36. package/dist/ui/collection_editor/UnsavedChangesDialog.d.ts +9 -0
  37. package/dist/ui/collection_editor/import/CollectionEditorImportDataPreview.d.ts +7 -0
  38. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
  39. package/dist/ui/collection_editor/import/clean_import_data.d.ts +7 -0
  40. package/dist/ui/collection_editor/properties/BlockPropertyField.d.ts +8 -0
  41. package/dist/ui/collection_editor/properties/BooleanPropertyField.d.ts +3 -0
  42. package/dist/ui/collection_editor/properties/CommonPropertyFields.d.ts +10 -0
  43. package/dist/ui/collection_editor/properties/DateTimePropertyField.d.ts +3 -0
  44. package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +8 -0
  45. package/dist/ui/collection_editor/properties/KeyValuePropertyField.d.ts +3 -0
  46. package/dist/ui/collection_editor/properties/MapPropertyField.d.ts +8 -0
  47. package/dist/ui/collection_editor/properties/NumberPropertyField.d.ts +3 -0
  48. package/dist/ui/collection_editor/properties/ReferencePropertyField.d.ts +13 -0
  49. package/dist/ui/collection_editor/properties/RepeatPropertyField.d.ts +10 -0
  50. package/dist/ui/collection_editor/properties/StoragePropertyField.d.ts +5 -0
  51. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +5 -0
  52. package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
  53. package/dist/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.d.ts +3 -0
  54. package/dist/ui/collection_editor/properties/validation/ArrayPropertyValidation.d.ts +5 -0
  55. package/dist/ui/collection_editor/properties/validation/GeneralPropertyValidation.d.ts +4 -0
  56. package/dist/ui/collection_editor/properties/validation/NumberPropertyValidation.d.ts +3 -0
  57. package/dist/ui/collection_editor/properties/validation/StringPropertyValidation.d.ts +11 -0
  58. package/dist/ui/collection_editor/properties/validation/ValidationPanel.d.ts +2 -0
  59. package/dist/ui/collection_editor/templates/blog_template.d.ts +2 -0
  60. package/dist/ui/collection_editor/templates/pages_template.d.ts +2 -0
  61. package/dist/ui/collection_editor/templates/products_template.d.ts +2 -0
  62. package/dist/ui/collection_editor/templates/users_template.d.ts +2 -0
  63. package/dist/ui/collection_editor/util.d.ts +5 -0
  64. package/dist/ui/collection_editor/utils/strings.d.ts +1 -0
  65. package/dist/ui/collection_editor/utils/supported_fields.d.ts +3 -0
  66. package/dist/ui/collection_editor/utils/update_property_for_widget.d.ts +2 -0
  67. package/dist/ui/collection_editor/utils/useTraceUpdate.d.ts +1 -0
  68. package/dist/useCollectionEditorController.d.ts +6 -0
  69. package/dist/useCollectionEditorPlugin.d.ts +49 -0
  70. package/dist/useCollectionsConfigController.d.ts +6 -0
  71. package/dist/utils/arrays.d.ts +1 -0
  72. package/dist/utils/entities.d.ts +3 -0
  73. package/package.json +85 -0
  74. package/src/ConfigControllerProvider.tsx +338 -0
  75. package/src/index.ts +35 -0
  76. package/src/types/collection_editor_controller.tsx +43 -0
  77. package/src/types/collection_inference.ts +3 -0
  78. package/src/types/config_controller.tsx +60 -0
  79. package/src/types/config_permissions.ts +20 -0
  80. package/src/types/persisted_collection.ts +9 -0
  81. package/src/ui/CollectionViewHeaderAction.tsx +43 -0
  82. package/src/ui/EditorCollectionAction.tsx +109 -0
  83. package/src/ui/HomePageEditorCollectionAction.tsx +84 -0
  84. package/src/ui/MissingReferenceWidget.tsx +37 -0
  85. package/src/ui/NewCollectionButton.tsx +16 -0
  86. package/src/ui/NewCollectionCard.tsx +48 -0
  87. package/src/ui/PropertyAddColumnComponent.tsx +42 -0
  88. package/src/ui/RootCollectionSuggestions.tsx +63 -0
  89. package/src/ui/collection_editor/CollectionDetailsForm.tsx +365 -0
  90. package/src/ui/collection_editor/CollectionEditorDialog.tsx +801 -0
  91. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +213 -0
  92. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +506 -0
  93. package/src/ui/collection_editor/CollectionYupValidation.tsx +7 -0
  94. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +37 -0
  95. package/src/ui/collection_editor/EnumForm.tsx +357 -0
  96. package/src/ui/collection_editor/GetCodeDialog.tsx +110 -0
  97. package/src/ui/collection_editor/PropertyEditView.tsx +615 -0
  98. package/src/ui/collection_editor/PropertyFieldPreview.tsx +207 -0
  99. package/src/ui/collection_editor/PropertySelectItem.tsx +32 -0
  100. package/src/ui/collection_editor/PropertyTree.tsx +252 -0
  101. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +262 -0
  102. package/src/ui/collection_editor/SwitchControl.tsx +39 -0
  103. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
  104. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
  105. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +268 -0
  106. package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
  107. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +138 -0
  108. package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +40 -0
  109. package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +110 -0
  110. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +86 -0
  111. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +114 -0
  112. package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
  113. package/src/ui/collection_editor/properties/MapPropertyField.tsx +150 -0
  114. package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
  115. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +160 -0
  116. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +109 -0
  117. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +200 -0
  118. package/src/ui/collection_editor/properties/StringPropertyField.tsx +79 -0
  119. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
  120. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +45 -0
  121. package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
  122. package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +61 -0
  123. package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +115 -0
  124. package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +150 -0
  125. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
  126. package/src/ui/collection_editor/templates/blog_template.ts +115 -0
  127. package/src/ui/collection_editor/templates/pages_template.ts +188 -0
  128. package/src/ui/collection_editor/templates/products_template.ts +88 -0
  129. package/src/ui/collection_editor/templates/users_template.ts +42 -0
  130. package/src/ui/collection_editor/util.ts +28 -0
  131. package/src/ui/collection_editor/utils/strings.ts +9 -0
  132. package/src/ui/collection_editor/utils/supported_fields.tsx +29 -0
  133. package/src/ui/collection_editor/utils/update_property_for_widget.ts +271 -0
  134. package/src/ui/collection_editor/utils/useTraceUpdate.tsx +23 -0
  135. package/src/useCollectionEditorController.tsx +9 -0
  136. package/src/useCollectionEditorPlugin.tsx +154 -0
  137. package/src/useCollectionsConfigController.tsx +9 -0
  138. package/src/utils/arrays.ts +3 -0
  139. package/src/utils/entities.ts +38 -0
  140. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,5 @@
1
+ export declare function idToPropertiesPath(id: string): string;
2
+ export declare function namespaceToPropertiesPath(namespace?: string): string;
3
+ export declare function namespaceToPropertiesOrderPath(namespace?: string): string;
4
+ export declare function getFullId(propertyKey: string, propertyNamespace?: string): string;
5
+ export declare function getFullIdPath(propertyKey: string, propertyNamespace?: string): string;
@@ -0,0 +1 @@
1
+ export declare function camelCase(str: string): string;
@@ -0,0 +1,3 @@
1
+ import { PropertyConfigId, PropertyConfig } from "@firecms/core";
2
+ export declare const supportedFieldsIds: PropertyConfigId[];
3
+ export declare const supportedFields: Record<string, PropertyConfig>;
@@ -0,0 +1,2 @@
1
+ import { Property, PropertyConfig } from "@firecms/core";
2
+ export declare function updatePropertyFromWidget(propertyData: any, selectedWidgetId: string | undefined, propertyConfigs: Record<string, PropertyConfig>): Property;
@@ -0,0 +1 @@
1
+ export declare function useTraceUpdate(props: any): void;
@@ -0,0 +1,6 @@
1
+ import { CollectionEditorController } from "./types/collection_editor_controller";
2
+ /**
3
+ * Hook to access the collection editor controller.
4
+ * The methods in this controller can be used to open the collection editor dialog.
5
+ */
6
+ export declare const useCollectionEditorController: () => CollectionEditorController;
@@ -0,0 +1,49 @@
1
+ import React from "react";
2
+ import { FireCMSPlugin, User } from "@firecms/core";
3
+ import { CollectionEditorPermissionsBuilder } from "./types/config_permissions";
4
+ import { PersistedCollection } from "./types/persisted_collection";
5
+ import { CollectionInference } from "./types/collection_inference";
6
+ import { CollectionsConfigController } from "./types/config_controller";
7
+ export interface CollectionConfigControllerProps<EC extends PersistedCollection = PersistedCollection, UserType extends User = User> {
8
+ /**
9
+ * Firebase app where the configuration is saved.
10
+ */
11
+ collectionConfigController: CollectionsConfigController;
12
+ /**
13
+ * Define what actions can be performed on the configuration.
14
+ */
15
+ configPermissions?: CollectionEditorPermissionsBuilder<UserType, EC>;
16
+ /**
17
+ * The words you define here will not be allowed to be used as group
18
+ * names when creating collections.
19
+ * e.g. ["admin"]
20
+ */
21
+ reservedGroups: string[];
22
+ extraView?: {
23
+ View: React.ComponentType<{
24
+ path: string;
25
+ }>;
26
+ icon: React.ReactNode;
27
+ };
28
+ pathSuggestions?: (path: string) => Promise<string[]>;
29
+ collectionInference?: CollectionInference;
30
+ getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
31
+ getUser: (uid: string) => UserType | null;
32
+ onAnalyticsEvent?: (event: string, params?: object) => void;
33
+ introMode?: "new_project" | "existing_project";
34
+ }
35
+ /**
36
+ * Use this hook to initialise the Collection Editor plugin.
37
+ * This is likely the only hook you will need to use.
38
+ * @param firebaseApp Firebase app where your project data lives.
39
+ * @param configPermissions
40
+ * @param reservedGroups
41
+ * @param extraView
42
+ * @param pathSuggestions
43
+ * @param getUser
44
+ * @param collectionInference
45
+ */
46
+ export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>({ collectionConfigController, introMode, configPermissions, reservedGroups, extraView, pathSuggestions, getUser, collectionInference, getData, onAnalyticsEvent }: CollectionConfigControllerProps<EC, UserType>): FireCMSPlugin<any, any, PersistedCollection>;
47
+ export declare function IntroWidget({ introMode }: {
48
+ introMode?: "new_project" | "existing_project";
49
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { CollectionsConfigController } from "./types/config_controller";
2
+ /**
3
+ * Use this hook to access the configuration controller.
4
+ * You can use it to get the list of collections, and to save/delete collections.
5
+ */
6
+ export declare const useCollectionsConfigController: () => CollectionsConfigController;
@@ -0,0 +1 @@
1
+ export declare function toArray<T>(input?: T | T[]): T[];
@@ -0,0 +1,3 @@
1
+ import { Properties, PropertiesOrBuilders, PropertyOrBuilder } from "@firecms/core";
2
+ export declare function editableProperty(property: PropertyOrBuilder | PropertyOrBuilder): boolean;
3
+ export declare function removeNonEditableProperties(properties: PropertiesOrBuilders<any>): Properties;
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@firecms/collection_editor",
3
+ "type": "module",
4
+ "version": "3.0.0-3.0.0-beta.4.pre.1.0",
5
+ "main": "./dist/index.umd.js",
6
+ "module": "./dist/index.es.js",
7
+ "types": "dist/index.d.ts",
8
+ "source": "src/index.ts",
9
+ "dependencies": {
10
+ "@firecms/data_import_export": "^3.0.0-3.0.0-beta.4.pre.1.0",
11
+ "@firecms/formex": "^3.0.0-3.0.0-beta.4.pre.1.0",
12
+ "@firecms/schema_inference": "^3.0.0-3.0.0-beta.4.pre.1.0",
13
+ "@firecms/ui": "^3.0.0-3.0.0-beta.4.pre.1.0",
14
+ "json5": "^2.2.3",
15
+ "prism-react-renderer": "^2.3.1"
16
+ },
17
+ "peerDependencies": {
18
+ "react": "^18.2.0",
19
+ "react-dom": "^18.2.0",
20
+ "react-router": "^6.22.2",
21
+ "react-router-dom": "^6.22.2"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "import": "./dist/index.es.js",
26
+ "require": "./dist/index.umd.js",
27
+ "types": "./dist/src/index.d.ts"
28
+ },
29
+ "./package.json": "./package.json"
30
+ },
31
+ "scripts": {
32
+ "dev": "vite",
33
+ "test": "jest",
34
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
35
+ "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
36
+ },
37
+ "eslintConfig": {
38
+ "extends": [
39
+ "react-app",
40
+ "react-app/jest"
41
+ ]
42
+ },
43
+ "browserslist": {
44
+ "production": [
45
+ ">0.2%",
46
+ "not dead",
47
+ "not op_mini all"
48
+ ],
49
+ "development": [
50
+ "last 1 chrome version",
51
+ "last 1 firefox version",
52
+ "last 1 safari version"
53
+ ]
54
+ },
55
+ "devDependencies": {
56
+ "@jest/globals": "^29.7.0",
57
+ "@types/react": "^18.2.67",
58
+ "@types/react-dom": "^18.2.22",
59
+ "@typescript-eslint/eslint-plugin": "^7.3.1",
60
+ "@typescript-eslint/parser": "^7.3.1",
61
+ "@vitejs/plugin-react": "^4.2.1",
62
+ "eslint": "^8.57.0",
63
+ "eslint-config-standard": "^17.1.0",
64
+ "eslint-plugin-import": "^2.29.1",
65
+ "eslint-plugin-n": "^16.6.2",
66
+ "eslint-plugin-promise": "^6.1.1",
67
+ "eslint-plugin-react": "^7.34.1",
68
+ "eslint-plugin-react-hooks": "^4.6.0",
69
+ "jest": "^29.7.0",
70
+ "react-router": "^6.22.3",
71
+ "react-router-dom": "^6.22.3",
72
+ "ts-jest": "^29.1.2",
73
+ "typescript": "^5.4.2",
74
+ "vite": "^5.1.6",
75
+ "vite-plugin-fonts": "^0.7.0"
76
+ },
77
+ "files": [
78
+ "dist",
79
+ "src"
80
+ ],
81
+ "publishConfig": {
82
+ "access": "public"
83
+ },
84
+ "gitHead": "bfe875c02132b5763ac0c9d8c8a885c791bb5a24"
85
+ }
@@ -0,0 +1,338 @@
1
+ import React, { PropsWithChildren, useCallback, useEffect } from "react";
2
+ import equal from "react-fast-compare"
3
+
4
+ import { CollectionsConfigController } from "./types/config_controller";
5
+ import {
6
+ Property,
7
+ useCustomizationController,
8
+ useNavigationController,
9
+ User,
10
+ useSnackbarController
11
+ } from "@firecms/core";
12
+ import { CollectionEditorDialog } from "./ui/collection_editor/CollectionEditorDialog";
13
+ import { useNavigate } from "react-router";
14
+ import { CollectionEditorController } from "./types/collection_editor_controller";
15
+ import { CollectionEditorPermissionsBuilder } from "./types/config_permissions";
16
+ import { CollectionInference } from "./types/collection_inference";
17
+ import { PropertyFormDialog } from "./ui/collection_editor/PropertyEditView";
18
+ import { PersistedCollection } from "./types/persisted_collection";
19
+
20
+ export const ConfigControllerContext = React.createContext<CollectionsConfigController>({} as any);
21
+ export const CollectionEditorContext = React.createContext<CollectionEditorController>({} as any);
22
+
23
+ export interface ConfigControllerProviderProps {
24
+ /**
25
+ * Controller for managing the collections' config.
26
+ */
27
+ collectionConfigController: CollectionsConfigController;
28
+
29
+ /**
30
+ * Callback used to infer the schema from the data.
31
+ */
32
+ collectionInference?: CollectionInference;
33
+
34
+ /**
35
+ * Use this builder to define the permissions for the configuration per user.
36
+ */
37
+ configPermissions?: CollectionEditorPermissionsBuilder;
38
+
39
+ /**
40
+ * Groups that cannot be used to create new collections.
41
+ */
42
+ reservedGroups?: string[];
43
+
44
+ extraView?: {
45
+ View: React.ComponentType<{
46
+ path: string
47
+ }>,
48
+ icon: React.ReactNode
49
+ };
50
+
51
+ pathSuggestions?: (path?: string) => Promise<string[]>;
52
+
53
+ getUser: (uid: string) => User | null
54
+
55
+ getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
56
+
57
+ onAnalyticsEvent?: (event: string, params?: object) => void;
58
+
59
+ }
60
+
61
+ export const ConfigControllerProvider = React.memo(
62
+ function ConfigControllerProvider({
63
+ children,
64
+ collectionConfigController,
65
+ configPermissions,
66
+ reservedGroups,
67
+ collectionInference,
68
+ extraView,
69
+ pathSuggestions,
70
+ getUser,
71
+ getData,
72
+ onAnalyticsEvent
73
+ }: PropsWithChildren<ConfigControllerProviderProps>) {
74
+
75
+ const navigation = useNavigationController();
76
+ const navigate = useNavigate();
77
+ const snackbarController = useSnackbarController();
78
+ const { propertyConfigs } = useCustomizationController();
79
+
80
+ const {
81
+ collections
82
+ } = navigation;
83
+ const existingPaths = (collections ?? []).map(col => col.path.trim().toLowerCase());
84
+
85
+ const [rootPathSuggestions, setRootPathSuggestions] = React.useState<string[] | undefined>();
86
+ useEffect(() => {
87
+ if (pathSuggestions) {
88
+ pathSuggestions().then((paths) => {
89
+ setRootPathSuggestions(paths.filter(p => !existingPaths.includes(p.trim().toLowerCase())));
90
+ });
91
+ }
92
+ }, [pathSuggestions]);
93
+
94
+ const [currentDialog, setCurrentDialog] = React.useState<{
95
+ isNewCollection: boolean,
96
+ parentCollection?: PersistedCollection,
97
+ editedCollectionId?: string,
98
+ fullPath?: string,
99
+ parentCollectionIds: string[],
100
+ initialValues?: {
101
+ path?: string,
102
+ group?: string,
103
+ name?: string
104
+ },
105
+ redirect: boolean
106
+ }>();
107
+
108
+ const [currentPropertyDialog, setCurrentPropertyDialog] = React.useState<{
109
+ propertyKey?: string,
110
+ property?: Property,
111
+ namespace?: string,
112
+ parentCollection?: PersistedCollection,
113
+ currentPropertiesOrder?: string[],
114
+ editedCollectionId: string,
115
+ fullPath?: string,
116
+ parentCollectionIds: string[],
117
+ collectionEditable: boolean;
118
+ }>();
119
+
120
+ const defaultConfigPermissions: CollectionEditorPermissionsBuilder = useCallback(() => ({
121
+ createCollections: true,
122
+ editCollections: true,
123
+ deleteCollections: true
124
+ }), []);
125
+
126
+ const editCollection = useCallback(({
127
+ id,
128
+ fullPath,
129
+ parentCollectionIds,
130
+ parentCollection
131
+ }: {
132
+ id?: string,
133
+ fullPath?: string,
134
+ parentCollectionIds: string[],
135
+ parentCollection?: PersistedCollection
136
+ }) => {
137
+ console.debug("Edit collection", id, fullPath, parentCollectionIds, parentCollection);
138
+ onAnalyticsEvent?.("edit_collection", { id, fullPath });
139
+ setCurrentDialog({
140
+ editedCollectionId: id,
141
+ fullPath,
142
+ parentCollectionIds,
143
+ isNewCollection: false,
144
+ parentCollection,
145
+ redirect: false
146
+ });
147
+ }, []);
148
+
149
+ const editProperty = useCallback(({
150
+ propertyKey,
151
+ property,
152
+ editedCollectionId,
153
+ currentPropertiesOrder,
154
+ parentCollectionIds,
155
+ collection
156
+ }: {
157
+ propertyKey?: string,
158
+ property?: Property,
159
+ currentPropertiesOrder?: string[],
160
+ editedCollectionId: string,
161
+ parentCollectionIds: string[],
162
+ collection: PersistedCollection,
163
+ }) => {
164
+ console.debug("Edit property", propertyKey, property, editedCollectionId, currentPropertiesOrder, parentCollectionIds, collection);
165
+ onAnalyticsEvent?.("edit_property", { propertyKey, editedCollectionId });
166
+ // namespace is all the path until the last dot
167
+ const namespace = propertyKey && propertyKey.includes(".")
168
+ ? propertyKey.substring(0, propertyKey.lastIndexOf("."))
169
+ : undefined;
170
+ const propertyKeyWithoutNamespace = propertyKey && propertyKey.includes(".")
171
+ ? propertyKey.substring(propertyKey.lastIndexOf(".") + 1)
172
+ : propertyKey;
173
+ setCurrentPropertyDialog({
174
+ propertyKey: propertyKeyWithoutNamespace,
175
+ property,
176
+ namespace,
177
+ currentPropertiesOrder,
178
+ editedCollectionId: editedCollectionId,
179
+ parentCollectionIds,
180
+ collectionEditable: collection?.editable ?? false
181
+ });
182
+ }, []);
183
+
184
+ const createCollection = React.useCallback(({
185
+ parentCollectionIds,
186
+ parentCollection,
187
+ initialValues,
188
+ redirect,
189
+ sourceClick
190
+ }: {
191
+ parentCollectionIds: string[],
192
+ parentCollection?: PersistedCollection
193
+ initialValues?: {
194
+ group?: string,
195
+ path?: string,
196
+ name?: string
197
+ },
198
+ redirect: boolean,
199
+ sourceClick?: string
200
+ }) => {
201
+ console.debug("Create collection", { parentCollectionIds, parentCollection, initialValues, redirect, sourceClick });
202
+ onAnalyticsEvent?.("create_collection", { parentCollectionIds, parentCollection, initialValues, redirect, sourceClick });
203
+ setCurrentDialog({
204
+ isNewCollection: true,
205
+ parentCollectionIds,
206
+ parentCollection,
207
+ initialValues,
208
+ redirect
209
+ });
210
+ }, []);
211
+
212
+ const getPathSuggestions = !pathSuggestions
213
+ ? undefined
214
+ : (path?: string) => {
215
+ if (!path && rootPathSuggestions)
216
+ return Promise.resolve(rootPathSuggestions);
217
+ else {
218
+ return pathSuggestions?.(path);
219
+ }
220
+ }
221
+
222
+ return (
223
+ <ConfigControllerContext.Provider value={collectionConfigController}>
224
+ <CollectionEditorContext.Provider
225
+ value={{
226
+ editCollection,
227
+ createCollection,
228
+ editProperty,
229
+ configPermissions: configPermissions ?? defaultConfigPermissions,
230
+ rootPathSuggestions
231
+ }}>
232
+
233
+ {children}
234
+
235
+ <CollectionEditorDialog
236
+ open={Boolean(currentDialog)}
237
+ configController={collectionConfigController}
238
+ isNewCollection={false}
239
+ collectionInference={collectionInference}
240
+ {...currentDialog}
241
+ getData={getData}
242
+ reservedGroups={reservedGroups}
243
+ extraView={extraView}
244
+ pathSuggestions={getPathSuggestions}
245
+ getUser={getUser}
246
+ handleClose={(collection) => {
247
+ if (currentDialog?.redirect) {
248
+ if (collection && currentDialog?.isNewCollection && !currentDialog.parentCollectionIds.length) {
249
+ const url = navigation.buildUrlCollectionPath(collection.id ?? collection.path);
250
+ navigate(url);
251
+ }
252
+ }
253
+ setCurrentDialog(undefined);
254
+ }}/>
255
+
256
+ {/* Used for editing properties*/}
257
+ <PropertyFormDialog
258
+ open={Boolean(currentPropertyDialog)}
259
+ includeIdAndName={true}
260
+ existingProperty={Boolean(currentPropertyDialog?.propertyKey)}
261
+ autoUpdateId={!currentPropertyDialog ? false : !currentPropertyDialog?.propertyKey}
262
+ autoOpenTypeSelect={!currentPropertyDialog ? false : !currentPropertyDialog?.propertyKey}
263
+ inArray={false}
264
+ collectionEditable={currentPropertyDialog?.collectionEditable ?? false}
265
+ getData={getData && currentPropertyDialog?.editedCollectionId
266
+ ? () => {
267
+ console.debug("get data for property", currentPropertyDialog?.editedCollectionId);
268
+ const resolvedPath = navigation.resolveAliasesFrom(currentPropertyDialog.editedCollectionId!)
269
+ return getData(resolvedPath, []);
270
+ }
271
+ : undefined}
272
+ onPropertyChanged={({
273
+ id,
274
+ property
275
+ }) => {
276
+ if (!currentPropertyDialog) return;
277
+ if (!id) return;
278
+ const newProperty = !(currentPropertyDialog.propertyKey);
279
+ return collectionConfigController.saveProperty({
280
+ path: currentPropertyDialog?.editedCollectionId,
281
+ property,
282
+ propertyKey: id,
283
+ newPropertiesOrder: newProperty && currentPropertyDialog.currentPropertiesOrder ? [...currentPropertyDialog.currentPropertiesOrder, id] : undefined,
284
+ namespace: currentPropertyDialog.namespace,
285
+ parentCollectionIds: currentPropertyDialog.parentCollectionIds
286
+ })
287
+ .catch((e) => {
288
+ console.error(e);
289
+ snackbarController.open({
290
+ type: "error",
291
+ message: "Error persisting property: " + (e.message ?? "Details in the console")
292
+ });
293
+ return false;
294
+ });
295
+ }}
296
+ onPropertyChangedImmediate={false}
297
+ onDelete={() => {
298
+ if (!currentPropertyDialog?.propertyKey) return;
299
+ const newPropertiesOrder = currentPropertyDialog?.currentPropertiesOrder?.filter(p => p !== currentPropertyDialog?.propertyKey);
300
+ return collectionConfigController.deleteProperty({
301
+ path: currentPropertyDialog?.editedCollectionId,
302
+ propertyKey: currentPropertyDialog?.propertyKey,
303
+ namespace: currentPropertyDialog?.namespace,
304
+ newPropertiesOrder,
305
+ parentCollectionIds: currentPropertyDialog?.parentCollectionIds
306
+ })
307
+ .then(() => {
308
+ setCurrentPropertyDialog(undefined);
309
+ }).catch((e) => {
310
+ console.error(e);
311
+ snackbarController.open({
312
+ type: "error",
313
+ message: "Error deleting property: " + (e.message ?? "Details in the console")
314
+ });
315
+ return false;
316
+ });
317
+ }}
318
+ onError={() => {
319
+ }}
320
+ onOkClicked={() => {
321
+ setCurrentPropertyDialog(undefined);
322
+ }}
323
+ onCancel={() => {
324
+ setCurrentPropertyDialog(undefined);
325
+ }}
326
+ initialErrors={{}}
327
+ forceShowErrors={false}
328
+ existingPropertyKeys={[]}
329
+ allowDataInference={true}
330
+ propertyConfigs={propertyConfigs}
331
+ property={currentPropertyDialog?.property}
332
+ propertyKey={currentPropertyDialog?.propertyKey}/>
333
+
334
+ </CollectionEditorContext.Provider>
335
+
336
+ </ConfigControllerContext.Provider>
337
+ );
338
+ }, equal);
package/src/index.ts ADDED
@@ -0,0 +1,35 @@
1
+ export {
2
+ useCollectionEditorPlugin
3
+ } from "./useCollectionEditorPlugin";
4
+
5
+ export {
6
+ useCollectionEditorController
7
+ } from "./useCollectionEditorController";
8
+ export {
9
+ useCollectionsConfigController
10
+ } from "./useCollectionsConfigController";
11
+
12
+ export {
13
+ editableProperty, removeNonEditableProperties
14
+ } from "./utils/entities";
15
+
16
+ export type {
17
+ CollectionsConfigController, DeleteCollectionParams, SaveCollectionParams, UpdateCollectionParams
18
+ } from "./types/config_controller";
19
+ export type {
20
+ CollectionEditorController
21
+ } from "./types/collection_editor_controller";
22
+ export type {
23
+ CollectionEditorPermissions, CollectionEditorPermissionsBuilder
24
+ } from "./types/config_permissions";
25
+ export type {
26
+ PersistedCollection
27
+ } from "./types/persisted_collection";
28
+
29
+ export type {
30
+ CollectionInference
31
+ } from "./types/collection_inference";
32
+
33
+ export { MissingReferenceWidget } from "./ui/MissingReferenceWidget";
34
+
35
+ export * from "./ui/collection_editor/util";
@@ -0,0 +1,43 @@
1
+ import { CollectionEditorPermissionsBuilder } from "./config_permissions";
2
+ import { Property } from "@firecms/core";
3
+ import { PersistedCollection } from "./persisted_collection";
4
+
5
+ /**
6
+ * Controller to open the collection editor dialog.
7
+ * @group Hooks and utilities
8
+ */
9
+ export interface CollectionEditorController {
10
+
11
+ editCollection: (props: {
12
+ id?: string,
13
+ fullPath?: string,
14
+ parentCollectionIds: string[],
15
+ parentCollection?: PersistedCollection
16
+ }) => void;
17
+
18
+ createCollection: (props: {
19
+ initialValues?: {
20
+ group?: string,
21
+ path?: string,
22
+ name?: string
23
+ },
24
+ parentCollectionIds: string[],
25
+ parentCollection?: PersistedCollection,
26
+ redirect: boolean,
27
+ sourceClick?: string
28
+ }) => void;
29
+
30
+ editProperty: (props: {
31
+ propertyKey?: string,
32
+ property?: Property,
33
+ currentPropertiesOrder?: string[],
34
+ editedCollectionId: string,
35
+ parentCollectionIds: string[],
36
+ collection: PersistedCollection
37
+ }) => void;
38
+
39
+ configPermissions: CollectionEditorPermissionsBuilder;
40
+
41
+ rootPathSuggestions?: string[];
42
+
43
+ }
@@ -0,0 +1,3 @@
1
+ import { EntityCollection } from "@firecms/core";
2
+
3
+ export type CollectionInference = (path: string, collectionGroup: boolean, parentCollectionIds: string[]) => Promise<Partial<EntityCollection> | null>;
@@ -0,0 +1,60 @@
1
+ import { CMSType, Property } from "@firecms/core";
2
+ import { PersistedCollection } from "./persisted_collection";
3
+
4
+ /**
5
+ * Use this controller to access the configuration that is stored externally,
6
+ * and not defined in code.
7
+ */
8
+ export interface CollectionsConfigController {
9
+
10
+ loading: boolean;
11
+
12
+ collections?: PersistedCollection[];
13
+
14
+ getCollection: (id: string) => PersistedCollection;
15
+
16
+ saveCollection: <M extends { [Key: string]: CMSType }>(params: SaveCollectionParams<M>) => Promise<void>;
17
+ updateCollection: <M extends { [Key: string]: CMSType }>(params: UpdateCollectionParams<M>) => Promise<void>;
18
+
19
+ saveProperty: (params: SavePropertyParams) => Promise<void>;
20
+ deleteProperty: (params: DeletePropertyParams) => Promise<void>;
21
+
22
+ deleteCollection: (props: DeleteCollectionParams) => Promise<void>;
23
+
24
+ }
25
+
26
+ export type UpdateCollectionParams<M extends Record<string, any>> = {
27
+ id: string,
28
+ collectionData: Partial<PersistedCollection<M>>,
29
+ previousId?: string,
30
+ parentCollectionIds?: string[]
31
+ }
32
+
33
+ export type SaveCollectionParams<M extends Record<string, any>> = {
34
+ id: string,
35
+ collectionData: PersistedCollection<M>,
36
+ previousId?: string,
37
+ parentCollectionIds?: string[]
38
+ }
39
+
40
+ export type SavePropertyParams = {
41
+ path: string,
42
+ propertyKey: string,
43
+ namespace?: string,
44
+ newPropertiesOrder?: string[],
45
+ property: Property,
46
+ parentCollectionIds?: string[]
47
+ }
48
+
49
+ export type DeletePropertyParams = {
50
+ path: string,
51
+ propertyKey: string,
52
+ namespace?: string,
53
+ newPropertiesOrder?: string[],
54
+ parentCollectionIds?: string[]
55
+ }
56
+
57
+ export type DeleteCollectionParams = {
58
+ id: string,
59
+ parentCollectionIds?: string[]
60
+ }