import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { CrossFade, useId, useStateWithDep } from '@gravityforms/react-utils';
import { spacerClasses } from '@gravityforms/utils';
import Button from '../../elements/Button';
import StatusIndicator from '../../elements/StatusIndicator';
import { ARROW_LEFT, ARROW_RIGHT, HOME, END } from '../../utils/keymap';
const { forwardRef, useState } = React;
/**
* @module Tabs
* @description A tabs component.
*
* @since 4.7.2
*
* @param {object} props The component props.
* @param {number} props.activeTab The active tab index.
* @param {boolean} props.automatic Whether to automatically switch tabs using arrow keys.
* @param {boolean} props.controlled Whether the component is controlled.
* @param {object} props.customAttributes Custom attributes for the component.
* @param {string|Array|object} props.customClasses Custom classes for the component.
* @param {string} props.iconPrefix The icon prefix for the component.
* @param {string} props.id The id for the component.
* @param {string|number|Array|object} props.spacing The spacing for the component, as a string, number, array, or object.
* @param {Array} props.tabs An array of objects for the tabs content.
* @param {string} props.tabWidth The tab width for the component, one of `auto` or `even`.
*
* @return {JSX.Element} The tabs component.
*
* @example
* import Tabs from '@gravityforms/components/react/admin/modules/Tabs';
*
* return (
* <Tabs
* automatic={ true }
* tabs={ [
* {
* label: 'Tab 1',
* panel: 'Tab 1 Content',
* },
* {
* icon: 'ellipsis',
* label: 'Tab 2',
* panel: 'Tab 2 Content',
* },
* {
* label: 'Tab 3',
* status: '2',
* panel: 'Tab 3 Content',
* },
* {
* icon: 'trash',
* label: 'Tab 4',
* status: '97',
* panel: 'Tab 4 Content',
* }
* ] }
* />
* );
*
*/
const Tabs = forwardRef( ( {
activeTab: defaultActiveTab = 0,
automatic = true,
controlled = false,
customAttributes = {},
customClasses = [],
iconPrefix = 'gravity-component-icon',
id: defaultId = '',
spacing = '',
tabs = [],
tabWidth = 'auto',
}, ref ) => {
const id = useId( defaultId );
const [ stateActiveTab, setStateActiveTab ] = useState( defaultActiveTab );
const [ controlActiveTab, setControlActiveTab ] = useStateWithDep( defaultActiveTab );
const activeTab = controlled ? controlActiveTab : stateActiveTab;
const lastTab = tabs.length - 1;
/**
* @function getTablist
* @description Get the tablist.
*
* @since 4.7.2
*
* @return {JSX.Element} The tablist.
*/
const getTablist = () => {
const tablistProps = {
className: 'gform-tabs__tablist',
role: 'tablist',
};
return (
<div { ...tablistProps }>
{ tabs.map( ( tab, index ) => {
const tabId = `${ id }-tab-${ index }`;
const tabPanelId = `${ id }-tabpanel-${ index }`;
const isActive = activeTab === index;
const tabProps = {
customAttributes: {
'aria-controls': tabPanelId,
'aria-selected': isActive ? 'true' : 'false',
id: tabId,
onKeyDown: ( event ) => {
if ( ! [ ARROW_LEFT, ARROW_RIGHT, HOME, END ].includes( event.key ) ) {
return;
}
let nextIndex;
switch ( event.key ) {
case ARROW_LEFT:
nextIndex = index === 0 ? lastTab : index - 1;
break;
case ARROW_RIGHT:
nextIndex = index === lastTab ? 0 : index + 1;
break;
case HOME:
nextIndex = 0;
break;
case END:
nextIndex = lastTab;
break;
default:
break;
}
document.getElementById( `${ id }-tab-${ nextIndex }` ).focus();
if ( activeTab === nextIndex ) {
return;
}
if ( automatic ) {
setStateActiveTab( nextIndex );
setControlActiveTab( nextIndex );
}
},
role: 'tab',
tabIndex: ! isActive ? '-1' : undefined,
},
customClasses: classnames( {
'gform-tabs__tab': true,
'gform-tabs__tab--active': isActive,
} ),
onClick: () => {
if ( activeTab === index ) {
return;
}
setStateActiveTab( index );
// setControlActiveTab( index );
},
label: tab.label,
size: 'size-height-m',
type: 'simplified',
};
if ( tab.icon ) {
tabProps.icon = tab.icon;
tabProps.iconPrefix = iconPrefix;
tabProps.iconPosition = 'leading';
}
if ( tab.status ) {
const statusProps = {
hasDot: false,
label: tab.status,
size: 'sm',
status: 'gray',
};
tabProps.children = <StatusIndicator { ...statusProps } />;
}
return <Button key={ tabId } { ...tabProps } />;
} ) }
</div>
);
};
/**
* @function getTabPanels
* @description Get the tab panels.
*
* @since 4.7.2
*
* @return {JSX.Element} The tab panels.
*/
const getTabPanels = () => {
const tabPanelsProps = {
activeIndex: activeTab,
customClasses: [ 'gform-tabs__tabpanels' ],
duration: 150,
id: `${ id }-tabpanels`,
};
return (
<CrossFade { ...tabPanelsProps }>
{ tabs.map( ( tab, index ) => {
const tabId = `${ id }-tab-${ index }`;
const tabPanelId = `${ id }-tabpanel-${ index }`;
const tabPanelProps = {
'aria-labelledby': tabId,
className: 'gform-tabs__tabpanel',
id: tabPanelId,
role: 'tabpanel',
tabIndex: '0',
};
return (
<div key={ tabPanelId } { ...tabPanelProps }>
{ tab.panel }
</div>
);
} ) }
</CrossFade>
);
};
const componentProps = {
className: classnames( {
'gform-tabs': true,
'gform-tabs--automatic': automatic,
[ `gform-tabs--tab-width-${ tabWidth }` ]: tabWidth,
...spacerClasses( spacing ),
}, customClasses ),
...customAttributes,
};
return (
<div { ...componentProps } ref={ ref }>
{ getTablist() }
{ getTabPanels() }
</div>
);
} );
Tabs.propTypes = {
activeTab: PropTypes.number,
automatic: PropTypes.bool,
controlled: PropTypes.bool,
customAttributes: PropTypes.object,
customClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
iconPrefix: PropTypes.string,
id: PropTypes.string,
spacing: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.number,
PropTypes.array,
PropTypes.object,
] ),
tabs: PropTypes.array,
tabWidth: PropTypes.oneOf( [ 'auto', 'even' ] ),
};
export default Tabs;