react-draggable-tab

repository·master·Indexed 40 days ago

https://github.com/georgeosddev/react-draggable-tab

A React component for creating Chrome-like draggable tabs, allowing users to select, move, add, and close tabs via mouse or keyboard shortcuts. Version 0.10.1 supports React v0.14+ (v0.4.0+) and React v0.13.x (v0.3.3).

Tokens
4.8K
Snippets
11
Records
17
Agent score
49%

What's inside react-draggable-tab

  1. Implement a draggable tab system

    master

    To use react-draggable-tab, manage the tabs array and the selectedTab key in your component state. Use the event handlers provided by Tabs to update this state when tabs are selected, closed, moved, or added.

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          tabs: [
            <Tab key='tab0' title='Tab 0'><div>Content 0</div></Tab>,
            <Tab key='tab1' title='Tab 1'><div>Content 1</div></Tab>
          ],
          selectedTab: 'tab0'
        };
      }
    
      handleTabSelect = (e, key, currentTabs) => {
        this.setState({ selectedTab: key, tabs: currentTabs });
      };
    
      handleTabClose = (e, key, currentTabs) => {
        this.setState({ tabs: currentTabs });
      };
    
      handleTabPositionChange = (e, key, currentTabs) => {
        this.setState({ tabs: currentTabs });
      };
    
      handleTabAddButtonClick = (e, currentTabs) => {
        const key = 'newTab_' + Date.now();
        const newTab = <Tab key={key} title='New Tab'><div>New Content</div></Tab>;
        this.setState({
          tabs: currentTabs.concat([newTab]),
          selectedTab: key
        });
      };
    
      render() {
        return (
          <Tabs
            tabs={this.state.tabs}
            selectedTab={this.state.selectedTab}
            onTabSelect={this.handleTabSelect}
            onTabClose={this.handleTabClose}
            onTabPositionChange={this.handleTabPositionChange}
            onTabAddButtonClick={this.handleTabAddButtonClick}
          />
        );
      }
    }
    class App extends React.Component {
      constructor(props) {
        super(props);
        let icon = (<image src='icon.png' style={{height:'13px'}}/>);
        let fonticon = (<icon className='icon-html5'/>);
        let badge = (<DynamicTabBadge />);
    
        this.state = {
          tabs:[
            (<Tab key={'tab0'} title={'unclosable tab'} unclosable={true} >
              <div>
                <h1>This tab cannot close</h1>
              </div>
            </Tab>),
            (<Tab key={'tab1'} title={'1stTab'} beforeTitle={icon} >
              <div>
                <h1>This is tab1</h1>
              </div>
            </Tab>),
            (<Tab key={'tab2'} title={'2ndTab Too long Toooooooooooooooooo long'} beforeTitle={fonticon} >
              <div>
                <pre>Lorem ipsum dolor sit amet, consectetur adipisicing elit,
                </pre>
              </div>
            </Tab>),
            (<Tab key={'tab3'} title={'Dynamic tab'} afterTitle={badge}>
              <DynamicTabContent/>
            </Tab>)
          ],
          badgeCount: 0
        };
      }
    
      handleTabSelect(e, key, currentTabs) {
        console.log('handleTabSelect key:', key);
        this.setState({selectedTab: key, tabs: currentTabs});
      }
    
      handleTabClose(e, key, currentTabs) {
        console.log('tabClosed key:', key);
        this.setState({tabs: currentTabs});
      }
    
      handleTabPositionChange(e, key, currentTabs) {
        console.log('tabPositionChanged key:', key);
        this.setState({tabs: currentTabs});
      }
    
      handleTabAddButtonClick(e, currentTabs) {
        // key must be unique
        const key = 'newTab_' + Date.now();
        let newTab = (<Tab key={key} title='untitled'>
                        <div>
                          <h1>New Empty Tab</h1>
                        </div
                      </Tab>);
        let newTabs = currentTabs.concat([newTab]);
    
        this.setState({
          tabs: newTabs,
          selectedTab: key
        });
      }
    
      render() {
    
        return (
          <Tabs
            tabsClassNames={tabsClassNames}
            tabsStyles={tabsStyles}
            selectedTab={this.state.selectedTab ? this.state.selectedTab : "tab2"}
            onTabSelect={this.handleTabSelect.bind(this)}
            onTabClose={this.handleTabClose.bind(this)}
            onTabAddButtonClick={this.handleTabAddButtonClick.bind(this)}
            onTabPositionChange={this.handleTabPositionChange.bind(this)}
            tabs={this.state.tabs}
            shortCutKeys={
              {
                'close': ['alt+command+w', 'alt+ctrl+w'],
                'create': ['alt+command+t', 'alt+ctrl+t'],
                'moveRight': ['alt+command+tab', 'alt+ctrl+tab'],
                'moveLeft': ['shift+alt+command+tab', 'shift+alt+ctrl+tab']
              }
            }
          />
        )
      }
    };
  2. Configure the Tabs container

    master

    The Tabs component is the main container that renders the tab bar and the content of the selected tab.

    Props

    • tabs (Array of Tab elements): The list of tabs to render.
    • selectedTab (string): The key of the currently selected tab. Defaults to the first tab.
    • disableDrag (boolean): If true, prevents tabs from being dragged. Defaults to false.
    • tabAddButton (element): The element used as the "add tab" button.
    • keepSelectedTab (boolean): Prevents tab selection when dragging/moving behind a tab. Defaults to false.
    • unclosable (boolean): Disables closing for all tabs. Defaults to false.
    • shouldTabClose(e, key) (function): A callback invoked before a tab is closed. Return false to cancel the close action. Defaults to true.
    • shortCutKeys (object): Mousetrap-style key bindings for:
      • close: Closes current tab (onTabClose).
      • create: Creates a new tab (onTabAddButtonClick).
      • moveRight: Moves selection right (onTabSelect).
      • moveLeft: Moves selection left (onTabSelect).

    Events

    • onTabSelect(e, key, currentTabs): Called when a tab is selected. currentTabs is the array of tabs in their current order.
    • onTabClose(e, key, currentTabs): Called when a tab is closed.
    • onTabPositionChange(e, key, currentTabs): Called when a tab is moved/dragged.
    • onTabAddButtonClick(e, currentTabs): Called when the add button is clicked. To add a tab, you should concat a new tab to currentTabs and update your state.

    Styling

    • tabsClassNames: Object of class names to be added to elements (e.g., tabWrapper, tabBar, tab).
    • tabsStyles: Object of inline styles that will overwrite default styles.
  3. Configure the Tab component

    master

    The Tab component is a case class used to define individual tabs within a Tabs container. The props.children of a Tab will be rendered into the content area when the tab is selected.

    Props

    • key (string, required): A unique key within the TabList.
    • title (string | element, required): The text or element to display in the tab.
    • beforeTitle (element): An element (e.g., an icon) to show before the title.
    • afterTitle (element): An element (e.g., a notification badge) to show after the title.
    • uncloseable (boolean): If true, the close button will not appear. Defaults to false.

    Styling

    You can customize the appearance of each tab using:

    • tabClassNames: Object containing class names to be added to rendered elements (e.g., tab, tabBefore, tabTitle, tabActive, etc.).
    • tabStyles: Object containing inline styles that will overwrite default styles (e.g., tab, tabTitle, tabActive, tabCloseIcon).
    • containerStyle: Style object for the rendered tab's container.
    • hiddenContainerStyle: Style object for the container of hidden tabs.

    Events

    Any standard React event handler (e.g., onMouseEnter) passed to Tab will be passed to the rendered element, except for onClick.

    <Tab key='tab1' title='Tab Title' beforeTitle={<Icon />} />
  4. Configure keyboard shortcuts in Tabs

    master

    The Tabs component supports custom keyboard shortcuts via the shortCutKeys prop. It uses Mousetrap to bind these keys.

    Supported shortcut types:

    • close: Triggers handleCloseButtonClick for the currently selected tab.
    • create: Triggers onTabAddButtonClick.
    • moveRight: Moves the selection to the next tab.
    • moveLeft: Moves the selection to the previous tab.

    Each shortcut can be a single string or an array of strings.

    <Tabs
      shortCutKeys={{
        close: 'ctrl+w',
        create: 'ctrl+t',
        moveRight: 'alt+right',
        moveLeft: 'alt+left'
      }}
    >
      {/* ... tabs ... */}
    </Tabs>
  5. Customize Tabs styling and class names

    master

    You can deeply customize the appearance of the Tabs component using tabsStyles (for inline objects) and tabsClassNames (for CSS classes).

    Styling Keys (tabsStyles):

    • tabWrapper, tabBar, tabBarAfter, tab, tabBefore, tabAfter, tabTitle, tabCloseIcon, tabCloseIconOnHover
    • Active states: tabActive, tabTitleActive, tabBeforeActive, tabAfterActive
    • Hover states: tabOnHover, tabTitleOnHover, tabBeforeOnHover, tabAfterOnHover

    Class Name Keys (tabsClassNames):

    • tabWrapper, tabBar, tabBarAfter, tab, tabBefore, tabAfter, tabTitle, tabBeforeTitle, tabAfterTitle, tabCloseIcon, tabActive, tabHover
  6. Configure the TabContainer component

    master

    The TabContainer component manages the visibility and styling of individual tab content areas. It uses the selected prop to determine whether to apply the primary style or the hiddenStyle. When selected is false, the component defaults to a hidden state with height: '0px' and overflow: 'hidden'.

    <TabContainer
      selected={true}
      style={{ backgroundColor: 'red' }}
      hiddenStyle={{ display: 'none' }}
    >
      <div>Tab Content</div>
    </TabContainer>
  7. Configure Tab component properties

    master

    Individual Tab components (passed as children to Tabs) accept several props to control their specific behavior and appearance:

    • title: The text displayed on the tab. If a string, it is also used as the HTML title attribute for tooltips.
    • unclosable: If true, the close icon will not be rendered for this tab.
    • beforeTitle: Content to render before the main title.
    • afterTitle: Content to render after the main title.
    • tabStyles: Object containing overrides for this specific tab's styles (e.g., tab, tabTitle, tabCloseIcon).
    • tabClassNames: Object containing overrides for this specific tab's CSS classes.
    • containerStyle: Style for the tab's content container.
    • hiddenContainerStyle: Style for the tab's content container when the tab is not selected.
  8. Use the Tabs component

    master

    The Tabs component is the primary container for managing a collection of draggable tabs. It handles tab selection, closing, reordering via drag-and-drop, and keyboard shortcuts.

    Key Requirements:

    • Each child Tab component must have a unique key prop.
    • You must provide callback functions to handle lifecycle events like selection, closing, and adding tabs.

    Core Props:

    • tabs: An array of Tab elements. Each must have a unique key.
    • selectedTab: The key of the currently active tab. If not provided, the first tab is selected by default.
    • onTabSelect(event, key, openTabs): Called when a tab is clicked. Returns the event, the key of the selected tab, and an array of currently open tabs.
    • onTabClose(event, key, openTabs): Called when a tab's close icon is clicked. Returns the event, the key of the closed tab, and an array of currently open tabs.
    • onTabAddButtonClick(event, openTabs): Called when the add button is clicked.
    • onTabPositionChange(event, key, openTabs): Called when a tab is dragged to a new position.
    • shouldTabClose(event, key): A predicate function that determines if a tab is allowed to close. Defaults to true.
    <Tabs
      selectedTab={this.state.activeKey}
      onTabSelect={(e, key, openTabs) => this.handleSelect(key)}
      onTabClose={(e, key, openTabs) => this.handleClose(key)}
      onTabPositionChange={(e, key, openTabs) => this.handleReorder(key)}
    >
      <Tab key="tab1" title="Tab 1">Content 1</Tab>
      <Tab key="tab2" title="Tab 2">Content 2</Tab>
    </Tabs>