@firecms/core 3.0.0-canary.241 → 3.0.0-canary.244
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/VirtualTable/VirtualTableProps.d.ts +0 -4
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/index.es.js +713 -376
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +721 -387
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collections.d.ts +4 -0
- package/package.json +8 -6
- package/src/components/ArrayContainer.tsx +408 -294
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +1 -0
- package/src/components/HomePage/DefaultHomePage.tsx +1 -0
- package/src/components/VirtualTable/VirtualTable.tsx +2 -27
- package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
- package/src/core/EntityEditView.tsx +1 -0
- package/src/core/EntitySidePanel.tsx +1 -13
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +222 -154
- package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
- package/src/types/collections.ts +5 -0
|
@@ -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,
|
|
@@ -132,13 +140,104 @@ 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
|
+
name,
|
|
161
|
+
metadata,
|
|
162
|
+
storagePathBuilder,
|
|
163
|
+
onFileUploadComplete,
|
|
164
|
+
onClear,
|
|
165
|
+
disabled,
|
|
166
|
+
isSortable // This prop might be redundant if SortableContext is always used for multiple items
|
|
167
|
+
}: SortableStorageItemProps) {
|
|
168
|
+
|
|
169
|
+
const {
|
|
170
|
+
attributes,
|
|
171
|
+
listeners,
|
|
172
|
+
setNodeRef,
|
|
173
|
+
transform,
|
|
174
|
+
transition,
|
|
175
|
+
isDragging
|
|
176
|
+
} = useSortable({ id });
|
|
177
|
+
|
|
178
|
+
const style: React.CSSProperties = {
|
|
179
|
+
transform: CSS.Transform.toString(transform),
|
|
180
|
+
transition,
|
|
181
|
+
zIndex: isDragging ? 100 : undefined, // Higher z-index when dragging
|
|
182
|
+
opacity: isDragging ? 0.8 : 1 // Slight opacity for dragged item
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const getImageSizeNumber = (previewSize: PreviewSize): number => {
|
|
186
|
+
switch (previewSize) {
|
|
187
|
+
case "small":
|
|
188
|
+
return 40;
|
|
189
|
+
case "medium":
|
|
190
|
+
return 118; // As per original logic for multiple items
|
|
191
|
+
case "large":
|
|
192
|
+
return 220; // As per original logic for single item
|
|
193
|
+
default:
|
|
194
|
+
return 118;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
let child: React.ReactNode;
|
|
199
|
+
if (entry.storagePathOrDownloadUrl) {
|
|
200
|
+
child = (
|
|
201
|
+
<StorageItemPreview
|
|
202
|
+
name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
|
|
203
|
+
property={property}
|
|
204
|
+
disabled={disabled}
|
|
205
|
+
value={entry.storagePathOrDownloadUrl}
|
|
206
|
+
onRemove={() => onClear(entry.storagePathOrDownloadUrl!)}
|
|
207
|
+
size={entry.size}/>
|
|
208
|
+
);
|
|
209
|
+
} else if (entry.file) {
|
|
210
|
+
child = (
|
|
211
|
+
<StorageUploadProgress
|
|
212
|
+
entry={entry}
|
|
213
|
+
metadata={metadata}
|
|
214
|
+
storagePath={storagePathBuilder(entry.file)}
|
|
215
|
+
onFileUploadComplete={onFileUploadComplete}
|
|
216
|
+
imageSize={getImageSizeNumber(entry.size)}
|
|
217
|
+
simple={false}
|
|
218
|
+
/>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<div
|
|
224
|
+
ref={setNodeRef}
|
|
225
|
+
style={style}
|
|
226
|
+
{...attributes}
|
|
227
|
+
{...listeners}
|
|
228
|
+
className={cls("rounded-md m-1")} // Added margin for spacing between items
|
|
229
|
+
tabIndex={-1}
|
|
230
|
+
>
|
|
231
|
+
{child}
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
135
236
|
function FileDropComponent({
|
|
136
237
|
storage,
|
|
137
238
|
disabled,
|
|
138
|
-
isDraggingOver,
|
|
139
239
|
onFilesAdded,
|
|
140
240
|
multipleFilesSupported,
|
|
141
|
-
droppableProvided,
|
|
142
241
|
autoFocus,
|
|
143
242
|
internalValue,
|
|
144
243
|
property,
|
|
@@ -146,26 +245,24 @@ function FileDropComponent({
|
|
|
146
245
|
metadata,
|
|
147
246
|
storagePathBuilder,
|
|
148
247
|
onFileUploadComplete,
|
|
149
|
-
size,
|
|
150
248
|
name,
|
|
151
|
-
helpText
|
|
249
|
+
helpText,
|
|
250
|
+
isDndItemDragging // New prop to disable dropzone when internal D&D is active
|
|
152
251
|
}: {
|
|
153
252
|
storage: StorageConfig,
|
|
154
253
|
disabled: boolean,
|
|
155
|
-
|
|
156
|
-
droppableProvided: any,
|
|
157
|
-
onFilesAdded: (acceptedFiles: File[]) => void,
|
|
254
|
+
onFilesAdded: (acceptedFiles: File[]) => Promise<void>, // useStorageUploadController returns Promise<void>
|
|
158
255
|
multipleFilesSupported: boolean,
|
|
159
256
|
autoFocus: boolean,
|
|
160
257
|
internalValue: StorageFieldItem[],
|
|
161
258
|
property: ResolvedStringProperty,
|
|
162
259
|
onClear: (clearedStoragePathOrDownloadUrl: string) => void,
|
|
163
|
-
metadata
|
|
260
|
+
metadata?: any,
|
|
164
261
|
storagePathBuilder: (file: File) => string,
|
|
165
262
|
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, fileMetadata?: any) => Promise<void>,
|
|
166
|
-
size: PreviewSize,
|
|
167
263
|
name: string,
|
|
168
|
-
helpText: string
|
|
264
|
+
helpText: string,
|
|
265
|
+
isDndItemDragging?: boolean
|
|
169
266
|
}) {
|
|
170
267
|
|
|
171
268
|
const snackbarContext = useSnackbarController();
|
|
@@ -173,16 +270,19 @@ function FileDropComponent({
|
|
|
173
270
|
const {
|
|
174
271
|
getRootProps,
|
|
175
272
|
getInputProps,
|
|
176
|
-
isDragActive,
|
|
273
|
+
isDragActive, // This is for files dragged from OS
|
|
177
274
|
isDragAccept,
|
|
178
275
|
isDragReject
|
|
179
276
|
} = useDropzone({
|
|
180
|
-
accept: storage.acceptedFiles ? storage.acceptedFiles.
|
|
181
|
-
|
|
277
|
+
accept: storage.acceptedFiles ? storage.acceptedFiles.reduce((acc, ext) => ({
|
|
278
|
+
...acc,
|
|
279
|
+
[ext]: []
|
|
280
|
+
}), {}) : undefined,
|
|
281
|
+
disabled: disabled || isDndItemDragging, // Disable if form field is disabled OR an internal item is being dragged
|
|
182
282
|
noDragEventsBubbling: true,
|
|
183
283
|
maxSize: storage.maxSize,
|
|
184
284
|
onDrop: onFilesAdded,
|
|
185
|
-
onDropRejected: (fileRejections
|
|
285
|
+
onDropRejected: (fileRejections) => {
|
|
186
286
|
for (const fileRejection of fileRejections) {
|
|
187
287
|
for (const error of fileRejection.errors) {
|
|
188
288
|
console.error("Error uploading file: ", error);
|
|
@@ -211,79 +311,44 @@ function FileDropComponent({
|
|
|
211
311
|
disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
|
|
212
312
|
disabled ? "text-surface-accent-600 dark:text-surface-accent-500" : "",
|
|
213
313
|
dropZoneClasses,
|
|
214
|
-
multipleFilesSupported && internalValue.length
|
|
314
|
+
multipleFilesSupported && internalValue.length === 0 && "flex", // Keep flex for empty state centering
|
|
215
315
|
{
|
|
216
316
|
[nonActiveDropClasses]: !isDragActive,
|
|
217
|
-
[activeDropClasses]: isDragActive,
|
|
218
|
-
[rejectDropClasses]: isDragReject,
|
|
219
|
-
[acceptDropClasses]: isDragAccept,
|
|
220
|
-
[disabledClasses]: disabled
|
|
317
|
+
[activeDropClasses]: isDragActive, // OS file drag active
|
|
318
|
+
[rejectDropClasses]: isDragReject, // OS file drag reject
|
|
319
|
+
[acceptDropClasses]: isDragAccept, // OS file drag accept
|
|
320
|
+
[disabledClasses]: disabled || isDndItemDragging // Visually disable if internal drag
|
|
221
321
|
})}
|
|
222
322
|
>
|
|
223
323
|
<div
|
|
224
|
-
{...droppableProvided.droppableProps}
|
|
225
|
-
ref={droppableProvided.innerRef}
|
|
226
324
|
className={cls("flex items-center p-1 no-scrollbar",
|
|
227
|
-
multipleFilesSupported && internalValue.length ? "overflow-auto" : "",
|
|
228
|
-
|
|
325
|
+
multipleFilesSupported && internalValue.length ? "flex-row overflow-x-auto" : "flex-col", // flex-col for single or empty
|
|
326
|
+
internalValue.length === 0 && "min-h-[250px] justify-center", // Centering for empty dropzone
|
|
327
|
+
multipleFilesSupported && internalValue.length > 0 && "min-h-[180px]", // Min height for multiple items
|
|
328
|
+
!multipleFilesSupported && internalValue.length > 0 && "min-h-[250px]" // Min height for single item
|
|
229
329
|
)}
|
|
230
330
|
>
|
|
231
|
-
|
|
232
331
|
<input
|
|
233
332
|
autoFocus={autoFocus}
|
|
234
333
|
{...getInputProps()} />
|
|
235
334
|
|
|
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
|
-
|
|
335
|
+
{internalValue.map((entry) => (
|
|
336
|
+
<SortableStorageItem
|
|
337
|
+
key={entry.id}
|
|
338
|
+
id={entry.id}
|
|
339
|
+
entry={entry}
|
|
340
|
+
property={property}
|
|
341
|
+
name={name}
|
|
342
|
+
metadata={metadata}
|
|
343
|
+
storagePathBuilder={storagePathBuilder}
|
|
344
|
+
onFileUploadComplete={onFileUploadComplete}
|
|
345
|
+
onClear={onClear}
|
|
346
|
+
disabled={disabled}
|
|
347
|
+
isSortable={multipleFilesSupported}
|
|
348
|
+
/>
|
|
349
|
+
))}
|
|
350
|
+
|
|
351
|
+
{/* Placeholder for empty dropzone text is handled by the outer Typography */}
|
|
287
352
|
</div>
|
|
288
353
|
|
|
289
354
|
<div
|
|
@@ -294,7 +359,6 @@ function FileDropComponent({
|
|
|
294
359
|
{helpText}
|
|
295
360
|
</Typography>
|
|
296
361
|
</div>
|
|
297
|
-
|
|
298
362
|
</div>
|
|
299
363
|
);
|
|
300
364
|
}
|
|
@@ -309,7 +373,7 @@ export interface StorageUploadProps {
|
|
|
309
373
|
autoFocus: boolean;
|
|
310
374
|
disabled: boolean;
|
|
311
375
|
storage: StorageConfig;
|
|
312
|
-
onFilesAdded: (acceptedFiles: File[]) => void
|
|
376
|
+
onFilesAdded: (acceptedFiles: File[]) => Promise<void>; // Updated from useStorageUploadController
|
|
313
377
|
storagePathBuilder: (file: File) => string;
|
|
314
378
|
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, fileMetadata?: any) => Promise<void>;
|
|
315
379
|
}
|
|
@@ -317,7 +381,7 @@ export interface StorageUploadProps {
|
|
|
317
381
|
export function StorageUpload({
|
|
318
382
|
property,
|
|
319
383
|
name,
|
|
320
|
-
value,
|
|
384
|
+
value, // This is internalValue from useStorageUploadController
|
|
321
385
|
setInternalValue,
|
|
322
386
|
onChange,
|
|
323
387
|
multipleFilesSupported,
|
|
@@ -344,10 +408,10 @@ export function StorageUpload({
|
|
|
344
408
|
}
|
|
345
409
|
|
|
346
410
|
const metadata: Record<string, unknown> | undefined = storage?.metadata;
|
|
347
|
-
const
|
|
411
|
+
const [isDndItemDragging, setIsDndItemDragging] = useState(false);
|
|
348
412
|
|
|
349
413
|
const moveItem = useCallback((fromIndex: number, toIndex: number) => {
|
|
350
|
-
if (!multipleFilesSupported) return;
|
|
414
|
+
if (!multipleFilesSupported || fromIndex === toIndex) return;
|
|
351
415
|
const newValue = [...value];
|
|
352
416
|
const item = newValue[fromIndex];
|
|
353
417
|
newValue.splice(fromIndex, 1);
|
|
@@ -359,84 +423,88 @@ export function StorageUpload({
|
|
|
359
423
|
onChange(fieldValue);
|
|
360
424
|
}, [multipleFilesSupported, onChange, setInternalValue, value]);
|
|
361
425
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
426
|
+
const sensors = useSensors(
|
|
427
|
+
useSensor(PointerSensor, {
|
|
428
|
+
activationConstraint: {
|
|
429
|
+
distance: 5, // Start dragging after 5px movement
|
|
430
|
+
},
|
|
431
|
+
}),
|
|
432
|
+
useSensor(KeyboardSensor, {
|
|
433
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
434
|
+
})
|
|
435
|
+
);
|
|
369
436
|
|
|
370
|
-
|
|
437
|
+
const handleDragStart = useCallback((event: DragStartEvent) => {
|
|
438
|
+
setIsDndItemDragging(true);
|
|
439
|
+
}, []);
|
|
440
|
+
|
|
441
|
+
const handleDragEnd = useCallback((event: DragEndEvent) => {
|
|
442
|
+
setIsDndItemDragging(false);
|
|
443
|
+
const {
|
|
444
|
+
active,
|
|
445
|
+
over
|
|
446
|
+
} = event;
|
|
447
|
+
if (over && active.id !== over.id) {
|
|
448
|
+
const oldIndex = value.findIndex(item => item.id === active.id);
|
|
449
|
+
const newIndex = value.findIndex(item => item.id === over.id);
|
|
450
|
+
if (oldIndex !== -1 && newIndex !== -1) {
|
|
451
|
+
moveItem(oldIndex, newIndex);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}, [value, moveItem]);
|
|
371
455
|
|
|
372
456
|
const onClear = useCallback((clearedStoragePathOrDownloadUrl: string) => {
|
|
457
|
+
let newValue: StorageFieldItem[];
|
|
373
458
|
if (multipleFilesSupported) {
|
|
374
|
-
|
|
459
|
+
newValue = value.filter(v => v.storagePathOrDownloadUrl !== clearedStoragePathOrDownloadUrl);
|
|
375
460
|
onChange(newValue.filter(v => !!v.storagePathOrDownloadUrl).map(v => v.storagePathOrDownloadUrl as string));
|
|
376
|
-
setInternalValue(newValue);
|
|
377
461
|
} else {
|
|
462
|
+
newValue = [];
|
|
378
463
|
onChange(null);
|
|
379
|
-
setInternalValue([]);
|
|
380
464
|
}
|
|
381
|
-
|
|
465
|
+
setInternalValue(newValue);
|
|
466
|
+
}, [value, multipleFilesSupported, onChange, setInternalValue]);
|
|
382
467
|
|
|
383
468
|
const helpText = multipleFilesSupported
|
|
384
|
-
? "Drag 'n' drop some files here, or click to select files"
|
|
469
|
+
? "Drag 'n' drop some files here, or click to select files. Drag to reorder."
|
|
385
470
|
: "Drag 'n' drop a file here, or click to select one";
|
|
386
471
|
|
|
387
472
|
const renderProperty: ResolvedStringProperty = multipleFilesSupported
|
|
388
|
-
? (property as
|
|
473
|
+
? (property as ResolvedArrayProperty<string[]>).of as ResolvedStringProperty
|
|
389
474
|
: property as ResolvedStringProperty;
|
|
390
475
|
|
|
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
|
-
);
|
|
476
|
+
const fileDropProps = {
|
|
477
|
+
storage,
|
|
478
|
+
disabled,
|
|
479
|
+
onFilesAdded,
|
|
480
|
+
multipleFilesSupported,
|
|
481
|
+
autoFocus,
|
|
482
|
+
internalValue: value, // Pass current internalValue
|
|
483
|
+
property: renderProperty,
|
|
484
|
+
onClear,
|
|
485
|
+
metadata,
|
|
486
|
+
storagePathBuilder,
|
|
487
|
+
onFileUploadComplete,
|
|
488
|
+
name,
|
|
489
|
+
helpText,
|
|
490
|
+
isDndItemDragging // Pass this down
|
|
491
|
+
};
|
|
441
492
|
|
|
493
|
+
if (multipleFilesSupported) {
|
|
494
|
+
return (
|
|
495
|
+
<DndContext
|
|
496
|
+
sensors={sensors}
|
|
497
|
+
collisionDetection={closestCenter}
|
|
498
|
+
onDragStart={handleDragStart}
|
|
499
|
+
onDragEnd={handleDragEnd}
|
|
500
|
+
>
|
|
501
|
+
<SortableContext items={value.map(v => v.id)} strategy={horizontalListSortingStrategy}>
|
|
502
|
+
<FileDropComponent {...fileDropProps} />
|
|
503
|
+
</SortableContext>
|
|
504
|
+
</DndContext>
|
|
505
|
+
);
|
|
506
|
+
} else {
|
|
507
|
+
// For single file, no D&D context is needed
|
|
508
|
+
return <FileDropComponent {...fileDropProps} isDndItemDragging={false}/>;
|
|
509
|
+
}
|
|
442
510
|
}
|
|
@@ -45,7 +45,7 @@ export function StringPropertyPreview({
|
|
|
45
45
|
if (!value) return <></>;
|
|
46
46
|
const lines = value.split("\n");
|
|
47
47
|
return value && value.includes("\n")
|
|
48
|
-
? <div className={cls("overflow-x-scroll", size === "small" ? "text-sm" : "")}>
|
|
48
|
+
? <div className={cls("overflow-x-scroll overflow-hidden", size === "small" ? "text-sm" : "")}>
|
|
49
49
|
{lines.map((str, index) =>
|
|
50
50
|
<React.Fragment key={`string_preview_${index}`}>
|
|
51
51
|
<span>{str}</span>
|
package/src/types/collections.ts
CHANGED
|
@@ -577,6 +577,11 @@ export interface EntityCustomViewParams<M extends Record<string, any> = any> {
|
|
|
577
577
|
* Use the form context to access the form state and methods
|
|
578
578
|
*/
|
|
579
579
|
formContext: FormContext;
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* If this is a subcollection, this is the path of the parent collections
|
|
583
|
+
*/
|
|
584
|
+
parentCollectionIds?: string[];
|
|
580
585
|
}
|
|
581
586
|
|
|
582
587
|
export type InferCollectionType<S extends EntityCollection> = S extends EntityCollection<infer M> ? M : never;
|