chore: add sortable group component which using react-beautiful-dnd
This commit is contained in:
parent
f309000a0c
commit
91ade2ab55
@ -288,6 +288,9 @@ importers:
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
react-beautiful-dnd:
|
||||
specifier: ^13.1.1
|
||||
version: 13.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react-day-picker:
|
||||
specifier: ^8.10.1
|
||||
version: 8.10.1(date-fns@3.6.0)(react@18.2.0)
|
||||
@ -376,6 +379,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: ^18.2.22
|
||||
version: 18.2.78
|
||||
'@types/react-beautiful-dnd':
|
||||
specifier: ^13.1.8
|
||||
version: 13.1.8
|
||||
'@types/react-dom':
|
||||
specifier: ^18.2.7
|
||||
version: 18.2.7
|
||||
@ -4707,6 +4713,9 @@ packages:
|
||||
'@types/range-parser@1.2.4':
|
||||
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
|
||||
|
||||
'@types/react-beautiful-dnd@13.1.8':
|
||||
resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==}
|
||||
|
||||
'@types/react-dom@18.2.7':
|
||||
resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==}
|
||||
|
||||
@ -6103,6 +6112,9 @@ packages:
|
||||
resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
css-box-model@1.2.1:
|
||||
resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==}
|
||||
|
||||
css-color-converter@2.0.0:
|
||||
resolution: {integrity: sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==}
|
||||
|
||||
@ -8729,6 +8741,9 @@ packages:
|
||||
resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
||||
memoize-one@5.2.1:
|
||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
||||
|
||||
meow@12.1.1:
|
||||
resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==}
|
||||
engines: {node: '>=16.10'}
|
||||
@ -10417,6 +10432,9 @@ packages:
|
||||
radix3@1.1.0:
|
||||
resolution: {integrity: sha512-pNsHDxbGORSvuSScqNJ+3Km6QAVqk8CfsCBIEoDgpqLrkD2f3QM4I7d1ozJJ172OmIcoUcerZaNWqtLkRXTV3A==}
|
||||
|
||||
raf-schd@4.0.3:
|
||||
resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==}
|
||||
|
||||
randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
|
||||
@ -10764,6 +10782,12 @@ packages:
|
||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||
hasBin: true
|
||||
|
||||
react-beautiful-dnd@13.1.1:
|
||||
resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.5 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
|
||||
|
||||
react-color@2.17.1:
|
||||
resolution: {integrity: sha512-S+I6TkUKJaqfALLkAIfiCZ/MANQyy7dKkf7g9ZU5GTUy2rf8c2Rx62otyvADAviWR+6HRkzdf2vL1Qvz9goCLQ==}
|
||||
peerDependencies:
|
||||
@ -12617,6 +12641,11 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
use-memo-one@1.1.3:
|
||||
resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
use-sidecar@1.1.2:
|
||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
||||
engines: {node: '>=10'}
|
||||
@ -18765,6 +18794,10 @@ snapshots:
|
||||
|
||||
'@types/range-parser@1.2.4': {}
|
||||
|
||||
'@types/react-beautiful-dnd@13.1.8':
|
||||
dependencies:
|
||||
'@types/react': 18.2.78
|
||||
|
||||
'@types/react-dom@18.2.7':
|
||||
dependencies:
|
||||
'@types/react': 18.2.78
|
||||
@ -20466,6 +20499,10 @@ snapshots:
|
||||
dependencies:
|
||||
type-fest: 1.4.0
|
||||
|
||||
css-box-model@1.2.1:
|
||||
dependencies:
|
||||
tiny-invariant: 1.3.3
|
||||
|
||||
css-color-converter@2.0.0:
|
||||
dependencies:
|
||||
color-convert: 0.5.3
|
||||
@ -23830,6 +23867,8 @@ snapshots:
|
||||
dependencies:
|
||||
fs-monkey: 1.0.5
|
||||
|
||||
memoize-one@5.2.1: {}
|
||||
|
||||
meow@12.1.1: {}
|
||||
|
||||
merge-descriptors@1.0.1: {}
|
||||
@ -26000,6 +26039,8 @@ snapshots:
|
||||
|
||||
radix3@1.1.0: {}
|
||||
|
||||
raf-schd@4.0.3: {}
|
||||
|
||||
randombytes@2.1.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
@ -26710,6 +26751,20 @@ snapshots:
|
||||
minimist: 1.2.8
|
||||
strip-json-comments: 2.0.1
|
||||
|
||||
react-beautiful-dnd@13.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
css-box-model: 1.2.1
|
||||
memoize-one: 5.2.1
|
||||
raf-schd: 4.0.3
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-redux: 7.2.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
redux: 4.2.1
|
||||
use-memo-one: 1.1.3(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- react-native
|
||||
|
||||
react-color@2.17.1(react@18.2.0):
|
||||
dependencies:
|
||||
'@icons/material': 0.2.4(react@18.2.0)
|
||||
@ -26896,6 +26951,18 @@ snapshots:
|
||||
|
||||
react-magic-dropzone@1.0.1: {}
|
||||
|
||||
react-redux@7.2.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
'@types/react-redux': 7.1.33
|
||||
hoist-non-react-statics: 3.3.2
|
||||
loose-envify: 1.4.0
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
react-is: 17.0.2
|
||||
optionalDependencies:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
react-redux@7.2.9(react-dom@18.2.0(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.0
|
||||
@ -29066,6 +29133,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.78
|
||||
|
||||
use-memo-one@1.1.3(react@18.2.0):
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
|
||||
use-sidecar@1.1.2(@types/react@18.2.78)(react@18.2.0):
|
||||
dependencies:
|
||||
detect-node-es: 1.1.0
|
||||
|
62
src/client/components/SortableGroup.tsx
Normal file
62
src/client/components/SortableGroup.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import React, { useState } from 'react';
|
||||
import { DragDropContext, DropResult } from 'react-beautiful-dnd';
|
||||
import { useEvent } from '@/hooks/useEvent';
|
||||
import { reorder } from '@/utils/reorder';
|
||||
|
||||
interface BasicItem {
|
||||
type: 'root' | 'group' | 'item';
|
||||
items?: BasicItem[];
|
||||
}
|
||||
|
||||
interface SortableGroupProps {
|
||||
list: BasicItem[];
|
||||
onChange: (list: BasicItem[]) => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SortableGroup: React.FC<SortableGroupProps> = React.memo(
|
||||
(props) => {
|
||||
const [list, setList] = useState(props.list);
|
||||
|
||||
const handleDragEnd = useEvent((result: DropResult) => {
|
||||
// dropped outside the list
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.type === 'root') {
|
||||
setList(reorder(list, result.source.index, result.destination.index));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.type === 'group') {
|
||||
const nestedIndex = list.findIndex((item): boolean => 'items' in item);
|
||||
|
||||
if (nestedIndex === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nested = list[nestedIndex].items;
|
||||
if (!nested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const children = Array.from(list);
|
||||
children[nestedIndex].items = reorder(
|
||||
nested,
|
||||
result.source.index,
|
||||
result.destination.index
|
||||
);
|
||||
|
||||
setList([...list]);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<DragDropContext onDragEnd={handleDragEnd}>
|
||||
{props.children}
|
||||
</DragDropContext>
|
||||
);
|
||||
}
|
||||
);
|
||||
SortableGroup.displayName = 'SortableGroup';
|
@ -75,6 +75,7 @@
|
||||
"next-themes": "^0.2.1",
|
||||
"pretty-ms": "^9.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-easy-sort": "^1.5.3",
|
||||
@ -106,6 +107,7 @@
|
||||
"@types/loadable__component": "^5.13.8",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/react": "^18.2.22",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-grid-layout": "^1.3.5",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
|
12
src/client/utils/reorder.ts
Normal file
12
src/client/utils/reorder.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// a little function to help us with reordering the result
|
||||
export function reorder<T>(
|
||||
list: T[],
|
||||
startIndex: number,
|
||||
endIndex: number
|
||||
): T[] {
|
||||
const result = Array.from(list);
|
||||
const [removed] = result.splice(startIndex, 1);
|
||||
result.splice(endIndex, 0, removed);
|
||||
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue
Block a user