@firecms/collection_editor 3.0.0-canary.102 → 3.0.0-canary.103

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.
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import { CollectionEditorPermissionsBuilder } from "./config_permissions";
2
3
  import { Entity, Property } from "@firecms/core";
3
4
  import { PersistedCollection } from "./persisted_collection";
@@ -35,4 +36,13 @@ export interface CollectionEditorController {
35
36
  }) => void;
36
37
  configPermissions: CollectionEditorPermissionsBuilder;
37
38
  getPathSuggestions?: (path: string) => Promise<string[]>;
39
+ components?: {
40
+ /**
41
+ * Custom component to render the database field
42
+ */
43
+ DatabaseField?: React.ComponentType<{
44
+ databaseId?: string;
45
+ onDatabaseIdUpdate: (databaseId: string) => void;
46
+ }>;
47
+ };
38
48
  }
@@ -0,0 +1,4 @@
1
+ export declare function MarkdownPropertyField({ disabled, showErrors }: {
2
+ disabled: boolean;
3
+ showErrors: boolean;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  export declare function StringPropertyField({ widgetId, disabled, showErrors }: {
2
- widgetId: "text_field" | "multiline" | "markdown" | "email";
2
+ widgetId: "text_field" | "multiline" | "email";
3
3
  disabled: boolean;
4
4
  showErrors: boolean;
5
5
  }): import("react/jsx-runtime").JSX.Element;
@@ -30,6 +30,15 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
30
30
  getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
31
31
  getUser?: (uid: string) => UserType | null;
32
32
  onAnalyticsEvent?: (event: string, params?: object) => void;
33
+ components?: {
34
+ /**
35
+ * Custom component to render the database field
36
+ */
37
+ DatabaseField?: React.ComponentType<{
38
+ databaseId?: string;
39
+ onDatabaseIdUpdate: (databaseId: string) => void;
40
+ }>;
41
+ };
33
42
  }
34
43
  /**
35
44
  * Use this hook to initialise the Collection Editor plugin.
@@ -42,5 +51,5 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
42
51
  * @param getUser
43
52
  * @param collectionInference
44
53
  */
45
- export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>({ collectionConfigController, configPermissions, reservedGroups, extraView, getPathSuggestions, getUser, collectionInference, getData, onAnalyticsEvent }: CollectionConfigControllerProps<EC, UserType>): FireCMSPlugin<any, any, PersistedCollection>;
54
+ export declare function useCollectionEditorPlugin<EC extends PersistedCollection = PersistedCollection, UserType extends User = User>({ collectionConfigController, configPermissions, reservedGroups, extraView, getPathSuggestions, getUser, collectionInference, getData, onAnalyticsEvent, components }: CollectionConfigControllerProps<EC, UserType>): FireCMSPlugin<any, any, PersistedCollection>;
46
55
  export declare function IntroWidget({}: {}): import("react/jsx-runtime").JSX.Element | null;
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@firecms/collection_editor",
3
3
  "type": "module",
4
- "version": "3.0.0-canary.102",
4
+ "version": "3.0.0-canary.103",
5
5
  "main": "./dist/index.umd.js",
6
6
  "module": "./dist/index.es.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "source": "src/index.ts",
9
9
  "dependencies": {
10
- "@firecms/data_export": "^3.0.0-canary.102",
11
- "@firecms/data_import": "^3.0.0-canary.102",
12
- "@firecms/data_import_export": "^3.0.0-canary.102",
13
- "@firecms/formex": "^3.0.0-canary.102",
14
- "@firecms/schema_inference": "^3.0.0-canary.102",
15
- "@firecms/ui": "^3.0.0-canary.102",
10
+ "@firecms/data_export": "^3.0.0-canary.103",
11
+ "@firecms/data_import": "^3.0.0-canary.103",
12
+ "@firecms/data_import_export": "^3.0.0-canary.103",
13
+ "@firecms/formex": "^3.0.0-canary.103",
14
+ "@firecms/schema_inference": "^3.0.0-canary.103",
15
+ "@firecms/ui": "^3.0.0-canary.103",
16
16
  "json5": "^2.2.3",
17
17
  "prism-react-renderer": "^2.3.1"
18
18
  },
@@ -67,5 +67,5 @@
67
67
  "publishConfig": {
68
68
  "access": "public"
69
69
  },
70
- "gitHead": "db22751d03517d639f4b23a26aa1fff81b11ee8d"
70
+ "gitHead": "c33fe736fee99aaf30789aed66e4385563011783"
71
71
  }
