@firecms/collection_editor 3.0.0-canary.28 → 3.0.0-canary.280
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/README.md +165 -1
- package/dist/ConfigControllerProvider.d.ts +1 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +10792 -4791
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +11458 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +3 -2
- package/dist/types/collection_inference.d.ts +4 -1
- package/dist/types/config_controller.d.ts +3 -1
- package/dist/types/config_permissions.d.ts +2 -2
- package/dist/types/persisted_collection.d.ts +1 -1
- package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/EditorEntityAction.d.ts +2 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -2
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +2 -2
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +4 -0
- package/dist/ui/collection_editor/EntityActionsSelectDialog.d.ts +4 -0
- package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
- package/dist/ui/collection_editor/PropertyTree.d.ts +11 -12
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +8 -1
- package/dist/ui/collection_editor/import/clean_import_data.d.ts +1 -1
- package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
- package/dist/ui/collection_editor/properties/ReferencePropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
- package/dist/useCollectionEditorPlugin.d.ts +8 -11
- package/dist/utils/collections.d.ts +6 -0
- package/package.json +25 -37
- package/src/ConfigControllerProvider.tsx +64 -66
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +6 -5
- package/src/types/collection_inference.ts +4 -1
- package/src/types/config_controller.tsx +4 -1
- package/src/types/config_permissions.ts +1 -1
- package/src/types/persisted_collection.ts +2 -3
- package/src/ui/CollectionViewHeaderAction.tsx +10 -5
- package/src/ui/EditorCollectionAction.tsx +12 -70
- package/src/ui/EditorCollectionActionStart.tsx +87 -0
- package/src/ui/EditorEntityAction.tsx +51 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +21 -14
- package/src/ui/NewCollectionButton.tsx +1 -1
- package/src/ui/NewCollectionCard.tsx +3 -3
- package/src/ui/PropertyAddColumnComponent.tsx +11 -6
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +157 -50
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +119 -39
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +24 -33
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +46 -41
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +163 -0
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +41 -0
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +11 -7
- package/src/ui/collection_editor/EnumForm.tsx +11 -7
- package/src/ui/collection_editor/GetCodeDialog.tsx +60 -28
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +266 -79
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +8 -10
- package/src/ui/collection_editor/PropertyTree.tsx +184 -138
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +9 -7
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +41 -9
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +43 -10
- package/src/ui/collection_editor/import/clean_import_data.ts +1 -1
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -7
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +7 -3
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +4 -9
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +2 -0
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
- package/src/ui/collection_editor/templates/pages_template.ts +1 -6
- package/src/ui/collection_editor/utils/strings.ts +13 -6
- package/src/ui/collection_editor/utils/supported_fields.tsx +2 -0
- package/src/ui/collection_editor/utils/update_property_for_widget.ts +37 -6
- package/src/useCollectionEditorPlugin.tsx +38 -32
- package/src/utils/collections.ts +46 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
- package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
- package/src/ui/RootCollectionSuggestions.tsx +0 -63
- package/src/ui/collection_editor/PropertySelectItem.tsx +0 -32
|
@@ -1,17 +1,33 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import equal from "react-fast-compare"
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
AdditionalFieldDelegate,
|
|
6
6
|
CMSType,
|
|
7
|
-
ErrorBoundary,
|
|
8
7
|
isPropertyBuilder,
|
|
9
8
|
PropertiesOrBuilders,
|
|
10
9
|
PropertyOrBuilder
|
|
11
10
|
} from "@firecms/core";
|
|
12
|
-
import {
|
|
11
|
+
import { AutorenewIcon, defaultBorderMixin, DragHandleIcon, IconButton, RemoveIcon, Tooltip } from "@firecms/ui";
|
|
13
12
|
import { NonEditablePropertyPreview, PropertyFieldPreview } from "./PropertyFieldPreview";
|
|
14
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
closestCenter,
|
|
15
|
+
DndContext,
|
|
16
|
+
DragEndEvent,
|
|
17
|
+
KeyboardSensor,
|
|
18
|
+
PointerSensor,
|
|
19
|
+
useSensor,
|
|
20
|
+
useSensors
|
|
21
|
+
} from "@dnd-kit/core";
|
|
22
|
+
import {
|
|
23
|
+
SortableContext,
|
|
24
|
+
sortableKeyboardCoordinates,
|
|
25
|
+
useSortable,
|
|
26
|
+
verticalListSortingStrategy
|
|
27
|
+
} from "@dnd-kit/sortable";
|
|
28
|
+
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
|
29
|
+
|
|
30
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
15
31
|
import { getFullId, getFullIdPath } from "./util";
|
|
16
32
|
import { editableProperty } from "../../utils/entities";
|
|
17
33
|
|
|
@@ -48,103 +64,109 @@ export const PropertyTree = React.memo(
|
|
|
48
64
|
|
|
49
65
|
const propertiesOrder = propertiesOrderProp ?? Object.keys(properties);
|
|
50
66
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
const sensors = useSensors(
|
|
68
|
+
useSensor(PointerSensor, {
|
|
69
|
+
activationConstraint: {
|
|
70
|
+
distance: 5,
|
|
71
|
+
}
|
|
72
|
+
}),
|
|
73
|
+
useSensor(KeyboardSensor, {
|
|
74
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const handleDragEnd = (event: DragEndEvent) => {
|
|
79
|
+
const {
|
|
80
|
+
active,
|
|
81
|
+
over
|
|
82
|
+
} = event;
|
|
83
|
+
|
|
84
|
+
if (!over || active.id === over.id) {
|
|
54
85
|
return;
|
|
55
86
|
}
|
|
56
|
-
const startIndex = result.source.index;
|
|
57
|
-
const endIndex = result.destination.index;
|
|
58
87
|
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
88
|
+
const activeId = String(active.id);
|
|
89
|
+
const overId = String(over.id);
|
|
90
|
+
|
|
91
|
+
// Extract property keys from the full IDs
|
|
92
|
+
const activeKey = activeId.includes(".") ? activeId.split(".").pop() : activeId;
|
|
93
|
+
const overKey = overId.includes(".") ? overId.split(".").pop() : overId;
|
|
94
|
+
|
|
95
|
+
if (!activeKey || !overKey) return;
|
|
96
|
+
|
|
97
|
+
const oldIndex = propertiesOrder.indexOf(activeKey);
|
|
98
|
+
const newIndex = propertiesOrder.indexOf(overKey);
|
|
99
|
+
|
|
100
|
+
if (oldIndex !== -1 && newIndex !== -1) {
|
|
101
|
+
const newPropertiesOrder = [...propertiesOrder];
|
|
102
|
+
const [removed] = newPropertiesOrder.splice(oldIndex, 1);
|
|
103
|
+
newPropertiesOrder.splice(newIndex, 0, removed);
|
|
104
|
+
|
|
105
|
+
if (onPropertyMove) {
|
|
106
|
+
onPropertyMove(newPropertiesOrder, namespace);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const items = propertiesOrder.map(key => getFullId(key, namespace));
|
|
65
112
|
|
|
66
113
|
return (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{droppableProvided.placeholder}
|
|
114
|
-
|
|
115
|
-
</div>
|
|
116
|
-
)}
|
|
117
|
-
</Droppable>
|
|
118
|
-
</DragDropContext>
|
|
119
|
-
|
|
120
|
-
</>
|
|
114
|
+
<DndContext
|
|
115
|
+
sensors={sensors}
|
|
116
|
+
collisionDetection={closestCenter}
|
|
117
|
+
onDragEnd={handleDragEnd}
|
|
118
|
+
modifiers={[restrictToVerticalAxis]}
|
|
119
|
+
>
|
|
120
|
+
<SortableContext
|
|
121
|
+
items={items}
|
|
122
|
+
strategy={verticalListSortingStrategy}
|
|
123
|
+
>
|
|
124
|
+
<div className={className}>
|
|
125
|
+
|
|
126
|
+
{propertiesOrder && propertiesOrder
|
|
127
|
+
.map((propertyKey: string, index: number) => {
|
|
128
|
+
const property = properties[propertyKey] as PropertyOrBuilder;
|
|
129
|
+
const additionalField = additionalFields?.find(field => field.key === propertyKey);
|
|
130
|
+
|
|
131
|
+
if (!property && !additionalField) {
|
|
132
|
+
console.warn(`Property ${propertyKey} not found in properties or additionalFields`);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const id = getFullId(propertyKey, namespace);
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<PropertyTreeEntry
|
|
140
|
+
key={id}
|
|
141
|
+
id={id}
|
|
142
|
+
propertyKey={propertyKey}
|
|
143
|
+
propertyOrBuilder={property}
|
|
144
|
+
additionalField={additionalField}
|
|
145
|
+
errors={errors}
|
|
146
|
+
namespace={namespace}
|
|
147
|
+
inferredPropertyKeys={inferredPropertyKeys}
|
|
148
|
+
onPropertyMove={onPropertyMove}
|
|
149
|
+
onPropertyRemove={onPropertyRemove}
|
|
150
|
+
onPropertyClick={onPropertyClick}
|
|
151
|
+
selectedPropertyKey={selectedPropertyKey}
|
|
152
|
+
collectionEditable={collectionEditable}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
}).filter(Boolean)}
|
|
156
|
+
</div>
|
|
157
|
+
</SortableContext>
|
|
158
|
+
</DndContext>
|
|
121
159
|
);
|
|
122
160
|
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const isSelected = nextProps.selectedPropertyKey?.startsWith(nextProps.namespace ?? "");
|
|
126
|
-
const wasSelected = prevProps.selectedPropertyKey?.startsWith(prevProps.namespace ?? "");
|
|
127
|
-
if (isSelected || wasSelected)
|
|
128
|
-
return false;
|
|
129
|
-
|
|
130
|
-
return equal(prevProps.properties, nextProps.properties) &&
|
|
131
|
-
prevProps.propertiesOrder === nextProps.propertiesOrder &&
|
|
132
|
-
equal(prevProps.additionalFields, nextProps.additionalFields) &&
|
|
133
|
-
equal(prevProps.errors, nextProps.errors) &&
|
|
134
|
-
equal(prevProps.onPropertyClick, nextProps.onPropertyClick) &&
|
|
135
|
-
// equal(prevProps.onPropertyMove, nextProps.onPropertyMove) &&
|
|
136
|
-
// equal(prevProps.onPropertyRemove, nextProps.onPropertyRemove) &&
|
|
137
|
-
prevProps.namespace === nextProps.namespace &&
|
|
138
|
-
prevProps.collectionEditable === nextProps.collectionEditable;
|
|
139
|
-
}
|
|
161
|
+
equal
|
|
140
162
|
);
|
|
141
163
|
|
|
142
164
|
export function PropertyTreeEntry({
|
|
165
|
+
id,
|
|
143
166
|
propertyKey,
|
|
144
167
|
namespace,
|
|
145
168
|
propertyOrBuilder,
|
|
146
169
|
additionalField,
|
|
147
|
-
provided,
|
|
148
170
|
selectedPropertyKey,
|
|
149
171
|
errors,
|
|
150
172
|
onPropertyClick,
|
|
@@ -153,12 +175,12 @@ export function PropertyTreeEntry({
|
|
|
153
175
|
inferredPropertyKeys,
|
|
154
176
|
collectionEditable
|
|
155
177
|
}: {
|
|
178
|
+
id: string;
|
|
156
179
|
propertyKey: string;
|
|
157
180
|
namespace?: string;
|
|
158
181
|
propertyOrBuilder: PropertyOrBuilder;
|
|
159
182
|
additionalField?: AdditionalFieldDelegate<any>;
|
|
160
183
|
selectedPropertyKey?: string;
|
|
161
|
-
provided: DraggableProvided;
|
|
162
184
|
errors: Record<string, any>;
|
|
163
185
|
onPropertyClick?: (propertyKey: string, namespace?: string) => void;
|
|
164
186
|
onPropertyMove?: (propertiesOrder: string[], namespace?: string) => void;
|
|
@@ -167,8 +189,27 @@ export function PropertyTreeEntry({
|
|
|
167
189
|
collectionEditable: boolean;
|
|
168
190
|
}) {
|
|
169
191
|
|
|
192
|
+
const {
|
|
193
|
+
attributes,
|
|
194
|
+
listeners,
|
|
195
|
+
setNodeRef,
|
|
196
|
+
transform,
|
|
197
|
+
transition,
|
|
198
|
+
isDragging
|
|
199
|
+
} = useSortable({
|
|
200
|
+
id
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const style = {
|
|
204
|
+
// Key change: use Translate instead of Transform to prevent stretching
|
|
205
|
+
transform: CSS.Translate.toString(transform),
|
|
206
|
+
transition,
|
|
207
|
+
zIndex: isDragging ? 10 : undefined,
|
|
208
|
+
position: "relative" as const,
|
|
209
|
+
};
|
|
210
|
+
|
|
170
211
|
const isPropertyInferred = inferredPropertyKeys?.includes(namespace ? `${namespace}.${propertyKey}` : propertyKey);
|
|
171
|
-
const fullId =
|
|
212
|
+
const fullId = id;
|
|
172
213
|
const fullIdPath = getFullIdPath(propertyKey, namespace);
|
|
173
214
|
const hasError = fullIdPath in errors;
|
|
174
215
|
|
|
@@ -190,63 +231,68 @@ export function PropertyTreeEntry({
|
|
|
190
231
|
}
|
|
191
232
|
}
|
|
192
233
|
|
|
193
|
-
// const hasError = fullId ? getIn(errors, idToPropertiesPath(fullId)) : false;
|
|
194
234
|
const selected = selectedPropertyKey === fullId;
|
|
195
235
|
const editable = propertyOrBuilder && ((collectionEditable && !isPropertyBuilder(propertyOrBuilder)) || editableProperty(propertyOrBuilder));
|
|
196
236
|
|
|
197
237
|
return (
|
|
198
238
|
<div
|
|
199
|
-
ref={
|
|
200
|
-
{
|
|
201
|
-
{...provided.dragHandleProps}
|
|
239
|
+
ref={setNodeRef}
|
|
240
|
+
style={style}
|
|
202
241
|
className="relative -ml-8"
|
|
203
242
|
>
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
{!isPropertyBuilder(propertyOrBuilder) && !additionalField && editable
|
|
213
|
-
? <PropertyFieldPreview
|
|
214
|
-
property={propertyOrBuilder}
|
|
215
|
-
onClick={onPropertyClick ? () => onPropertyClick(propertyKey, namespace) : undefined}
|
|
216
|
-
includeName={true}
|
|
217
|
-
selected={selected}
|
|
218
|
-
hasError={hasError}/>
|
|
219
|
-
: <NonEditablePropertyPreview name={propertyKey}
|
|
220
|
-
property={propertyOrBuilder}
|
|
221
|
-
onClick={onPropertyClick ? () => onPropertyClick(propertyKey, namespace) : undefined}
|
|
222
|
-
selected={selected}/>}
|
|
223
|
-
|
|
224
|
-
<div className="absolute top-2 right-2 flex flex-row ">
|
|
225
|
-
|
|
226
|
-
{isPropertyInferred && <Tooltip title={"Inferred property"}>
|
|
227
|
-
<AutoAwesomeIcon size="small" className={"p-2"}/>
|
|
228
|
-
</Tooltip>}
|
|
229
|
-
|
|
230
|
-
{onPropertyRemove && <Tooltip title={"Remove"}>
|
|
231
|
-
<IconButton size="small"
|
|
232
|
-
color="inherit"
|
|
233
|
-
onClick={() => onPropertyRemove(propertyKey, namespace)}>
|
|
234
|
-
<RemoveIcon size={"small"}/>
|
|
235
|
-
</IconButton>
|
|
236
|
-
</Tooltip>}
|
|
237
|
-
|
|
238
|
-
{onPropertyMove && <Tooltip title={"Move"}>
|
|
239
|
-
<IconButton
|
|
240
|
-
component={"span"}
|
|
241
|
-
size="small"
|
|
242
|
-
>
|
|
243
|
-
<DragHandleIcon size={"small"}/>
|
|
244
|
-
</IconButton>
|
|
245
|
-
</Tooltip>}
|
|
246
|
-
</div>
|
|
243
|
+
<div className="relative">
|
|
244
|
+
{subtree && <div
|
|
245
|
+
className={"absolute border-l " + defaultBorderMixin}
|
|
246
|
+
style={{
|
|
247
|
+
left: "32px",
|
|
248
|
+
top: "64px",
|
|
249
|
+
bottom: "16px"
|
|
250
|
+
}}/>}
|
|
247
251
|
|
|
248
|
-
|
|
252
|
+
<div>
|
|
253
|
+
{!isPropertyBuilder(propertyOrBuilder) && !additionalField && editable
|
|
254
|
+
? <PropertyFieldPreview
|
|
255
|
+
property={propertyOrBuilder}
|
|
256
|
+
onClick={onPropertyClick ? () => onPropertyClick(propertyKey, namespace) : undefined}
|
|
257
|
+
includeName={true}
|
|
258
|
+
selected={selected}
|
|
259
|
+
hasError={hasError}/>
|
|
260
|
+
: <NonEditablePropertyPreview name={propertyKey}
|
|
261
|
+
property={propertyOrBuilder}
|
|
262
|
+
onClick={onPropertyClick ? () => onPropertyClick(propertyKey, namespace) : undefined}
|
|
263
|
+
selected={selected}/>}
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div className="absolute top-2 right-2 flex flex-row">
|
|
267
|
+
{isPropertyInferred && <Tooltip title={"Inferred property"}>
|
|
268
|
+
<AutorenewIcon size="small" className={"p-2"}/>
|
|
269
|
+
</Tooltip>}
|
|
270
|
+
|
|
271
|
+
{onPropertyRemove && !isPropertyInferred && <Tooltip title={"Remove"}
|
|
272
|
+
asChild={true}>
|
|
273
|
+
<IconButton size="small"
|
|
274
|
+
color="inherit"
|
|
275
|
+
onClick={() => onPropertyRemove(propertyKey, namespace)}>
|
|
276
|
+
<RemoveIcon size={"small"}/>
|
|
277
|
+
</IconButton>
|
|
278
|
+
</Tooltip>}
|
|
279
|
+
|
|
280
|
+
{onPropertyMove && <Tooltip title={"Move"}
|
|
281
|
+
asChild={true}>
|
|
282
|
+
<IconButton
|
|
283
|
+
component={"span"}
|
|
284
|
+
size="small"
|
|
285
|
+
{...attributes}
|
|
286
|
+
{...listeners}
|
|
287
|
+
>
|
|
288
|
+
<DragHandleIcon size={"small"}/>
|
|
289
|
+
</IconButton>
|
|
290
|
+
</Tooltip>}
|
|
291
|
+
|
|
292
|
+
</div>
|
|
293
|
+
|
|
294
|
+
{subtree && <div className={"ml-16"}>{subtree}</div>}
|
|
295
|
+
</div>
|
|
249
296
|
</div>
|
|
250
297
|
);
|
|
251
|
-
|
|
252
298
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
ConfirmationDialog,
|
|
4
4
|
EntityCollection,
|
|
5
5
|
EntityCustomView,
|
|
6
6
|
resolveEntityView,
|
|
@@ -41,7 +41,7 @@ export function SubcollectionsEditTab({
|
|
|
41
41
|
parentCollection?: EntityCollection,
|
|
42
42
|
configController: CollectionsConfigController;
|
|
43
43
|
collectionInference?: CollectionInference;
|
|
44
|
-
getUser
|
|
44
|
+
getUser?: (uid: string) => User | null;
|
|
45
45
|
parentCollectionIds?: string[];
|
|
46
46
|
}) {
|
|
47
47
|
|
|
@@ -61,7 +61,7 @@ export function SubcollectionsEditTab({
|
|
|
61
61
|
setFieldValue,
|
|
62
62
|
} = useFormex<EntityCollection>();
|
|
63
63
|
|
|
64
|
-
const subcollections = collection.subcollections ?? [];
|
|
64
|
+
const [subcollections, setSubcollections] = React.useState<EntityCollection[]>(collection.subcollections ?? []);
|
|
65
65
|
const resolvedEntityViews = values.entityViews?.filter(e => typeof e === "string")
|
|
66
66
|
.map(e => resolveEntityView(e, contextEntityViews))
|
|
67
67
|
.filter(Boolean) as EntityCustomView[] ?? [];
|
|
@@ -95,7 +95,8 @@ export function SubcollectionsEditTab({
|
|
|
95
95
|
</TableCell>
|
|
96
96
|
<TableCell
|
|
97
97
|
align="right">
|
|
98
|
-
<Tooltip title={"Remove"}
|
|
98
|
+
<Tooltip title={"Remove"}
|
|
99
|
+
asChild={true}>
|
|
99
100
|
<IconButton size="small"
|
|
100
101
|
onClick={(e) => {
|
|
101
102
|
e.preventDefault();
|
|
@@ -135,7 +136,7 @@ export function SubcollectionsEditTab({
|
|
|
135
136
|
{totalEntityViews === 0 &&
|
|
136
137
|
<Alert action={<Button variant="text"
|
|
137
138
|
size={"small"}
|
|
138
|
-
href={"https://firecms.co/docs/
|
|
139
|
+
href={"https://firecms.co/docs/cloud/quickstart"}
|
|
139
140
|
component={"a"}
|
|
140
141
|
rel="noopener noreferrer"
|
|
141
142
|
target="_blank">More info</Button>}>
|
|
@@ -157,7 +158,8 @@ export function SubcollectionsEditTab({
|
|
|
157
158
|
</TableCell>
|
|
158
159
|
<TableCell
|
|
159
160
|
align="right">
|
|
160
|
-
<Tooltip title={"Remove"}
|
|
161
|
+
<Tooltip title={"Remove"}
|
|
162
|
+
asChild={true}>
|
|
161
163
|
<IconButton size="small"
|
|
162
164
|
onClick={(e) => {
|
|
163
165
|
e.preventDefault();
|
|
@@ -209,30 +211,32 @@ export function SubcollectionsEditTab({
|
|
|
209
211
|
<div style={{ height: "52px" }}/>
|
|
210
212
|
|
|
211
213
|
{subcollectionToDelete &&
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
+
<ConfirmationDialog open={Boolean(subcollectionToDelete)}
|
|
215
|
+
onAccept={() => {
|
|
214
216
|
const props = {
|
|
215
217
|
id: subcollectionToDelete,
|
|
216
218
|
parentCollectionIds: [...(parentCollectionIds ?? []), collection.id]
|
|
217
219
|
};
|
|
218
220
|
console.debug("Deleting subcollection", props)
|
|
219
|
-
configController.deleteCollection(props)
|
|
220
|
-
|
|
221
|
+
configController.deleteCollection(props).then(() => {
|
|
222
|
+
setSubcollectionToDelete(undefined);
|
|
223
|
+
setSubcollections(subcollections?.filter(e => e.id !== subcollectionToDelete))
|
|
224
|
+
});
|
|
221
225
|
}}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
226
|
+
onCancel={() => setSubcollectionToDelete(undefined)}
|
|
227
|
+
title={<>Delete this subcollection?</>}
|
|
228
|
+
body={<> This will <b>not
|
|
225
229
|
delete any data</b>, only
|
|
226
230
|
the collection in the CMS</>}/>}
|
|
227
231
|
{viewToDelete &&
|
|
228
|
-
<
|
|
229
|
-
|
|
232
|
+
<ConfirmationDialog open={Boolean(viewToDelete)}
|
|
233
|
+
onAccept={() => {
|
|
230
234
|
setFieldValue("entityViews", values.entityViews?.filter(e => e !== viewToDelete));
|
|
231
235
|
setViewToDelete(undefined);
|
|
232
236
|
}}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
onCancel={() => setViewToDelete(undefined)}
|
|
238
|
+
title={<>Remove this view?</>}
|
|
239
|
+
body={<>This will <b>not
|
|
236
240
|
delete any data</b>, only
|
|
237
241
|
the view in the CMS</>}/>}
|
|
238
242
|
|
|
@@ -245,7 +249,10 @@ export function SubcollectionsEditTab({
|
|
|
245
249
|
isNewCollection={false}
|
|
246
250
|
{...currentDialog}
|
|
247
251
|
getUser={getUser}
|
|
248
|
-
handleClose={() => {
|
|
252
|
+
handleClose={(updatedCollection) => {
|
|
253
|
+
if (updatedCollection && !subcollections.map(e => e.id).includes(updatedCollection.id)) {
|
|
254
|
+
setSubcollections([...subcollections, updatedCollection]);
|
|
255
|
+
}
|
|
249
256
|
setCurrentDialog(undefined);
|
|
250
257
|
}}/>
|
|
251
258
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Button, Dialog, DialogActions, DialogContent, Typography } from "@firecms/ui";
|
|
2
|
+
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from "@firecms/ui";
|
|
3
3
|
|
|
4
4
|
export interface UnsavedChangesDialogProps {
|
|
5
5
|
open: boolean;
|
|
@@ -24,11 +24,9 @@ export function UnsavedChangesDialog({
|
|
|
24
24
|
aria-labelledby="alert-dialog-title"
|
|
25
25
|
aria-describedby="alert-dialog-description"
|
|
26
26
|
>
|
|
27
|
-
<DialogContent>
|
|
28
|
-
<Typography variant={"h6"}>
|
|
29
|
-
{title ?? "Unsaved changes"}
|
|
30
|
-
</Typography>
|
|
31
27
|
|
|
28
|
+
<DialogTitle>{title ?? "Unsaved changes"}</DialogTitle>
|
|
29
|
+
<DialogContent>
|
|
32
30
|
{body && <Typography>
|
|
33
31
|
{body}
|
|
34
32
|
</Typography>}
|
|
@@ -39,8 +37,12 @@ export function UnsavedChangesDialog({
|
|
|
39
37
|
</DialogContent>
|
|
40
38
|
|
|
41
39
|
<DialogActions>
|
|
42
|
-
<Button variant="text"
|
|
43
|
-
|
|
40
|
+
<Button variant="text"
|
|
41
|
+
color={"primary"}
|
|
42
|
+
onClick={handleCancel} autoFocus> Cancel </Button>
|
|
43
|
+
<Button
|
|
44
|
+
color={"primary"}
|
|
45
|
+
onClick={handleOk}> Ok </Button>
|
|
44
46
|
</DialogActions>
|
|
45
47
|
</Dialog>
|
|
46
48
|
);
|
|
@@ -1,21 +1,48 @@
|
|
|
1
|
-
import { convertDataToEntity,
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { convertDataToEntity, ImportConfig } from "@firecms/data_import";
|
|
2
|
+
import {
|
|
3
|
+
CircularProgressCenter,
|
|
4
|
+
EntityCollectionTable,
|
|
5
|
+
Properties,
|
|
6
|
+
useAuthController,
|
|
7
|
+
useNavigationController,
|
|
8
|
+
useSelectionController
|
|
9
|
+
} from "@firecms/core";
|
|
10
|
+
import { useEffect, useState } from "react";
|
|
4
11
|
import { Typography } from "@firecms/ui";
|
|
5
12
|
|
|
6
|
-
export function CollectionEditorImportDataPreview({
|
|
13
|
+
export function CollectionEditorImportDataPreview({
|
|
14
|
+
importConfig,
|
|
15
|
+
properties,
|
|
16
|
+
propertiesOrder
|
|
17
|
+
}: {
|
|
7
18
|
importConfig: ImportConfig,
|
|
8
19
|
properties: Properties,
|
|
9
20
|
propertiesOrder: string[]
|
|
10
21
|
}) {
|
|
11
22
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
const authController = useAuthController();
|
|
24
|
+
const navigation = useNavigationController();
|
|
25
|
+
const [loading, setLoading] = useState<boolean>(false);
|
|
26
|
+
|
|
27
|
+
async function loadEntities() {
|
|
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));
|
|
15
36
|
importConfig.setEntities(mappedData);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
loadEntities().finally(() => setLoading(false));
|
|
16
41
|
}, []);
|
|
17
42
|
|
|
18
43
|
const selectionController = useSelectionController();
|
|
44
|
+
if (loading)
|
|
45
|
+
return <CircularProgressCenter/>
|
|
19
46
|
|
|
20
47
|
return <EntityCollectionTable
|
|
21
48
|
title={<div>
|
|
@@ -31,7 +58,12 @@ export function CollectionEditorImportDataPreview({ importConfig, properties, pr
|
|
|
31
58
|
filterable={false}
|
|
32
59
|
sortable={false}
|
|
33
60
|
selectionController={selectionController}
|
|
34
|
-
displayedColumnIds={propertiesOrder.map(p => ({
|
|
35
|
-
|
|
61
|
+
displayedColumnIds={propertiesOrder.map(p => ({
|
|
62
|
+
key: p,
|
|
63
|
+
disabled: false
|
|
64
|
+
}))}
|
|
65
|
+
openEntityMode={"side_panel"}
|
|
66
|
+
properties={properties}
|
|
67
|
+
enablePopupIcon={false}/>
|
|
36
68
|
|
|
37
69
|
}
|