@firecms/core 3.0.1 → 3.1.0-canary.9e89e98
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/README.md +1 -1
- package/dist/components/AIIcon.d.ts +16 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +7 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +14 -0
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +6 -0
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -4
- package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +6 -0
- package/dist/components/EntityCollectionView/Board.d.ts +2 -0
- package/dist/components/EntityCollectionView/BoardColumn.d.ts +42 -0
- package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +9 -0
- package/dist/components/EntityCollectionView/BoardSortableList.d.ts +14 -0
- package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +26 -0
- package/dist/components/EntityCollectionView/EntityCard.d.ts +19 -0
- package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +20 -0
- package/dist/components/EntityCollectionView/EntityCollectionCardView.d.ts +31 -0
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +7 -3
- package/dist/components/EntityCollectionView/FiltersDialog.d.ts +14 -0
- package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +44 -0
- package/dist/components/EntityCollectionView/board_types.d.ts +105 -0
- package/dist/components/EntityCollectionView/useBoardDataController.d.ts +60 -0
- package/dist/components/SelectableTable/SelectableTable.d.ts +5 -1
- package/dist/components/SelectableTable/filters/DateTimeFilterField.d.ts +2 -1
- package/dist/components/VirtualTable/VirtualTableCell.d.ts +6 -0
- package/dist/components/VirtualTable/VirtualTableHeader.d.ts +2 -0
- package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +11 -0
- package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +1 -0
- package/dist/components/VirtualTable/types.d.ts +2 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/contexts/index.d.ts +10 -0
- package/dist/core/DrawerNavigationGroup.d.ts +45 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/form/validation.d.ts +3 -2
- package/dist/hooks/useBreadcrumbsController.d.ts +16 -0
- package/dist/hooks/useCollapsedGroups.d.ts +4 -1
- package/dist/index.es.js +5185 -1561
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5179 -1556
- package/dist/index.umd.js.map +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +5 -0
- package/dist/preview/components/DatePreview.d.ts +13 -3
- package/dist/preview/components/ImagePreview.d.ts +5 -1
- package/dist/preview/components/StorageThumbnail.d.ts +2 -1
- package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
- package/dist/preview/property_previews/ArrayOfStorageComponentsPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
- package/dist/preview/property_previews/SkeletonPropertyComponent.d.ts +1 -1
- package/dist/types/collections.d.ts +50 -2
- package/dist/types/datasource.d.ts +0 -1
- package/dist/types/plugins.d.ts +46 -1
- package/dist/types/properties.d.ts +259 -4
- package/dist/util/__tests__/conditions.test.d.ts +1 -0
- package/dist/util/__tests__/objects.test.d.ts +1 -0
- package/dist/util/conditions.d.ts +26 -0
- package/dist/util/entities.d.ts +1 -2
- package/dist/util/index.d.ts +2 -1
- package/dist/util/property_utils.d.ts +2 -1
- package/dist/util/resolutions.d.ts +1 -1
- package/package.json +10 -7
- package/src/app/Scaffold.tsx +14 -15
- package/src/components/AIIcon.tsx +39 -0
- package/src/components/ArrayContainer.tsx +1 -4
- package/src/components/ClearFilterSortButton.tsx +19 -16
- package/src/components/ConfirmationDialog.tsx +0 -2
- package/src/components/DeleteEntityDialog.tsx +2 -4
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +74 -41
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +130 -79
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +121 -104
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +132 -103
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +20 -42
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +90 -49
- package/src/components/EntityCollectionView/Board.tsx +324 -0
- package/src/components/EntityCollectionView/BoardColumn.tsx +158 -0
- package/src/components/EntityCollectionView/BoardColumnTitle.tsx +45 -0
- package/src/components/EntityCollectionView/BoardSortableList.tsx +172 -0
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +212 -0
- package/src/components/EntityCollectionView/EntityCard.tsx +231 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +713 -0
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +244 -0
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +490 -203
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +31 -19
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +84 -15
- package/src/components/EntityCollectionView/FiltersDialog.tsx +249 -0
- package/src/components/EntityCollectionView/ViewModeToggle.tsx +199 -0
- package/src/components/EntityCollectionView/board_types.ts +113 -0
- package/src/components/EntityCollectionView/useBoardDataController.tsx +490 -0
- package/src/components/ErrorTooltip.tsx +2 -1
- package/src/components/HomePage/DefaultHomePage.tsx +47 -10
- package/src/components/HomePage/HomePageDnD.tsx +56 -41
- package/src/components/HomePage/NavigationCard.tsx +20 -18
- package/src/components/HomePage/NavigationGroup.tsx +17 -16
- package/src/components/HomePage/RenameGroupDialog.tsx +0 -2
- package/src/components/HomePage/SmallNavigationCard.tsx +10 -9
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -10
- package/src/components/ReferenceWidget.tsx +2 -4
- package/src/components/SelectableTable/SelectableTable.tsx +75 -67
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +7 -6
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +39 -40
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -38
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +49 -58
- package/src/components/UnsavedChangesDialog.tsx +0 -2
- package/src/components/UserDisplay.tsx +4 -4
- package/src/components/VirtualTable/VirtualTable.tsx +170 -19
- package/src/components/VirtualTable/VirtualTableCell.tsx +18 -2
- package/src/components/VirtualTable/VirtualTableHeader.tsx +20 -11
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +158 -42
- package/src/components/VirtualTable/VirtualTableProps.tsx +14 -1
- package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -0
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +17 -4
- package/src/components/VirtualTable/types.tsx +2 -0
- package/src/components/common/useColumnsIds.tsx +95 -3
- package/src/components/index.tsx +4 -0
- package/src/contexts/BreacrumbsContext.tsx +15 -8
- package/src/contexts/index.ts +10 -0
- package/src/core/DefaultAppBar.tsx +39 -26
- package/src/core/DefaultDrawer.tsx +42 -56
- package/src/core/DrawerNavigationGroup.tsx +118 -0
- package/src/core/DrawerNavigationItem.tsx +4 -3
- package/src/core/EntityEditView.tsx +41 -43
- package/src/core/SideDialogs.tsx +4 -2
- package/src/core/index.tsx +1 -0
- package/src/form/PropertyFieldBinding.tsx +58 -43
- package/src/form/components/StorageItemPreview.tsx +2 -1
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +0 -1
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +17 -16
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -1
- package/src/form/field_bindings/MapFieldBinding.tsx +69 -67
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +21 -17
- package/src/form/field_bindings/TextFieldBinding.tsx +71 -35
- package/src/form/validation.ts +245 -160
- package/src/hooks/useBreadcrumbsController.tsx +18 -0
- package/src/hooks/useBuildNavigationController.tsx +42 -19
- package/src/hooks/useCollapsedGroups.ts +12 -4
- package/src/internal/useBuildDataSource.ts +69 -34
- package/src/internal/useBuildSideDialogsController.tsx +11 -8
- package/src/internal/useBuildSideEntityController.tsx +2 -4
- package/src/internal/useRestoreScroll.tsx +26 -14
- package/src/preview/PropertyPreview.tsx +40 -32
- package/src/preview/PropertyPreviewProps.tsx +6 -0
- package/src/preview/components/DatePreview.tsx +72 -4
- package/src/preview/components/EmptyValue.tsx +1 -1
- package/src/preview/components/ImagePreview.tsx +37 -21
- package/src/preview/components/StorageThumbnail.tsx +16 -12
- package/src/preview/components/UrlComponentPreview.tsx +28 -25
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +9 -7
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +11 -9
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +26 -24
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +61 -56
- package/src/routes/CustomCMSRoute.tsx +1 -0
- package/src/routes/FireCMSRoute.tsx +26 -13
- package/src/types/collections.ts +57 -3
- package/src/types/datasource.ts +54 -56
- package/src/types/plugins.tsx +51 -1
- package/src/types/properties.ts +347 -27
- package/src/util/__tests__/conditions.test.ts +506 -0
- package/src/util/__tests__/objects.test.ts +196 -0
- package/src/util/callbacks.ts +6 -3
- package/src/util/collections.ts +51 -6
- package/src/util/conditions.ts +339 -0
- package/src/util/entities.ts +28 -29
- package/src/util/entity_cache.ts +2 -1
- package/src/util/index.ts +2 -1
- package/src/util/objects.ts +31 -13
- package/src/util/{references.ts → previews.ts} +14 -0
- package/src/util/property_utils.tsx +36 -10
- package/src/util/resolutions.ts +57 -55
- /package/dist/util/{references.d.ts → previews.d.ts} +0 -0
|
@@ -21,6 +21,16 @@ import { VirtualTableRow } from "./VirtualTableRow";
|
|
|
21
21
|
import { VirtualTableCell } from "./VirtualTableCell";
|
|
22
22
|
import { AssignmentIcon, CenteredView, cls, Typography } from "@firecms/ui";
|
|
23
23
|
import { useDebounceCallback } from "../common/useDebouncedCallback";
|
|
24
|
+
import {
|
|
25
|
+
closestCenter,
|
|
26
|
+
DndContext,
|
|
27
|
+
DragEndEvent,
|
|
28
|
+
DragStartEvent,
|
|
29
|
+
PointerSensor,
|
|
30
|
+
useSensor,
|
|
31
|
+
useSensors
|
|
32
|
+
} from "@dnd-kit/core";
|
|
33
|
+
import { arrayMove, horizontalListSortingStrategy, SortableContext, useSortable } from "@dnd-kit/sortable";
|
|
24
34
|
|
|
25
35
|
const VirtualListContext = createContext<VirtualTableContextProps<any>>({} as any);
|
|
26
36
|
VirtualListContext.displayName = "VirtualListContext";
|
|
@@ -56,7 +66,7 @@ const innerElementType = forwardRef<HTMLDivElement, InnerElementProps>(({
|
|
|
56
66
|
minHeight: "100%",
|
|
57
67
|
position: "relative"
|
|
58
68
|
}}>
|
|
59
|
-
<VirtualTableHeaderRow {...virtualTableProps}/>
|
|
69
|
+
<VirtualTableHeaderRow {...virtualTableProps} />
|
|
60
70
|
{!customView && children}
|
|
61
71
|
</div>
|
|
62
72
|
|
|
@@ -115,6 +125,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
115
125
|
endAdornment,
|
|
116
126
|
AddColumnComponent,
|
|
117
127
|
initialScroll = 0,
|
|
128
|
+
onColumnsOrderChange,
|
|
118
129
|
}: VirtualTableProps<T>) {
|
|
119
130
|
|
|
120
131
|
const sortByProperty: string | undefined = sortBy ? sortBy[0] : undefined;
|
|
@@ -127,6 +138,44 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
127
138
|
|
|
128
139
|
const debouncedScroll = useDebounceCallback(onScrollProp, 200);
|
|
129
140
|
|
|
141
|
+
// Drag and drop state
|
|
142
|
+
const [draggingColumnId, setDraggingColumnId] = useState<string | null>(null);
|
|
143
|
+
|
|
144
|
+
const sensors = useSensors(
|
|
145
|
+
useSensor(PointerSensor, {
|
|
146
|
+
activationConstraint: {
|
|
147
|
+
distance: 5,
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const handleDragStart = useCallback((event: DragStartEvent) => {
|
|
153
|
+
setDraggingColumnId(event.active.id as string);
|
|
154
|
+
}, []);
|
|
155
|
+
|
|
156
|
+
const handleDragEnd = useCallback((event: DragEndEvent) => {
|
|
157
|
+
const {
|
|
158
|
+
active,
|
|
159
|
+
over
|
|
160
|
+
} = event;
|
|
161
|
+
setDraggingColumnId(null);
|
|
162
|
+
|
|
163
|
+
if (over && active.id !== over.id && onColumnsOrderChange) {
|
|
164
|
+
const oldIndex = columns.findIndex((col) => col.key === active.id);
|
|
165
|
+
const newIndex = columns.findIndex((col) => col.key === over.id);
|
|
166
|
+
|
|
167
|
+
if (oldIndex !== -1 && newIndex !== -1) {
|
|
168
|
+
const newColumns = arrayMove(columns, oldIndex, newIndex);
|
|
169
|
+
setColumns(newColumns);
|
|
170
|
+
onColumnsOrderChange(newColumns);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}, [columns, onColumnsOrderChange]);
|
|
174
|
+
|
|
175
|
+
const handleDragCancel = useCallback(() => {
|
|
176
|
+
setDraggingColumnId(null);
|
|
177
|
+
}, []);
|
|
178
|
+
|
|
130
179
|
// Set initial scroll position
|
|
131
180
|
useEffect(() => {
|
|
132
181
|
if (tableRef.current && initialScroll) {
|
|
@@ -315,14 +364,28 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
315
364
|
createFilterField,
|
|
316
365
|
rowClassName,
|
|
317
366
|
endAdornment,
|
|
318
|
-
AddColumnComponent
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
367
|
+
AddColumnComponent,
|
|
368
|
+
onColumnsOrderChange: onColumnsOrderChange ? (newColumns: VirtualTableColumn[]) => {
|
|
369
|
+
setColumns(newColumns);
|
|
370
|
+
onColumnsOrderChange(newColumns);
|
|
371
|
+
} : undefined,
|
|
372
|
+
draggingColumnId
|
|
373
|
+
}), [data, rowHeight, cellRenderer, columns, currentSort, onRowClick, customView, onColumnResizeInternal, onColumnResizeEndInternal, filterInput, onColumnSort, onFilterUpdateInternal, sortByProperty, hoverRow, createFilterField, rowClassName, endAdornment, AddColumnComponent, onColumnsOrderChange, draggingColumnId]);
|
|
374
|
+
|
|
375
|
+
// Get sortable column keys (excluding frozen columns)
|
|
376
|
+
const sortableColumnKeys = columns
|
|
377
|
+
.filter(col => !col.frozen)
|
|
378
|
+
.map(col => col.key);
|
|
379
|
+
|
|
380
|
+
const tableContent = (
|
|
322
381
|
<div
|
|
323
382
|
ref={measureRef}
|
|
324
383
|
style={style}
|
|
325
|
-
className={cls(
|
|
384
|
+
className={cls(
|
|
385
|
+
"h-full w-full",
|
|
386
|
+
className,
|
|
387
|
+
draggingColumnId && "overflow-hidden"
|
|
388
|
+
)}>
|
|
326
389
|
<VirtualListContext.Provider
|
|
327
390
|
value={virtualListController}>
|
|
328
391
|
|
|
@@ -332,16 +395,89 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
332
395
|
width={bounds.width}
|
|
333
396
|
height={bounds.height}
|
|
334
397
|
itemCount={(data?.length ?? 0) + (endAdornment ? 1 : 0)}
|
|
335
|
-
onScroll={onScroll}
|
|
398
|
+
onScroll={draggingColumnId ? undefined : onScroll}
|
|
336
399
|
includeAddColumn={Boolean(AddColumnComponent)}
|
|
337
400
|
itemSize={rowHeight}/>
|
|
338
401
|
|
|
339
402
|
</VirtualListContext.Provider>
|
|
340
403
|
</div>
|
|
341
404
|
);
|
|
405
|
+
|
|
406
|
+
// Wrap with DndContext if column reorder is enabled
|
|
407
|
+
if (onColumnsOrderChange) {
|
|
408
|
+
return (
|
|
409
|
+
<DndContext
|
|
410
|
+
sensors={sensors}
|
|
411
|
+
collisionDetection={closestCenter}
|
|
412
|
+
onDragStart={handleDragStart}
|
|
413
|
+
onDragEnd={handleDragEnd}
|
|
414
|
+
onDragCancel={handleDragCancel}
|
|
415
|
+
>
|
|
416
|
+
<SortableContext
|
|
417
|
+
items={sortableColumnKeys}
|
|
418
|
+
strategy={horizontalListSortingStrategy}
|
|
419
|
+
>
|
|
420
|
+
{tableContent}
|
|
421
|
+
</SortableContext>
|
|
422
|
+
</DndContext>
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return tableContent;
|
|
342
427
|
},
|
|
343
428
|
equal
|
|
344
429
|
);
|
|
430
|
+
// Wrapper that applies sortable transforms to cells
|
|
431
|
+
const SortableCellWrapper = ({
|
|
432
|
+
columnKey,
|
|
433
|
+
width,
|
|
434
|
+
isDragging,
|
|
435
|
+
isDraggable,
|
|
436
|
+
frozen,
|
|
437
|
+
children
|
|
438
|
+
}: {
|
|
439
|
+
columnKey: string;
|
|
440
|
+
width: number;
|
|
441
|
+
isDragging: boolean;
|
|
442
|
+
isDraggable: boolean;
|
|
443
|
+
frozen?: boolean;
|
|
444
|
+
children: React.ReactNode;
|
|
445
|
+
}) => {
|
|
446
|
+
const {
|
|
447
|
+
attributes,
|
|
448
|
+
listeners,
|
|
449
|
+
setNodeRef,
|
|
450
|
+
transform,
|
|
451
|
+
transition,
|
|
452
|
+
} = useSortable({
|
|
453
|
+
id: columnKey,
|
|
454
|
+
disabled: !isDraggable || frozen
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const style = {
|
|
458
|
+
// Only use translate, ignore any scale transforms
|
|
459
|
+
transform: transform ? `translateX(${transform.x}px)` : undefined,
|
|
460
|
+
// Don't transition the dragged item - only other items should animate
|
|
461
|
+
transition: isDragging ? undefined : transition,
|
|
462
|
+
minWidth: width,
|
|
463
|
+
maxWidth: width,
|
|
464
|
+
width: width,
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
return (
|
|
468
|
+
<div
|
|
469
|
+
ref={setNodeRef}
|
|
470
|
+
style={style}
|
|
471
|
+
className={cls(
|
|
472
|
+
"flex-shrink-0",
|
|
473
|
+
frozen && "sticky left-0 z-10 bg-white dark:bg-surface-950"
|
|
474
|
+
)}
|
|
475
|
+
{...attributes}
|
|
476
|
+
>
|
|
477
|
+
{children}
|
|
478
|
+
</div>
|
|
479
|
+
);
|
|
480
|
+
};
|
|
345
481
|
|
|
346
482
|
function MemoizedList({
|
|
347
483
|
outerRef,
|
|
@@ -356,7 +492,7 @@ function MemoizedList({
|
|
|
356
492
|
width: number;
|
|
357
493
|
height: number;
|
|
358
494
|
itemCount: number;
|
|
359
|
-
onScroll
|
|
495
|
+
onScroll?: (params: {
|
|
360
496
|
scrollDirection: "forward" | "backward",
|
|
361
497
|
scrollOffset: number,
|
|
362
498
|
scrollUpdateWasRequested: boolean;
|
|
@@ -378,7 +514,9 @@ function MemoizedList({
|
|
|
378
514
|
cellRenderer,
|
|
379
515
|
hoverRow,
|
|
380
516
|
rowClassName,
|
|
381
|
-
endAdornment
|
|
517
|
+
endAdornment,
|
|
518
|
+
draggingColumnId,
|
|
519
|
+
onColumnsOrderChange
|
|
382
520
|
}) => {
|
|
383
521
|
|
|
384
522
|
if (endAdornment && index === (data ?? []).length) {
|
|
@@ -411,16 +549,29 @@ function MemoizedList({
|
|
|
411
549
|
|
|
412
550
|
{columns.map((column: VirtualTableColumn, columnIndex: number) => {
|
|
413
551
|
const cellData = rowData && rowData[column.key];
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
552
|
+
const isDragging = draggingColumnId === column.key;
|
|
553
|
+
const isDraggable = !column.frozen && !!onColumnsOrderChange;
|
|
554
|
+
|
|
555
|
+
return (
|
|
556
|
+
<SortableCellWrapper
|
|
557
|
+
key={`cell_wrapper_${column.key}`}
|
|
558
|
+
columnKey={column.key}
|
|
559
|
+
width={column.width}
|
|
560
|
+
isDragging={isDragging}
|
|
561
|
+
isDraggable={isDraggable}
|
|
562
|
+
frozen={column.frozen}
|
|
563
|
+
>
|
|
564
|
+
<VirtualTableCell
|
|
565
|
+
dataKey={column.key}
|
|
566
|
+
cellRenderer={cellRenderer}
|
|
567
|
+
column={column}
|
|
568
|
+
columns={columns}
|
|
569
|
+
rowData={rowData}
|
|
570
|
+
cellData={cellData}
|
|
571
|
+
rowIndex={index}
|
|
572
|
+
columnIndex={columnIndex}/>
|
|
573
|
+
</SortableCellWrapper>
|
|
574
|
+
);
|
|
424
575
|
})}
|
|
425
576
|
|
|
426
577
|
{includeAddColumn && <div className={"w-20"}/>}
|
|
@@ -13,6 +13,13 @@ type VirtualTableCellProps<T extends any> = {
|
|
|
13
13
|
rowIndex: any;
|
|
14
14
|
columnIndex: number;
|
|
15
15
|
cellRenderer: React.ComponentType<CellRendererParams<T>>;
|
|
16
|
+
// Sortable props
|
|
17
|
+
sortableNodeRef?: (node: HTMLElement | null) => void;
|
|
18
|
+
sortableStyle?: React.CSSProperties;
|
|
19
|
+
sortableAttributes?: Record<string, any>;
|
|
20
|
+
isDragging?: boolean;
|
|
21
|
+
isDraggable?: boolean;
|
|
22
|
+
frozen?: boolean;
|
|
16
23
|
};
|
|
17
24
|
|
|
18
25
|
export const VirtualTableCell = React.memo<VirtualTableCellProps<any>>(
|
|
@@ -27,7 +34,13 @@ export const VirtualTableCell = React.memo<VirtualTableCellProps<any>>(
|
|
|
27
34
|
column: props.column,
|
|
28
35
|
columns: props.columns,
|
|
29
36
|
columnIndex: props.columnIndex,
|
|
30
|
-
width: props.column.width
|
|
37
|
+
width: props.column.width,
|
|
38
|
+
sortableNodeRef: props.sortableNodeRef,
|
|
39
|
+
sortableStyle: props.sortableStyle,
|
|
40
|
+
sortableAttributes: props.sortableAttributes,
|
|
41
|
+
isDragging: props.isDragging,
|
|
42
|
+
isDraggable: props.isDraggable,
|
|
43
|
+
frozen: props.frozen
|
|
31
44
|
} as CellRendererParams<T>
|
|
32
45
|
);
|
|
33
46
|
},
|
|
@@ -37,6 +50,9 @@ export const VirtualTableCell = React.memo<VirtualTableCellProps<any>>(
|
|
|
37
50
|
equal(a.cellData, b.cellData) &&
|
|
38
51
|
equal(a.rowIndex, b.rowIndex) &&
|
|
39
52
|
equal(a.cellRenderer, b.cellRenderer) &&
|
|
40
|
-
equal(a.columnIndex, b.columnIndex)
|
|
53
|
+
equal(a.columnIndex, b.columnIndex) &&
|
|
54
|
+
a.isDragging === b.isDragging &&
|
|
55
|
+
a.isDraggable === b.isDraggable &&
|
|
56
|
+
a.frozen === b.frozen
|
|
41
57
|
}
|
|
42
58
|
);
|
|
@@ -45,6 +45,8 @@ type VirtualTableHeaderProps<M extends Record<string, any>> = {
|
|
|
45
45
|
onClickResizeColumn?: (columnIndex: number, column: VirtualTableColumn) => void;
|
|
46
46
|
createFilterField?: (props: FilterFormFieldProps<any>) => React.ReactNode;
|
|
47
47
|
AdditionalHeaderWidget?: (props: { onHover: boolean }) => React.ReactNode;
|
|
48
|
+
isDragging?: boolean;
|
|
49
|
+
isDraggable?: boolean;
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
|
|
@@ -59,7 +61,9 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
|
|
|
59
61
|
column,
|
|
60
62
|
onClickResizeColumn,
|
|
61
63
|
createFilterField,
|
|
62
|
-
AdditionalHeaderWidget
|
|
64
|
+
AdditionalHeaderWidget,
|
|
65
|
+
isDragging,
|
|
66
|
+
isDraggable
|
|
63
67
|
}: VirtualTableHeaderProps<M>) {
|
|
64
68
|
|
|
65
69
|
const [onHover, setOnHover] = useState(false);
|
|
@@ -85,14 +89,16 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
|
|
|
85
89
|
return (
|
|
86
90
|
<ErrorBoundary>
|
|
87
91
|
<div
|
|
88
|
-
className={cls("flex py-0 px-3 h-full text-xs uppercase font-semibold relative select-none items-center
|
|
92
|
+
className={cls("flex py-0 px-3 h-full text-xs uppercase font-semibold relative select-none items-center",
|
|
93
|
+
isDragging
|
|
94
|
+
? "bg-primary-bg dark:bg-primary-bg-dark"
|
|
95
|
+
: "bg-surface-50 dark:bg-surface-900",
|
|
89
96
|
"text-text-secondary hover:text-text-primary dark:text-text-secondary-dark dark:hover:text-text-primary-dark",
|
|
90
|
-
"hover:bg-surface-100 dark:hover:bg-surface-800 hover:bg-opacity-50 dark:hover:bg-opacity-50",
|
|
91
|
-
column.frozen ? "sticky left-0 z-10" : "relative z-0"
|
|
97
|
+
!isDragging && "hover:bg-surface-100 dark:hover:bg-surface-800 hover:bg-opacity-50 hover:bg-surface-100/50 dark:hover:bg-opacity-50 dark:hover:bg-surface-800/50",
|
|
98
|
+
column.frozen ? "sticky left-0 z-10" : "relative z-0",
|
|
99
|
+
isDraggable && "cursor-grab"
|
|
92
100
|
)}
|
|
93
101
|
style={{
|
|
94
|
-
// transition: "color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms",
|
|
95
|
-
// fontSize: "0.750rem",
|
|
96
102
|
left: column.frozen ? 0 : undefined,
|
|
97
103
|
minWidth: column.width,
|
|
98
104
|
maxWidth: column.width
|
|
@@ -179,11 +185,17 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
|
|
|
179
185
|
|
|
180
186
|
{column.resizable && <div
|
|
181
187
|
ref={resizeHandleRef}
|
|
188
|
+
data-no-dnd="true"
|
|
182
189
|
className={cls(
|
|
183
190
|
"absolute h-full w-[6px] top-0 right-0 cursor-col-resize",
|
|
184
191
|
hovered && "bg-surface-300 dark:bg-surface-700"
|
|
185
192
|
)}
|
|
186
|
-
|
|
193
|
+
onPointerDown={(e) => {
|
|
194
|
+
e.stopPropagation();
|
|
195
|
+
if (onClickResizeColumn) {
|
|
196
|
+
onClickResizeColumn(columnIndex, column);
|
|
197
|
+
}
|
|
198
|
+
}}
|
|
187
199
|
/>}
|
|
188
200
|
</div>
|
|
189
201
|
|
|
@@ -243,7 +255,7 @@ function FilterForm<M>({
|
|
|
243
255
|
className={cls(defaultBorderMixin, "py-4 px-6 typography-label border-b")}>
|
|
244
256
|
{column.title ?? id}
|
|
245
257
|
</div>
|
|
246
|
-
{filterField && <div className="m-4">
|
|
258
|
+
{filterField && <div className="m-4 w-[400px]">
|
|
247
259
|
{filterField}
|
|
248
260
|
</div>}
|
|
249
261
|
<div className="flex justify-end m-4">
|
|
@@ -251,13 +263,10 @@ function FilterForm<M>({
|
|
|
251
263
|
className="mr-4"
|
|
252
264
|
disabled={!filterIsSet}
|
|
253
265
|
variant={"text"}
|
|
254
|
-
color="primary"
|
|
255
266
|
type="reset"
|
|
256
267
|
aria-label="filter clear"
|
|
257
268
|
onClick={reset}>Clear</Button>
|
|
258
269
|
<Button
|
|
259
|
-
variant="outlined"
|
|
260
|
-
color="primary"
|
|
261
270
|
type="submit">Filter</Button>
|
|
262
271
|
</div>
|
|
263
272
|
</form>
|
|
@@ -1,25 +1,132 @@
|
|
|
1
|
-
import React, { createRef, useCallback, useEffect, useState } from "react";
|
|
1
|
+
import React, { createRef, useCallback, useEffect, useMemo, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { VirtualTableColumn, VirtualTableWhereFilterOp } from "./VirtualTableProps";
|
|
4
4
|
import { ErrorBoundary } from "../ErrorBoundary";
|
|
5
5
|
import { VirtualTableHeader } from "./VirtualTableHeader";
|
|
6
6
|
import { VirtualTableContextProps } from "./types";
|
|
7
7
|
import { cls, defaultBorderMixin } from "@firecms/ui";
|
|
8
|
+
import { useSortable } from "@dnd-kit/sortable";
|
|
9
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
10
|
+
|
|
11
|
+
// Sortable column header wrapper
|
|
12
|
+
const SortableColumnHeader = ({
|
|
13
|
+
column,
|
|
14
|
+
columnIndex,
|
|
15
|
+
columnRefs,
|
|
16
|
+
isResizing,
|
|
17
|
+
onFilterUpdate,
|
|
18
|
+
filter,
|
|
19
|
+
sortByProperty,
|
|
20
|
+
currentSort,
|
|
21
|
+
onColumnSort,
|
|
22
|
+
onClickResizeColumn,
|
|
23
|
+
createFilterField,
|
|
24
|
+
isDragging,
|
|
25
|
+
isDraggable
|
|
26
|
+
}: {
|
|
27
|
+
column: VirtualTableColumn;
|
|
28
|
+
columnIndex: number;
|
|
29
|
+
columnRefs: React.RefObject<HTMLDivElement>[];
|
|
30
|
+
isResizing: number;
|
|
31
|
+
onFilterUpdate: any;
|
|
32
|
+
filter: [VirtualTableWhereFilterOp, any] | undefined;
|
|
33
|
+
sortByProperty: string | undefined;
|
|
34
|
+
currentSort: "asc" | "desc" | undefined;
|
|
35
|
+
onColumnSort: any;
|
|
36
|
+
onClickResizeColumn: (index: number) => void;
|
|
37
|
+
createFilterField: any;
|
|
38
|
+
isDragging: boolean;
|
|
39
|
+
isDraggable: boolean;
|
|
40
|
+
}) => {
|
|
41
|
+
const [isPressing, setIsPressing] = useState(false);
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
attributes,
|
|
45
|
+
listeners,
|
|
46
|
+
setNodeRef,
|
|
47
|
+
transform,
|
|
48
|
+
transition,
|
|
49
|
+
} = useSortable({
|
|
50
|
+
id: column.key,
|
|
51
|
+
disabled: !isDraggable || column.frozen
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const style = {
|
|
55
|
+
// Only use translate, ignore any scale transforms
|
|
56
|
+
transform: transform ? `translateX(${transform.x}px)` : undefined,
|
|
57
|
+
// Don't transition the dragged item - only other items should animate
|
|
58
|
+
transition: isDragging ? undefined : transition,
|
|
59
|
+
minWidth: column.width,
|
|
60
|
+
maxWidth: column.width,
|
|
61
|
+
width: column.width,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Combine our press handlers with dnd-kit listeners
|
|
65
|
+
const combinedListeners = isDraggable ? {
|
|
66
|
+
...listeners,
|
|
67
|
+
onPointerDown: (e: React.PointerEvent) => {
|
|
68
|
+
setIsPressing(true);
|
|
69
|
+
listeners?.onPointerDown?.(e);
|
|
70
|
+
},
|
|
71
|
+
onPointerUp: () => setIsPressing(false),
|
|
72
|
+
onPointerCancel: () => setIsPressing(false),
|
|
73
|
+
} : {};
|
|
74
|
+
|
|
75
|
+
// Reset pressing state when drag ends
|
|
76
|
+
React.useEffect(() => {
|
|
77
|
+
if (!isDragging) {
|
|
78
|
+
setIsPressing(false);
|
|
79
|
+
}
|
|
80
|
+
}, [isDragging]);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
ref={setNodeRef}
|
|
85
|
+
style={style}
|
|
86
|
+
className={cls(
|
|
87
|
+
"flex-shrink-0 h-full",
|
|
88
|
+
column.frozen && "sticky left-0 z-10"
|
|
89
|
+
)}
|
|
90
|
+
{...attributes}
|
|
91
|
+
{...combinedListeners}
|
|
92
|
+
>
|
|
93
|
+
<VirtualTableHeader
|
|
94
|
+
resizeHandleRef={columnRefs[columnIndex]}
|
|
95
|
+
columnIndex={columnIndex}
|
|
96
|
+
isResizingIndex={isResizing}
|
|
97
|
+
onFilterUpdate={onFilterUpdate}
|
|
98
|
+
filter={filter}
|
|
99
|
+
sort={sortByProperty === column.key ? currentSort : undefined}
|
|
100
|
+
onColumnSort={onColumnSort}
|
|
101
|
+
onClickResizeColumn={onClickResizeColumn}
|
|
102
|
+
column={column}
|
|
103
|
+
createFilterField={createFilterField}
|
|
104
|
+
AdditionalHeaderWidget={column.AdditionalHeaderWidget}
|
|
105
|
+
isDragging={isDragging || isPressing}
|
|
106
|
+
isDraggable={isDraggable} />
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
8
110
|
|
|
9
111
|
export const VirtualTableHeaderRow = ({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
112
|
+
columns,
|
|
113
|
+
currentSort,
|
|
114
|
+
onColumnSort,
|
|
115
|
+
onFilterUpdate,
|
|
116
|
+
sortByProperty,
|
|
117
|
+
filter,
|
|
118
|
+
onColumnResize,
|
|
119
|
+
onColumnResizeEnd,
|
|
120
|
+
createFilterField,
|
|
121
|
+
AddColumnComponent,
|
|
122
|
+
onColumnsOrderChange,
|
|
123
|
+
data,
|
|
124
|
+
cellRenderer: CellRenderer,
|
|
125
|
+
rowHeight = 54,
|
|
126
|
+
draggingColumnId
|
|
127
|
+
}: VirtualTableContextProps<any>) => {
|
|
128
|
+
|
|
129
|
+
const columnRefs = useMemo(() => columns.map(() => createRef<HTMLDivElement>()), [columns.length]);
|
|
23
130
|
const [isResizing, setIsResizing] = useState(-1);
|
|
24
131
|
|
|
25
132
|
const adjustWidthColumn = useCallback((index: number, width: number, end: boolean) => {
|
|
@@ -99,33 +206,42 @@ export const VirtualTableHeaderRow = ({
|
|
|
99
206
|
}, [setCursorDocument]);
|
|
100
207
|
|
|
101
208
|
return (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
209
|
+
<>
|
|
210
|
+
<div
|
|
211
|
+
className={cls(defaultBorderMixin, "z-20 sticky min-w-full flex w-fit flex-row top-0 left-0 h-12 border-b bg-surface-50 dark:bg-surface-900")}>
|
|
212
|
+
{columns.map((column, columnIndex) => {
|
|
213
|
+
const filterForThisProperty: [VirtualTableWhereFilterOp, any] | undefined =
|
|
214
|
+
column && filter && filter[column.key]
|
|
215
|
+
? filter[column.key]
|
|
216
|
+
: undefined;
|
|
217
|
+
|
|
218
|
+
const isDraggable = !column.frozen && !!onColumnsOrderChange;
|
|
219
|
+
const isDragging = draggingColumnId === column.key;
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<ErrorBoundary key={"header_" + column.key}>
|
|
223
|
+
<SortableColumnHeader
|
|
224
|
+
column={column}
|
|
225
|
+
columnIndex={columnIndex}
|
|
226
|
+
columnRefs={columnRefs}
|
|
227
|
+
isResizing={isResizing}
|
|
228
|
+
onFilterUpdate={onFilterUpdate}
|
|
229
|
+
filter={filterForThisProperty}
|
|
230
|
+
sortByProperty={sortByProperty}
|
|
231
|
+
currentSort={currentSort}
|
|
232
|
+
onColumnSort={onColumnSort}
|
|
233
|
+
onClickResizeColumn={onClickResizeColumn}
|
|
234
|
+
createFilterField={createFilterField}
|
|
235
|
+
isDragging={isDragging}
|
|
236
|
+
isDraggable={isDraggable}
|
|
237
|
+
/>
|
|
238
|
+
</ErrorBoundary>
|
|
239
|
+
);
|
|
240
|
+
})}
|
|
241
|
+
|
|
242
|
+
{AddColumnComponent && <AddColumnComponent />}
|
|
243
|
+
|
|
244
|
+
</div>
|
|
245
|
+
</>
|
|
130
246
|
);
|
|
131
247
|
};
|
|
@@ -38,7 +38,7 @@ export interface VirtualTableProps<T extends Record<string, any>> {
|
|
|
38
38
|
* @param sortBy
|
|
39
39
|
*/
|
|
40
40
|
checkFilterCombination?: (filterValues: VirtualTableFilterValues<Extract<keyof T, string>>,
|
|
41
|
-
|
|
41
|
+
sortBy?: [string, "asc" | "desc"]) => boolean;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* A callback function when scrolling the table to near the end
|
|
@@ -162,6 +162,12 @@ export interface VirtualTableProps<T extends Record<string, any>> {
|
|
|
162
162
|
*/
|
|
163
163
|
initialScroll?: number;
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Callback when columns are reordered via drag-and-drop.
|
|
167
|
+
* @param columns The new column order
|
|
168
|
+
*/
|
|
169
|
+
onColumnsOrderChange?: (columns: VirtualTableColumn[]) => void;
|
|
170
|
+
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
export type CellRendererParams<T = any> = {
|
|
@@ -172,6 +178,13 @@ export type CellRendererParams<T = any> = {
|
|
|
172
178
|
rowIndex: number;
|
|
173
179
|
width: number;
|
|
174
180
|
isScrolling?: boolean;
|
|
181
|
+
// Sortable props for dnd-kit integration
|
|
182
|
+
sortableNodeRef?: (node: HTMLElement | null) => void;
|
|
183
|
+
sortableStyle?: React.CSSProperties;
|
|
184
|
+
sortableAttributes?: Record<string, any>;
|
|
185
|
+
isDragging?: boolean;
|
|
186
|
+
isDraggable?: boolean;
|
|
187
|
+
frozen?: boolean;
|
|
175
188
|
};
|
|
176
189
|
|
|
177
190
|
/**
|
|
@@ -29,7 +29,7 @@ export const VirtualTableRow = React.memo<VirtualTableRowProps<any>>(
|
|
|
29
29
|
return (
|
|
30
30
|
<div
|
|
31
31
|
className={cls(
|
|
32
|
-
"flex min-w-full text-sm border-b border-surface-200 dark:border-surface-800 border-opacity-40 dark:border-opacity-40",
|
|
32
|
+
"flex min-w-full text-sm border-b border-surface-200 dark:border-surface-800 border-opacity-40 border-surface-200/40 dark:border-opacity-40 dark:border-surface-800/40",
|
|
33
33
|
rowClassName ? rowClassName(rowData) : "",
|
|
34
34
|
{
|
|
35
35
|
"hover:bg-opacity-95": hoverRow,
|
|
@@ -6,6 +6,7 @@ export function VirtualTableDateField(props: {
|
|
|
6
6
|
name: string;
|
|
7
7
|
error: Error | undefined;
|
|
8
8
|
mode?: "date" | "date_time";
|
|
9
|
+
timezone?: string;
|
|
9
10
|
internalValue: Date | undefined | null;
|
|
10
11
|
updateValue: (newValue: (Date | null)) => void;
|
|
11
12
|
focused: boolean;
|
|
@@ -18,6 +19,7 @@ export function VirtualTableDateField(props: {
|
|
|
18
19
|
disabled,
|
|
19
20
|
error,
|
|
20
21
|
mode,
|
|
22
|
+
timezone,
|
|
21
23
|
internalValue,
|
|
22
24
|
updateValue
|
|
23
25
|
} = props;
|
|
@@ -31,6 +33,7 @@ export function VirtualTableDateField(props: {
|
|
|
31
33
|
inputClassName={cls("w-full h-full", focusedDisabled)}
|
|
32
34
|
className={cls("w-full h-full", focusedDisabled)}
|
|
33
35
|
mode={mode}
|
|
36
|
+
timezone={timezone}
|
|
34
37
|
locale={locale}
|
|
35
38
|
/>
|
|
36
39
|
);
|