@firecms/collection_editor 3.0.0-rc.1 → 3.0.0-rc.3
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.
- package/dist/index.es.js +837 -509
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +837 -509
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_inference.d.ts +1 -1
- package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
- package/dist/utils/entities.d.ts +1 -1
- package/package.json +8 -8
- package/src/ConfigControllerProvider.tsx +2 -2
- package/src/types/collection_inference.ts +1 -1
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +13 -0
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +1 -1
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +0 -8
- package/src/ui/collection_editor/PropertyEditView.tsx +5 -3
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +5 -5
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +1 -1
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +10 -2
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +1 -1
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +135 -5
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +5 -1
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +1 -1
- package/src/ui/collection_editor/utils/supported_fields.tsx +1 -0
- package/src/ui/collection_editor/utils/update_property_for_widget.ts +28 -6
- package/src/utils/collections.ts +0 -1
- package/src/utils/entities.ts +6 -9
|
@@ -2,4 +2,4 @@ import { EntityCollection } from "@firecms/core";
|
|
|
2
2
|
/**
|
|
3
3
|
* This function is used to infer the configuration of a collection given its path.
|
|
4
4
|
*/
|
|
5
|
-
export type CollectionInference = (path: string, collectionGroup: boolean, parentCollectionPaths: string[]) => Promise<Partial<EntityCollection> | null>;
|
|
5
|
+
export type CollectionInference = (path: string, collectionGroup: boolean, parentCollectionPaths: string[], databaseId?: string) => Promise<Partial<EntityCollection> | null>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare function StringPropertyField({ widgetId, disabled, showErrors }: {
|
|
2
|
-
widgetId: "text_field" | "multiline" | "email";
|
|
2
|
+
widgetId: "text_field" | "multiline" | "email" | "user_select";
|
|
3
3
|
disabled: boolean;
|
|
4
4
|
showErrors: boolean;
|
|
5
5
|
}): import("react/jsx-runtime").JSX.Element;
|
package/dist/utils/entities.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Properties, PropertiesOrBuilders, PropertyOrBuilder } from "@firecms/core";
|
|
2
|
-
export declare function editableProperty(property: PropertyOrBuilder
|
|
2
|
+
export declare function editableProperty(property: PropertyOrBuilder): boolean;
|
|
3
3
|
export declare function removeNonEditableProperties(properties: PropertiesOrBuilders<any>): Properties;
|
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-rc.
|
|
4
|
+
"version": "3.0.0-rc.3",
|
|
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-rc.
|
|
11
|
-
"@firecms/data_import": "^3.0.0-rc.
|
|
12
|
-
"@firecms/data_import_export": "^3.0.0-rc.
|
|
13
|
-
"@firecms/formex": "^3.0.0-rc.
|
|
14
|
-
"@firecms/schema_inference": "^3.0.0-rc.
|
|
15
|
-
"@firecms/ui": "^3.0.0-rc.
|
|
10
|
+
"@firecms/data_export": "^3.0.0-rc.3",
|
|
11
|
+
"@firecms/data_import": "^3.0.0-rc.3",
|
|
12
|
+
"@firecms/data_import_export": "^3.0.0-rc.3",
|
|
13
|
+
"@firecms/formex": "^3.0.0-rc.3",
|
|
14
|
+
"@firecms/schema_inference": "^3.0.0-rc.3",
|
|
15
|
+
"@firecms/ui": "^3.0.0-rc.3",
|
|
16
16
|
"json5": "^2.2.3",
|
|
17
17
|
"prism-react-renderer": "^2.4.1"
|
|
18
18
|
},
|
|
@@ -69,5 +69,5 @@
|
|
|
69
69
|
"publishConfig": {
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "213f4c106eb3e6639eb1ad23c7d94282ef54d444"
|
|
73
73
|
}
|
|
@@ -49,7 +49,7 @@ export interface ConfigControllerProviderProps {
|
|
|
49
49
|
icon: React.ReactNode
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
getUser?: (uid: string) => User | null
|
|
52
|
+
getUser?: (uid: string) => User | null;
|
|
53
53
|
|
|
54
54
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
55
55
|
|
|
@@ -174,7 +174,7 @@ export const ConfigControllerProvider = React.memo(
|
|
|
174
174
|
currentPropertiesOrder,
|
|
175
175
|
editedCollectionId,
|
|
176
176
|
parentCollectionIds,
|
|
177
|
-
collectionEditable: collection?.editable
|
|
177
|
+
collectionEditable: collection?.editable === undefined || collection?.editable === true,
|
|
178
178
|
existingEntities
|
|
179
179
|
});
|
|
180
180
|
};
|
|
@@ -3,4 +3,4 @@ import { EntityCollection } from "@firecms/core";
|
|
|
3
3
|
/**
|
|
4
4
|
* This function is used to infer the configuration of a collection given its path.
|
|
5
5
|
*/
|
|
6
|
-
export type CollectionInference = (path: string, collectionGroup: boolean, parentCollectionPaths: string[]) => Promise<Partial<EntityCollection> | null>;
|
|
6
|
+
export type CollectionInference = (path: string, collectionGroup: boolean, parentCollectionPaths: string[], databaseId?:string) => Promise<Partial<EntityCollection> | null>;
|
|
@@ -356,6 +356,19 @@ export function CollectionDetailsForm({
|
|
|
356
356
|
</FieldCaption>
|
|
357
357
|
</div>
|
|
358
358
|
|
|
359
|
+
<div className={"col-span-12"}>
|
|
360
|
+
<BooleanSwitchWithLabel
|
|
361
|
+
position={"start"}
|
|
362
|
+
size={"large"}
|
|
363
|
+
label={values.inlineEditing === undefined || values.inlineEditing ? "Data can be edited directly in the table view" : "Data can be edited only in the form view"}
|
|
364
|
+
onValueChange={(v) => setFieldValue("inlineEditing", v)}
|
|
365
|
+
value={values.inlineEditing === undefined ? true : values.inlineEditing}
|
|
366
|
+
/>
|
|
367
|
+
<FieldCaption>
|
|
368
|
+
Allow editing data directly in the table view, without opening the form view.
|
|
369
|
+
</FieldCaption>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
359
372
|
<div className={"col-span-12"}>
|
|
360
373
|
<Select
|
|
361
374
|
name="customId"
|
|
@@ -329,7 +329,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
329
329
|
|
|
330
330
|
const doCollectionInference = collectionInference ? (collection: PersistedCollection<any>) => {
|
|
331
331
|
if (!collectionInference) return undefined;
|
|
332
|
-
return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentPaths ?? []);
|
|
332
|
+
return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentPaths ?? [], collection.databaseId);
|
|
333
333
|
} : undefined;
|
|
334
334
|
|
|
335
335
|
const inferCollectionFromData = async (newCollection: PersistedCollection<M>) => {
|
|
@@ -255,14 +255,6 @@ export function CollectionPropertiesEditorForm({
|
|
|
255
255
|
setFieldTouched(previousPropertyPath, false, false);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
console.debug("onPropertyChanged", {
|
|
259
|
-
id,
|
|
260
|
-
property,
|
|
261
|
-
previousId,
|
|
262
|
-
namespace,
|
|
263
|
-
propertyPath
|
|
264
|
-
})
|
|
265
|
-
|
|
266
258
|
if (propertyPath) {
|
|
267
259
|
setFieldValue(propertyPath, property, false);
|
|
268
260
|
setFieldTouched(propertyPath, true, false);
|
|
@@ -119,7 +119,7 @@ export const PropertyForm = React.memo(
|
|
|
119
119
|
name: ""
|
|
120
120
|
} as PropertyWithId;
|
|
121
121
|
|
|
122
|
-
const disabled = (Boolean(property && !editableProperty(property))
|
|
122
|
+
const disabled = (Boolean(property && !editableProperty(property)) || !collectionEditable);
|
|
123
123
|
|
|
124
124
|
const lastSubmittedProperty = useRef<OnPropertyChangedParams | undefined>(property ? {
|
|
125
125
|
id: propertyKey,
|
|
@@ -150,7 +150,6 @@ export const PropertyForm = React.memo(
|
|
|
150
150
|
validateOnChange: true,
|
|
151
151
|
validateOnInitialRender: true,
|
|
152
152
|
onSubmit: (newPropertyWithId, controller) => {
|
|
153
|
-
console.debug("onSubmit", newPropertyWithId);
|
|
154
153
|
const {
|
|
155
154
|
id,
|
|
156
155
|
...property
|
|
@@ -401,6 +400,7 @@ function PropertyEditFormFields({
|
|
|
401
400
|
let childComponent;
|
|
402
401
|
if (selectedFieldConfigId === "text_field" ||
|
|
403
402
|
selectedFieldConfigId === "multiline" ||
|
|
403
|
+
selectedFieldConfigId === "user_select" ||
|
|
404
404
|
selectedFieldConfigId === "email") {
|
|
405
405
|
childComponent =
|
|
406
406
|
<StringPropertyField widgetId={selectedFieldConfigId}
|
|
@@ -499,7 +499,7 @@ function PropertyEditFormFields({
|
|
|
499
499
|
<Typography>This property can't be edited</Typography>
|
|
500
500
|
<Typography variant={"caption"}>
|
|
501
501
|
You may not have permission to
|
|
502
|
-
edit it or it is defined in code with
|
|
502
|
+
edit it or it is defined in code with the <code>editable</code> flag set to <code>false</code>.
|
|
503
503
|
</Typography>
|
|
504
504
|
</InfoLabel>}
|
|
505
505
|
|
|
@@ -605,6 +605,7 @@ const WIDGET_TYPE_MAP: Record<PropertyConfigId, string> = {
|
|
|
605
605
|
url: "Text",
|
|
606
606
|
email: "Text",
|
|
607
607
|
switch: "Boolean",
|
|
608
|
+
user_select: "Users",
|
|
608
609
|
select: "Select",
|
|
609
610
|
multi_select: "Select",
|
|
610
611
|
number_input: "Number",
|
|
@@ -786,6 +787,7 @@ export function WidgetSelectViewItem({
|
|
|
786
787
|
<WarningIcon size="smallest" className={"w-4"}/>
|
|
787
788
|
</Tooltip>}
|
|
788
789
|
<Typography
|
|
790
|
+
variant={"label"}
|
|
789
791
|
color={shouldWarnChangingDataType ? "secondary" : undefined}>{propertyConfig.name}</Typography>
|
|
790
792
|
</div>
|
|
791
793
|
|
|
@@ -69,7 +69,7 @@ export function PropertyFieldPreview({
|
|
|
69
69
|
|
|
70
70
|
{includeName &&
|
|
71
71
|
<ErrorBoundary>
|
|
72
|
-
<Typography variant="
|
|
72
|
+
<Typography variant="label"
|
|
73
73
|
component="span"
|
|
74
74
|
className="flex-grow pr-2">
|
|
75
75
|
{property.name
|
|
@@ -82,7 +82,7 @@ export function PropertyFieldPreview({
|
|
|
82
82
|
<div className="flex flex-row items-center">
|
|
83
83
|
<ErrorBoundary>
|
|
84
84
|
<Typography className="flex-grow pr-2"
|
|
85
|
-
variant={includeName ? "body2" : "
|
|
85
|
+
variant={includeName ? "body2" : "label"}
|
|
86
86
|
component="span"
|
|
87
87
|
color="secondary">
|
|
88
88
|
{propertyConfig?.name}
|
|
@@ -131,8 +131,8 @@ export function NonEditablePropertyPreview({
|
|
|
131
131
|
<div className={"relative m-4"}>
|
|
132
132
|
{propertyConfig && <PropertyConfigBadge propertyConfig={propertyConfig}/>}
|
|
133
133
|
{!propertyConfig && <div
|
|
134
|
-
className={"h-8 w-8
|
|
135
|
-
<FunctionsIcon color={"inherit"} size={"
|
|
134
|
+
className={"h-8 w-8 flex items-center justify-center rounded-full shadow text-white bg-surface-500"}>
|
|
135
|
+
<FunctionsIcon color={"inherit"} size={"small"}/>
|
|
136
136
|
</div>}
|
|
137
137
|
<DoNotDisturbOnIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
|
|
138
138
|
</div>
|
|
@@ -147,7 +147,7 @@ export function NonEditablePropertyPreview({
|
|
|
147
147
|
>
|
|
148
148
|
|
|
149
149
|
<div className="w-full flex flex-col">
|
|
150
|
-
<Typography variant="
|
|
150
|
+
<Typography variant="label"
|
|
151
151
|
component="span"
|
|
152
152
|
className="flex-grow pr-2">
|
|
153
153
|
{property?.name
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
EntityCollectionTable,
|
|
5
5
|
Properties,
|
|
6
6
|
useAuthController,
|
|
7
|
+
useNavigationController,
|
|
7
8
|
useSelectionController
|
|
8
9
|
} from "@firecms/core";
|
|
9
10
|
import { useEffect, useState } from "react";
|
|
@@ -20,11 +21,18 @@ export function CollectionEditorImportDataPreview({
|
|
|
20
21
|
}) {
|
|
21
22
|
|
|
22
23
|
const authController = useAuthController();
|
|
24
|
+
const navigation = useNavigationController();
|
|
23
25
|
const [loading, setLoading] = useState<boolean>(false);
|
|
24
26
|
|
|
25
27
|
async function loadEntities() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const mappedData = importConfig.importData.map(d => convertDataToEntity(authController,
|
|
29
|
+
navigation,
|
|
30
|
+
d,
|
|
31
|
+
importConfig.idColumn,
|
|
32
|
+
importConfig.headersMapping,
|
|
33
|
+
properties,
|
|
34
|
+
"TEMP_PATH",
|
|
35
|
+
importConfig.defaultValues));
|
|
28
36
|
importConfig.setEntities(mappedData);
|
|
29
37
|
}
|
|
30
38
|
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
ExpandablePanel,
|
|
7
7
|
MultiSelect,
|
|
8
8
|
MultiSelectItem,
|
|
9
|
+
Select,
|
|
10
|
+
SelectItem,
|
|
9
11
|
Typography
|
|
10
12
|
} from "@firecms/ui";
|
|
11
13
|
|
|
@@ -47,20 +49,31 @@ export function StoragePropertyField({
|
|
|
47
49
|
const storagePath = `${baseStoragePath}.storagePath`;
|
|
48
50
|
const storeUrl = `${baseStoragePath}.storeUrl`;
|
|
49
51
|
|
|
52
|
+
// Image resize config paths
|
|
53
|
+
const imageResize = `${baseStoragePath}.imageResize`;
|
|
54
|
+
const imageResizeMaxWidth = `${imageResize}.maxWidth`;
|
|
55
|
+
const imageResizeMaxHeight = `${imageResize}.maxHeight`;
|
|
56
|
+
const imageResizeMode = `${imageResize}.mode`;
|
|
57
|
+
const imageResizeFormat = `${imageResize}.format`;
|
|
58
|
+
const imageResizeQuality = `${imageResize}.quality`;
|
|
59
|
+
|
|
50
60
|
const fileNameValue = getIn(values, fileName) ?? "{rand}_{file}";
|
|
51
61
|
const storagePathValue = getIn(values, storagePath) ?? "/";
|
|
52
62
|
const maxSizeValue = getIn(values, maxSize);
|
|
53
63
|
|
|
64
|
+
// Image resize values
|
|
65
|
+
const imageResizeMaxWidthValue = getIn(values, imageResizeMaxWidth);
|
|
66
|
+
const imageResizeMaxHeightValue = getIn(values, imageResizeMaxHeight);
|
|
67
|
+
const imageResizeModeValue = getIn(values, imageResizeMode) ?? "cover";
|
|
68
|
+
const imageResizeFormatValue = getIn(values, imageResizeFormat) ?? "original";
|
|
69
|
+
const imageResizeQualityValue = getIn(values, imageResizeQuality);
|
|
70
|
+
|
|
54
71
|
const storedValue = getIn(values, acceptedFiles);
|
|
55
72
|
const fileTypesValue: string[] | undefined = Array.isArray(storedValue) ? storedValue : undefined;
|
|
56
73
|
const allFileTypesSelected = !fileTypesValue || fileTypesValue.length === 0;
|
|
57
74
|
|
|
58
75
|
const handleTypesChange = (value: string[]) => {
|
|
59
76
|
if (!value) setFieldValue(acceptedFiles, undefined);
|
|
60
|
-
// else if (value.includes("all")) setFieldValue(acceptedFiles, undefined);
|
|
61
|
-
// else if (value.length >= Object.keys(fileTypes).length) setFieldValue(acceptedFiles, undefined);
|
|
62
|
-
// else if (allFileTypesSelected)
|
|
63
|
-
// setFieldValue(acceptedFiles, Object.keys(fileTypes).filter((v) => !value.includes(v)));
|
|
64
77
|
else setFieldValue(acceptedFiles, value);
|
|
65
78
|
};
|
|
66
79
|
|
|
@@ -77,7 +90,7 @@ export function StoragePropertyField({
|
|
|
77
90
|
<div className="flex flex-row text-surface-500">
|
|
78
91
|
<CloudUploadIcon/>
|
|
79
92
|
<Typography variant={"subtitle2"}
|
|
80
|
-
className="ml-
|
|
93
|
+
className="ml-4">
|
|
81
94
|
File upload config
|
|
82
95
|
</Typography>
|
|
83
96
|
</div>
|
|
@@ -193,6 +206,123 @@ export function StoragePropertyField({
|
|
|
193
206
|
/>
|
|
194
207
|
</div>
|
|
195
208
|
|
|
209
|
+
<div className={"col-span-12 mt-4"}>
|
|
210
|
+
<Typography variant={"subtitle2"}
|
|
211
|
+
color={"secondary"}
|
|
212
|
+
className={"mb-2 block"}>
|
|
213
|
+
Image Resize Configuration
|
|
214
|
+
</Typography>
|
|
215
|
+
<Typography variant={"caption"} className={"mb-2 block text-xs"}>
|
|
216
|
+
Automatically resize and optimize images before upload (JPEG, PNG, WebP only)
|
|
217
|
+
</Typography>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<div className={"col-span-6"}>
|
|
221
|
+
<DebouncedTextField
|
|
222
|
+
name={imageResizeMaxWidth}
|
|
223
|
+
type={"number"}
|
|
224
|
+
label={"Max width (px)"}
|
|
225
|
+
size={"small"}
|
|
226
|
+
disabled={disabled}
|
|
227
|
+
value={imageResizeMaxWidthValue !== undefined && imageResizeMaxWidthValue !== null ? imageResizeMaxWidthValue.toString() : ""}
|
|
228
|
+
onChange={(e) => {
|
|
229
|
+
const value = e.target.value;
|
|
230
|
+
if (value === "") setFieldValue(imageResizeMaxWidth, undefined);
|
|
231
|
+
else setFieldValue(imageResizeMaxWidth, parseInt(value));
|
|
232
|
+
}}
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<div className={"col-span-6"}>
|
|
237
|
+
<DebouncedTextField
|
|
238
|
+
name={imageResizeMaxHeight}
|
|
239
|
+
type={"number"}
|
|
240
|
+
label={"Max height (px)"}
|
|
241
|
+
size={"small"}
|
|
242
|
+
disabled={disabled}
|
|
243
|
+
value={imageResizeMaxHeightValue !== undefined && imageResizeMaxHeightValue !== null ? imageResizeMaxHeightValue.toString() : ""}
|
|
244
|
+
onChange={(e) => {
|
|
245
|
+
const value = e.target.value;
|
|
246
|
+
if (value === "") setFieldValue(imageResizeMaxHeight, undefined);
|
|
247
|
+
else setFieldValue(imageResizeMaxHeight, parseInt(value));
|
|
248
|
+
}}
|
|
249
|
+
/>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div className={"col-span-6"}>
|
|
253
|
+
<Select
|
|
254
|
+
disabled={disabled}
|
|
255
|
+
name={imageResizeMode}
|
|
256
|
+
fullWidth
|
|
257
|
+
size={"medium"}
|
|
258
|
+
value={imageResizeModeValue || "cover"}
|
|
259
|
+
onValueChange={(value) => setFieldValue(imageResizeMode, value || "cover")}
|
|
260
|
+
label={"Resize mode"}
|
|
261
|
+
renderValue={(selected) => {
|
|
262
|
+
if (!selected) return "Cover";
|
|
263
|
+
return selected === "contain" ? "Contain (fit within bounds)" : "Cover (fill bounds, may crop)";
|
|
264
|
+
}}>
|
|
265
|
+
<SelectItem value="contain">
|
|
266
|
+
Contain (fit within bounds)
|
|
267
|
+
</SelectItem>
|
|
268
|
+
<SelectItem value="cover">
|
|
269
|
+
Cover (fill bounds, may crop)
|
|
270
|
+
</SelectItem>
|
|
271
|
+
</Select>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div className={"col-span-6"}>
|
|
275
|
+
<Select
|
|
276
|
+
disabled={disabled}
|
|
277
|
+
size={"medium"}
|
|
278
|
+
fullWidth
|
|
279
|
+
name={imageResizeFormat}
|
|
280
|
+
value={imageResizeFormatValue || "original"}
|
|
281
|
+
onValueChange={(value) => setFieldValue(imageResizeFormat, value || "original")}
|
|
282
|
+
label={"Output format"}
|
|
283
|
+
renderValue={(selected) => {
|
|
284
|
+
if (!selected) return "Original";
|
|
285
|
+
return selected.charAt(0).toUpperCase() + selected.slice(1);
|
|
286
|
+
}}>
|
|
287
|
+
<SelectItem value="original">
|
|
288
|
+
Original (keep same format)
|
|
289
|
+
</SelectItem>
|
|
290
|
+
<SelectItem value="jpeg">
|
|
291
|
+
JPEG
|
|
292
|
+
</SelectItem>
|
|
293
|
+
<SelectItem value="png">
|
|
294
|
+
PNG
|
|
295
|
+
</SelectItem>
|
|
296
|
+
<SelectItem value="webp">
|
|
297
|
+
WebP (best compression)
|
|
298
|
+
</SelectItem>
|
|
299
|
+
</Select>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<div className={"col-span-12"}>
|
|
303
|
+
<DebouncedTextField
|
|
304
|
+
name={imageResizeQuality}
|
|
305
|
+
type={"number"}
|
|
306
|
+
label={"Quality (0-100)"}
|
|
307
|
+
size={"small"}
|
|
308
|
+
disabled={disabled}
|
|
309
|
+
value={imageResizeQualityValue !== undefined && imageResizeQualityValue !== null ? imageResizeQualityValue.toString() : ""}
|
|
310
|
+
onChange={(e) => {
|
|
311
|
+
const value = e.target.value;
|
|
312
|
+
if (value === "") setFieldValue(imageResizeQuality, undefined);
|
|
313
|
+
else {
|
|
314
|
+
const numValue = parseInt(value);
|
|
315
|
+
if (numValue >= 0 && numValue <= 100) {
|
|
316
|
+
setFieldValue(imageResizeQuality, numValue);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}}
|
|
320
|
+
/>
|
|
321
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
322
|
+
Higher quality = larger file size. Recommended: 80-90 for photos, 90-100 for graphics
|
|
323
|
+
</Typography>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
196
326
|
</div>
|
|
197
327
|
</ExpandablePanel>
|
|
198
328
|
|
|
@@ -10,7 +10,7 @@ export function StringPropertyField({
|
|
|
10
10
|
disabled,
|
|
11
11
|
showErrors
|
|
12
12
|
}: {
|
|
13
|
-
widgetId: "text_field" | "multiline" | "email";
|
|
13
|
+
widgetId: "text_field" | "multiline" | "email" | "user_select";
|
|
14
14
|
disabled: boolean;
|
|
15
15
|
showErrors: boolean;
|
|
16
16
|
}) {
|
|
@@ -33,6 +33,10 @@ export function StringPropertyField({
|
|
|
33
33
|
trim={true}
|
|
34
34
|
uppercase={true}
|
|
35
35
|
showErrors={showErrors}/>}
|
|
36
|
+
{widgetId === "user_select" &&
|
|
37
|
+
<StringPropertyValidation disabled={disabled}
|
|
38
|
+
showErrors={showErrors}/>}
|
|
39
|
+
|
|
36
40
|
{widgetId === "multiline" &&
|
|
37
41
|
<StringPropertyValidation disabled={disabled}
|
|
38
42
|
length={true}
|
|
@@ -27,7 +27,24 @@ export function updatePropertyFromWidget(propertyData: any,
|
|
|
27
27
|
markdown: undefined,
|
|
28
28
|
email: undefined,
|
|
29
29
|
url: undefined,
|
|
30
|
-
enumValues: undefined
|
|
30
|
+
enumValues: undefined,
|
|
31
|
+
userSelect: undefined
|
|
32
|
+
} satisfies StringProperty
|
|
33
|
+
);
|
|
34
|
+
} else if (selectedWidgetId === "user_select") {
|
|
35
|
+
updatedProperty = mergeDeep(
|
|
36
|
+
propertyData,
|
|
37
|
+
{
|
|
38
|
+
dataType: "string",
|
|
39
|
+
propertyConfig: "user_select",
|
|
40
|
+
editable: propertyData.editable !== undefined ? propertyData.editable : true,
|
|
41
|
+
storage: undefined,
|
|
42
|
+
multiline: undefined,
|
|
43
|
+
markdown: undefined,
|
|
44
|
+
email: undefined,
|
|
45
|
+
url: undefined,
|
|
46
|
+
enumValues: undefined,
|
|
47
|
+
userSelect: true
|
|
31
48
|
} satisfies StringProperty
|
|
32
49
|
);
|
|
33
50
|
} else if (selectedWidgetId === "multiline") {
|
|
@@ -42,7 +59,8 @@ export function updatePropertyFromWidget(propertyData: any,
|
|
|
42
59
|
markdown: undefined,
|
|
43
60
|
email: undefined,
|
|
44
61
|
url: undefined,
|
|
45
|
-
enumValues: undefined
|
|
62
|
+
enumValues: undefined,
|
|
63
|
+
userSelect: undefined
|
|
46
64
|
} satisfies StringProperty
|
|
47
65
|
);
|
|
48
66
|
} else if (selectedWidgetId === "markdown") {
|
|
@@ -56,7 +74,8 @@ export function updatePropertyFromWidget(propertyData: any,
|
|
|
56
74
|
multiline: undefined,
|
|
57
75
|
markdown: true,
|
|
58
76
|
email: undefined,
|
|
59
|
-
url: undefined
|
|
77
|
+
url: undefined,
|
|
78
|
+
userSelect: undefined
|
|
60
79
|
} satisfies StringProperty
|
|
61
80
|
);
|
|
62
81
|
} else if (selectedWidgetId === "url") {
|
|
@@ -71,7 +90,8 @@ export function updatePropertyFromWidget(propertyData: any,
|
|
|
71
90
|
markdown: undefined,
|
|
72
91
|
email: undefined,
|
|
73
92
|
url: true,
|
|
74
|
-
enumValues: undefined
|
|
93
|
+
enumValues: undefined,
|
|
94
|
+
userSelect: undefined
|
|
75
95
|
} satisfies StringProperty
|
|
76
96
|
);
|
|
77
97
|
} else if (selectedWidgetId === "email") {
|
|
@@ -86,7 +106,8 @@ export function updatePropertyFromWidget(propertyData: any,
|
|
|
86
106
|
markdown: undefined,
|
|
87
107
|
email: true,
|
|
88
108
|
url: undefined,
|
|
89
|
-
enumValues: undefined
|
|
109
|
+
enumValues: undefined,
|
|
110
|
+
userSelect: undefined
|
|
90
111
|
} satisfies StringProperty
|
|
91
112
|
);
|
|
92
113
|
} else if (selectedWidgetId === "select") {
|
|
@@ -101,7 +122,8 @@ export function updatePropertyFromWidget(propertyData: any,
|
|
|
101
122
|
markdown: undefined,
|
|
102
123
|
email: undefined,
|
|
103
124
|
url: undefined,
|
|
104
|
-
enumValues: propertyData.enumValues ?? []
|
|
125
|
+
enumValues: propertyData.enumValues ?? [],
|
|
126
|
+
userSelect: undefined
|
|
105
127
|
} satisfies StringProperty
|
|
106
128
|
);
|
|
107
129
|
} else if (selectedWidgetId === "multi_select") {
|
package/src/utils/collections.ts
CHANGED
package/src/utils/entities.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { isPropertyBuilder, Properties, PropertiesOrBuilders, Property, PropertyOrBuilder } from "@firecms/core";
|
|
2
2
|
|
|
3
|
-
export function editableProperty(property: PropertyOrBuilder
|
|
4
|
-
if (isPropertyBuilder(property))
|
|
3
|
+
export function editableProperty(property: PropertyOrBuilder): boolean {
|
|
4
|
+
if (isPropertyBuilder(property)) {
|
|
5
5
|
return false;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
else {
|
|
9
|
-
const eProperty = property as Property;
|
|
10
|
-
if (eProperty.dataType === "array" && typeof eProperty.of === "function")
|
|
6
|
+
} else {
|
|
7
|
+
if (property.dataType === "array" && typeof property.of === "function")
|
|
11
8
|
return false;
|
|
12
|
-
else if (
|
|
9
|
+
else if (property.dataType === "array" && Array.isArray(property.of))
|
|
13
10
|
return false;
|
|
14
|
-
return Boolean(
|
|
11
|
+
return property.editable === undefined ? true : Boolean(property.editable);
|
|
15
12
|
}
|
|
16
13
|
}
|
|
17
14
|
|