'use client' import { Children, createContext, forwardRef, KeyboardEvent, ReactNode, Ref, useContext, useEffect, useRef, } from 'react' import { PktTabItem } from './TabItem' export type TSkin = 'blue' | 'green' | 'red' | 'beige' | 'yellow' | 'grey' | 'gray' | 'blue-light' // Context for passing tab navigation logic to children interface ITabsContext { arrowNav: boolean registerTabRef: ( index: number, el: HTMLAnchorElement | HTMLButtonElement | null, disabled?: boolean, ) => void handleKeyPress: (index: number, event: KeyboardEvent) => void selectTab: (index: number) => void } const TabsContext = createContext(null) export const useTabsContext = () => { const context = useContext(TabsContext) if (!context) { throw new Error('TabItem must be used within a Tabs component') } return context } export interface IPktTab { text: string href?: string action?: (index: number) => void disabled?: boolean icon?: string controls?: string tag?: { text: string skin: TSkin | undefined } active?: boolean } export interface IPktTabs { arrowNav?: boolean disableArrowNav?: boolean tabs?: IPktTab[] onTabSelected?: (index: number) => void children?: ReactNode } export const PktTabs = forwardRef( ( { arrowNav = true, disableArrowNav = false, tabs, onTabSelected, children }: IPktTabs, ref: Ref, ): JSX.Element => { const tabRefs = useRef>([]) const disabledMap = useRef>({}) const useArrowNav = arrowNav && !disableArrowNav // Determine if we're using children or tabs array const hasChildren = children && Children.count(children) > 0 const tabCount = hasChildren ? Children.count(children) : tabs?.length || 0 useEffect(() => { tabRefs.current = tabRefs.current.slice(0, tabCount) Object.keys(disabledMap.current).forEach((key) => { const index = Number(key) if (index >= tabCount) delete disabledMap.current[index] }) }, [tabCount]) const isTabDisabled = (index: number): boolean => { if (tabs) return !!tabs[index]?.disabled return !!disabledMap.current[index] } const selectTab = (index: number): void => { if (isTabDisabled(index)) return const tab = tabs?.[index] if (tab?.action) { tab.action(index) } if (onTabSelected) onTabSelected(index) } const findEnabledIndex = (startIndex: number, direction: -1 | 1): number | null => { let current = startIndex + direction while (current >= 0 && current < tabCount) { if (!isTabDisabled(current)) return current current += direction } return null } const handleKeyPress = (index: number, event: KeyboardEvent) => { if (!useArrowNav) return if (event.key === 'ArrowLeft') { event.preventDefault() const previousEnabled = findEnabledIndex(index, -1) if (previousEnabled !== null) tabRefs.current[previousEnabled]?.focus() } if (event.key === 'ArrowRight') { event.preventDefault() const nextEnabled = findEnabledIndex(index, 1) if (nextEnabled !== null) tabRefs.current[nextEnabled]?.focus() } if (event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar' || event.key === 'ArrowDown') { event.preventDefault() if (!isTabDisabled(index)) { selectTab(index) } } } const registerTabRef = ( index: number, el: HTMLAnchorElement | HTMLButtonElement | null, disabled = false, ) => { tabRefs.current[index] = el disabledMap.current[index] = disabled } // If tabs as prop instead of children const tabItems = tabs?.map((tab, index) => ( {tab.text} )) return (
{children || tabItems}
) }, ) export { PktTabItem } from './TabItem' PktTabs.displayName = 'PktTabs'