@vendure/dashboard 3.5.2-master-202512170238 → 3.5.2-master-202512180239
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/plugin/dashboard.plugin.js +1 -1
- package/package.json +3 -3
- package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
- package/src/app/routes/_authenticated/_collections/collections.tsx +249 -167
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +8 -0
- package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +4 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +2 -9
- package/src/lib/components/data-input/number-input.tsx +24 -5
- package/src/lib/components/data-table/data-table-utils.ts +241 -1
- package/src/lib/components/data-table/data-table.tsx +189 -60
- package/src/lib/components/shared/paginated-list-data-table.tsx +19 -0
- package/src/lib/components/ui/alert.tsx +1 -1
- package/src/lib/framework/page/list-page.tsx +62 -38
- package/src/lib/hooks/use-drag-and-drop.ts +86 -0
|
@@ -361,6 +361,22 @@ export interface ListPageProps<
|
|
|
361
361
|
* the list needs to be refreshed.
|
|
362
362
|
*/
|
|
363
363
|
registerRefresher?: PaginatedListRefresherRegisterFn;
|
|
364
|
+
/**
|
|
365
|
+
* @description
|
|
366
|
+
* Callback when items are reordered via drag and drop.
|
|
367
|
+
* Only applies to top-level items. When provided, enables drag-and-drop functionality.
|
|
368
|
+
*
|
|
369
|
+
* @param oldIndex - The original index of the dragged item
|
|
370
|
+
* @param newIndex - The new index where the item was dropped
|
|
371
|
+
* @param item - The data of the item that was moved
|
|
372
|
+
*/
|
|
373
|
+
onReorder?: (oldIndex: number, newIndex: number, item: any) => void | Promise<void>;
|
|
374
|
+
/**
|
|
375
|
+
* @description
|
|
376
|
+
* When true, drag and drop will be disabled. This will only have an effect if the onReorder prop is also set Useful when filtering or searching.
|
|
377
|
+
* Defaults to false. Only relevant when `onReorder` is provided.
|
|
378
|
+
*/
|
|
379
|
+
disableDragAndDrop?: boolean;
|
|
364
380
|
}
|
|
365
381
|
|
|
366
382
|
/**
|
|
@@ -481,6 +497,8 @@ export function ListPage<
|
|
|
481
497
|
setTableOptions,
|
|
482
498
|
bulkActions,
|
|
483
499
|
registerRefresher,
|
|
500
|
+
onReorder,
|
|
501
|
+
disableDragAndDrop = false,
|
|
484
502
|
}: Readonly<ListPageProps<T, U, V, AC>>) {
|
|
485
503
|
const route = typeof routeOrFn === 'function' ? routeOrFn() : routeOrFn;
|
|
486
504
|
const routeSearch = route.useSearch();
|
|
@@ -536,6 +554,47 @@ export function ListPage<
|
|
|
536
554
|
});
|
|
537
555
|
}
|
|
538
556
|
|
|
557
|
+
const commonTableProps = {
|
|
558
|
+
listQuery,
|
|
559
|
+
deleteMutation,
|
|
560
|
+
transformVariables,
|
|
561
|
+
customizeColumns: customizeColumns as any,
|
|
562
|
+
additionalColumns: additionalColumns as any,
|
|
563
|
+
defaultColumnOrder: columnOrder as any,
|
|
564
|
+
defaultVisibility: columnVisibility as any,
|
|
565
|
+
onSearchTermChange,
|
|
566
|
+
page: pagination.page,
|
|
567
|
+
itemsPerPage: pagination.itemsPerPage,
|
|
568
|
+
sorting,
|
|
569
|
+
columnFilters,
|
|
570
|
+
onPageChange: (table: Table<any>, page: number, perPage: number) => {
|
|
571
|
+
persistListStateToUrl(table, { page, perPage });
|
|
572
|
+
if (pageId) {
|
|
573
|
+
setTableSettings(pageId, 'pageSize', perPage);
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
onSortChange: (table: Table<any>, sorting: SortingState) => {
|
|
577
|
+
persistListStateToUrl(table, { sort: sorting });
|
|
578
|
+
},
|
|
579
|
+
onFilterChange: (table: Table<any>, filters: ColumnFiltersState) => {
|
|
580
|
+
persistListStateToUrl(table, { filters });
|
|
581
|
+
if (pageId) {
|
|
582
|
+
setTableSettings(pageId, 'columnFilters', filters);
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
onColumnVisibilityChange: (table: Table<any>, columnVisibility: any) => {
|
|
586
|
+
if (pageId) {
|
|
587
|
+
setTableSettings(pageId, 'columnVisibility', columnVisibility);
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
facetedFilters,
|
|
591
|
+
rowActions,
|
|
592
|
+
bulkActions,
|
|
593
|
+
setTableOptions,
|
|
594
|
+
transformData,
|
|
595
|
+
registerRefresher,
|
|
596
|
+
};
|
|
597
|
+
|
|
539
598
|
return (
|
|
540
599
|
<Page pageId={pageId}>
|
|
541
600
|
<PageTitle>{title}</PageTitle>
|
|
@@ -543,44 +602,9 @@ export function ListPage<
|
|
|
543
602
|
<PageLayout>
|
|
544
603
|
<FullWidthPageBlock blockId="list-table">
|
|
545
604
|
<PaginatedListDataTable
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
customizeColumns={customizeColumns as any}
|
|
550
|
-
additionalColumns={additionalColumns as any}
|
|
551
|
-
defaultColumnOrder={columnOrder as any}
|
|
552
|
-
defaultVisibility={columnVisibility as any}
|
|
553
|
-
onSearchTermChange={onSearchTermChange}
|
|
554
|
-
page={pagination.page}
|
|
555
|
-
itemsPerPage={pagination.itemsPerPage}
|
|
556
|
-
sorting={sorting}
|
|
557
|
-
columnFilters={columnFilters}
|
|
558
|
-
onPageChange={(table, page, perPage) => {
|
|
559
|
-
persistListStateToUrl(table, { page, perPage });
|
|
560
|
-
if (pageId) {
|
|
561
|
-
setTableSettings(pageId, 'pageSize', perPage);
|
|
562
|
-
}
|
|
563
|
-
}}
|
|
564
|
-
onSortChange={(table, sorting) => {
|
|
565
|
-
persistListStateToUrl(table, { sort: sorting });
|
|
566
|
-
}}
|
|
567
|
-
onFilterChange={(table, filters) => {
|
|
568
|
-
persistListStateToUrl(table, { filters });
|
|
569
|
-
if (pageId) {
|
|
570
|
-
setTableSettings(pageId, 'columnFilters', filters);
|
|
571
|
-
}
|
|
572
|
-
}}
|
|
573
|
-
onColumnVisibilityChange={(table, columnVisibility) => {
|
|
574
|
-
if (pageId) {
|
|
575
|
-
setTableSettings(pageId, 'columnVisibility', columnVisibility);
|
|
576
|
-
}
|
|
577
|
-
}}
|
|
578
|
-
facetedFilters={facetedFilters}
|
|
579
|
-
rowActions={rowActions}
|
|
580
|
-
bulkActions={bulkActions}
|
|
581
|
-
setTableOptions={setTableOptions}
|
|
582
|
-
transformData={transformData}
|
|
583
|
-
registerRefresher={registerRefresher}
|
|
605
|
+
{...commonTableProps}
|
|
606
|
+
onReorder={onReorder}
|
|
607
|
+
disableDragAndDrop={disableDragAndDrop}
|
|
584
608
|
/>
|
|
585
609
|
</FullWidthPageBlock>
|
|
586
610
|
</PageLayout>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { DragEndEvent, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
|
2
|
+
import { arrayMove, sortableKeyboardCoordinates } from '@dnd-kit/sortable';
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
interface UseDragAndDropOptions<TData> {
|
|
6
|
+
data: TData[];
|
|
7
|
+
onReorder?: (oldIndex: number, newIndex: number, item: TData, allItems?: TData[]) => void | Promise<void>;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @description
|
|
14
|
+
* Provides the sensors and state management for drag and drop functionality.
|
|
15
|
+
*
|
|
16
|
+
*
|
|
17
|
+
* @docsCategory hooks
|
|
18
|
+
* @docsPage useDragAndDrop
|
|
19
|
+
* @docsWeight 0
|
|
20
|
+
* @since 3.3.0
|
|
21
|
+
*/
|
|
22
|
+
export function useDragAndDrop<TData = any>(options: UseDragAndDropOptions<TData>) {
|
|
23
|
+
const sensors = useSensors(
|
|
24
|
+
useSensor(PointerSensor),
|
|
25
|
+
useSensor(KeyboardSensor, {
|
|
26
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const { data, onReorder, disabled = false } = options;
|
|
31
|
+
|
|
32
|
+
const [localData, setLocalData] = useState<TData[]>(data);
|
|
33
|
+
const [isReordering, setIsReordering] = useState(false);
|
|
34
|
+
|
|
35
|
+
// Update local data when data prop changes (but not during reordering)
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!isReordering) {
|
|
38
|
+
setLocalData(data);
|
|
39
|
+
}
|
|
40
|
+
}, [data, isReordering]);
|
|
41
|
+
|
|
42
|
+
const handleDragEnd = useCallback(
|
|
43
|
+
async (event: DragEndEvent) => {
|
|
44
|
+
const { active, over } = event;
|
|
45
|
+
|
|
46
|
+
if (!over || active.id === over.id || !onReorder || disabled) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const oldIndex = localData.findIndex(item => (item as { id: string }).id === active.id);
|
|
51
|
+
const newIndex = localData.findIndex(item => (item as { id: string }).id === over.id);
|
|
52
|
+
|
|
53
|
+
if (oldIndex === -1 || newIndex === -1) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Optimistically update the UI
|
|
58
|
+
const originalState = [...localData];
|
|
59
|
+
const newData = arrayMove(localData, oldIndex, newIndex);
|
|
60
|
+
setLocalData(newData);
|
|
61
|
+
setIsReordering(true);
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
// Call the user's onReorder callback with all items for context
|
|
65
|
+
await onReorder(oldIndex, newIndex, localData[oldIndex], localData);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// Revert on error
|
|
68
|
+
setLocalData(originalState);
|
|
69
|
+
options.onError?.(error as Error);
|
|
70
|
+
} finally {
|
|
71
|
+
setIsReordering(false);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
[localData, onReorder, disabled],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const itemIds = useMemo(() => localData.map(item => (item as { id: string }).id), [localData]);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
sensors,
|
|
81
|
+
localData,
|
|
82
|
+
handleDragEnd,
|
|
83
|
+
itemIds,
|
|
84
|
+
isReordering,
|
|
85
|
+
};
|
|
86
|
+
}
|