react-dyn-tabs
TypeScript icon, indicating that this package has built-in type declarations

5.1.1 • Public • Published

react-dyn-tabs

React Dynamic Tabs with full API

Support react >= v16.8.0

Demo

Features

  • Based on React hook
  • Open & Close & Select & Refresh
  • lazy/eager loading
  • Customizable style
  • Full API
  • Return to last used tab when closing selected tab
  • PanelList can be rendered outside the TabList container
  • ARIA accessible
  • Supporting custom Tab component
  • Batching updates
  • It's about 21kb

Table of Contents

Installation

$ npm install react-dyn-tabs --save

or

$ yarn add react-dyn-tabs

Basic Example

import React from 'react';
import 'react-dyn-tabs/style/react-dyn-tabs.css';
import 'react-dyn-tabs/themes/react-dyn-tabs-card.css';
import useDynTabs from 'react-dyn-tabs';

export default () => {
  const options = {
    tabs: [
      {
        id: '1',
        title: 'tab 1',
        panelComponent: (porps) => <p> panel 1 </p>,
      },
      {
        id: '2',
        title: 'tab 2',
        panelComponent: (porps) => <p> panel 2 </p>,
      },
    ],
    selectedTabID: '1',
  };
  const [TabList, PanelList] = useDynTabs(options);
  return (
    <>
      <TabList></TabList>
      <PanelList></PanelList>
    </>
  );
};

Simple manipulation

import React from 'react';
import 'react-dyn-tabs/style/scss/react-dyn-tabs.scss';
import 'react-dyn-tabs/themes/scss/react-dyn-tabs-card.scss';
import useDynTabs from 'react-dyn-tabs';

export default () => {
  const options = {
    tabs: [
      {
        id: '1',
        title: 'tab1',
        panelComponent: (porps) => <p> panel 1 </p>,
      },
      {
        id: '2',
        title: 'tab2',
        panelComponent: (porps) => <p> panel 2 </p>,
      },
    ],
    selectedTabID: '1',
  };
  let _instance;
  const [TabList, PanelList, ready] = useDynTabs(options);
  ready((instance) => {
    _instance = instance;
  });
  const addTab3 = function () {
    // open tab 3
    _instance.open({id: '3', title: 'Tab 3', panelComponent: (porps) => <p> panel 3 </p>}).then(() => {
      console.log('tab 3 is open');
    });
    // switch to tab 3
    _instance.select('3').then(() => {
      console.log('tab 3 is selected');
    });
  };
  return (
    <>
      <button onClick={addTab3}>Add tab 3</button>
      <TabList></TabList>
      <PanelList></PanelList>
    </>
  );
};

NOTE :

  • Tabs can't be manipulated safely before the first render, use ready() to access the instance object, ready accepts a function as its parameter and calls it when tabs are mounted.

  • ready function identity is stable and won’t change on re-renders.

Options

tabs

type default value required description

Array of tabData

[] false initial opened tabs

Example

const [TabList, PanelList, ready] = useDynTabs({
  tabs: [
    {
      id: '1',
      title: 'home',
      iconClass: 'fa fa-home',
      closable: true,
      panelComponent: (porps) => <p> home content </p>,
    },
    {
      id: '2',
      title: 'contact',
      tooltip: 'contact',
      disable: true,
      closable: false,
      panelComponent: (porps) => <p> contact content </p>,
    },
  ],
});

selectedTabID

type default value required description
string ' ' false specifies initial selected tab

Example

const [TabList, PanelList, ready] = useDynTabs({
  tabs: [
    {
      id: '1',
      title: 'home',
      iconClass: 'fa fa-home',
      closable: true,
      panelComponent: (porps) => <p> home content </p>,
    },
    {
      id: '2',
      title: 'contact',
      tooltip: 'contact',
      disable: true,
      closable: false,
      panelComponent: (porps) => <p> contact content </p>,
    },
  ],
  selectedTabID: '2',
});

direction

type default value required description
string 'ltr' false can be either of 'ltr' or 'rtl'

Example

const [TabList, PanelList, ready] = useDynTabs({direction: 'rtl'});

tabComponent

type required description
React component false custom tab component

Example

