import type { JSX } from 'solid-js'; import { createComponent, createEffect, mergeProps, onCleanup } from 'solid-js'; import { omitProps } from 'solid-use/props'; import type { SelectStateRenderProps } from '../../states/create-select-state'; import { SelectStateChild, useSelectState } from '../../states/create-select-state'; import createDynamic from '../../utils/create-dynamic'; import type { DynamicProps, HeadlessPropsWithRef, ValidConstructor, } from '../../utils/dynamic-prop'; import { createForwardRef } from '../../utils/dynamic-prop'; import { createHasActiveState, createHasSelectedState } from '../../utils/state-props'; import useEventListener from '../../utils/use-event-listener'; import { useTabGroupContext } from './TabGroupContext'; import { TabListContext, createTabFocusNavigator } from './TabListContext'; import { TAB_LIST_TAG } from './tags'; export type TabListProps = HeadlessPropsWithRef< T, SelectStateRenderProps >; /** * The row of tabs in a `TabGroup`, and the element that owns their arrow-key * navigation. * * Renders a `
` by default. * * @see {@link https://github.com/lxsmnsyc/terracotta/blob/main/docs/components/tabs.md} */ export function TabList( props: TabListProps, ): JSX.Element { const rootContext = useTabGroupContext('TabList'); const controller = createTabFocusNavigator(); const state = useSelectState(); const [ref, setRef] = createForwardRef(props); createEffect(() => { const current = ref(); if (current instanceof HTMLElement) { controller.setRef(current); onCleanup(() => { controller.clearRef(); }); useEventListener(current, 'keydown', (e) => { if (!state.disabled()) { switch (e.key) { case 'ArrowUp': { if (!rootContext.horizontal) { e.preventDefault(); controller.setPrevChecked(true); } break; } case 'ArrowLeft': { if (rootContext.horizontal) { e.preventDefault(); controller.setPrevChecked(true); } break; } case 'ArrowDown': { if (!rootContext.horizontal) { e.preventDefault(); controller.setNextChecked(true); } break; } case 'ArrowRight': { if (rootContext.horizontal) { e.preventDefault(); controller.setNextChecked(true); } break; } case 'Home': { e.preventDefault(); controller.setFirstChecked(); break; } case 'End': { e.preventDefault(); controller.setLastChecked(); break; } } } }); useEventListener(current, 'focusin', (e) => { if (e.target && e.target !== current) { controller.setCurrent(e.target as HTMLElement); } }); } }); return createComponent(TabListContext.Provider, { value: controller, get children() { return createDynamic( () => props.as ?? ('div' as T), mergeProps( TAB_LIST_TAG, { role: 'tablist', get 'aria-orientation'() { return rootContext.horizontal ? 'horizontal' : 'vertical'; }, ref: setRef, get children() { return createComponent(SelectStateChild, { get children() { return props.children; }, }); }, }, createHasSelectedState(() => state.hasSelected()), createHasActiveState(() => state.hasActive()), omitProps(props, ['as', 'ref', 'children']), ) as DynamicProps, ); }, }); }