UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

113 lines (104 loc) 2.25 kB
import { type Component, component, focus, fromEvents, fromSelector, setProperty, show, } from '../../..' import { requireDescendant } from '../../../src/core/dom' export type ModuleTabgroupProps = { tabs: HTMLButtonElement[] selected: string } const getAriaControls = (target: HTMLElement) => target?.getAttribute('aria-controls') ?? '' const getSelected = ( elements: HTMLElement[], isCurrent: (element: HTMLElement) => boolean, offset = 0, ) => getAriaControls( elements[ Math.min( Math.max(elements.findIndex(isCurrent) + offset, 0), elements.length - 1, ) ], ) const handleClick = ({ target }) => getAriaControls(target) const handleKeyup = ({ event, host, target }) => { const key = event.key if ( [ 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', ].includes(key) ) { event.preventDefault() event.stopPropagation() return getSelected( host.tabs, tab => tab === target, key === 'Home' ? -host.tabs.length : key === 'End' ? host.tabs.length : key === 'ArrowLeft' || key === 'ArrowUp' ? -1 : 1, ) } } export default component( 'module-tabgroup', { tabs: fromSelector<HTMLButtonElement>('[role="tab"]'), selected: fromEvents< string, HTMLButtonElement, HTMLElement & { tabs: HTMLButtonElement[] } >( (el: HTMLElement & { tabs: HTMLButtonElement[] }) => getSelected(el.tabs, tab => tab.ariaSelected === 'true'), '[role="tab"]', { click: handleClick, keyup: handleKeyup, }, ), }, (el, { all }) => { requireDescendant(el, '[role="tablist"]') requireDescendant(el, '[role="tab"]') requireDescendant(el, '[role="tabpanel"]') const isCurrentTab = (tab: HTMLButtonElement) => el.selected === getAriaControls(tab) return [ all<HTMLButtonElement>( '[role="tab"]', setProperty('ariaSelected', target => String(isCurrentTab(target)), ), setProperty('tabIndex', target => isCurrentTab(target) ? 0 : -1, ), focus(isCurrentTab), ), all( '[role="tabpanel"]', show(target => el.selected === target.id), ), ] }, ) declare global { interface HTMLElementTagNameMap { 'module-tabgroup': Component<ModuleTabgroupProps> } }