Skip to content

uiwjs/react-tabs-draggable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

react-tabs-draggable

CI Open in unpkg npm version

Draggable tabs for React. Demo Preview: @uiwjs.github.io/react-tabs-draggable

Install

Not dependent on uiw.

npm install @uiw/react-tabs-draggable --save

Base Usage

import React, { useState } from 'react'; import Tabs, { Tab } from '@uiw/react-tabs-draggable'; function App() { const [activeKey, setActiveKey] = useState('tab-1'); return ( <div> <Tabs activeKey={activeKey} style={{ gap: 12 }} onTabClick={(id) => setActiveKey(id)}> <Tab id="tab-1">{activeKey === 'tab-1' && 'â–¶'}Google</Tab> <Tab id="tab-2">{activeKey === 'tab-2' && 'â–¶'}MicroSoft</Tab> <Tab id="tab-3">{activeKey === 'tab-3' && 'â–¶'}Baidu</Tab> <Tab id="tab-4">{activeKey === 'tab-4' && 'â–¶'}Taobao</Tab> <Tab id="tab-5">{activeKey === 'tab-5' && 'â–¶'}JD</Tab> </Tabs> <div style={{ background: '#fff', padding: 12 }}>{activeKey}</div> </div> ); } export default App;

Disable Draggable

The first tab is disabled.

import React, { useState } from 'react'; import Tabs, { Tab } from '@uiw/react-tabs-draggable'; import styled from 'styled-components'; const TabItem = styled(Tab)`  background-color: #b9b9b9;  padding: 3px 7px;  border-radius: 5px 5px 0 0;  &.w-active {  color: #fff;  background-color: #333;  } `; const Content = styled.div`  border-top: 1px solid #333; `; function App() { const [activeKey, setActiveKey] = useState('tab-0-1'); return ( <div> <Tabs activeKey={activeKey} style={{ gap: 6 }} onTabClick={(id) => setActiveKey(id)}> <TabItem id="tab-0-1">Google</TabItem> <TabItem id="tab-0-2">MicroSoft</TabItem> <TabItem id="tab-0-3">Baidu</TabItem> <TabItem id="tab-0-4">Taobao</TabItem> <TabItem id="tab-0-5">JD</TabItem> </Tabs> <Content>{activeKey}</Content> </div> ); } export default App;

Add & Close tab

The first tab is disabled.

