@firecms/collection_editor 3.0.0-alpha.34 → 3.0.0-alpha.35

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 (26) hide show
  1. package/dist/index.es.js +1586 -1554
  2. package/dist/index.es.js.map +1 -1
  3. package/dist/index.umd.js +1 -1
  4. package/dist/index.umd.js.map +1 -1
  5. package/dist/types/collection_editor_controller.d.ts +1 -1
  6. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
  7. package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
  8. package/dist/ui/collection_editor/templates/blog_template.d.ts +1 -1
  9. package/package.json +5 -5
  10. package/src/types/collection_editor_controller.tsx +1 -1
  11. package/src/ui/collection_editor/CollectionDetailsForm.tsx +52 -1
  12. package/src/ui/collection_editor/CollectionEditorDialog.tsx +40 -26
  13. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +9 -9
  14. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +3 -1
  15. package/src/ui/collection_editor/PropertyEditView.tsx +6 -3
  16. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +1 -1
  17. package/src/ui/collection_editor/properties/MapPropertyField.tsx +1 -1
  18. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +1 -0
  19. package/src/ui/collection_editor/properties/StringPropertyField.tsx +1 -7
  20. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
  21. package/src/ui/collection_editor/templates/blog_template.ts +3 -4
  22. package/src/ui/collection_editor/templates/products_template.ts +3 -5
  23. package/src/ui/collection_editor/templates/users_template.ts +3 -4
  24. package/src/useCollectionEditorPlugin.tsx +1 -2
  25. package/dist/utils/join_collections.d.ts +0 -13
  26. package/src/utils/join_collections.ts +0 -113
@@ -3,7 +3,7 @@ import { EntityCollection, Property } from "@firecms/core";
3
3
  import { PersistedCollection } from "./persisted_collection";
4
4
  /**
5
5
  * Controller to open the collection editor dialog.
6
- * @category Hooks and utilities
6
+ * @group Hooks and utilities
7
7
  */