@@ -57,6 +57,13 @@ export interface ConfigControllerProviderProps {
57
57
 
58
58
  onAnalyticsEvent?: (event: string, params?: object) => void;
59
59
 
60
+ components?: {
61
+ /**
62
+ * Custom component to render the database field
63
+ */
64
+ DatabaseField?: React.ComponentType<{ databaseId?: string, onDatabaseIdUpdate: (databaseId:string) => void }>;
65
+ };
66
+
60
67
  }
61
68
 
62
69
  export const ConfigControllerProvider = React.memo(
@@ -70,7 +77,8 @@ export const ConfigControllerProvider = React.memo(
70
77
  getPathSuggestions,
71
78
  getUser,
72
79
  getData,
73
- onAnalyticsEvent
80
+ onAnalyticsEvent,
81
+ components
74
82
  }: PropsWithChildren<ConfigControllerProviderProps>) {
75
83
 
76
84
  const navigation = useNavigationController();
@@ -230,7 +238,8 @@ export const ConfigControllerProvider = React.memo(
230
238
  createCollection,
231
239
  editProperty,
232
240
  configPermissions: configPermissions ?? defaultConfigPermissions,
233
- getPathSuggestions
241
+ getPathSuggestions,
242
+ components
234
243
  }}>
235
244
 
236
245
  {children}
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import { CollectionEditorPermissionsBuilder } from "./config_permissions";
2
3
  import { Entity, Property } from "@firecms/core";
3
4
  import { PersistedCollection } from "./persisted_collection";
@@ -42,4 +43,11 @@ export interface CollectionEditorController {
42
43
 
43
44
  getPathSuggestions?: (path: string) => Promise<string[]>;
44
45
 
46
+ components?: {
47
+ /**
48
+ * Custom component to render the database field
49
+ */
50
+ DatabaseField?: React.ComponentType<{ databaseId?: string, onDatabaseIdUpdate: (databaseId: string) => void }>;
51
+ };
52
+
45
53
  }
@@ -22,6 +22,7 @@ import {
22
22
  } from "@firecms/ui";
23
23
 
24
24
  import { Field, getIn, useFormex } from "@firecms/formex";
25
+ import { useCollectionEditorController } from "../../useCollectionEditorController";
25
26
 