const [TabList, PanelList, ready] = useDynTabs({
  tabComponent: (props) => {
    const {id, isSelected, api: instance} = props;
    return (
      <button {...props.tabProps}>
        {props.children}
        {props.iconProps && <span {...props.iconProps}></span>}
      </button>
    );
  },
});

defaultPanelComponent

Default value for panelComponent option.

type required description
React component | React element false

Example

const [TabList, PanelList, ready] = useDynTabs({
  defaultPanelComponent: (props) => {
    const {id, isSelected, api: instance} = props;
    return <div>loading...</div>;
  },
});

accessibility

type default value required description
boolean true false

Example

const [TabList, PanelList, ready] = useDynTabs({accessibility: false});

NOTE :

When accessibility option is true, it sets the id attribute of panel and button elements.

isVertical

type default value required description
boolean false false

Example

const [TabList, PanelList, ready] = useDynTabs({isVertical: true});

onLoad

type required description
function false This event is fired only once, after the first render

Example

const [TabList, PanelList, ready] = useDynTabs({
  onLoad: function () {
    console.log('[onLoad]');
  },
});

NOTE :

You can use 'this' keyword inside all callback options. It refers to the instance object.

onInit

type required description
function false This event is triggered after every render.

Example

const [TabList, PanelList, ready] = useDynTabs({
  onInit: function () {
    console.log('[onInit]');
  },
});

NOTE :

Do not use setState inside the onInit callback because it leads to an infinite loop.

onChange

type required description
function false fires when we open|close|select a tab. this event is not fired initially

Example

const [TabList, PanelList, ready] = useDynTabs({
  onChange: function ({currentData, previousData, closedTabIDs, openedTabIDs}) {
    console.log('[onChange]');
  },
});

beforeSelect

type required description
function false fires when the user clicks on the tab, but before select them. This event should return boolean true or false, If the event return false the tab is not selected.

Example

const [TabList, PanelList, ready] = useDynTabs({
  beforeSelect: function (e, id) {
    console.log('[beforeSelect]');
    return true;
  },
});

onFirstSelect

type required description
function false fires after selecting a tab for the first time. It is not fired for the initial selected tab

Example

const [TabList, PanelList, ready] = useDynTabs({
  onFirstSelect: function ({currentSelectedTabId, previousSelectedTabId}) {
    console.log('[onFirstSelect]');
  },
});

onSelect

type required description
function false fires after selecting a tab. this event is not fired initially

Example

const [TabList, PanelList, ready] = useDynTabs({
  onSelect: function ({currentSelectedTabId, previousSelectedTabId}) {
    console.log('[onSelect]');
  },
});

onOpen

type required description
function false fires after opening tabs. this event is not fired initially

Example

const [TabList, PanelList, ready] = useDynTabs({
  onOpen: function (openedTabIDs) {
    console.log('[onOpen]');
  },
});

beforeClose

type required description
function false fires when the user clicks on the close icon, but before close them. This event should return boolean true or false, If the event return false the tab is not closed.

Example

const [TabList, PanelList, ready] = useDynTabs({
  beforeClose: function (e, id) {
    console.log('[beforeClose]');
    return true;
  },
});

onClose

type required description
function false fires after closing tabs. this event is not fired initially

Example

const [TabList, PanelList, ready] = useDynTabs({
  onClose: function (closedTabIDs) {
    console.log('[onClose]');
  },
});

onDestroy

type required description
function false fires before destroying useDynTabs hook

Example

const [TabList, PanelList, ready] = useDynTabs({
  onDestroy: function () {
    console.log('[onDestroy]');
  },
});

Instance methods

isOpen

Return value : boolean

Parameters:

  • id: String

Example

const result = instance.isOpen('Your tab ID');

open

Triggers 'onInit', 'onChange' and 'onOpen' event.

It only triggers 'onInit' event, if the tab is already open.

Return value : Promise

Parameters:

  • tabData: Object

Example

if (instance.isOpen('contact') == false) {
  instance
    .open({
      id: 'contact',
      title: 'contact',
      tooltip: 'contact',
      disable: false,
      closable: true,
      iconClass: '',
      panelComponent: <ContactPanel></ContactPanel>,
    })
    .then(({currentData, instance}) => {
      console.log('contact tab is open');
    });
}