8
8
  export interface CollectionEditorController {
9
9
  editCollection: (props: {
@@ -1,5 +1,5 @@
1
1
  export declare function StringPropertyField({ widgetId, disabled, showErrors }: {
2
- widgetId: "text_field" | "multiline" | "markdown" | "url" | "email";
2
+ widgetId: "text_field" | "multiline" | "markdown" | "email";
3
3
  disabled: boolean;
4
4
  showErrors: boolean;
5
5
  }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ export declare function UrlPropertyField({ disabled, showErrors }: {
2
+ disabled: boolean;
3
+ showErrors: boolean;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -1,2 +1,2 @@
1
- import { EntityCollection } from "@firecms/firebase";
1
+ import { EntityCollection } from "@firecms/core";
2
2
  export declare const blogCollectionTemplate: EntityCollection;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firecms/collection_editor",
3
- "version": "3.0.0-alpha.34",
3
+ "version": "3.0.0-alpha.35",
4
4
  "main": "./dist/index.umd.js",
5
5
  "module": "./dist/index.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,8 +14,8 @@
14
14
  "./package.json": "./package.json"
15
15
  },
16
16
  "dependencies": {
17
- "@firecms/data_import": "^3.0.0-alpha.34",
18
- "@firecms/schema_inference": "^3.0.0-alpha.34",
17
+ "@firecms/data_import": "^3.0.0-alpha.35",
18
+ "@firecms/schema_inference": "^3.0.0-alpha.35",
19
19
  "json5": "^2.2.3",
20
20
  "prism-react-renderer": "^2.3.0"
21
21
  },
@@ -67,7 +67,7 @@
67
67
  "react-router": "^6.17.0",
68
68
  "react-router-dom": "^6.17.0",
69
69
  "ts-jest": "^29.1.1",
70
- "typescript": "^5.2.2",
70
+ "typescript": "^5.3.0",
71
71
  "vite": "^4.5.0",
72
72
  "vite-plugin-fonts": "^0.7.0"
73
73
  },
@@ -78,5 +78,5 @@
78
78
  "publishConfig": {
79
79
  "access": "public"
80
80
  },
81
- "gitHead": "97fea385cfd4a0ab9301d45d50e1d145021c1df5"
81
+ "gitHead": "f0f3ef616f13176166c193f98b8198f7fa53a1d2"
82
82
  }
@@ -4,7 +4,7 @@ import { PersistedCollection } from "./persisted_collection";
4
4
 
5
5
  /**
6
6
  * Controller to open the collection editor dialog.
7
- * @category Hooks and utilities
7
+ * @group Hooks and utilities
8
8
  */
9
9
  export interface CollectionEditorController {
10
10
 
@@ -109,6 +109,18 @@ export function CollectionDetailsForm({
109
109
 
110
110
  const isSubcollection = !!parentCollection;
111
111
 
112
+ let customIdValue: "true" | "false" | "optional" | "code_defined" | undefined;
113
+ if (customIdValue) {
114
+ if (typeof values.customId === "object") {
115
+ customIdValue = "code_defined";
116
+ } else if (values.customId === true) {
117
+ customIdValue = "true";
118
+ } else if (values.customId === false) {
119
+ customIdValue = "false";
120
+ } else if (values.customId === "optional") {
121
+ customIdValue = "optional";
122
+ }
123
+ }
112
124
  return (
113
125
  <div className={"overflow-auto my-auto"}>
114
126
  <Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
@@ -117,7 +129,7 @@ export function CollectionDetailsForm({
117
129
  <div
118
130
  className="flex flex-row py-2 pt-3 items-center">
119
131
  <Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
120
- {isNewCollection ? "New collection" : `${values.name} collection`}
132
+ {isNewCollection ? "New collection" : `${values?.name} collection`}
121
133
  </Typography>
122
134
  <Tooltip title={"Change icon"}>
123
135
  <IconButton
@@ -269,6 +281,45 @@ export function CollectionDetailsForm({
269
281
  ))}
270
282
  </Select>
271
283
  </div>
284
+ <div className={"col-span-12"}>
285
+ <Select
286
+ name="customId"
287
+ label="ID generation"
288
+ position={"item-aligned"}
289
+ disabled={customIdValue === "code_defined"}
290
+ onValueChange={(v) => {
291
+ if (v === "code_defined")
292
+ throw new Error("This should not happen");
293
+ else if (v === "true")
294
+ setFieldValue("customId", true);
295
+ else if (v === "false")
296
+ setFieldValue("customId", false);
297
+ else if (v === "optional")
298
+ setFieldValue("customId", "optional");
299
+ }}
300
+ value={customIdValue ?? ""}
301
+ renderValue={(value: any) => {
302
+ if (value === "code_defined")
303
+ return "Code defined";
304
+ else if (value === "true")
305
+ return "Users must define an ID";
306
+ else if (value === "optional")
307
+ return "Users can define an ID, but it is not required";
308
+ else
309
+ return "ID is generated automatically";
310
+ }}
311
+ >
312
+ <SelectItem value={"false"}>
313
+ ID is generated automatically
314
+ </SelectItem>
315
+ <SelectItem value={"true"}>
316
+ Users must define an ID
317
+ </SelectItem>
318
+ <SelectItem value={"optional"}>
319
+ Users can define an ID, but it is not required
320
+ </SelectItem>
321
+ </Select>
322
+ </div>
272
323
  <div className={"col-span-12"}>
273
324
  <BooleanSwitchWithLabel
274
325
  position={"start"}
@@ -266,39 +266,48 @@ export function CollectionEditorDialogInternal<M extends {
266
266
 
267
267
  const inferCollectionFromData = useCallback(async (newCollection: PersistedCollection<M>) => {
268
268
 
269
- if (!doCollectionInference) {
270
- setCollection(newCollection);
271
- return Promise.resolve(newCollection);
272
- }
269
+ try {
270
+ if (!doCollectionInference) {
271
+ setCollection(newCollection);
272
+ return Promise.resolve(newCollection);
273
+ }
273
274
 
274
- setCurrentView("loading");
275
+ setCurrentView("loading");
275
276
 
276
- const inferredCollection = await doCollectionInference?.(newCollection);
277
+ const inferredCollection = await doCollectionInference?.(newCollection);
277
278
 
278
- if (!inferredCollection) {
279
- setCollection(newCollection);
280
- return Promise.resolve(newCollection);
281
- }
282
- const values = {
283
- ...(newCollection ?? {}),
284
- };
279
+ if (!inferredCollection) {
280
+ setCollection(newCollection);
281
+ return Promise.resolve(newCollection);
282
+ }
283
+ const values = {
284
+ ...(newCollection ?? {}),
285
+ };
285
286
 
286
- if (Object.keys(inferredCollection.properties ?? {}).length > 0) {
287
- values.properties = inferredCollection.properties as Properties<M>;
288
- values.propertiesOrder = inferredCollection.propertiesOrder as Extract<keyof M, string>[];
289
- }
287
+ if (Object.keys(inferredCollection.properties ?? {}).length > 0) {
288
+ values.properties = inferredCollection.properties as Properties<M>;
289
+ values.propertiesOrder = inferredCollection.propertiesOrder as Extract<keyof M, string>[];
290
+ }
290
291
 
291
- if (!values.propertiesOrder) {
292
- values.propertiesOrder = Object.keys(values.properties) as Extract<keyof M, string>[];
292
+ if (!values.propertiesOrder) {
293
+ values.propertiesOrder = Object.keys(values.properties) as Extract<keyof M, string>[];
294
+ return values;
295
+ }
296
+
297
+ setCollection(values);
298
+ console.log("Inferred collection", {
299
+ newCollection: newCollection ?? {},
300
+ values
301
+ });
293
302
  return values;
303
+ } catch (e:any) {
304
+ console.error(e);
305
+ snackbarController.open({
306
+ type: "error",
307
+ message: "Error inferring collection: " + (e.message ?? "Details in the console")
308
+ });
309
+ return newCollection;
294
310
  }
295
-
296
- setCollection(values);
297
- console.log("Inferred collection", {
298
- newCollection: newCollection ?? {},
299
- values
300
- });
301
- return values;
302
311
  }, [parentPathSegments, doCollectionInference]);
303
312
 
304
313
  const onSubmit = (newCollectionState: PersistedCollection<M>, formikHelpers: FormikHelpers<PersistedCollection<M>>) => {
@@ -390,6 +399,11 @@ export function CollectionEditorDialogInternal<M extends {
390
399
  submitCount
391
400
  }) => {
392
401
 
402
+ console.debug({
403
+ valuesPath: values.path,
404
+ editedCollectionPath,
405
+ fullPath
406
+ })
393
407
  const path = values.path ?? editedCollectionPath;
394
408
  const updatedFullPath = fullPath?.includes("/") ? fullPath?.split("/").slice(0, -1).join("/") + "/" + path : path; // TODO: this path is wrong
395
409
  const resolvedPath = navigation.resolveAliasesFrom(updatedFullPath);
@@ -312,7 +312,7 @@ export function CollectionPropertiesEditorForm({
312
312
  placeholder={"Collection name"}
313
313
  size={"small"}
314
314
  required
315
- error={Boolean(errors.name)}/>
315
+ error={Boolean(errors?.name)}/>
316
316
 
317
317
  {owner &&
318
318
  <Typography variant={"body2"}
@@ -327,14 +327,14 @@ export function CollectionPropertiesEditorForm({
327
327
  </div>}
328
328
 
329
329
  <div className="ml-1 mt-2 flex flex-row gap-2">
330
- <Tooltip title={"Get the code for this collection"}>
331
- <IconButton
332
- variant={"filled"}
333
- disabled={inferringProperties}
334
- onClick={() => setCodeDialogOpen(true)}>
335
- <CodeIcon/>
336
- </IconButton>
337
- </Tooltip>
330
+ {/*<Tooltip title={"Get the code for this collection"}>*/}
331
+ {/* <IconButton*/}
332
+ {/* variant={"filled"}*/}
333
+ {/* disabled={inferringProperties}*/}
334
+ {/* onClick={() => setCodeDialogOpen(true)}>*/}
335
+ {/* <CodeIcon/>*/}
336
+ {/* </IconButton>*/}
337
+ {/*</Tooltip>*/}
338
338
  {inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}>
339
339
  <IconButton
340
340
  variant={"filled"}
@@ -2,7 +2,9 @@ import { Button, Dialog, DialogActions, DialogContent, Typography, useFireCMSCon
2
2
  import React from "react";
3
3
 
4
4
  export function EntityCustomViewsSelectDialog({ open, onClose }: { open: boolean, onClose: (selectedViewKey?: string) => void }) {
5
- const { entityViews } = useFireCMSContext();
5
+ const {
6
+ entityViews,
7
+ } = useFireCMSContext();
6
8
 
7
9
  return <Dialog
8
10
  maxWidth={"md"}
@@ -20,7 +20,7 @@ import {
20
20
  isPropertyBuilder,
21
21
  mergeDeep,
22
22
  Property,
23
- PropertyConfig, PropertyOrBuilder,
23
+ PropertyConfig,
24
24
  Select,
25
25
  toSnakeCase,
26
26
  Typography
@@ -41,6 +41,7 @@ import { editableProperty } from "../../utils/entities";
41
41
  import { KeyValuePropertyField } from "./properties/KeyValuePropertyField";
42
42
  import { updatePropertyFromWidget } from "./utils/update_property_for_widget";
43
43
  import { PropertySelectItem } from "./PropertySelectItem";
44
+ import { UrlPropertyField } from "./properties/UrlPropertyField";
44
45
 
45
46
  export type PropertyWithId = Property & {
46
47
  id?: string
@@ -106,7 +107,6 @@ export const PropertyForm = React.memo(
106
107
  } as PropertyWithId;
107
108
 
108
109
  const disabled = (Boolean(property && !editableProperty(property)) && !collectionEditable);
109
- console.log("PropertyForm disabled", disabled)
110
110
 
111
111
  const lastSubmittedProperty = useRef<OnPropertyChangedParams | undefined>(property ? {
112
112
  id: propertyKey,
@@ -362,12 +362,15 @@ function PropertyEditView({
362
362
  if (selectedFieldConfigId === "text_field" ||
363
363
  selectedFieldConfigId === "multiline" ||
364
364
  selectedFieldConfigId === "markdown" ||
365
- selectedFieldConfigId === "url" ||
366
365
  selectedFieldConfigId === "email") {
367
366
  childComponent =
368
367
  <StringPropertyField widgetId={selectedFieldConfigId}
369
368
  disabled={disabled}
370
369
  showErrors={showErrors}/>;
370
+ } else if (selectedFieldConfigId === "url") {
371
+ childComponent =
372
+ <UrlPropertyField disabled={disabled}
373
+ showErrors={showErrors}/>;
371
374
  } else if (selectedFieldConfigId === "select" ||
372
375
  selectedFieldConfigId === "number_select") {
373
376
  childComponent = <EnumPropertyField
@@ -179,7 +179,7 @@ export function SubcollectionsEditTab({
179
179
 
180
180
  {totalEntityViews === 0 &&
181
181
  <InfoLabel>
182
- COMING SOON: You can define your own custom views by uploading it with the CLI
182
+ <b>COMING SOON</b> Define your own custom views by uploading it with the CLI
183
183
  </InfoLabel>
184
184
  }
185
185
 
@@ -105,7 +105,7 @@ export function MapPropertyField({ disabled, getData, allowDataInference, custom
105
105
 
106
106
  {empty &&
107
107
  <Typography variant={"label"}
108
- className="h-full flex items-center justify-center p-2">
108
+ className="h-full flex items-center justify-center p-4">
109
109
  Add the first property to this group
110
110
  </Typography>}
111
111
  </Paper>
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { Field, getIn, useFormikContext } from "formik";
3
3
  import {
4
4
  CircularProgress,
5
+ EntityCollection,
5
6
  getIconForView,
6
7
  NumberProperty,
7
8
  Select,
@@ -10,7 +10,7 @@ export function StringPropertyField({
10
10
  disabled,
11
11
  showErrors
12
12
  }: {
13
- widgetId: "text_field" | "multiline" | "markdown" | "url" | "email";
13
+ widgetId: "text_field" | "multiline" | "markdown" | "email";
14
14
  disabled: boolean;
15
15
  showErrors: boolean;
16
16
  }) {
@@ -51,12 +51,6 @@ export function StringPropertyField({
51
51
  trim={true}
52
52
  uppercase={true}
53
53
  showErrors={showErrors}/>}
54
- {widgetId === "url" &&
55
- <StringPropertyValidation disabled={disabled}
56
- max={true}
57
- min={true}
58
- trim={true}
59
- showErrors={showErrors}/>}
60
54
 
61
55
  {widgetId === "email" &&
62
56
  <StringPropertyValidation disabled={disabled}
@@ -0,0 +1,89 @@
1
+ import React from "react";
2
+ import { StringPropertyValidation } from "./validation/StringPropertyValidation";
3
+ import { ValidationPanel } from "./validation/ValidationPanel";
4
+ import { getIn, useFormikContext } from "formik";
5
+
6
+ import { Select, SelectItem, TextField } from "@firecms/core";
7
+
8
+ export function UrlPropertyField({
9
+ disabled,
10
+ showErrors
11
+ }: {
12
+ disabled: boolean;
13
+ showErrors: boolean;
14
+ }) {
15
+
16
+ const { values, setFieldValue } = useFormikContext();
17
+
18
+ const urlValue = getIn(values, "url");
19
+
20
+ return (
21
+ <>
22
+ <div className={"col-span-12"}>
23
+
24
+ <Select
25
+ disabled={disabled}
26
+ position={"item-aligned"}
27
+ onValueChange={(value: string) => {
28
+ if (value === "[NONE]")
29
+ setFieldValue("url", true);
30
+ else
31
+ setFieldValue("url", value);
32
+ }}
33
+ label={"Preview type"}
34
+ renderValue={(value: string) => {
35
+ switch (value) {
36
+ case "image":
37
+ return "Image";
38
+ case "video":
39
+ return "Video";
40
+ case "audio":
41
+ return "Audio";
42
+ default:
43
+ return "Display URL";
44
+ }
45
+ }}
46
+ value={urlValue ?? "[NONE]"}>
47
+ <SelectItem value={"[NONE]"}>
48
+ Display URL
49
+ </SelectItem>
50
+ <SelectItem value={"image"}>
51
+ Image
52
+ </SelectItem>
53
+ <SelectItem value={"video"}>
54
+ Video
55
+ </SelectItem>
56
+ <SelectItem value={"audio"}>
57
+ Audio
58
+ </SelectItem>
59
+ </Select>
60
+ </div>
61
+
62
+ <div className={"col-span-12"}>
63
+
64
+ <ValidationPanel>
65
+
66
+ <StringPropertyValidation disabled={disabled}
67
+ max={true}
68
+ min={true}
69
+ trim={true}
70
+ showErrors={showErrors}/>
71
+
72
+ </ValidationPanel>
73
+
74
+ </div>
75
+
76
+ <div className={"col-span-12"}>
77
+
78
+ <TextField name={"defaultValue"}
79
+ disabled={disabled}
80
+ onChange={(e: any) => {
81
+ setFieldValue("defaultValue", e.target.value === "" ? undefined : e.target.value);
82
+ }}
83
+ label={"Default value"}
84
+ value={getIn(values, "defaultValue") ?? ""}/>
85
+
86
+ </div>
87
+ </>
88
+ );
89
+ }
@@ -1,14 +1,13 @@
1
- import { EntityCollection } from "@firecms/firebase";
1
+ import { EntityCollection, makePropertiesEditable } from "@firecms/core";
2
2
 
3
3
  export const blogCollectionTemplate:EntityCollection = {
4
4
  path: "blog",
5
5
  name: "Blog",
6
6
  singularName: "Blog entry",
7
- group: "Content",
8
7
  icon: "article",
9
8
  description: "A collection of blog entries",
10
9
  defaultSize: "l",
11
- properties: {
10
+ properties: makePropertiesEditable({
12
11
  name: {
13
12
  name: "Name",
14
13
  validation: { required: true },
@@ -108,7 +107,7 @@ export const blogCollectionTemplate:EntityCollection = {
108
107
  previewAsTag: true
109
108
  }
110
109
  }
111
- },
110
+ }),
112
111
  initialFilter: {
113
112
  status: ["==", "published"]
114
113
  }
@@ -1,13 +1,12 @@
1
- import { EntityCollection } from "@firecms/core";
1
+ import { EntityCollection, makePropertiesEditable } from "@firecms/core";
2
2
 
3
3
  export const productsCollectionTemplate: EntityCollection = {
4
4
  path: "products",
5
5
  name: "Products",
6
6
  singularName: "Product",
7
- group: "Main",
8
7
  icon: "shopping_cart",
9
8
  description: "List of the products currently sold in your shop",
10
- properties: {
9
+ properties: makePropertiesEditable({
11
10
  name: {
12
11
  dataType: "string",
13
12
  name: "Name",
@@ -84,6 +83,5 @@ export const productsCollectionTemplate: EntityCollection = {
84
83
  name: "Added on",
85
84
  autoValue: "on_create"
86
85
  }
87
- }
88
-
86
+ })
89
87
  };
@@ -1,13 +1,12 @@
1
- import { EntityCollection } from "@firecms/core";
1
+ import { EntityCollection, makePropertiesEditable } from "@firecms/core";
2
2
 
3
3
  export const usersCollectionTemplate: EntityCollection = {
4
4
  path: "users",
5
5
  name: "Users",
6
6
  singularName: "User",
7
- group: "Main",
8
7
  description: "Registered users in the app/web",
9
8
  icon: "person",
10
- properties: {
9
+ properties: makePropertiesEditable({
11
10
  displayName: {
12
11
  name: "Display name",
13
12
  dataType: "string"
@@ -30,5 +29,5 @@ export const usersCollectionTemplate: EntityCollection = {
30
29
  dataType: "string",
31
30
  url: "image"
32
31
  }
33
- },
32
+ }),
34
33
  };
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback } from "react";
2
- import { EntityCollection, FireCMSPlugin, makePropertiesEditable, User } from "@firecms/core";
2
+ import { EntityCollection, FireCMSPlugin, joinCollectionLists, makePropertiesEditable, User } from "@firecms/core";
3
3
  import { ConfigControllerProvider } from "./ConfigControllerProvider";
4
4
  import { CollectionEditorPermissionsBuilder } from "./types/config_permissions";
5
5
  import { EditorCollectionAction } from "./ui/EditorCollectionAction";
@@ -9,7 +9,6 @@ import { PersistedCollection } from "./types/persisted_collection";
9
9
  import { CollectionInference } from "./types/collection_inference";
10
10
  import { CollectionsConfigController } from "./types/config_controller";
11
11
  import { RootCollectionSuggestions } from "./ui/RootCollectionSuggestions";
12
- import { joinCollectionLists } from "./utils/join_collections";
13
12
  import { CollectionViewHeaderAction } from "./ui/CollectionViewHeaderAction";
14
13
  import { PropertyAddColumnComponent } from "./ui/PropertyAddColumnComponent";
15
14
 
@@ -1,13 +0,0 @@
1
- import { EntityCollection } from "@firecms/core";
2
- /**
3
- *
4
- * @param storedCollections
5
- * @param codedCollections
6
- */
7
- export declare function joinCollectionLists(storedCollections: EntityCollection[], codedCollections: EntityCollection[] | undefined): EntityCollection[];
8
- /**
9
- *
10
- * @param target
11
- * @param source
12
- */
13
- export declare function mergeCollection(target: EntityCollection, source: EntityCollection): EntityCollection;