@firecms/collection_editor 3.0.0-canary.12 → 3.0.0-canary.121
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/LICENSE +114 -21
- package/dist/ConfigControllerProvider.d.ts +11 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +4675 -3456
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +6680 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +14 -2
- package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +4 -3
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +1 -1
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +9 -9
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
- package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
- package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
- package/dist/useCollectionEditorPlugin.d.ts +15 -9
- package/dist/utils/collections.d.ts +6 -0
- package/package.json +20 -34
- package/src/ConfigControllerProvider.tsx +75 -63
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +14 -4
- package/src/ui/CollectionViewHeaderAction.tsx +9 -4
- package/src/ui/EditorCollectionAction.tsx +10 -63
- package/src/ui/EditorCollectionActionStart.tsx +88 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +16 -11
- package/src/ui/NewCollectionButton.tsx +12 -10
- package/src/ui/NewCollectionCard.tsx +3 -3
- package/src/ui/PropertyAddColumnComponent.tsx +9 -4
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +67 -7
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +56 -31
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +6 -5
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +33 -27
- package/src/ui/collection_editor/GetCodeDialog.tsx +15 -3
- package/src/ui/collection_editor/PropertyEditView.tsx +15 -7
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +3 -6
- package/src/ui/collection_editor/PropertySelectItem.tsx +3 -3
- package/src/ui/collection_editor/PropertyTree.tsx +7 -5
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +6 -4
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +25 -9
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +9 -7
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +14 -8
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +1 -1
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +5 -5
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +31 -16
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +1 -10
- package/src/useCollectionEditorPlugin.tsx +37 -27
- package/src/utils/collections.ts +30 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
- package/src/ui/RootCollectionSuggestions.tsx +0 -63
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import {
|
|
4
4
|
CircularProgressCenter,
|
|
5
|
+
Entity,
|
|
5
6
|
EntityCollection,
|
|
6
7
|
ErrorView,
|
|
7
8
|
isPropertyBuilder,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
import {
|
|
26
27
|
ArrowBackIcon,
|
|
27
28
|
Button,
|
|
28
|
-
|
|
29
|
+
cls,
|
|
29
30
|
coolIconKeys,
|
|
30
31
|
defaultBorderMixin,
|
|
31
32
|
Dialog,
|
|
@@ -76,9 +77,10 @@ export interface CollectionEditorDialogProps {
|
|
|
76
77
|
icon: React.ReactNode
|
|
77
78
|
};
|
|
78
79
|
pathSuggestions?: (path?: string) => Promise<string[]>;
|
|
79
|
-
getUser
|
|
80
|
+
getUser?: (uid: string) => User | null;
|
|
80
81
|
getData?: (path: string, parentPaths: string[]) => Promise<object[]>;
|
|
81
82
|
parentCollection?: PersistedCollection;
|
|
83
|
+
existingEntities?: Entity<any>[];
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
@@ -88,13 +90,13 @@ export function CollectionEditorDialog(props: CollectionEditorDialogProps) {
|
|
|
88
90
|
const [formDirty, setFormDirty] = React.useState<boolean>(false);
|
|
89
91
|
const [unsavedChangesDialogOpen, setUnsavedChangesDialogOpen] = React.useState<boolean>(false);
|
|
90
92
|
|
|
91
|
-
const handleCancel =
|
|
93
|
+
const handleCancel = () => {
|
|
92
94
|
if (!formDirty) {
|
|
93
95
|
props.handleClose(undefined);
|
|
94
96
|
} else {
|
|
95
97
|
setUnsavedChangesDialogOpen(true);
|
|
96
98
|
}
|
|
97
|
-
}
|
|
99
|
+
};
|
|
98
100
|
|
|
99
101
|
useEffect(() => {
|
|
100
102
|
if (!open) {
|
|
@@ -136,7 +138,7 @@ type EditorView = "welcome"
|
|
|
136
138
|
| "extra_view"
|
|
137
139
|
| "subcollections";
|
|
138
140
|
|
|
139
|
-
export function CollectionEditor
|
|
141
|
+
export function CollectionEditor(props: CollectionEditorDialogProps & {
|
|
140
142
|
handleCancel: () => void,
|
|
141
143
|
setFormDirty: (dirty: boolean) => void
|
|
142
144
|
}) {
|
|
@@ -154,14 +156,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
154
156
|
const collectionsInThisLevel = (props.parentCollection ? props.parentCollection.subcollections : collections) ?? [];
|
|
155
157
|
const existingPaths = collectionsInThisLevel.map(col => col.path.trim().toLowerCase());
|
|
156
158
|
const existingIds = collectionsInThisLevel.map(col => col.id?.trim().toLowerCase()).filter(Boolean) as string[];
|
|
157
|
-
const [collection, setCollection] = React.useState<PersistedCollection<
|
|
159
|
+
const [collection, setCollection] = React.useState<PersistedCollection<any> | undefined>();
|
|
158
160
|
const [initialLoadingCompleted, setInitialLoadingCompleted] = React.useState(false);
|
|
159
161
|
|
|
160
162
|
useEffect(() => {
|
|
161
163
|
try {
|
|
162
164
|
if (navigation.initialised) {
|
|
163
165
|
if (props.editedCollectionId) {
|
|
164
|
-
setCollection(navigation.getCollectionFromPaths
|
|
166
|
+
setCollection(navigation.getCollectionFromPaths([...(props.parentCollectionIds ?? []), props.editedCollectionId]));
|
|
165
167
|
} else {
|
|
166
168
|
setCollection(undefined);
|
|
167
169
|
}
|
|
@@ -170,7 +172,8 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
170
172
|
} catch (e) {
|
|
171
173
|
console.error(e);
|
|
172
174
|
}
|
|
173
|
-
}, [
|
|
175
|
+
}, [props.editedCollectionId, props.parentCollectionIds, navigation.initialised, navigation.getCollectionFromPaths]);
|
|
176
|
+
|
|
174
177
|
if (!topLevelNavigation) {
|
|
175
178
|
throw Error("Internal: Navigation not ready in collection editor");
|
|
176
179
|
}
|
|
@@ -186,14 +189,14 @@ export function CollectionEditor<M extends Record<string, any>>(props: Collectio
|
|
|
186
189
|
}
|
|
187
190
|
: undefined;
|
|
188
191
|
|
|
189
|
-
const initialValues: PersistedCollection<
|
|
192
|
+
const initialValues: PersistedCollection<any> = initialCollection
|
|
190
193
|
? applyPropertyConfigs(initialCollection, propertyConfigs)
|
|
191
194
|
: {
|
|
192
195
|
id: initialValuesProp?.path ?? randomString(16),
|
|
193
196
|
path: initialValuesProp?.path ?? "",
|
|
194
197
|
name: initialValuesProp?.name ?? "",
|
|
195
198
|
group: initialValuesProp?.group ?? "",
|
|
196
|
-
properties: {} as PropertiesOrBuilders
|
|
199
|
+
properties: {} as PropertiesOrBuilders,
|
|
197
200
|
propertiesOrder: [],
|
|
198
201
|
icon: coolIconKeys[Math.floor(Math.random() * coolIconKeys.length)],
|
|
199
202
|
ownerId: authController.user?.uid ?? ""
|
|
@@ -243,7 +246,8 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
243
246
|
setCollection,
|
|
244
247
|
initialValues,
|
|
245
248
|
propertyConfigs,
|
|
246
|
-
groups
|
|
249
|
+
groups,
|
|
250
|
+
existingEntities
|
|
247
251
|
}: CollectionEditorDialogProps & {
|
|
248
252
|
handleCancel: () => void,
|
|
249
253
|
setFormDirty: (dirty: boolean) => void,
|
|
@@ -293,7 +297,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
293
297
|
});
|
|
294
298
|
};
|
|
295
299
|
|
|
296
|
-
const setNextMode =
|
|
300
|
+
const setNextMode = () => {
|
|
297
301
|
if (currentView === "details") {
|
|
298
302
|
if (importConfig.inUse) {
|
|
299
303
|
setCurrentView("import_data_saving");
|
|
@@ -314,14 +318,14 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
314
318
|
setCurrentView("details");
|
|
315
319
|
}
|
|
316
320
|
|
|
317
|
-
}
|
|
321
|
+
};
|
|
318
322
|
|
|
319
|
-
const doCollectionInference =
|
|
323
|
+
const doCollectionInference = (collection: PersistedCollection<any>) => {
|
|
320
324
|
if (!collectionInference) return undefined;
|
|
321
325
|
return collectionInference?.(collection.path, collection.collectionGroup ?? false, parentCollectionIds ?? []);
|
|
322
|
-
}
|
|
326
|
+
};
|
|
323
327
|
|
|
324
|
-
const inferCollectionFromData =
|
|
328
|
+
const inferCollectionFromData = async (newCollection: PersistedCollection<M>) => {
|
|
325
329
|
|
|
326
330
|
try {
|
|
327
331
|
if (!doCollectionInference) {
|
|
@@ -365,10 +369,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
365
369
|
});
|
|
366
370
|
return newCollection;
|
|
367
371
|
}
|
|
368
|
-
}
|
|
372
|
+
};
|
|
369
373
|
|
|
370
374
|
const onSubmit = (newCollectionState: PersistedCollection<M>, formexController: FormexController<PersistedCollection<M>>) => {
|
|
371
|
-
console.
|
|
375
|
+
console.debug("Submitting collection", newCollectionState);
|
|
372
376
|
try {
|
|
373
377
|
|
|
374
378
|
if (!isNewCollection) {
|
|
@@ -480,25 +484,36 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
480
484
|
|
|
481
485
|
const parentPaths = !pathError && parentCollectionIds ? navigation.convertIdsToPaths(parentCollectionIds) : undefined;
|
|
482
486
|
const resolvedPath = !pathError ? navigation.resolveAliasesFrom(updatedFullPath) : undefined;
|
|
483
|
-
const getDataWithPath = resolvedPath && getData ? () =>
|
|
487
|
+
const getDataWithPath = resolvedPath && getData ? async () => {
|
|
488
|
+
const data = await getData(resolvedPath, parentPaths ?? []);
|
|
489
|
+
if (existingEntities) {
|
|
490
|
+
const existingData = existingEntities.map(e => e.values);
|
|
491
|
+
data.push(...existingData);
|
|
492
|
+
}
|
|
493
|
+
return data;
|
|
494
|
+
} : undefined;
|
|
484
495
|
|
|
485
496
|
useEffect(() => {
|
|
486
497
|
setFormDirty(dirty);
|
|
487
498
|
}, [dirty]);
|
|
488
499
|
|
|
489
|
-
function onImportDataSet(data: object[]) {
|
|
500
|
+
function onImportDataSet(data: object[], propertiesOrder?: string[]) {
|
|
490
501
|
importConfig.setInUse(true);
|
|
491
502
|
buildEntityPropertiesFromData(data, getInferenceType)
|
|
492
503
|
.then((properties) => {
|
|
493
504
|
const res = cleanPropertiesFromImport(properties);
|
|
494
505
|
|
|
495
|
-
setFieldValue("properties", res.properties);
|
|
496
|
-
setFieldValue("propertiesOrder", Object.keys(res.properties));
|
|
497
|
-
|
|
498
506
|
importConfig.setIdColumn(res.idColumn);
|
|
499
507
|
importConfig.setImportData(data);
|
|
500
508
|
importConfig.setHeadersMapping(res.headersMapping);
|
|
509
|
+
const filteredHeadingsOrder = ((propertiesOrder ?? [])
|
|
510
|
+
.filter((key) => res.headersMapping[key]) as string[]) ?? Object.keys(res.properties);
|
|
511
|
+
importConfig.setHeadingsOrder(filteredHeadingsOrder);
|
|
501
512
|
importConfig.setOriginProperties(res.properties);
|
|
513
|
+
|
|
514
|
+
const mappedHeadings = (propertiesOrder ?? []).map((key) => res.headersMapping[key]).filter(Boolean) as string[] ?? Object.keys(res.properties);
|
|
515
|
+
setFieldValue("properties", res.properties);
|
|
516
|
+
setFieldValue("propertiesOrder", mappedHeadings);
|
|
502
517
|
});
|
|
503
518
|
}
|
|
504
519
|
|
|
@@ -521,7 +536,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
521
536
|
|
|
522
537
|
<>
|
|
523
538
|
{!isNewCollection && <Tabs value={currentView}
|
|
524
|
-
className={
|
|
539
|
+
className={cls(defaultBorderMixin, "justify-end bg-gray-50 dark:bg-gray-950 border-b")}
|
|
525
540
|
onValueChange={(v) => setCurrentView(v as EditorView)}>
|
|
526
541
|
<Tab value={"details"}>
|
|
527
542
|
Details
|
|
@@ -536,7 +551,7 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
536
551
|
|
|
537
552
|
<form noValidate
|
|
538
553
|
onSubmit={formController.handleSubmit}
|
|
539
|
-
className={
|
|
554
|
+
className={cls(
|
|
540
555
|
isNewCollection ? "h-full" : "h-[calc(100%-48px)]",
|
|
541
556
|
"flex-grow flex flex-col relative")}>
|
|
542
557
|
|
|
@@ -551,9 +566,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
551
566
|
{currentView === "welcome" &&
|
|
552
567
|
<CollectionEditorWelcomeView
|
|
553
568
|
path={path}
|
|
554
|
-
onContinue={(importData) => {
|
|
569
|
+
onContinue={(importData, propertiesOrder) => {
|
|
570
|
+
// console.log("Import data", importData, propertiesOrder)
|
|
555
571
|
if (importData) {
|
|
556
|
-
onImportDataSet(importData);
|
|
572
|
+
onImportDataSet(importData, propertiesOrder);
|
|
557
573
|
setCurrentView("import_data_mapping");
|
|
558
574
|
} else {
|
|
559
575
|
setCurrentView("details");
|
|
@@ -733,7 +749,10 @@ function CollectionEditorInternal<M extends Record<string, any>>({
|
|
|
733
749
|
}
|
|
734
750
|
|
|
735
751
|
function applyPropertyConfigs<M extends Record<string, any> = any>(collection: PersistedCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): PersistedCollection<M> {
|
|
736
|
-
const {
|
|
752
|
+
const {
|
|
753
|
+
properties,
|
|
754
|
+
...rest
|
|
755
|
+
} = collection;
|
|
737
756
|
const propertiesResult: PropertiesOrBuilders<any> = {};
|
|
738
757
|
if (properties) {
|
|
739
758
|
Object.keys(properties).forEach((key) => {
|
|
@@ -741,7 +760,10 @@ function applyPropertyConfigs<M extends Record<string, any> = any>(collection: P
|
|
|
741
760
|
});
|
|
742
761
|
}
|
|
743
762
|
|
|
744
|
-
return {
|
|
763
|
+
return {
|
|
764
|
+
...rest,
|
|
765
|
+
properties: propertiesResult
|
|
766
|
+
};
|
|
745
767
|
}
|
|
746
768
|
|
|
747
769
|
function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Record<string, PropertyConfig<any>>) {
|
|
@@ -761,7 +783,10 @@ function applyPropertiesConfig(property: PropertyOrBuilder, propertyConfigs: Rec
|
|
|
761
783
|
Object.keys(internalProperty.properties).forEach((key) => {
|
|
762
784
|
properties[key] = applyPropertiesConfig(((internalProperty as MapProperty).properties as Properties)[key] as Property, propertyConfigs);
|
|
763
785
|
});
|
|
764
|
-
internalProperty = {
|
|
786
|
+
internalProperty = {
|
|
787
|
+
...internalProperty,
|
|
788
|
+
properties
|
|
789
|
+
};
|
|
765
790
|
}
|
|
766
791
|
|
|
767
792
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { EntityCollection, unslugify, } from "@firecms/core";
|
|
3
|
-
import { Button, Card, Chip, CircularProgress,
|
|
3
|
+
import { Button, Card, Chip, CircularProgress, cls, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
|
|
4
4
|
|
|
5
5
|
import { productsCollectionTemplate } from "./templates/products_template";
|
|
6
6
|
import { blogCollectionTemplate } from "./templates/blog_template";
|
|
@@ -19,7 +19,7 @@ export function CollectionEditorWelcomeView({
|
|
|
19
19
|
path: string;
|
|
20
20
|
pathSuggestions?: (path: string) => Promise<string[]>;
|
|
21
21
|
parentCollection?: EntityCollection;
|
|
22
|
-
onContinue: (importData?: object[]) => void;
|
|
22
|
+
onContinue: (importData?: object[], propertiesOrder?: string[]) => void;
|
|
23
23
|
existingCollectionPaths?: string[];
|
|
24
24
|
}) {
|
|
25
25
|
|
|
@@ -154,7 +154,7 @@ export function CollectionEditorWelcomeView({
|
|
|
154
154
|
● Create a collection from a file (csv, json, xls, xslx...)
|
|
155
155
|
</Typography>
|
|
156
156
|
|
|
157
|
-
<ImportFileUpload onDataAdded={(data) => onContinue(data)}/>
|
|
157
|
+
<ImportFileUpload onDataAdded={(data, propertiesOrder) => onContinue(data, propertiesOrder)}/>
|
|
158
158
|
|
|
159
159
|
</div>}
|
|
160
160
|
|
|
@@ -185,10 +185,11 @@ export function TemplateButton({
|
|
|
185
185
|
}) {
|
|
186
186
|
|
|
187
187
|
return (
|
|
188
|
-
<Tooltip title={subtitle}
|
|
188
|
+
<Tooltip title={subtitle}
|
|
189
|
+
asChild={true}>
|
|
189
190
|
<Card
|
|
190
191
|
onClick={onClick}
|
|
191
|
-
className={
|
|
192
|
+
className={cls(
|
|
192
193
|
"my-2 rounded-md border mx-0 p-6 px-4 focus:outline-none transition ease-in-out duration-150 flex flex-row gap-4 items-center",
|
|
193
194
|
"text-gray-700 dark:text-slate-300",
|
|
194
195
|
"hover:border-primary-dark hover:text-primary-dark dark:hover:text-primary focus:ring-primary hover:ring-1 hover:ring-primary",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { Field, getIn, useFormex } from "@firecms/formex";
|
|
4
4
|
import {
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
AutoAwesomeIcon,
|
|
20
20
|
Button,
|
|
21
21
|
CircularProgress,
|
|
22
|
-
|
|
22
|
+
cls,
|
|
23
23
|
CodeIcon,
|
|
24
24
|
DebouncedTextField,
|
|
25
25
|
defaultBorderMixin,
|
|
@@ -43,7 +43,7 @@ type CollectionEditorFormProps = {
|
|
|
43
43
|
setDirty?: (dirty: boolean) => void;
|
|
44
44
|
reservedGroups?: string[];
|
|
45
45
|
extraIcon: React.ReactNode;
|
|
46
|
-
getUser
|
|
46
|
+
getUser?: (uid: string) => User | null;
|
|
47
47
|
getData?: () => Promise<object[]>;
|
|
48
48
|
doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
|
|
49
49
|
propertyConfigs: Record<string, PropertyConfig>;
|
|
@@ -158,20 +158,20 @@ export function CollectionPropertiesEditorForm({
|
|
|
158
158
|
}
|
|
159
159
|
: undefined;
|
|
160
160
|
|
|
161
|
-
const getCurrentPropertiesOrder =
|
|
162
|
-
if (!namespace) return currentPropertiesOrderRef.current[""];
|
|
161
|
+
const getCurrentPropertiesOrder = (namespace?: string) => {
|
|
162
|
+
if (!namespace) return currentPropertiesOrderRef.current[""] ?? getIn(values, namespaceToPropertiesOrderPath());
|
|
163
163
|
return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
|
|
164
|
-
}
|
|
164
|
+
};
|
|
165
165
|
|
|
166
|
-
const updatePropertiesOrder =
|
|
166
|
+
const updatePropertiesOrder = (newPropertiesOrder: string[], namespace?: string) => {
|
|
167
167
|
const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
|
|
168
168
|
|
|
169
169
|
setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
|
|
170
170
|
currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
|
|
171
171
|
|
|
172
|
-
}
|
|
172
|
+
};
|
|
173
173
|
|
|
174
|
-
const deleteProperty =
|
|
174
|
+
const deleteProperty = (propertyKey?: string, namespace?: string) => {
|
|
175
175
|
const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
|
|
176
176
|
if (!fullId)
|
|
177
177
|
throw Error("collection editor miss config");
|
|
@@ -179,15 +179,17 @@ export function CollectionPropertiesEditorForm({
|
|
|
179
179
|
setFieldValue(idToPropertiesPath(fullId), undefined, false);
|
|
180
180
|
|
|
181
181
|
const currentPropertiesOrder = getCurrentPropertiesOrder(namespace);
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
if (currentPropertiesOrder) {
|
|
183
|
+
const newPropertiesOrder = currentPropertiesOrder.filter((p) => p !== propertyKey);
|
|
184
|
+
updatePropertiesOrder(newPropertiesOrder, namespace);
|
|
185
|
+
}
|
|
184
186
|
|
|
185
187
|
setNewPropertyDialogOpen(false);
|
|
186
188
|
|
|
187
189
|
setSelectedPropertyIndex(undefined);
|
|
188
190
|
setSelectedPropertyKey(undefined);
|
|
189
191
|
setSelectedPropertyNamespace(undefined);
|
|
190
|
-
}
|
|
192
|
+
};
|
|
191
193
|
|
|
192
194
|
const onPropertyMove = (propertiesOrder: string[], namespace?: string) => {
|
|
193
195
|
setFieldValue(namespaceToPropertiesOrderPath(namespace), propertiesOrder, false);
|
|
@@ -207,8 +209,8 @@ export function CollectionPropertiesEditorForm({
|
|
|
207
209
|
...(values.properties ?? {}),
|
|
208
210
|
[id]: property
|
|
209
211
|
}, false);
|
|
210
|
-
const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties)), id];
|
|
211
212
|
|
|
213
|
+
const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties)), id];
|
|
212
214
|
updatePropertiesOrder(newPropertiesOrder);
|
|
213
215
|
|
|
214
216
|
setNewPropertyDialogOpen(false);
|
|
@@ -273,7 +275,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
273
275
|
|
|
274
276
|
};
|
|
275
277
|
|
|
276
|
-
const onPropertyErrorInternal =
|
|
278
|
+
const onPropertyErrorInternal = (id: string, namespace?: string, error?: Record<string, any>) => {
|
|
277
279
|
const propertyPath = id ? getFullId(id, namespace) : undefined;
|
|
278
280
|
console.debug("onPropertyErrorInternal", {
|
|
279
281
|
id,
|
|
@@ -286,7 +288,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
286
288
|
onPropertyError(id, namespace, hasError ? error : undefined);
|
|
287
289
|
setFieldError(idToPropertiesPath(propertyPath), hasError ? "Property error" : undefined);
|
|
288
290
|
}
|
|
289
|
-
}
|
|
291
|
+
}
|
|
290
292
|
|
|
291
293
|
const closePropertyDialog = () => {
|
|
292
294
|
setSelectedPropertyIndex(undefined);
|
|
@@ -301,9 +303,9 @@ export function CollectionPropertiesEditorForm({
|
|
|
301
303
|
? values.propertiesOrder
|
|
302
304
|
: Object.keys(values.properties)) as string[];
|
|
303
305
|
|
|
304
|
-
const owner = useMemo(() => values.ownerId ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
|
|
306
|
+
const owner = useMemo(() => values.ownerId && getUser ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
|
|
305
307
|
|
|
306
|
-
const onPropertyClick =
|
|
308
|
+
const onPropertyClick = (propertyKey: string, namespace?: string) => {
|
|
307
309
|
console.debug("CollectionEditor: onPropertyClick", {
|
|
308
310
|
propertyKey,
|
|
309
311
|
namespace
|
|
@@ -311,11 +313,11 @@ export function CollectionPropertiesEditorForm({
|
|
|
311
313
|
setSelectedPropertyIndex(usedPropertiesOrder.indexOf(propertyKey));
|
|
312
314
|
setSelectedPropertyKey(propertyKey);
|
|
313
315
|
setSelectedPropertyNamespace(namespace);
|
|
314
|
-
}
|
|
316
|
+
};
|
|
315
317
|
|
|
316
318
|
const body = (
|
|
317
319
|
<div className={"grid grid-cols-12 gap-2 h-full bg-gray-50 dark:bg-gray-900"}>
|
|
318
|
-
<div className={
|
|
320
|
+
<div className={cls(
|
|
319
321
|
"p-4 md:p-8 pb-20 md:pb-20",
|
|
320
322
|
"col-span-12 lg:col-span-5 h-full overflow-auto",
|
|
321
323
|
!asDialog && "border-r " + defaultBorderMixin
|
|
@@ -349,7 +351,8 @@ export function CollectionPropertiesEditorForm({
|
|
|
349
351
|
</div>}
|
|
350
352
|
|
|
351
353
|
<div className="ml-1 mt-2 flex flex-row gap-2">
|
|
352
|
-
<Tooltip title={"Get the code for this collection"}
|
|
354
|
+
<Tooltip title={"Get the code for this collection"}
|
|
355
|
+
asChild={true}>
|
|
353
356
|
<IconButton
|
|
354
357
|
variant={"filled"}
|
|
355
358
|
disabled={inferringProperties}
|
|
@@ -357,7 +360,8 @@ export function CollectionPropertiesEditorForm({
|
|
|
357
360
|
<CodeIcon/>
|
|
358
361
|
</IconButton>
|
|
359
362
|
</Tooltip>
|
|
360
|
-
{inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}
|
|
363
|
+
{inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}
|
|
364
|
+
asChild={true}>
|
|
361
365
|
<IconButton
|
|
362
366
|
variant={"filled"}
|
|
363
367
|
disabled={inferringProperties}
|
|
@@ -365,7 +369,8 @@ export function CollectionPropertiesEditorForm({
|
|
|
365
369
|
{inferringProperties ? <CircularProgress size={"small"}/> : <AutoAwesomeIcon/>}
|
|
366
370
|
</IconButton>
|
|
367
371
|
</Tooltip>}
|
|
368
|
-
<Tooltip title={"Add new property"}
|
|
372
|
+
<Tooltip title={"Add new property"}
|
|
373
|
+
asChild={true}>
|
|
369
374
|
<Button
|
|
370
375
|
variant={"outlined"}
|
|
371
376
|
onClick={() => setNewPropertyDialogOpen(true)}>
|
|
@@ -496,11 +501,12 @@ export function CollectionPropertiesEditorForm({
|
|
|
496
501
|
collectionEditable={collectionEditable}
|
|
497
502
|
existingPropertyKeys={values.propertiesOrder as string[]}/>
|
|
498
503
|
|
|
499
|
-
<
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
+
<ErrorBoundary>
|
|
505
|
+
<GetCodeDialog
|
|
506
|
+
collection={values}
|
|
507
|
+
open={codeDialogOpen}
|
|
508
|
+
onOpenChange={setCodeDialogOpen}/>
|
|
509
|
+
</ErrorBoundary>
|
|
504
510
|
</>
|
|
505
511
|
);
|
|
506
512
|
}
|
|
@@ -5,11 +5,17 @@ import JSON5 from "json5";
|
|
|
5
5
|
import { Highlight, themes } from "prism-react-renderer"
|
|
6
6
|
import { camelCase } from "./utils/strings";
|
|
7
7
|
|
|
8
|
-
export function GetCodeDialog({
|
|
8
|
+
export function GetCodeDialog({
|
|
9
|
+
collection,
|
|
10
|
+
onOpenChange,
|
|
11
|
+
open
|
|
12
|
+
}: { onOpenChange: (open: boolean) => void, collection: any, open: any }) {
|
|
9
13
|
|
|
10
14
|
const snackbarController = useSnackbarController();
|
|
11
15
|
|
|
12
|
-
const code =
|
|
16
|
+
const code = collection
|
|
17
|
+
? "import { EntityCollection } from \"@firecms/core\";\n\nconst " + (collection?.name ? camelCase(collection.name) : "my") + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t")
|
|
18
|
+
: "No collection selected";
|
|
13
19
|
return <Dialog open={open}
|
|
14
20
|
onOpenChange={onOpenChange}
|
|
15
21
|
maxWidth={"4xl"}>
|
|
@@ -29,7 +35,13 @@ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange
|
|
|
29
35
|
code={code}
|
|
30
36
|
language="typescript"
|
|
31
37
|
>
|
|
32
|
-
{({
|
|
38
|
+
{({
|
|
39
|
+
className,
|
|
40
|
+
style,
|
|
41
|
+
tokens,
|
|
42
|
+
getLineProps,
|
|
43
|
+
getTokenProps
|
|
44
|
+
}) => (
|
|
33
45
|
<pre style={style} className={"p-4 rounded text-sm"}>
|
|
34
46
|
{tokens.map((line, i) => (
|
|
35
47
|
<div key={i} {...getLineProps({ line })}>
|
|
@@ -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,10 +13,11 @@ import {
|
|
|
14
13
|
Property,
|
|
15
14
|
PropertyConfig,
|
|
16
15
|
PropertyConfigBadge,
|
|
16
|
+
PropertyConfigId,
|
|
17
17
|
} from "@firecms/core";
|
|
18
18
|
import {
|
|
19
19
|
Button,
|
|
20
|
-
|
|
20
|
+
cls,
|
|
21
21
|
DeleteIcon,
|
|
22
22
|
Dialog,
|
|
23
23
|
DialogActions,
|
|
@@ -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: {
|
|
152
|
+
property: {
|
|
153
|
+
...property,
|
|
154
|
+
editable: property.editable ?? true
|
|
155
|
+
}
|
|
152
156
|
});
|
|
153
157
|
if (!existingProperty)
|
|
154
158
|
controller.resetForm({ values: initialValue });
|
|
@@ -228,6 +232,7 @@ export const PropertyForm = React.memo(
|
|
|
228
232
|
a.includeIdAndName === b.includeIdAndName &&
|
|
229
233
|
a.autoOpenTypeSelect === b.autoOpenTypeSelect &&
|
|
230
234
|
a.autoUpdateId === b.autoUpdateId &&
|
|
235
|
+
a.existingPropertyKeys === b.existingPropertyKeys &&
|
|
231
236
|
a.existingProperty === b.existingProperty
|
|
232
237
|
);
|
|
233
238
|
|
|
@@ -367,13 +372,13 @@ function PropertyEditFormFields({
|
|
|
367
372
|
}
|
|
368
373
|
}
|
|
369
374
|
}
|
|
370
|
-
}, [deferredValues, includeIdAndTitle,
|
|
375
|
+
}, [deferredValues, includeIdAndTitle, propertyNamespace]);
|
|
371
376
|
|
|
372
377
|
useEffect(() => {
|
|
373
378
|
if (values?.id && onError) {
|
|
374
379
|
onError(values?.id, propertyNamespace, errors);
|
|
375
380
|
}
|
|
376
|
-
}, [errors,
|
|
381
|
+
}, [errors, propertyNamespace, values?.id]);
|
|
377
382
|
|
|
378
383
|
const onWidgetSelectChanged = (newSelectedWidgetId: PropertyConfigId) => {
|
|
379
384
|
setSelectedFieldConfigId(newSelectedWidgetId);
|
|
@@ -387,7 +392,6 @@ function PropertyEditFormFields({
|
|
|
387
392
|
let childComponent;
|
|
388
393
|
if (selectedFieldConfigId === "text_field" ||
|
|
389
394
|
selectedFieldConfigId === "multiline" ||
|
|
390
|
-
selectedFieldConfigId === "markdown" ||
|
|
391
395
|
selectedFieldConfigId === "email") {
|
|
392
396
|
childComponent =
|
|
393
397
|
<StringPropertyField widgetId={selectedFieldConfigId}
|
|
@@ -397,6 +401,10 @@ function PropertyEditFormFields({
|
|
|
397
401
|
childComponent =
|
|
398
402
|
<UrlPropertyField disabled={disabled}
|
|
399
403
|
showErrors={showErrors}/>;
|
|
404
|
+
} else if (selectedFieldConfigId === "markdown") {
|
|
405
|
+
childComponent =
|
|
406
|
+
<MarkdownPropertyField disabled={disabled}
|
|
407
|
+
showErrors={showErrors}/>;
|
|
400
408
|
} else if (selectedFieldConfigId === "select" ||
|
|
401
409
|
selectedFieldConfigId === "number_select") {
|
|
402
410
|
childComponent = <EnumPropertyField
|
|
@@ -508,7 +516,7 @@ function PropertyEditFormFields({
|
|
|
508
516
|
e.preventDefault();
|
|
509
517
|
}
|
|
510
518
|
}}
|
|
511
|
-
className={
|
|
519
|
+
className={cls(
|
|
512
520
|
"flex items-center",
|
|
513
521
|
optionDisabled ? "w-full pointer-events-none opacity-50" : "")}>
|
|
514
522
|
<div className={"mr-8"}>
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
cardClickableMixin,
|
|
12
12
|
cardMixin,
|
|
13
13
|
cardSelectedMixin,
|
|
14
|
-
|
|
14
|
+
cls,
|
|
15
15
|
FunctionsIcon,
|
|
16
16
|
Paper,
|
|
17
17
|
RemoveCircleIcon,
|
|
@@ -45,9 +45,6 @@ export function PropertyFieldPreview({
|
|
|
45
45
|
? "border-red-500 dark:border-red-500 border-opacity-100 dark:border-opacity-100 ring-0 dark:ring-0"
|
|
46
46
|
: (selected ? "border-primary" : "border-transparent");
|
|
47
47
|
|
|
48
|
-
if(hasError)
|
|
49
|
-
console.log("PropertyFieldPreview", property)
|
|
50
|
-
|
|
51
48
|
return <ErrorBoundary>
|
|
52
49
|
<div
|
|
53
50
|
onClick={onClick}
|
|
@@ -56,7 +53,7 @@ export function PropertyFieldPreview({
|
|
|
56
53
|
<PropertyConfigBadge propertyConfig={propertyConfig}/>
|
|
57
54
|
</div>
|
|
58
55
|
<Paper
|
|
59
|
-
className={
|
|
56
|
+
className={cls(
|
|
60
57
|
"border",
|
|
61
58
|
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
62
59
|
cardMixin,
|
|
@@ -139,7 +136,7 @@ export function NonEditablePropertyPreview({
|
|
|
139
136
|
<RemoveCircleIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
|
|
140
137
|
</div>
|
|
141
138
|
<Paper
|
|
142
|
-
className={
|
|
139
|
+
className={cls(
|
|
143
140
|
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
144
141
|
cardMixin,
|
|
145
142
|
onClick ? cardClickableMixin : "",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PropertyConfigBadge, PropertyConfig } from "@firecms/core";
|
|
2
|
-
import {
|
|
2
|
+
import { cls, SelectItem, Typography } from "@firecms/ui";
|
|
3
3
|
|
|
4
4
|
export interface PropertySelectItemProps {
|
|
5
5
|
value: string;
|
|
@@ -13,8 +13,8 @@ export function PropertySelectItem({ value, optionDisabled, propertyConfig, exis
|
|
|
13
13
|
disabled={optionDisabled}
|
|
14
14
|
className={"flex flex-row items-center"}>
|
|
15
15
|
<div
|
|
16
|
-
className={
|
|
17
|
-
"flex flex-row items-center text-base min-h-[
|
|
16
|
+
className={cls(
|
|
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}/>
|