isSelected

Return value : boolean

Parameters:

  • id: String

Example

const result = instance.isSelected('Your tab ID');

select

Makes current and previous selected tab to be re-rendered

Triggers 'onInit', 'onChange' and 'onSelect' event.

It only triggers 'onInit' event, if the tab is already selected.

Return value : Promise

Parameters:

  • id: string

Example

if (instance.isSelected('1') == false) {
  instance.select('1').then(({currentData, instance}) => {
    console.log('tab 1 is selected');
  });
}

close

Triggers 'onInit', 'onChange' and 'onClose' event.

It only triggers 'onInit' event, if the tab is already closed.

When switching parameter is true, it switches to previous selected tab

Return value : Promise

Parameters:

  • id: string
  • switching: boolean (default : true)

Example

if (instance.isOpen('2') == true) {
  instance.close('2').then(({currentData, instance}) => {
    console.log('tab 2 is closed');
  });
}

refresh

Makes all tabs to be re-rendered.

triggers onInit event.

Return value : Promise

Example

instance.refresh().then(({currentData, instance}) => {});

getOption

Parameters:

  • optionName : String

Example

const direction = instance.getOption('direction');
const onSelect = instance.getOption('onSelect');

setOption

You can use this method for setting all options except selectedTabID and tabs options.

For re-rendering immediately after this function, you should call refresh method after it.

Return value : instance object

Parameters:

  • optionName : String
  • optionValue : string|boolean|object|function

Example

instance.setOption('direction', 'rtl');
instance.setOption('onSelect', () => {});
instance.setOption('beforeSelect', () => false);

getTab

get tabData by id

Return value : tabData object

Parameters:

  • id : String

Example

const tabData = instance.getTab('3');
console.log(tabData.id); // 3

setTab

set tabData by id. for re-rendering immediately after this function, you should call refresh method after it.

Return value : instance

Parameters:

  • tab id : String
  • source object : containing the properties you want to apply

Example

instance.setTab('home', {disable: true});
instance.setTab('contact', {closable: false, panelComponent: (props) => <p>contact panel</p>});

on

Attach an event handler function for one event.

Return value : instance

Parameters:

  • event Name : String (can be either of onFirstSelect|onSelect|onClose|onOpen|onInit|onChange|onDestroy)
  • handler : function

Example

const handler = React.useCallback(function (params) {
  const {currentSelectedTabId, previousSelectedTabId} = params;
}, []);
instance.on('onSelect', handler);

one

Attach a handler to an event. The handler is executed at most once.

Return value : instance

Parameters:

  • event Name : String (can be either of onFirstSelect|onSelect|onClose|onOpen|onInit|onChange|onDestroy)
  • handler : function

Example

instance.one('onSelect', function ({currentSelectedTabId, previousSelectedTabId}) {});

off

Remove an event handler.

Return value : instance

Parameters:

  • event Name : String (can be either of onFirstSelect|onSelect|onClose|onOpen|onInit|onChange|onDestroy)
  • handler : function (A handler function previously attached for the event)

Example

const handler = React.useCallback(function () {}, []);
const attachHandler = () => {
  instance.on('onSelect', handler);
};
const deattachHandler = () => {
  instance.off('onSelect', handler);
};

getData

get a copy of data

Return value : Object

Example

const {selectedTabID, openTabIDs} = instance.getData();

NOTE :

  • getCopyData function is an older version of getData function and it is enabled by default so that existing users do not have to change their code. You are free to use both conventions.

getPreviousData

get a copy of data in previous render

Return value : Object

Example

const {selectedTabID, openTabIDs} = instance.getPreviousData();

NOTE :

  • getCopyPerviousData function is an older version of getPreviousData function and it is enabled by default so that existing users do not have to change their code. You are free to use both conventions.

sort

Useful for sorting tabs manually.

Triggers onInit event.

Return value : Promise

Parameters:

  • Array of all tabs IDs

Example

const {openTabIDs} = instance.getData();
instance.sort(openTabIDs.reverse()).then(({currentData, instance}) => {
  console.log('sorting tabs has finished');
});

tabData

