UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

107 lines (100 loc) 2.35 kB
import { type Component, component, fromEvents, fromSelector, setProperty, show, } from '../../..' export type ModuleTabgroupProps = { readonly tabs: HTMLButtonElement[] readonly selected: string } const getAriaControls = (element: HTMLElement) => element.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, ) ], ) export default component( 'module-tabgroup', { tabs: fromSelector('button[role="tab"]'), selected: fromEvents( 'button[role="tab"]', { click: ({ target }) => getAriaControls(target), keyup: ({ event, host, target }) => { const key = event.key if ( [ 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End', ].includes(key) ) { event.preventDefault() event.stopPropagation() const current = getSelected( host.tabs, tab => tab === target, key === 'Home' ? -host.tabs.length : key === 'End' ? host.tabs.length : key === 'ArrowLeft' || key === 'ArrowUp' ? -1 : 1, ) host.tabs .filter(tab => getAriaControls(tab) === current)[0] .focus() return current } }, }, (el: HTMLElement & { tabs: HTMLButtonElement[] }) => getSelected(el.tabs, tab => tab.ariaSelected === 'true'), ), }, (el, { all }) => { const isCurrentTab = (tab: HTMLButtonElement) => el.selected === getAriaControls(tab) return [ all( 'button[role="tab"]', [ setProperty('ariaSelected', target => String(isCurrentTab(target)), ), setProperty('tabIndex', target => isCurrentTab(target) ? 0 : -1, ), ], 'At least 2 tabs as children of a <[role="tablist"]> element are needed. Each tab must reference a unique id of a <[role="tabpanel"]> element.', ), all( '[role="tabpanel"]', show(target => el.selected === target.id), 'At least 2 tabpanels are needed. Each tabpanel must have a unique id.', ), ] }, ) declare global { interface HTMLElementTagNameMap { 'module-tabgroup': Component<ModuleTabgroupProps> } }