rsuite
Version:
A suite of react components
211 lines (208 loc) • 5.72 kB
JavaScript
'use client';
import _extends from "@babel/runtime/helpers/esm/extends";
import React from 'react';
import Nav from "../Nav/index.js";
import Tab from "./Tab.js";
import TabPanel from "./TabPanel.js";
import Box from "../internals/Box/index.js";
import { forwardRef, rch } from "../internals/utils/index.js";
import { useStyles, useCustom, useControlled, useEventCallback, useUniqueId } from "../internals/hooks/index.js";
/**
* Props for the Tabs component.
*/
function getFocusableTabs(tablist) {
const tabs = tablist?.querySelectorAll('[role=tab]');
return Array.from(tabs).filter(tab => !(tab.getAttribute('aria-disabled') === 'true'));
}
function getFocusedTab(tablist) {
const tabs = getFocusableTabs(tablist);
return tabs.find(tab => tab.getAttribute('aria-selected') === 'true');
}
function nextItem(tablist) {
if (!tablist) {
return null;
}
const item = getFocusedTab(tablist);
const items = getFocusableTabs(tablist);
if (!item) {
return items[0];
}
const nextItem = items[items.indexOf(item) + 1];
if (!nextItem || nextItem.getAttribute('role') !== 'tab') {
return items[0];
}
return nextItem;
}
function previousItem(tablist) {
if (!tablist) {
return null;
}
const item = getFocusedTab(tablist);
const items = getFocusableTabs(tablist);
if (!item) {
return items[items.length - 1];
}
const previousItem = items[items.indexOf(item) - 1];
if (!previousItem || previousItem.getAttribute('role') !== 'tab') {
return items[items.length - 1];
}
return previousItem;
}
const renderPanels = (children, tabProps) => {
const {
id,
activeKey
} = tabProps;
return rch.map(children, child => {
const {
eventKey,
children
} = child.props;
const selected = eventKey === activeKey;
return /*#__PURE__*/React.createElement(TabPanel, {
"aria-labelledby": `${id}-${eventKey}`,
"aria-hidden": !selected,
id: `${id}-panel-${eventKey}`,
active: selected
}, children);
});
};
const renderTabs = (children, tabPanelProps) => {
const {
id,
activeKey
} = tabPanelProps;
return rch.map(children, child => {
const {
eventKey,
title,
disabled,
icon
} = child.props;
const selected = eventKey === activeKey;
return /*#__PURE__*/React.createElement(Nav.Item, {
role: "tab",
as: "button",
type: "button",
"aria-selected": selected,
"aria-controls": `${id}-panel-${eventKey}`,
"aria-disabled": disabled,
"data-event-key": eventKey,
disabled: disabled,
icon: icon,
id: `${id}-${eventKey}`,
tabIndex: selected ? undefined : -1,
eventKey: eventKey
}, title);
});
};
const Subcomponents = {
Tab
};
/**
* Tabs are a set of layered sections of content, known as tab panels, that display one panel of content at a time.
*
* @version 5.53.0
* @see https://rsuitejs.com/components/tabs
*/
const Tabs = forwardRef((props, ref) => {
const {
propsWithDefaults,
rtl
} = useCustom('Tabs', props);
const {
as,
classPrefix = 'tabs',
appearance = 'tabs',
className,
children,
activeKey: activeKeyProp,
defaultActiveKey,
id: idProp,
reversed,
vertical,
onSelect,
...rest
} = propsWithDefaults;
const id = useUniqueId('tab-', idProp);
const [activeKey, setActiveKey] = useControlled(activeKeyProp, defaultActiveKey);
const {
withPrefix,
prefix,
merge
} = useStyles(classPrefix);
const tablistRef = React.useRef(null);
const handleSelect = useEventCallback((eventKey, event) => {
setActiveKey(eventKey);
onSelect?.(eventKey, event);
});
const handleKeyDown = useEventCallback(event => {
const target = event.target;
if (target.getAttribute('role') !== 'tab') {
return;
}
let previousItemKey = vertical ? 'ArrowUp' : 'ArrowLeft';
let nextItemKey = vertical ? 'ArrowDown' : 'ArrowRight';
if (!vertical && rtl) {
previousItemKey = 'ArrowRight';
nextItemKey = 'ArrowLeft';
}
let item = null;
switch (event.key) {
case previousItemKey:
item = previousItem(tablistRef.current);
event.preventDefault();
break;
case nextItemKey:
item = nextItem(tablistRef.current);
event.preventDefault();
break;
case 'Home':
item = getFocusableTabs(tablistRef.current)?.[0];
event.preventDefault();
break;
case 'End':
{
const tabs = getFocusableTabs(tablistRef.current);
item = tabs[tabs.length - 1];
event.preventDefault();
break;
}
}
if (item) {
const eventKey = item ? item.dataset.eventKey : undefined;
handleSelect(eventKey, event);
item.focus();
}
});
const hasChildren = React.Children.toArray(children).some(child => /*#__PURE__*/React.isValidElement(child) && child.props.children);
return /*#__PURE__*/React.createElement(Box, _extends({
as: as,
className: merge(className, withPrefix({
reversed,
vertical
}))
}, rest, {
ref: ref
}), /*#__PURE__*/React.createElement(Nav, {
role: "tablist",
"aria-orientation": vertical ? 'vertical' : 'horizontal',
reversed: reversed,
vertical: vertical,
appearance: appearance,
activeKey: activeKey,
onSelect: handleSelect,
onKeyDown: handleKeyDown,
ref: tablistRef
}, renderTabs(children, {
id,
activeKey
})), hasChildren && /*#__PURE__*/React.createElement("div", {
className: prefix`content`
}, renderPanels(children, {
id,
activeKey
})));
}, Subcomponents);
Tabs.displayName = 'Tabs';
export default Tabs;