简体   繁体   English

需要帮助创建拖放材质 UI 选项卡

[英]Need help creating Drag and Drop Material UI Tabs

I've got a simple setup for some MUI tabs that I'm working to implement some drag and drop functionality to.我有一些 MUI 选项卡的简单设置,我正在努力实现一些拖放功能。 The issue I'm running into is that when the SortableContext is nested inside of the TabList component, drag and drop works but the values no longer work for the respective tabs.我遇到的问题是,当 SortableContext 嵌套在 TabList 组件内时,拖放有效,但值不再适用于各个选项卡。 When I move the SortableContext outside of the TabList component the values work again, but the drag and drop doesn't.当我将 SortableContext 移到 TabList 组件之外时,值再次起作用,但拖放不起作用。 If anybody has any guidance here that would be greatly appreciated!如果有人在这里有任何指导,将不胜感激!

Here is a link to a CodeSandbox using the code below: https://codesandbox.io/s/material-ui-tabs-with-drag-n-drop-functionality-05ktf3这是使用以下代码的 CodeSandbox 链接: https://codesandbox.io/s/material-ui-tabs-with-drag-n-drop-functionality-05ktf3

Below is my code snippet:下面是我的代码片段:

import { Box } from "@mui/material";
import { useState } from "react";
import { DndContext, closestCenter } from "@dnd-kit/core";
import { arrayMove, SortableContext, rectSortingStrategy } from "@dnd-kit/sortable";
import SortableTab from "./SortableTab";
import { TabContext, TabList } from "@mui/lab";

const App = () => {
    const [items, setItems] = useState(["Item One", "Item Two", "Item Three", "Item Four", "Item Five"]);
    const [activeTab, setActiveTab] = useState("0");

    const handleDragEnd = (event) => {
        const { active, over } = event;
        console.log("Drag End Called");

        if (active.id !== over.id) {
            setItems((items) => {
                const oldIndex = items.indexOf(active.id);
                const newIndex = items.indexOf(over.id);
                return arrayMove(items, oldIndex, newIndex);
            });
        }
    };

    const handleChange = (event, newValue) => {
        setActiveTab(newValue);
    };

    return (
        <div>
            <Box sx={{ width: "100%" }}>
                <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
                    <TabContext value={activeTab}>
                        <DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
                            <SortableContext items={items} strategy={rectSortingStrategy}>
                                <TabList onChange={handleChange} aria-label="basic tabs example">
                                    {items.map((item, index) => (
                                        <SortableTab value={index.toString()} key={item} id={item} index={index} label={item} />
                                    ))}
                                </TabList>
                            </SortableContext>
                        </DndContext>
                    </TabContext>
                </Box>
            </Box>
        </div>
    );
};

export default App;
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Tab, IconButton } from "@mui/material";
import DragIndicatorIcon from "@mui/icons-material/DragIndicator";

const SortableTab = (props) => {
    const { attributes, listeners, setNodeRef, transform, transition, setActivatorNodeRef } = useSortable({
        id: props.id,
    });

    const style = {
        transform: CSS.Transform.toString(transform),
        transition,
    };

    return (
        <div ref={setNodeRef} {...attributes} style={style}>
            <Tab {...props} />
            <IconButton ref={setActivatorNodeRef} {...listeners} size="small">
                <DragIndicatorIcon fontSize="5px" />
            </IconButton>
        </div>
    );
};

export default SortableTab;

You can make drag and drop tabs work by moving <SortableContext> inside the <TabList> component something like the below.您可以通过在<TabList>组件内移动<SortableContext>来使拖放选项卡工作,如下所示。 Then change sorting started to horizontalListSortingStrategy .然后将 sorting started 更改为 horizo horizontalListSortingStrategy

In this case, your Dnd will work, but you will lose all MUI transitions/animations.在这种情况下,您的 Dnd 将起作用,但您将丢失所有 MUI 转换/动画。 Because now DndContext is overriding MuiContext .因为现在DndContext覆盖MuiContext The best solution to create something like this is to create custom Tabs components where your context does not depend on TabContext .创建此类内容的最佳解决方案是创建自定义Tabs组件,其中您的上下文不依赖于TabContext

I hope it helps.我希望它有所帮助。

     <TabContext value={activeTab}>
        <DndContext
          collisionDetection={closestCenter}
          onDragEnd={handleDragEnd}
        >
          <TabList onChange={handleChange} aria-label="basic tabs example">
            <SortableContext
              items={items}
              strategy={horizontalListSortingStrategy}
            >
              {items.map((item, index) => (
                <SortableTab
                  value={index.toString()}
                  key={item}
                  id={item}
                  index={index}
                  label={item}
                />
              ))}
            </SortableContext>
          </TabList>
        </DndContext>
      </TabContext>

So in order for this to work I ended up figuring out that without a draghandle on the Tab the click event would only either fire for the drag event or setting the value depending on where the SortableContext was placed.因此,为了使其正常工作,我最终发现如果 Tab 上没有拖动手柄,则单击事件只会为拖动事件触发或根据 SortableContext 的放置位置设置值。 This was my solution:这是我的解决方案:

SortableTab排序选项卡

import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Tab, IconButton } from "@mui/material";
import MoreVertRoundedIcon from "@mui/icons-material/MoreVertRounded";

const SortableTab = (props) => {
    const { attributes, listeners, setNodeRef, transform, transition, setActivatorNodeRef } = useSortable({
        id: props.id,
    });

    const style = {
        transform: CSS.Transform.toString(transform),
        transition,
    };

    return (
        <div ref={setNodeRef} {...attributes} style={style}>
            <Tab {...props} />
            <IconButton ref={setActivatorNodeRef} {...listeners} size="small">
                <MoreVertRoundedIcon fontSize="5px" />
            </IconButton>
        </div>
    );
};

export default SortableTab;

DndContext code chunk: DndContext代码块:

const renderedTab = selectedScenario ? (
        scenarioModels.map((item, index) => <SortableTab key={item} label={models.data[item].model} id={item} index={index} value={index + 1} onClick={() => handleModelClick(item)} />)
    ) : (
        <Tab label="Pick a Scenario" value={0} />
    );

<DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
                    <SortableContext items={scenarioModels} strategy={horizontalListSortingStrategy}>
                        <Tabs value={selectedTab} onChange={handleChange} centered>
                            {selectedScenario ? <Tab label="Source Data" /> : ""}
                            {renderedTab}
                        </Tabs>
                    </SortableContext>
                </DndContext>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM