@firecms/core 3.0.0-beta.13 → 3.0.0-beta.15
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/components/ArrayContainer.d.ts +7 -12
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
- package/dist/components/EntityPreview.d.ts +4 -2
- package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
- package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
- package/dist/components/HomePage/NavigationCard.d.ts +3 -1
- package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
- package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
- package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/core/EntityEditView.d.ts +3 -0
- package/dist/core/EntityEditViewFormActions.d.ts +1 -1
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/form/EntityForm.d.ts +2 -1
- package/dist/form/EntityFormActions.d.ts +6 -2
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/form/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +50 -8
- package/dist/index.es.js +3259 -1649
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3265 -1658
- package/dist/index.umd.js.map +1 -1
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +28 -4
- package/dist/types/customization_controller.d.ts +8 -0
- package/dist/types/entity_actions.d.ts +46 -6
- package/dist/types/firecms.d.ts +10 -1
- package/dist/types/navigation.d.ts +26 -5
- package/dist/types/plugins.d.ts +32 -1
- package/dist/types/properties.d.ts +7 -0
- package/dist/types/property_config.d.ts +1 -1
- package/dist/types/side_entity_controller.d.ts +8 -0
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/icons.d.ts +2 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_from_path.d.ts +4 -0
- package/dist/util/resolutions.d.ts +2 -1
- package/package.json +8 -6
- package/src/components/ArrayContainer.tsx +409 -294
- package/src/components/ClearFilterSortButton.tsx +1 -1
- package/src/components/ConfirmationDialog.tsx +10 -9
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +26 -4
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +11 -4
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
- package/src/components/EntityPreview.tsx +18 -14
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/FireCMSLogo.tsx +7 -51
- package/src/components/HomePage/DefaultHomePage.tsx +491 -156
- package/src/components/HomePage/FavouritesView.tsx +3 -3
- package/src/components/HomePage/HomePageDnD.tsx +599 -0
- package/src/components/HomePage/NavigationCard.tsx +47 -38
- package/src/components/HomePage/NavigationCardBinding.tsx +13 -7
- package/src/components/HomePage/NavigationGroup.tsx +63 -29
- package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
- package/src/components/SelectableTable/SelectableTable.tsx +140 -143
- package/src/components/UnsavedChangesDialog.tsx +6 -2
- package/src/components/VirtualTable/VirtualTable.tsx +8 -30
- package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
- package/src/components/common/default_entity_actions.tsx +25 -9
- package/src/components/common/useDataSourceTableController.tsx +2 -2
- package/src/components/index.tsx +2 -0
- package/src/core/DefaultDrawer.tsx +8 -8
- package/src/core/DrawerNavigationItem.tsx +1 -1
- package/src/core/EntityEditView.tsx +65 -18
- package/src/core/EntityEditViewFormActions.tsx +154 -29
- package/src/core/EntitySidePanel.tsx +19 -21
- package/src/core/FireCMS.tsx +9 -1
- package/src/core/field_configs.tsx +15 -1
- package/src/form/EntityForm.tsx +37 -5
- package/src/form/EntityFormActions.tsx +51 -9
- package/src/form/PropertyFieldBinding.tsx +0 -1
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/components/LabelWithIcon.tsx +1 -1
- package/src/form/components/StorageItemPreview.tsx +1 -1
- package/src/form/components/StorageUploadProgress.tsx +3 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
- package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +216 -153
- package/src/form/index.tsx +1 -0
- package/src/hooks/data/save.ts +0 -6
- package/src/hooks/useBuildNavigationController.tsx +299 -96
- package/src/internal/useBuildSideEntityController.tsx +8 -5
- package/src/preview/PropertyPreview.tsx +14 -0
- package/src/preview/components/ImagePreview.tsx +2 -2
- package/src/preview/components/ReferencePreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
- package/src/routes/FireCMSRoute.tsx +3 -3
- package/src/types/analytics.ts +3 -0
- package/src/types/collections.ts +32 -4
- package/src/types/customization_controller.tsx +9 -0
- package/src/types/entity_actions.tsx +57 -6
- package/src/types/firecms.tsx +11 -2
- package/src/types/navigation.ts +34 -6
- package/src/types/plugins.tsx +40 -1
- package/src/types/properties.ts +8 -0
- package/src/types/property_config.tsx +1 -0
- package/src/types/side_entity_controller.tsx +10 -5
- package/src/util/callbacks.ts +119 -0
- package/src/util/icons.tsx +22 -7
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +15 -5
- package/src/util/navigation_utils.ts +72 -57
- package/src/util/objects.ts +53 -20
- package/src/util/resolutions.ts +13 -1
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React, { useCallback } from "react";
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
ArrayProperty,
|
|
5
4
|
FieldProps,
|
|
6
5
|
PropertyOrBuilder,
|
|
7
6
|
ResolvedArrayProperty,
|
|
@@ -14,7 +13,23 @@ import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
|
14
13
|
|
|
15
14
|
import { getIconForProperty, isReadOnly, resolveProperty } from "../../util";
|
|
16
15
|
import { useAuthController, useSnackbarController, useStorageSource } from "../../hooks";
|
|
17
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
closestCenter,
|
|
18
|
+
DndContext,
|
|
19
|
+
DragEndEvent,
|
|
20
|
+
DragStartEvent,
|
|
21
|
+
KeyboardSensor,
|
|
22
|
+
PointerSensor,
|
|
23
|
+
useSensor,
|
|
24
|
+
useSensors
|
|
25
|
+
} from "@dnd-kit/core";
|
|
26
|
+
import {
|
|
27
|
+
horizontalListSortingStrategy,
|
|
28
|
+
SortableContext,
|
|
29
|
+
sortableKeyboardCoordinates,
|
|
30
|
+
useSortable
|
|
31
|
+
} from "@dnd-kit/sortable";
|
|
32
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
18
33
|
import { StorageFieldItem, useStorageUploadController } from "../../util/useStorageUploadController";
|
|
19
34
|
import { StorageUploadProgress } from "../components/StorageUploadProgress";
|
|
20
35
|
import { StorageItemPreview } from "../components/StorageItemPreview";
|
|
@@ -36,13 +51,6 @@ const rejectDropClasses = "transition-colors duration-200 ease-[cubic-bezier(0,0
|
|
|
36
51
|
|
|
37
52
|
type StorageUploadFieldProps = FieldProps<string | string[]>;
|
|
38
53
|
|
|
39
|
-
/**
|
|
40
|
-
* Field that allows to upload files to Google Cloud Storage.
|
|
41
|
-
*
|
|
42
|
-
* This is one of the internal components that get mapped natively inside forms
|
|
43
|
-
* and tables to the specified properties.
|
|
44
|
-
* @group Form fields
|
|
45
|
-
*/
|
|
46
54
|
export function StorageUploadFieldBinding({
|
|
47
55
|
propertyKey,
|
|
48
56
|
value,
|
|
@@ -106,7 +114,7 @@ export function StorageUploadFieldBinding({
|
|
|
106
114
|
icon={getIconForProperty(property, "small")}
|
|
107
115
|
required={property.validation?.required}
|
|
108
116
|
title={property.name}
|
|
109
|
-
className={"h-
|
|
117
|
+
className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
|
|
110
118
|
|
|
111
119
|
<StorageUpload
|
|
112
120
|
value={internalValue}
|
|
@@ -132,13 +140,101 @@ export function StorageUploadFieldBinding({
|
|
|
132
140
|
);
|
|
133
141
|
}
|
|
134
142
|
|
|
143
|
+
interface SortableStorageItemProps {
|
|
144
|
+
id: number;
|
|
145
|
+
entry: StorageFieldItem;
|
|
146
|
+
property: ResolvedStringProperty;
|
|
147
|
+
name: string;
|
|
148
|
+
metadata?: Record<string, unknown>;
|
|
149
|
+
storagePathBuilder: (file: File) => string;
|
|
150
|
+
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, fileMetadata?: any) => Promise<void>;
|
|
151
|
+
onClear: (clearedStoragePathOrDownloadUrl: string) => void;
|
|
152
|
+
disabled: boolean;
|
|
153
|
+
isSortable: boolean; // Kept for consistency, though dnd-kit handles sortability via context
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function SortableStorageItem({
|
|
157
|
+
id,
|
|
158
|
+
entry,
|
|
159
|
+
property,
|
|
160
|
+
metadata,
|
|
161
|
+
storagePathBuilder,
|
|
162
|
+
onFileUploadComplete,
|
|
163
|
+
onClear,
|
|
164
|
+
disabled,
|
|
165
|
+
}: SortableStorageItemProps) {
|
|
166
|
+
|
|
167
|
+
const {
|
|
168
|
+
attributes,
|
|
169
|
+
listeners,
|
|
170
|
+
setNodeRef,
|
|
171
|
+
transform,
|
|
172
|
+
transition,
|
|
173
|
+
isDragging
|
|
174
|
+
} = useSortable({ id });
|
|
175
|
+
|
|
176
|
+
const style: React.CSSProperties = {
|
|
177
|
+
transform: CSS.Transform.toString(transform),
|
|
178
|
+
transition,
|
|
179
|
+
zIndex: isDragging ? 100 : undefined
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const getImageSizeNumber = (previewSize: PreviewSize): number => {
|
|
183
|
+
switch (previewSize) {
|
|
184
|
+
case "small":
|
|
185
|
+
return 40;
|
|
186
|
+
case "medium":
|
|
187
|
+
return 118;
|
|
188
|
+
case "large":
|
|
189
|
+
return 220;
|
|
190
|
+
default:
|
|
191
|
+
return 118;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
let child: React.ReactNode;
|
|
196
|
+
if (entry.storagePathOrDownloadUrl) {
|
|
197
|
+
child = (
|
|
198
|
+
<StorageItemPreview
|
|
199
|
+
name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
|
|
200
|
+
property={property}
|
|
201
|
+
disabled={disabled}
|
|
202
|
+
value={entry.storagePathOrDownloadUrl}
|
|
203
|
+
onRemove={() => onClear(entry.storagePathOrDownloadUrl!)}
|
|
204
|
+
size={entry.size}/>
|
|
205
|
+
);
|
|
206
|
+
} else if (entry.file) {
|
|
207
|
+
child = (
|
|
208
|
+
<StorageUploadProgress
|
|
209
|
+
entry={entry}
|
|
210
|
+
metadata={metadata}
|
|
211
|
+
storagePath={storagePathBuilder(entry.file)}
|
|
212
|
+
onFileUploadComplete={onFileUploadComplete}
|
|
213
|
+
imageSize={getImageSizeNumber(entry.size)}
|
|
214
|
+
simple={false}
|
|
215
|
+
/>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<div
|
|
221
|
+
ref={setNodeRef}
|
|
222
|
+
style={style}
|
|
223
|
+
{...attributes}
|
|
224
|
+
{...listeners}
|
|
225
|
+
className={cls("rounded-md m-1")}
|
|
226
|
+
tabIndex={-1}
|
|
227
|
+
>
|
|
228
|
+
{child}
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
135
233
|
function FileDropComponent({
|
|
136
234
|
storage,
|
|
137
235
|
disabled,
|
|
138
|
-
isDraggingOver,
|
|
139
236
|
onFilesAdded,
|
|
140
237
|
multipleFilesSupported,
|
|
141
|
-
droppableProvided,
|
|
142
238
|
autoFocus,
|
|
143
239
|
internalValue,
|
|
144
240
|
property,
|
|
@@ -146,26 +242,24 @@ function FileDropComponent({
|
|
|
146
242
|
metadata,
|
|
147
243
|
storagePathBuilder,
|
|
148
244
|
onFileUploadComplete,
|
|
149
|
-
size,
|
|
150
245
|
name,
|
|
151
|
-
helpText
|
|
246
|
+
helpText,
|
|
247
|
+
isDndItemDragging
|
|
152
248
|
}: {
|
|
153
249
|
storage: StorageConfig,
|
|
154
250
|
disabled: boolean,
|
|
155
|
-
|
|
156
|
-
droppableProvided: any,
|
|
157
|
-
onFilesAdded: (acceptedFiles: File[]) => void,
|
|
251
|
+
onFilesAdded: (acceptedFiles: File[]) => Promise<void>,
|
|
158
252
|
multipleFilesSupported: boolean,
|
|
159
253
|
autoFocus: boolean,
|
|
160
254
|
internalValue: StorageFieldItem[],
|
|
161
255
|
property: ResolvedStringProperty,
|
|
162
256
|
onClear: (clearedStoragePathOrDownloadUrl: string) => void,
|
|
163
|
-
metadata
|
|
257
|
+
metadata?: any,
|
|
164
258
|
storagePathBuilder: (file: File) => string,
|
|
165
259
|
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, fileMetadata?: any) => Promise<void>,
|
|
166
|
-
size: PreviewSize,
|
|
167
260
|
name: string,
|
|
168
|
-
helpText: string
|
|
261
|
+
helpText: string,
|
|
262
|
+
isDndItemDragging?: boolean
|
|
169
263
|
}) {
|
|
170
264
|
|
|
171
265
|
const snackbarContext = useSnackbarController();
|
|
@@ -173,16 +267,19 @@ function FileDropComponent({
|
|
|
173
267
|
const {
|
|
174
268
|
getRootProps,
|
|
175
269
|
getInputProps,
|
|
176
|
-
isDragActive,
|
|
270
|
+
isDragActive, // This is for files dragged from OS
|
|
177
271
|
isDragAccept,
|
|
178
272
|
isDragReject
|
|
179
273
|
} = useDropzone({
|
|
180
|
-
accept: storage.acceptedFiles ? storage.acceptedFiles.
|
|
181
|
-
|
|
274
|
+
accept: storage.acceptedFiles ? storage.acceptedFiles.reduce((acc, ext) => ({
|
|
275
|
+
...acc,
|
|
276
|
+
[ext]: []
|
|
277
|
+
}), {}) : undefined,
|
|
278
|
+
disabled: disabled || isDndItemDragging,
|
|
182
279
|
noDragEventsBubbling: true,
|
|
183
280
|
maxSize: storage.maxSize,
|
|
184
281
|
onDrop: onFilesAdded,
|
|
185
|
-
onDropRejected: (fileRejections
|
|
282
|
+
onDropRejected: (fileRejections) => {
|
|
186
283
|
for (const fileRejection of fileRejections) {
|
|
187
284
|
for (const error of fileRejection.errors) {
|
|
188
285
|
console.error("Error uploading file: ", error);
|
|
@@ -214,76 +311,39 @@ function FileDropComponent({
|
|
|
214
311
|
multipleFilesSupported && internalValue.length ? "" : "flex",
|
|
215
312
|
{
|
|
216
313
|
[nonActiveDropClasses]: !isDragActive,
|
|
217
|
-
[activeDropClasses]: isDragActive,
|
|
218
|
-
[rejectDropClasses]: isDragReject,
|
|
219
|
-
[acceptDropClasses]: isDragAccept,
|
|
220
|
-
[disabledClasses]: disabled
|
|
314
|
+
[activeDropClasses]: isDragActive, // OS file drag active
|
|
315
|
+
[rejectDropClasses]: isDragReject, // OS file drag reject
|
|
316
|
+
[acceptDropClasses]: isDragAccept, // OS file drag accept
|
|
317
|
+
[disabledClasses]: disabled || isDndItemDragging // Visually disable if internal drag
|
|
221
318
|
})}
|
|
222
319
|
>
|
|
223
320
|
<div
|
|
224
|
-
{
|
|
225
|
-
ref={droppableProvided.innerRef}
|
|
226
|
-
className={cls("flex items-center p-1 no-scrollbar",
|
|
321
|
+
className={cls("flex items-center p-1 px-4 no-scrollbar",
|
|
227
322
|
multipleFilesSupported && internalValue.length ? "overflow-auto" : "",
|
|
228
323
|
multipleFilesSupported && internalValue.length ? "min-h-[180px]" : "min-h-[250px]"
|
|
229
324
|
)}
|
|
230
325
|
>
|
|
231
|
-
|
|
232
326
|
<input
|
|
233
327
|
autoFocus={autoFocus}
|
|
234
328
|
{...getInputProps()} />
|
|
235
329
|
|
|
236
|
-
{internalValue.map((entry
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
storagePath={storagePathBuilder(entry.file)}
|
|
254
|
-
onFileUploadComplete={onFileUploadComplete}
|
|
255
|
-
imageSize={size === "large" ? 220 : 118}
|
|
256
|
-
simple={false}
|
|
257
|
-
/>
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return (
|
|
262
|
-
<Draggable
|
|
263
|
-
key={`array_field_${name}_${entry.id}`}
|
|
264
|
-
draggableId={`array_field_${name}_${entry.id}`}
|
|
265
|
-
index={index}>
|
|
266
|
-
{(provided, snapshot) => (
|
|
267
|
-
<div
|
|
268
|
-
tabIndex={-1}
|
|
269
|
-
ref={provided.innerRef}
|
|
270
|
-
{...provided.draggableProps}
|
|
271
|
-
{...provided.dragHandleProps}
|
|
272
|
-
className={cls("rounded-md")}
|
|
273
|
-
style={{
|
|
274
|
-
...provided.draggableProps.style
|
|
275
|
-
}}
|
|
276
|
-
>
|
|
277
|
-
{child}
|
|
278
|
-
</div>
|
|
279
|
-
)}
|
|
280
|
-
</Draggable>
|
|
281
|
-
);
|
|
282
|
-
})
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
{droppableProvided.placeholder}
|
|
286
|
-
|
|
330
|
+
{internalValue.map((entry) => (
|
|
331
|
+
<SortableStorageItem
|
|
332
|
+
key={entry.id}
|
|
333
|
+
id={entry.id}
|
|
334
|
+
entry={entry}
|
|
335
|
+
property={property}
|
|
336
|
+
name={name}
|
|
337
|
+
metadata={metadata}
|
|
338
|
+
storagePathBuilder={storagePathBuilder}
|
|
339
|
+
onFileUploadComplete={onFileUploadComplete}
|
|
340
|
+
onClear={onClear}
|
|
341
|
+
disabled={disabled}
|
|
342
|
+
isSortable={multipleFilesSupported}
|
|
343
|
+
/>
|
|
344
|
+
))}
|
|
345
|
+
|
|
346
|
+
{/* Placeholder for empty dropzone text is handled by the outer Typography */}
|
|
287
347
|
</div>
|
|
288
348
|
|
|
289
349
|
<div
|
|
@@ -294,7 +354,6 @@ function FileDropComponent({
|
|
|
294
354
|
{helpText}
|
|
295
355
|
</Typography>
|
|
296
356
|
</div>
|
|
297
|
-
|
|
298
357
|
</div>
|
|
299
358
|
);
|
|
300
359
|
}
|
|
@@ -309,7 +368,7 @@ export interface StorageUploadProps {
|
|
|
309
368
|
autoFocus: boolean;
|
|
310
369
|
disabled: boolean;
|
|
311
370
|
storage: StorageConfig;
|
|
312
|
-
onFilesAdded: (acceptedFiles: File[]) => void
|
|
371
|
+
onFilesAdded: (acceptedFiles: File[]) => Promise<void>; // Updated from useStorageUploadController
|
|
313
372
|
storagePathBuilder: (file: File) => string;
|
|
314
373
|
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, fileMetadata?: any) => Promise<void>;
|
|
315
374
|
}
|
|
@@ -317,7 +376,7 @@ export interface StorageUploadProps {
|
|
|
317
376
|
export function StorageUpload({
|
|
318
377
|
property,
|
|
319
378
|
name,
|
|
320
|
-
value,
|
|
379
|
+
value, // This is internalValue from useStorageUploadController
|
|
321
380
|
setInternalValue,
|
|
322
381
|
onChange,
|
|
323
382
|
multipleFilesSupported,
|
|
@@ -344,10 +403,10 @@ export function StorageUpload({
|
|
|
344
403
|
}
|
|
345
404
|
|
|
346
405
|
const metadata: Record<string, unknown> | undefined = storage?.metadata;
|
|
347
|
-
const
|
|
406
|
+
const [isDndItemDragging, setIsDndItemDragging] = useState(false);
|
|
348
407
|
|
|
349
408
|
const moveItem = useCallback((fromIndex: number, toIndex: number) => {
|
|
350
|
-
if (!multipleFilesSupported) return;
|
|
409
|
+
if (!multipleFilesSupported || fromIndex === toIndex) return;
|
|
351
410
|
const newValue = [...value];
|
|
352
411
|
const item = newValue[fromIndex];
|
|
353
412
|
newValue.splice(fromIndex, 1);
|
|
@@ -359,84 +418,88 @@ export function StorageUpload({
|
|
|
359
418
|
onChange(fieldValue);
|
|
360
419
|
}, [multipleFilesSupported, onChange, setInternalValue, value]);
|
|
361
420
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
421
|
+
const sensors = useSensors(
|
|
422
|
+
useSensor(PointerSensor, {
|
|
423
|
+
activationConstraint: {
|
|
424
|
+
distance: 5, // Start dragging after 5px movement
|
|
425
|
+
},
|
|
426
|
+
}),
|
|
427
|
+
useSensor(KeyboardSensor, {
|
|
428
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
429
|
+
})
|
|
430
|
+
);
|
|
369
431
|
|
|
370
|
-
|
|
432
|
+
const handleDragStart = useCallback((event: DragStartEvent) => {
|
|
433
|
+
setIsDndItemDragging(true);
|
|
434
|
+
}, []);
|
|
435
|
+
|
|
436
|
+
const handleDragEnd = useCallback((event: DragEndEvent) => {
|
|
437
|
+
setIsDndItemDragging(false);
|
|
438
|
+
const {
|
|
439
|
+
active,
|
|
440
|
+
over
|
|
441
|
+
} = event;
|
|
442
|
+
if (over && active.id !== over.id) {
|
|
443
|
+
const oldIndex = value.findIndex(item => item.id === active.id);
|
|
444
|
+
const newIndex = value.findIndex(item => item.id === over.id);
|
|
445
|
+
if (oldIndex !== -1 && newIndex !== -1) {
|
|
446
|
+
moveItem(oldIndex, newIndex);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}, [value, moveItem]);
|
|
371
450
|
|
|
372
451
|
const onClear = useCallback((clearedStoragePathOrDownloadUrl: string) => {
|
|
452
|
+
let newValue: StorageFieldItem[];
|
|
373
453
|
if (multipleFilesSupported) {
|
|
374
|
-
|
|
454
|
+
newValue = value.filter(v => v.storagePathOrDownloadUrl !== clearedStoragePathOrDownloadUrl);
|
|
375
455
|
onChange(newValue.filter(v => !!v.storagePathOrDownloadUrl).map(v => v.storagePathOrDownloadUrl as string));
|
|
376
|
-
setInternalValue(newValue);
|
|
377
456
|
} else {
|
|
457
|
+
newValue = [];
|
|
378
458
|
onChange(null);
|
|
379
|
-
setInternalValue([]);
|
|
380
459
|
}
|
|
381
|
-
|
|
460
|
+
setInternalValue(newValue);
|
|
461
|
+
}, [value, multipleFilesSupported, onChange, setInternalValue]);
|
|
382
462
|
|
|
383
463
|
const helpText = multipleFilesSupported
|
|
384
|
-
? "Drag 'n' drop some files here, or click to select files"
|
|
464
|
+
? "Drag 'n' drop some files here, or click to select files. Drag to reorder."
|
|
385
465
|
: "Drag 'n' drop a file here, or click to select one";
|
|
386
466
|
|
|
387
467
|
const renderProperty: ResolvedStringProperty = multipleFilesSupported
|
|
388
|
-
? (property as
|
|
468
|
+
? (property as ResolvedArrayProperty<string[]>).of as ResolvedStringProperty
|
|
389
469
|
: property as ResolvedStringProperty;
|
|
390
470
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
>
|
|
408
|
-
<StorageItemPreview
|
|
409
|
-
name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
|
|
410
|
-
placeholder={true}
|
|
411
|
-
property={renderProperty}
|
|
412
|
-
disabled={true}
|
|
413
|
-
value={entry.storagePathOrDownloadUrl as string}
|
|
414
|
-
onRemove={onClear}
|
|
415
|
-
size={entry.size}/>
|
|
416
|
-
</div>
|
|
417
|
-
);
|
|
418
|
-
}}
|
|
419
|
-
>
|
|
420
|
-
{(provided, snapshot) => {
|
|
421
|
-
return <FileDropComponent storage={storage}
|
|
422
|
-
disabled={disabled}
|
|
423
|
-
isDraggingOver={snapshot.isDraggingOver}
|
|
424
|
-
droppableProvided={provided}
|
|
425
|
-
onFilesAdded={onFilesAdded}
|
|
426
|
-
multipleFilesSupported={multipleFilesSupported}
|
|
427
|
-
autoFocus={autoFocus}
|
|
428
|
-
internalValue={value}
|
|
429
|
-
property={renderProperty}
|
|
430
|
-
onClear={onClear}
|
|
431
|
-
metadata={metadata}
|
|
432
|
-
storagePathBuilder={storagePathBuilder}
|
|
433
|
-
onFileUploadComplete={onFileUploadComplete}
|
|
434
|
-
size={size}
|
|
435
|
-
name={name}
|
|
436
|
-
helpText={helpText}/>
|
|
437
|
-
}}
|
|
438
|
-
</Droppable>
|
|
439
|
-
</DragDropContext>
|
|
440
|
-
);
|
|
471
|
+
const fileDropProps = {
|
|
472
|
+
storage,
|
|
473
|
+
disabled,
|
|
474
|
+
onFilesAdded,
|
|
475
|
+
multipleFilesSupported,
|
|
476
|
+
autoFocus,
|
|
477
|
+
internalValue: value, // Pass current internalValue
|
|
478
|
+
property: renderProperty,
|
|
479
|
+
onClear,
|
|
480
|
+
metadata,
|
|
481
|
+
storagePathBuilder,
|
|
482
|
+
onFileUploadComplete,
|
|
483
|
+
name,
|
|
484
|
+
helpText,
|
|
485
|
+
isDndItemDragging // Pass this down
|
|
486
|
+
};
|
|
441
487
|
|
|
488
|
+
if (multipleFilesSupported) {
|
|
489
|
+
return (
|
|
490
|
+
<DndContext
|
|
491
|
+
sensors={sensors}
|
|
492
|
+
collisionDetection={closestCenter}
|
|
493
|
+
onDragStart={handleDragStart}
|
|
494
|
+
onDragEnd={handleDragEnd}
|
|
495
|
+
>
|
|
496
|
+
<SortableContext items={value.map(v => v.id)} strategy={horizontalListSortingStrategy}>
|
|
497
|
+
<FileDropComponent {...fileDropProps} />
|
|
498
|
+
</SortableContext>
|
|
499
|
+
</DndContext>
|
|
500
|
+
);
|
|
501
|
+
} else {
|
|
502
|
+
// For single file, no D&D context is needed
|
|
503
|
+
return <FileDropComponent {...fileDropProps} isDndItemDragging={false}/>;
|
|
504
|
+
}
|
|
442
505
|
}
|
package/src/form/index.tsx
CHANGED
|
@@ -8,6 +8,7 @@ export { TextFieldBinding } from "./field_bindings/TextFieldBinding";
|
|
|
8
8
|
export { SwitchFieldBinding } from "./field_bindings/SwitchFieldBinding";
|
|
9
9
|
export { DateTimeFieldBinding } from "./field_bindings/DateTimeFieldBinding";
|
|
10
10
|
export { ReferenceFieldBinding } from "./field_bindings/ReferenceFieldBinding";
|
|
11
|
+
export { ReferenceAsStringFieldBinding } from "./field_bindings/ReferenceAsStringFieldBinding";
|
|
11
12
|
export { MapFieldBinding } from "./field_bindings/MapFieldBinding";
|
|
12
13
|
export { KeyValueFieldBinding } from "./field_bindings/KeyValueFieldBinding";
|
|
13
14
|
export { RepeatFieldBinding } from "./field_bindings/RepeatFieldBinding";
|
package/src/hooks/data/save.ts
CHANGED
|
@@ -100,11 +100,6 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, USE
|
|
|
100
100
|
updatedValues = values;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
console.debug("Saving entity", {
|
|
104
|
-
entityId,
|
|
105
|
-
updatedValues,
|
|
106
|
-
collection
|
|
107
|
-
});
|
|
108
103
|
return dataSource.saveEntity({
|
|
109
104
|
collection,
|
|
110
105
|
path: resolvedPath,
|
|
@@ -142,7 +137,6 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, USE
|
|
|
142
137
|
onSaveSuccess(entity);
|
|
143
138
|
})
|
|
144
139
|
.catch((e) => {
|
|
145
|
-
console.error("!!!", e);
|
|
146
140
|
if (callbacks?.onSaveFailure) {
|
|
147
141
|
|
|
148
142
|
const resolvedCollection = resolveCollection<M>({
|