@vertisanpro/flowbite-react
Version:
Non-Official React components built for Flowbite and Tailwind CSS
54 lines (53 loc) • 3.43 kB
JavaScript
'use client';
import { twMerge } from '@vertisanpro/tailwind-merge';
import { nanoid } from 'nanoid';
import React, { Children, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { mergeDeep } from '../../helpers/merge-deep';
import { getTheme } from '../../theme-store';
import { TabItem } from './TabItem';
const TabsComponent = forwardRef(({ children, className, onActiveTabChange, style = 'default', theme: customTheme = {}, ...props }, ref) => {
const theme = mergeDeep(getTheme().tabs, customTheme);
const id = useMemo(() => nanoid(), []);
const tabs = useMemo(() => Children.map(Children.toArray(children), ({ props }) => props), [children]);
const tabRefs = useRef([]);
const [activeTab, setActiveTab] = useState(Math.max(0, tabs.findIndex((tab) => tab.active)));
const [focusedTab, setFocusedTab] = useState(-1);
const setActiveTabWithCallback = (activeTab) => {
setActiveTab(activeTab);
if (onActiveTabChange)
onActiveTabChange(activeTab);
};
const handleClick = ({ target }) => {
setActiveTabWithCallback(target);
setFocusedTab(target);
};
const handleKeyboard = ({ event, target }) => {
if (event.key === 'ArrowLeft') {
setFocusedTab(Math.max(0, focusedTab - 1));
}
if (event.key === 'ArrowRight') {
setFocusedTab(Math.min(tabs.length - 1, focusedTab + 1));
}
if (event.key === 'Enter') {
setActiveTabWithCallback(target);
setFocusedTab(target);
}
};
const tabItemStyle = theme.tablist.tabitem.styles[style];
const tabItemContainerStyle = theme.tabitemcontainer.styles[style];
useEffect(() => {
tabRefs.current[focusedTab]?.focus();
}, [focusedTab]);
useImperativeHandle(ref, () => ({
setActiveTab: setActiveTabWithCallback,
}));
return (React.createElement("div", { className: twMerge(theme.base, className) },
React.createElement("div", { "aria-label": "Tabs", role: "tablist", className: twMerge(theme.tablist.base, theme.tablist.styles[style], className), ...props }, tabs.map((tab, index) => (React.createElement("button", { key: index, type: "button", "aria-controls": `${id}-tabpanel-${index}`, "aria-selected": index === activeTab, className: twMerge(theme.tablist.tabitem.base, tabItemStyle.base, index === activeTab && tabItemStyle.active.on, index !== activeTab && !tab.disabled && tabItemStyle.active.off), disabled: tab.disabled, id: `${id}-tab-${index}`, onClick: () => handleClick({ target: index }), onKeyDown: (event) => handleKeyboard({ event, target: index }), ref: (element) => (tabRefs.current[index] = element), role: "tab", tabIndex: index === focusedTab ? 0 : -1, style: { zIndex: index === focusedTab ? 2 : 1 } },
tab.icon && React.createElement(tab.icon, { className: theme.tablist.tabitem.icon }),
tab.title)))),
React.createElement("div", { className: twMerge(theme.tabitemcontainer.base, tabItemContainerStyle) }, tabs.map((tab, index) => (React.createElement("div", { key: index, "aria-labelledby": `${id}-tab-${index}`, className: theme.tabpanel, hidden: index !== activeTab, id: `${id}-tabpanel-${index}`, role: "tabpanel", tabIndex: 0 }, tab.children))))));
});
TabsComponent.displayName = 'Tabs';
export const Tabs = Object.assign(TabsComponent, {
Item: TabItem,
});