'use client'; import * as React from 'react'; import { useTabsterAttributes } from '@fluentui/react-tabster'; import { mergeCallbacks, useEventCallback, useMergedRefs, slot, omit } from '@fluentui/react-utilities'; import { useTabListContext_unstable } from '../TabList'; /** * Create the state required to render Tab. * * The returned state can be modified with hooks such as useTabStyles_unstable, * before being passed to renderTab_unstable. * * @param props - props from this instance of Tab * @param ref - reference to root HTMLElement of Tab */ export const useTab_unstable = (props, ref)=>{ const { content } = props; const state = useTabBase_unstable(props, ref); const focusAttributes = useTabA11yBehavior_unstable(state); const appearance = useTabListContext_unstable((ctx)=>ctx.appearance); const reserveSelectedTabSpace = useTabListContext_unstable((ctx)=>ctx.reserveSelectedTabSpace); const size = useTabListContext_unstable((ctx)=>{ var _ctx_size; return (_ctx_size = ctx.size) !== null && _ctx_size !== void 0 ? _ctx_size : 'medium'; }); const contentReservedSpace = content && typeof content === 'object' ? omit(content, [ 'ref' ]) : content; return { ...state, // eslint-disable-next-line @typescript-eslint/no-deprecated components: { ...state.components, contentReservedSpace: 'span' }, root: { ...focusAttributes, ...state.root }, contentReservedSpace: slot.optional(contentReservedSpace, { renderByDefault: !state.selected && !state.iconOnly && reserveSelectedTabSpace, defaultProps: { children: props.children }, elementType: 'span' }), appearance, size }; }; /** * Create the based state required to render Tab without design specifics and focus attributes. * * @param props - props from this instance of Tab * @param ref - reference to root HTMLElement of Tab */ export const useTabBase_unstable = (props, ref)=>{ const { content, disabled: tabDisabled = false, icon, onClick, onFocus, value, ...rest } = props; const selectTabOnFocus = useTabListContext_unstable((ctx)=>ctx.selectTabOnFocus); const listDisabled = useTabListContext_unstable((ctx)=>ctx.disabled); const selected = useTabListContext_unstable((ctx)=>ctx.selectedValue === value); const onRegister = useTabListContext_unstable((ctx)=>ctx.onRegister); const onUnregister = useTabListContext_unstable((ctx)=>ctx.onUnregister); const onSelect = useTabListContext_unstable((ctx)=>ctx.onSelect); const vertical = useTabListContext_unstable((ctx)=>!!ctx.vertical); const disabled = listDisabled || tabDisabled; const innerRef = React.useRef(null); const onSelectCallback = (event)=>onSelect(event, { value }); const onTabClick = useEventCallback(mergeCallbacks(onClick, onSelectCallback)); const onTabFocus = useEventCallback(mergeCallbacks(onFocus, onSelectCallback)); React.useEffect(()=>{ onRegister({ value, ref: innerRef }); return ()=>{ onUnregister({ value, ref: innerRef }); }; }, [ onRegister, onUnregister, innerRef, value ]); const iconSlot = slot.optional(icon, { elementType: 'span' }); const contentSlot = slot.always(content, { defaultProps: { children: props.children }, elementType: 'span' }); const iconOnly = Boolean((iconSlot === null || iconSlot === void 0 ? void 0 : iconSlot.children) && !contentSlot.children); return { components: { root: 'button', icon: 'span', content: 'span', contentReservedSpace: 'span' }, root: slot.always({ ref: useMergedRefs(ref, innerRef), role: 'tab', type: 'button', // aria-selected undefined indicates it is not selectable // according to https://www.w3.org/TR/wai-aria-1.1/#aria-selected 'aria-selected': disabled ? undefined : `${selected}`, value, ...rest, disabled, onClick: onTabClick, onFocus: selectTabOnFocus ? onTabFocus : onFocus }, { elementType: 'button' }), icon: iconSlot, iconOnly, content: contentSlot, disabled, selected, value, vertical }; }; /** * Hook to return a11y attributes to a Tab based on selected state. * Should be applied on the button with role="tab". * * @param selected - whether the Tab is selected * @returns Tabster DOM attributes */ export const useTabA11yBehavior_unstable = ({ selected })=>{ return useTabsterAttributes({ focusable: { isDefault: selected } }); };