property name type default value required description
id string false an unique identifier for each tab
title string ' ' false
tooltip string ' ' false
panelComponent React Element | React Component | null A function component which returns empty div false
lazy boolean false false If set to false the panel will be rendered initially. if set to true the panel will not be rendered until the tab is activated
closable boolean true false
iconClass string ' ' false class name for the icon
disable boolean false false

Example

const tabData = {
  id: 'contactID',
  title: 'contactTitle',
  tooltip: 'contactTooltip',
  disable: true,
  lazy: true,
  iconClass: 'fa fa-home',
  closable: false,
  panelComponent: (porps) => <p> contact content </p>,
};
const [TabList, PanelList, ready] = useDynTabs({tabs: [tabData]});
// or
if (instance.isOpen(tabData.id) == false) {
  instance.open(tabData).then(() => {});
}

Lazy Loading

Defer loading of tab content until the tab is activated

Example 1

const Panel3 = React.lazy(() => import('./components/panel3.js'));
function LazyLoadingPanel3(porps) {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Panel3 {...porps}></Panel3>
    </Suspense>
  );
}
useDynTabs({
  tabs: [
    {id: '1', title: 'eager loading tab 1', panelComponent: <p>panel 1</p>},
    {id: '2', title: 'eager loading tab 2', lazy: true, panelComponent: <p>panel 2</p>},
    {id: '3', title: 'lazy loading tab 3', lazy: true, panelComponent: LazyLoadingPanel3},
  ],
  selectedTabID: '1',
});

NOTE :

  • panel 1 is eagerly loaded and rendered.
  • panel 2 is eagerly loaded but will not be rendered until tab 2 is activated.
  • panel 3 will not be loaded and rendered until tab 3 is activated.

Example 2 ( using onFirstSelect event )

useDynTabs({
  tabs: [
    {id: '1', title: 'eager loading tab 1', panelComponent: <p>panel 1</p>},
    {id: '2', title: 'eager loading tab 2', lazy: true, panelComponent: <p>panel 2</p>},
    {id: '3', title: 'lazy loading tab 3', lazy: true},
  ],
  selectedTabID: '1',
  defaultPanelComponent: function DefaultPanel() {
    return <div>loading...</div>;
  },
  onFirstSelect: function ({currentSelectedTabId}) {
    const instance = this;
    if (currentSelectedTabId === '3') {
      import('path to/panel3.js').then((defaultExportedModule) => {
        const Panel3 = defaultExportedModule.default;
        instance.setTab('3', {panelComponent: Panel3});
        instance.refresh();
      });
    }
  },
});

Styling

react-dyn-tabs does not include any style loading by default. Default stylesheets and themes are provided and can be included in your application if desired.

import 'react-dyn-tabs/style/react-dyn-tabs.min.css';
// or import 'react-dyn-tabs/style/scss/react-dyn-tabs.scss';
import 'react-dyn-tabs/themes/react-dyn-tabs-card.min.css';
// or import 'react-dyn-tabs/themes/scss/react-dyn-tabs-card.scss';

NOTE :

You can find other themes at themes folder and multiple themes example at example/multi-themes-example folder.

Caveats

  • Some actions like open, select, close and refresh cause re-rendering, and using them immediately after calling useDynTabs hook will create an infinite loop and other bugs that most likely you don't want to cause. you should use them inside event listeners or subscriptions.

  • Do not use setState inside the onInit callback because it leads to an infinite loop.

Deprecated features

These deprecated features can still be used, but should be used with caution because they are expected to be removed entirely sometime in the future. You should work to remove their use from your code.

  • Third element of returned array by useDynTabs hook should not be used as an object, it is no longer recommended and only be kept for backwards compatibility purposes, it should be used as a function.

  • First parameter of onSelect function is an object and has perviousSelectedTabId property which is deprecated. you should use previousSelectedTabId property instead of perviousSelectedTabId property.

  • First parameter of onChange function is an object and has perviousData property which is deprecated. you should use previousData property instead of perviousData property.

Test

$ npm run test

License

MIT

Dependencies (1)

Dev Dependencies (33)

Package Sidebar

Install

npm i react-dyn-tabs

Weekly Downloads

329

Version

5.1.1

License

MIT

Unpacked Size

1.24 MB

Total Files

142

Last publish

Collaborators

  • dev-javascript