使用 react-beautiful-dnd 拖动时自动滚动水平 Mui TabList

Auto Scroll Horizontal Mui TabList on Drag with react-beautiful-dnd

我尝试使用 react-beautiful-dnd. The dnd is working fine but, I am facing an issue making horizontal list of tabs auto scroll while dragging when there are too many tabs to fit in the screen and the variant prop of Mui TabList is scrollable. See this codesandbox example - https://codesandbox.io/s/draggable-and-scrollable-mui-tabs-xqgl77 在 Mui TabList 中实现拖放功能。这似乎是一个标准用例,因此必须有一些解决方案。请参考下面我的代码-

App.js

import * as React from "react";
import "./styles.css";
import DraggableTabsList from "./components/DraggableTabsList";

export default function App() {
  const [tabs, setTabs] = React.useState(
    [...Array(55)].map((_, index) => ({
      id: `tab${index + 1}`,
      label: `Tab ${index + 1}`,
      value: `${index + 1}`,
      content: `Content ${index + 1}`
    }))
  );

  const onDragEnd = (result) => {
    const newTabs = Array.from(tabs);
    const draggedTab = newTabs.splice(result.source.index, 1)[0];
    newTabs.splice(result.destination.index, 0, draggedTab);
    setTabs(newTabs);
  };

  return (
    <div className="App">
      <DraggableTabsList onDragEnd={onDragEnd} tabs={tabs} />
    </div>
  );
}

/components/DraggableTabsList.jsx

import * as React from "react";
import Box from "@mui/material/Box";
import TabContext from "@mui/lab/TabContext";
import TabList from "@mui/lab/TabList";
import TabPanel from "@mui/lab/TabPanel";
import { DragDropContext, Droppable } from "react-beautiful-dnd";
import DraggableTab from "./DraggableTab";
import Tab from "@mui/material/Tab";
import Stack from "@mui/material/Stack";

export default function DraggableTabsList(props) {
  const [value, setValue] = React.useState("1");

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

  const _renderTabList = (droppableProvided) => (
    <TabList
      onChange={handleChange}
      aria-label="Draggable Tabs"
      variant="scrollable"
    >
      {props.tabs.map((tab, index) => {
        const child = <Tab label={tab.label} value={tab.value} key={index} />;

        return (
          <DraggableTab
            label={tab.label}
            value={tab.value}
            index={index}
            key={index}
            child={child}
          />
        );
      })}
      {droppableProvided ? droppableProvided.placeholder : null}
    </TabList>
  );

  const _renderTabListWrappedInDroppable = () => (
    <DragDropContext onDragEnd={props.onDragEnd}>
      <Droppable droppableId="1" direction="horizontal">
        {(droppableProvided) => (
          <div
            ref={droppableProvided.innerRef}
            {...droppableProvided.droppableProps}
          >
            {_renderTabList(droppableProvided)}
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );

  return (
    <Box sx={{ width: "100%", typography: "body1" }}>
      <TabContext value={value}>
        <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
          <Stack direction="column">{_renderTabListWrappedInDroppable()}</Stack>
        </Box>
        {props.tabs.map((tab, index) => (
          <TabPanel value={tab.value} key={index}>
            {tab.content}
          </TabPanel>
        ))}
      </TabContext>
    </Box>
  );
}

components/DraggableTab.jsx

import * as React from "react";
import { Draggable } from "react-beautiful-dnd";

export default function DraggableTab(props) {
  return (
    <Draggable
      draggableId={`${props.index}`}
      index={props.index}
      disableInteractiveElementBlocking
    >
      {(draggableProvided) => (
        <div
          ref={draggableProvided.innerRef}
          {...draggableProvided.draggableProps}
        >
          {React.cloneElement(props.child, {
            ...props,
            ...draggableProvided.dragHandleProps,
            style: { cursor: "inherit" }
          })}
        </div>
      )}
    </Draggable>
  );
}

谢谢!

这可以通过以下方式解决 - 在 /components/DraggableTabsList.jsx 中将 Droppable 包装在 div 容器中,样式为 {display:"flex", overflow:"auto"}。新代码看起来像 -

<div style={{ display: 'flex', overflow: 'auto' }}>
    <Droppable droppableId="1" direction="horizontal">
        {(droppableProvided) => (
            <div
                ref={droppableProvided.innerRef}
                {...droppableProvided.droppableProps}
            >
                {_renderTabList(droppableProvided)}
            </div>
        )}
    </Droppable>
</div>

这将使选项卡可在整个选项卡列表中水平拖动,但不会呈现 MUI TabList 中的滚动按钮。

Codesandbox - https://codesandbox.io/s/mr9y10