2424
2525 */
2626
27- import React , { Children , cloneElement , FC } from 'react'
27+ import React , {
28+ Children ,
29+ cloneElement ,
30+ forwardRef ,
31+ KeyboardEvent ,
32+ useRef ,
33+ Ref ,
34+ } from 'react'
2835import styled , { css } from 'styled-components'
36+ import { moveFocus , useForkedRef } from '../utils'
37+ import { TabContext } from './TabContext'
2938import { Tab } from '.'
3039
3140export interface TabListProps {
@@ -36,29 +45,59 @@ export interface TabListProps {
3645 distribute ?: boolean
3746}
3847
39- const TabListLayout : FC < TabListProps > = ( {
40- children,
41- selectedIndex,
42- onSelectTab,
43- className,
44- } ) => {
45- const clonedChildren = Children . map (
46- children ,
47- ( child : JSX . Element , index : number ) => {
48- return cloneElement ( child , {
49- index,
50- onSelect : ( ) => onSelectTab && onSelectTab ( index ) ,
51- selected : index === selectedIndex ,
52- selectedIndex,
53- } )
48+ const TabListLayout = forwardRef (
49+ (
50+ { children, selectedIndex, onSelectTab, className } : TabListProps ,
51+ ref : Ref < HTMLDivElement >
52+ ) => {
53+ const wrapperRef = useRef < HTMLDivElement > ( null )
54+ const forkedRef = useForkedRef ( wrapperRef , ref )
55+
56+ const clonedChildren = Children . map (
57+ children ,
58+ ( child : JSX . Element , index : number ) => {
59+ return cloneElement ( child , {
60+ index,
61+ onSelect : ( ) => onSelectTab && onSelectTab ( index ) ,
62+ selected : index === selectedIndex ,
63+ selectedIndex,
64+ } )
65+ }
66+ )
67+
68+ function handleArrowKey ( direction : number , initial : number ) {
69+ moveFocus ( direction , initial , wrapperRef )
5470 }
55- )
56- return (
57- < div aria-label = "Tabs" className = { className } role = "tablist" >
58- { clonedChildren }
59- </ div >
60- )
61- }
71+
72+ const context = {
73+ handleArrowLeft : ( e : KeyboardEvent < HTMLButtonElement > ) => {
74+ e . preventDefault ( )
75+ handleArrowKey ( - 1 , - 1 )
76+ return false
77+ } ,
78+ handleArrowRight : ( e : KeyboardEvent < HTMLButtonElement > ) => {
79+ e . preventDefault ( )
80+ handleArrowKey ( 1 , 0 )
81+ return false
82+ } ,
83+ }
84+
85+ return (
86+ < TabContext . Provider value = { context } >
87+ < div
88+ aria-label = "Tabs"
89+ className = { className }
90+ ref = { forkedRef }
91+ role = "tablist"
92+ >
93+ { clonedChildren }
94+ </ div >
95+ </ TabContext . Provider >
96+ )
97+ }
98+ )
99+
100+ TabListLayout . displayName = 'TabListLayout'
62101
63102const defaultLayoutCSS = css `
64103 ${ Tab } {
0 commit comments