import React, { Fragment, useState, useCallback } from 'react'; import Tabs, { Tab, useDataContext } from '@uiw/react-tabs-draggable'; import styled from 'styled-components'; const TabWarp = styled(Tabs)`  max-width: 450px;  border-bottom: 1px solid #333;  margin-bottom: -2px;  &:hover::-webkit-scrollbar {  height: 0px;  background-color: red;  }  &:hover::-webkit-scrollbar-track {  background-color: #333;  }  &:hover::-webkit-scrollbar-thumb {  background-color: green;  } `; const TabItem = styled(Tab)`  background-color: #b9b9b9;  padding: 3px 7px;  border-radius: 5px 5px 0 0;  user-select: none;  &.w-active {  color: #fff;  background-color: #333;  } `; function insertAndShift(arr, from, to) { let cutOut = arr.splice(from, 1)[0]; arr.splice(to, 0, cutOut); return arr; } let count = 9; function App() { const [data, setData] = useState([ { id: 'tab-4-1', children: 'Google' }, { id: 'tab-4-2', children: 'MicroSoft' }, { id: 'tab-4-3', children: 'Baidu' }, { id: 'tab-4-4', children: 'Taobao' }, { id: 'tab-4-5', children: 'JD' }, { id: 'tab-4-6', children: 'Apple' }, { id: 'tab-4-7', children: 'Bing' }, { id: 'tab-4-8', children: 'Gmail' }, { id: 'tab-4-9', children: 'Gitter' }, ]); const [test, setTest] = useState(1); const [activeKey, setActiveKey] = useState(''); const tabClick = (id, evn) => { evn.stopPropagation(); setActiveKey(id); setTest(test + 1); }; const closeHandle = (item, evn) => { evn.stopPropagation(); const idx = data.findIndex((m) => m.id === item.id); let active = ''; if (idx > -1 && activeKey) { active = data[idx - 1] ? data[idx - 1].id : data[idx].id; setActiveKey(active || ''); } setData(data.filter((m) => m.id !== item.id)); }; const addHandle = () => { ++count; const newData = [...data, { id: `tab-3-${count}`, children: `New Tab ${count}` }]; setData(newData); }; const tabDrop = (id, index) => { const oldIndex = [...data].findIndex((m) => m.id === id); const newData = insertAndShift([...data], oldIndex, index); setData(newData); }; return ( <Fragment> <button onClick={addHandle}>Add{count}</button> <TabWarp activeKey={activeKey} style={{ gap: 3, overflow: 'auto' }} onTabClick={(id, evn) => tabClick(id, evn)} onTabDrop={(id, index) => tabDrop(id, index)} > {data.map((m, idx) => { return ( <TabItem key={idx} id={m.id} draggable={idx !== 0}> {m.children} <button onClick={(evn) => closeHandle(m, evn)}>x</button> </TabItem> ); })} </TabWarp> <div>{activeKey}</div> </Fragment> ); } export default App;
import React, { Fragment, useState, useCallback } from 'react'; import Tabs, { Tab, useDataContext } from '@uiw/react-tabs-draggable'; import styled from 'styled-components'; const TabWarp = styled(Tabs)`  max-width: 450px;  border-bottom: 1px solid #333;  margin-bottom: -2px;  gap: 3px; `; const TabItem = styled(Tab)`  background-color: #b9b9b9;  padding: 3px 7px;  border-radius: 5px 5px 0 0;  user-select: none;  flex-wrap: nowrap;  overflow: hidden;  word-break: keep-all;  align-items: center;  display: flex;  position: relative;  flex-direction: row;  &.w-active {  color: #fff;  background-color: #333;  } `; function insertAndShift(arr, from, to) { let cutOut = arr.splice(from, 1)[0]; arr.splice(to, 0, cutOut); return arr; } let count = 9; function App() { const [data, setData] = useState([ { id: 'tab-4-1', children: 'Google' }, { id: 'tab-4-2', children: 'MicroSoft' }, { id: 'tab-4-3', children: 'Baidu' }, { id: 'tab-4-4', children: 'Taobao' }, { id: 'tab-4-5', children: 'JD' }, { id: 'tab-4-6', children: 'Apple' }, { id: 'tab-4-7', children: 'Bing' }, { id: 'tab-4-8', children: 'Gmail' }, { id: 'tab-4-9', children: 'Gitter' }, ]); const [test, setTest] = useState(1); const [activeKey, setActiveKey] = useState(''); const tabClick = (id, evn) => { evn.stopPropagation(); setActiveKey(id); setTest(test + 1); }; const closeHandle = (item, evn) => { evn.stopPropagation(); setData(data.filter((m) => m.id !== item.id)); }; const addHandle = () => { ++count; const newData = [...data, { id: `tab-3-${count}`, children: `New Tab ${count}` }]; setData(newData); }; const tabDrop = (id, index, offset) => { const oldIndex = [...data].findIndex((m) => m.id === id); const newData = insertAndShift([...data], oldIndex, index); setData(newData); }; return ( <Fragment> <button onClick={addHandle}>Add{count}</button> <TabWarp onTabClick={(id, evn) => tabClick(id, evn)} onTabDrop={(id, index, offset) => tabDrop(id, index, offset)} > {data.map((m, idx) => { return ( <TabItem key={idx} id={m.id}> {m.children} <button onClick={(evn) => closeHandle(m, evn)}>x</button> </TabItem> ); })} </TabWarp> <div>{activeKey}</div> </Fragment> ); } export default App;

Props

export interface TabsProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> { activeKey?: string; onTabClick?: (id: string, evn: React.MouseEvent<HTMLDivElement>) => void; /**  * Optional. Called when a compatible item is dropped on the target.  */ onTabDrop?: (id: string, index?: number, offset?: XYCoord | null) => void; } export interface TabProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> { id: string; index?: number; /** Whether the Y axis can be dragged */ dragableY?: boolean; } export declare const Tab: FC<PropsWithChildren<TabProps>>;

Development

npm run watch # Listen create type and .tsx files. npm run start # Preview code example.

Contributors

As always, thanks to our amazing contributors!

Made with action-contributors.

License

Licensed under the MIT License.

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •