如何在 React 中实现嵌套列表的拖放?



我正在创建按节划分的侧边栏组件。我想做的是实现拖放功能。但这里有一个问题,这些部分在代码中是这样的:

const sidebarSections = [
{
name: 'General',
routePrefix: '/general',
elements: [
{
name: "Home",
icon: <HomeIcon/>,
href: "/home",
show: true
},
{
name: "Clients",
icon: <SupervisedUserCircleIcon/>,
href: "/clients",
show: true
},
{
name: "ACL",
icon: <ManageAccountsIcon/>,
href: "/acl",
show: true
},
{
name: "Modify roles",
icon: <AddModeratorIcon/>,
href: "/roles",
show: true
},
]
},
...

每个section(在本例中为General)都有其elements。正如你可能猜到的那样,我想只对这些元素实现拖放,而不是对整个sidebarSections

下面是这个侧边栏列表在代码中的样子:
const [sidebar, setSidebar] = React.useState(sidebarSections)
const list = () => (
<Box
sx={{
width: anchor === 'top' || anchor === 'bottom' ? 'auto' : 250,
}}
role="presentation"
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
{sidebarSections.map((section, key) => (
<Box
key={key}
sx={{
marginTop: 2,
}}
>
{section.name ? (
<ListItemText
sx={{
px: 2,
color: 'primary.light',
}}
>
{section.name}
</ListItemText>
) : null}
<List>
{section.elements.map((subsection) =>
subsection.show || settings ? (
<ListItem key={subsection.name}>
{settings ? (
<Checkbox
checked={subsection.show}
onChange={() => {
subsection.show = !subsection.show;
setSidebar((prev) => [...prev]);
}}
/>
) : null}
<Link href={section.routePrefix + subsection.href} passHref>
<Button
sx={{
color: router.pathname.includes(
`${section.routePrefix + subsection.href}`
)
? "secondary.main"
: "primary.main",
width: "100%",
justifyContent: "start"
}}
startIcon={subsection.icon}
>
{subsection.name}
</Button>
</Link>
</ListItem>
) : null
)}
</List>
<Divider
sx={{color: 'primary.light'}}
/>
</Box>
))}
</List>
</Box>
)

当我尝试这样做的时候,我正在使用这个指南,但是,由于嵌套列表,我不能这样做。

我是这样解决这个问题的。而不是{subsection.name},你可以传递你的组件(Button在我的情况下)。

DragDropContext必须只在您想要启用拖放的列表上。我还改变了handleDrop函数,使其解析嵌套列表,并渲染它更多的时间来显示元素的新顺序。


const handleDrop = (droppedItem) => {
if (!droppedItem.destination) return;
const updatedSidebar = [...sidebar];
updatedSidebar.forEach(section => {
section.elements.forEach(subsection => {
if (droppedItem.draggableId === subsection.name) {
const [reorderedItem] = section.elements.splice(droppedItem.source.index, 1);
section.elements.splice(droppedItem.destination.index, 0, reorderedItem);
}
})
})
setSidebar(updatedSidebar);
};
const list2 = () => (
<Box
sx={{
width: anchor === 'top' || anchor === 'bottom' ? 'auto' : 250,
}}
role="presentation"
onKeyDown={toggleDrawer(anchor, false)}
>
<List>
{sidebarSections.map((section, key) => (
<Box
key={key}
sx={{
marginTop: 2,
}}
>
{section.name ? (
<ListItemText
sx={{
px: 2,
color: 'primary.light',
}}
>
{section.name}
</ListItemText>
) : null}
<List>
<DragDropContext onDragEnd={handleDrop}>
<Droppable droppableId="list-container">
{(provided) => (
<div
className="list-container"
{...provided.droppableProps}
ref={provided.innerRef}
>
{section.elements.map((subsection, index) =>
subsection.show || settings ? (
<ListItem key={subsection.name}>
{settings ? (
<Box>
<Draggable key={subsection.name} draggableId={subsection.name} index={index}>
{(provided) => (
<div
className="item-container"
ref={provided.innerRef}
{...provided.dragHandleProps}
{...provided.draggableProps}
>
{subsection.name}
</div>
)}
</Draggable>
{provided.placeholder}
</Box>
) : null}
{!settings ? (
<Link href={section.routePrefix + subsection.href} passHref>
<Button
sx={{
color: router.pathname.includes(
`${section.routePrefix + subsection.href}`
)
? "secondary.main"
: "primary.main",
width: "100%",
justifyContent: "start"
}}
startIcon={subsection.icon}
>
{subsection.name}
</Button>
</Link>
) : null}
</ListItem>
) : null
)}
</div>
)}
</Droppable>
</DragDropContext>
</List>
<Divider
sx={{color: 'primary.light'}}
/>
</Box>
))}
</List>
</Box>
);

最新更新