2

I have added drag & drop feature to the MUI Tabs List using react-beautiful-dnd.

Code -

import * as React from 'react';
import TabContext from '@mui/lab/TabContext';
import TabList from '@mui/lab/TabList';
import TabPanel from '@mui/lab/TabPanel';
import Tab from '@mui/material/Tab';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import { Draggable } from 'react-beautiful-dnd';
import { styled } from '@mui/material/styles';
import { Stack } from '@mui/material';

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,
                    })}
                </div>
            )}
        </Draggable>
    );
}

const StyledTabList = styled(TabList)();
const StyledTab = styled(Tab)();

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

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

    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);
    };

    const _renderTabList = (droppableProvided) => (
        <StyledTabList onChange={handleChange} variant="scrollable">
            {tabs.map((tab, index) => {
                const child = (
                    <StyledTab
                        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}
        </StyledTabList>
    );

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

    return (
        <TabContext value={value}>
            <Stack>{_renderTabListWrappedInDroppable()}</Stack>
            {tabs.map((tab, index) => (
                <TabPanel value={tab.value} key={index}>
                    {tab.content}
                </TabPanel>
            ))}
        </TabContext>
    );
}

Working codesandbox example - https://codesandbox.io/s/mui-tab-list-drag-and-drop-jceqnz

I am facing trouble making the tab list auto scroll while a tab is being dragged to the end of the visible list.

Also, I tried some hacks to make it work - see this, but I'm losing out on the scroll buttons which is a feature loss and not wanted. As auto scrolling is a standard d&d feature I'm hoping there must be some solution. Could you please help?

Thanks!

qwertyvipul
  • 29
  • 1
  • 7

1 Answers1

1

First of all thank you for such good quality code. I was searching for implementation of MUI tabs with react-beautiful-dnd and found your implementation really helpful.

To make it scrollable i along with dragable I removed unnecessary div and it worked perfectly for me

Here's a screen shot of my code.

enter image description here

And Heres the picture of dragable tab with scroll

enter image description here

Also I'm importing tabs from mui directly like this enter image description here

Waleed Tariq
  • 825
  • 8
  • 19
  • Hi Waleed, I'm trying to implement something similar except in typescript, would it be possible to see the entire tabs component code please? Completely understand if it isn't – Hank Oct 24 '22 at 10:46
  • Hi Waleed, would it be possible for you to share a codesandbox reproducer of your code? It would help me better understand if your implementation would solve my use case. Thanks! – qwertyvipul Dec 02 '22 at 09:22
  • @Waleed Can you share a codesandbox? – arunmmanoharan Dec 22 '22 at 15:06