Reputation: 31
I have tried to implement Drag & Drop feature in Mui TabList using 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. This seems like a standard use case, so there must be some solution. Please refer my code below -
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>
);
}
Thanks!
Upvotes: 0
Views: 6371
Reputation: 31
This can be solved by - in /components/DraggableTabsList.jsx
wrap the Droppable
inside a div container with style {display:"flex", overflow:"auto"}
. The new code would look like -
<div style={{ display: 'flex', overflow: 'auto' }}>
<Droppable droppableId="1" direction="horizontal">
{(droppableProvided) => (
<div
ref={droppableProvided.innerRef}
{...droppableProvided.droppableProps}
>
{_renderTabList(droppableProvided)}
</div>
)}
</Droppable>
</div>
This will make the tabs horizontally draggable through the entire tab list, but the scroll buttons from the MUI TabList will not render.
Codesandbox - https://codesandbox.io/s/mr9y10
Upvotes: 1