26
27
  export function CollectionDetailsForm({
27
28
  isNewCollection,
@@ -52,9 +53,15 @@ export function CollectionDetailsForm({
52
53
  submitCount
53
54
  } = useFormex<EntityCollection>();
54
55
 
56
+ const collectionEditor = useCollectionEditorController();
57
+
55
58
  const [iconDialogOpen, setIconDialogOpen] = useState(false);
56
59
  const [advancedPanelExpanded, setAdvancedPanelExpanded] = useState(false);
57
60
 
61
+ const updateDatabaseId = (databaseId: string) => {
62
+ setFieldValue("databaseId", databaseId ?? undefined);
63
+ }
64
+
58
65
  const updateName = (name: string) => {
59
66
  setFieldValue("name", name);
60
67
 
@@ -81,6 +88,8 @@ export function CollectionDetailsForm({
81
88
  }
82
89
  }, [errors.id]);
83
90
 
91
+ const DatabaseField = collectionEditor.components?.DatabaseField ?? DefaultDatabaseField;
92
+
84
93
  const collectionIcon = <IconForView collectionOrView={values}/>;
85
94
 
86
95
  const groupOptions = groups?.filter((group) => !reservedGroups?.includes(group));
@@ -113,10 +122,13 @@ export function CollectionDetailsForm({
113
122
 
114
123
  <div>
115
124
  <div
116
- className="flex flex-row py-2 pt-3 items-center">
125
+ className="flex flex-row gap-2 py-2 pt-3 items-center">
117
126
  <Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
118
127
  {isNewCollection ? "New collection" : `${values?.name} collection`}
119
128
  </Typography>
129
+ <DatabaseField databaseId={values.databaseId}
130
+ onDatabaseIdUpdate={updateDatabaseId}/>
131
+
120
132
  <Tooltip title={"Change icon"}>
121
133
  <IconButton
122
134
  shape={"square"}
@@ -191,7 +203,7 @@ export function CollectionDetailsForm({
191
203
  })}
192
204
  </Autocomplete>
193
205
  <FieldCaption>
194
- {showErrors && Boolean(errors.group) ? errors.group : "Group of the collection"}
206
+ {showErrors && Boolean(errors.group) ? errors.group : "Group in the home page"}
195
207
  </FieldCaption>
196
208
  </div>}
197
209
 
@@ -393,3 +405,20 @@ export function CollectionDetailsForm({
393
405
  </div>
394
406
  );
395
407
  }
408
+
409
+ function DefaultDatabaseField({
410
+ databaseId,
411
+ onDatabaseIdUpdate
412
+ }: { databaseId?: string, onDatabaseIdUpdate: (databaseId: string) => void }) {
413
+
414
+ return <Tooltip title={"Database ID"}
415
+ side={"top"}
416
+ align={"start"}>
417
+ <TextField size={"smallest"}
418
+ invisible={true}
419
+ inputClassName={"text-end"}
420
+ value={databaseId ?? ""}
421
+ onChange={(e: any) => onDatabaseIdUpdate(e.target.value)}
422
+ placeholder={"(default)"}></TextField>
423
+ </Tooltip>
424
+ }
@@ -5,7 +5,6 @@ import { Formex, FormexController, getIn, useCreateFormex } from "@firecms/forme
5
5
  import {
6
6
  DEFAULT_FIELD_CONFIGS,
7
7
  DeleteConfirmationDialog,
8
- PropertyConfigId,
9
8
  getFieldConfig,
10
9
  getFieldId,
11
10
  isPropertyBuilder,
@@ -14,6 +13,7 @@ import {
14
13
  Property,
15
14
  PropertyConfig,
16
15
  PropertyConfigBadge,
16
+ PropertyConfigId,
17
17
  } from "@firecms/core";
18
18
  import {
19
19
  Button,
@@ -45,6 +45,7 @@ import { updatePropertyFromWidget } from "./utils/update_property_for_widget";
45
45
  import { PropertySelectItem } from "./PropertySelectItem";
46
46
  import { UrlPropertyField } from "./properties/UrlPropertyField";
47
47
  import { supportedFields } from "./utils/supported_fields";
48
+ import { MarkdownPropertyField } from "./properties/MarkdownPropertyField";
48
49
 
49
50
  export type PropertyWithId = Property & {
50
51
  id?: string
@@ -148,7 +149,10 @@ export const PropertyForm = React.memo(
148
149
  } = newPropertyWithId;
149
150
  doOnPropertyChanged({
150
151
  id,
151
- property: { ...property, editable: property.editable ?? true }
152
+ property: {
153
+ ...property,
154
+ editable: property.editable ?? true
155
+ }
152
156
  });
153
157
  if (!existingProperty)
154
158
  controller.resetForm({ values: initialValue });
@@ -388,7 +392,6 @@ function PropertyEditFormFields({
388
392
  let childComponent;
389
393
  if (selectedFieldConfigId === "text_field" ||
390
394
  selectedFieldConfigId === "multiline" ||
391
- selectedFieldConfigId === "markdown" ||
392
395
  selectedFieldConfigId === "email") {
393
396
  childComponent =
394
397
  <StringPropertyField widgetId={selectedFieldConfigId}
@@ -398,6 +401,10 @@ function PropertyEditFormFields({
398
401
  childComponent =
399
402
  <UrlPropertyField disabled={disabled}
400
403
  showErrors={showErrors}/>;
404
+ } else if (selectedFieldConfigId === "markdown") {
405
+ childComponent =
406
+ <MarkdownPropertyField disabled={disabled}
407
+ showErrors={showErrors}/>;
401
408
  } else if (selectedFieldConfigId === "select" ||
402
409
  selectedFieldConfigId === "number_select") {
403
410
  childComponent = <EnumPropertyField
@@ -14,7 +14,7 @@ export function PropertySelectItem({ value, optionDisabled, propertyConfig, exis
14
14
  className={"flex flex-row items-center"}>
15
15
  <div
16
16
  className={cls(
17
- "flex flex-row items-center text-base min-h-[52px]",
17
+ "flex flex-row items-center text-base min-h-[48px]",
18
18
  optionDisabled ? "w-full" : "")}>
19
19
  <div className={"mr-8"}>
20
20
  <PropertyConfigBadge propertyConfig={propertyConfig}/>
@@ -0,0 +1,139 @@
1
+ import React from "react";
2
+ import { StringPropertyValidation } from "./validation/StringPropertyValidation";
3
+ import { ValidationPanel } from "./validation/ValidationPanel";
4
+ import { Field, getIn, useFormex } from "@firecms/formex";
5
+
6
+ import { DebouncedTextField, ExpandablePanel, FileUploadIcon, TextField, Typography } from "@firecms/ui";
7
+
8
+ export function MarkdownPropertyField({
9
+ disabled,
10
+ showErrors
11
+ }: {
12
+ disabled: boolean;
13
+ showErrors: boolean;
14
+ }) {
15
+
16
+ const {
17
+ values,
18
+ setFieldValue
19
+ } = useFormex();
20
+
21
+ const baseStoragePath = "storage";
22
+
23
+ const metadata = `${baseStoragePath}.metadata`;
24
+ const fileName = `${baseStoragePath}.fileName`;
25
+ const maxSize = `${baseStoragePath}.maxSize`;
26
+ const storagePath = `${baseStoragePath}.storagePath`;
27
+
28
+ const fileNameValue = getIn(values, fileName) ?? "{rand}_{file}";
29
+ const storagePathValue = getIn(values, storagePath) ?? "/";
30
+ const maxSizeValue = getIn(values, maxSize);
31
+
32
+ const hasFilenameCallback = typeof fileNameValue === "function";
33
+ const hasStoragePathCallback = typeof storagePathValue === "function";
34
+
35
+ return (
36
+ <>
37
+ <div className={"col-span-12"}>
38
+
39
+ <ValidationPanel>
40
+
41
+ <StringPropertyValidation disabled={disabled}
42
+ length={true}
43
+ lowercase={true}
44
+ max={true}
45
+ min={true}
46
+ trim={true}
47
+ uppercase={true}
48
+ showErrors={showErrors}/>
49
+
50
+ </ValidationPanel>
51
+
52
+ </div>
53
+
54
+ <div className={"col-span-12"}>
55
+ <ExpandablePanel
56
+ title={
57
+ <div className="flex flex-row text-gray-500">
58
+ <FileUploadIcon/>
59
+ <Typography variant={"subtitle2"}
60
+ className="ml-2">
61
+ File upload config
62
+ </Typography>
63
+ </div>
64
+ }>
65
+
66
+ <div className={"grid grid-cols-12 gap-2 p-4"}>
67
+
68
+
69
+ <div className={"col-span-12"}>
70
+ <Field name={fileName}
71
+ as={DebouncedTextField}
72
+ label={"File name"}
73
+ size={"small"}
74
+ disabled={hasFilenameCallback || disabled}
75
+ value={hasFilenameCallback ? "-" : fileNameValue}
76
+ />
77
+ </div>
78
+ <div className={"col-span-12"}>
79
+ <Field name={storagePath}
80
+ as={DebouncedTextField}
81
+ label={"Storage path"}
82
+ disabled={hasStoragePathCallback || disabled}
83
+ size={"small"}
84
+ value={hasStoragePathCallback ? "-" : storagePathValue}
85
+ />
86
+ <Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
87
+ <p>You can use the following placeholders in
88
+ the file name
89
+ and storage path values:</p>
90
+ <ul>
91
+ <li>{"{file} - Full name of the uploaded file"}</li>
92
+ <li>{"{file.name} - Name of the uploaded file without extension"}</li>
93
+ <li>{"{file.ext} - Extension of the uploaded file"}</li>
94
+ <li>{"{entityId} - ID of the entity"}</li>
95
+ <li>{"{propertyKey} - ID of this field"}</li>
96
+ <li>{"{path} - Path of this entity"}</li>
97
+ <li>{"{rand} - Random value used to avoid name collisions"}</li>
98
+ </ul>
99
+ </Typography>
100
+
101
+ <Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
102
+ When using Markdown, the URL of the uploaded files are always saved in the text value
103
+ (not
104
+ the path).
105
+ </Typography>
106
+ </div>
107
+
108
+ <div className={"col-span-12"}>
109
+ <DebouncedTextField name={maxSize}
110
+ type={"number"}
111
+ label={"Max size (in bytes)"}
112
+ size={"small"}
113
+ value={maxSizeValue !== undefined && maxSizeValue !== null ? maxSizeValue.toString() : ""}
114
+ onChange={(e) => {
115
+ const value = e.target.value;
116
+ if (value === "") setFieldValue(maxSize, undefined);
117
+ else setFieldValue(maxSize, parseInt(value));
118
+ }}
119
+ />
120
+ </div>
121
+
122
+ </div>
123
+ </ExpandablePanel>
124
+ </div>
125
+
126
+ <div className={"col-span-12"}>
127
+
128
+ <TextField name={"defaultValue"}
129
+ disabled={disabled}
130
+ onChange={(e: any) => {
131
+ setFieldValue("defaultValue", e.target.value === "" ? undefined : e.target.value);
132
+ }}
133
+ label={"Default value"}
134
+ value={getIn(values, "defaultValue") ?? ""}/>
135
+
136
+ </div>
137
+ </>
138
+ );
139
+ }
@@ -10,7 +10,7 @@ export function StringPropertyField({
10
10
  disabled,
11
11
  showErrors
12
12
  }: {
13
- widgetId: "text_field" | "multiline" | "markdown" | "email";
13
+ widgetId: "text_field" | "multiline" | "email";
14
14
  disabled: boolean;
15
15
  showErrors: boolean;
16
16
  }) {
@@ -42,15 +42,6 @@ export function StringPropertyField({
42
42
  trim={true}
43
43
  uppercase={true}
44
44
  showErrors={showErrors}/>}
45
- {widgetId === "markdown" &&
46
- <StringPropertyValidation disabled={disabled}
47
- length={true}
48
- lowercase={true}
49
- max={true}
50
- min={true}
51
- trim={true}
52
- uppercase={true}
53
- showErrors={showErrors}/>}
54
45
 
55
46
  {widgetId === "email" &&
56
47
  <StringPropertyValidation disabled={disabled}
@@ -51,6 +51,13 @@ export interface CollectionConfigControllerProps<EC extends PersistedCollection
51
51
 
52
52
  onAnalyticsEvent?: (event: string, params?: object) => void;
53
53
 
54
+ components?: {
55
+ /**
56
+ * Custom component to render the database field
57
+ */
58
+ DatabaseField?: React.ComponentType<{ databaseId?: string, onDatabaseIdUpdate: (databaseId:string) => void }>;
59
+ };
60
+
54
61
  }
55
62
 
56
63
  /**
@@ -74,7 +81,8 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
74
81
  getUser,
75
82
  collectionInference,
76
83
  getData,
77
- onAnalyticsEvent
84
+ onAnalyticsEvent,
85
+ components
78
86
  }: CollectionConfigControllerProps<EC, UserType>): FireCMSPlugin<any, any, PersistedCollection> {
79
87
 
80
88
  return {
@@ -91,7 +99,8 @@ export function useCollectionEditorPlugin<EC extends PersistedCollection = Persi
91
99
  getPathSuggestions,
92
100
  getUser,
93
101
  getData,
94
- onAnalyticsEvent
102
+ onAnalyticsEvent,
103
+ components
95
104
  }
96
105
  },
97
106
  homePage: {