11import classNames from 'classnames' ;
2- import React , { cloneElement } from 'react' ;
2+ import React , { cloneElement , findDOMNode } from 'react' ;
33
44import Col from './Col' ;
55import Nav from './Nav' ;
66import NavItem from './NavItem' ;
77import styleMaps from './styleMaps' ;
8-
8+ import keycode from 'keycode' ;
9+ import createChainedFunction from './utils/createChainedFunction' ;
910import ValidComponentChildren from './utils/ValidComponentChildren' ;
1011
1112let paneId = ( props , child ) => child . props . id ? child . props . id : props . id && ( props . id + '___pane___' + child . props . eventKey ) ;
1213let tabId = ( props , child ) => child . props . id ? child . props . id + '___tab' : props . id && ( props . id + '___tab___' + child . props . eventKey ) ;
1314
15+ let findChild = ValidComponentChildren . find ;
16+
1417function getDefaultActiveKeyFromChildren ( children ) {
1518 let defaultActiveKey ;
1619
@@ -23,6 +26,30 @@ function getDefaultActiveKeyFromChildren(children) {
2326 return defaultActiveKey ;
2427}
2528
29+ function move ( children , currentKey , keys , moveNext ) {
30+ let lastIdx = keys . length - 1 ;
31+ let stopAt = keys [ moveNext ? Math . max ( lastIdx , 0 ) : 0 ] ;
32+ let nextKey = currentKey ;
33+
34+ function getNext ( ) {
35+ let idx = keys . indexOf ( nextKey ) ;
36+ nextKey = moveNext
37+ ? keys [ Math . min ( lastIdx , idx + 1 ) ]
38+ : keys [ Math . max ( 0 , idx - 1 ) ] ;
39+
40+ return findChild ( children ,
41+ _child => _child . props . eventKey === nextKey ) ;
42+ }
43+
44+ let next = getNext ( ) ;
45+
46+ while ( next . props . eventKey !== stopAt && next . props . disabled ) {
47+ next = getNext ( ) ;
48+ }
49+
50+ return next . props . disabled ? currentKey : next . props . eventKey ;
51+ }
52+
2653const Tabs = React . createClass ( {
2754 propTypes : {
2855 activeKey : React . PropTypes . any ,
@@ -103,6 +130,22 @@ const Tabs = React.createClass({
103130 }
104131 } ,
105132
133+ componentDidUpdate ( ) {
134+ let tabs = this . _tabs ;
135+ let tabIdx = this . _eventKeys ( ) . indexOf ( this . getActiveKey ( ) ) ;
136+
137+ if ( this . _needsRefocus ) {
138+ this . _needsRefocus = false ;
139+ if ( tabs && tabIdx !== - 1 ) {
140+ let tabNode = findDOMNode ( tabs [ tabIdx ] ) ;
141+
142+ if ( tabNode ) {
143+ tabNode . firstChild . focus ( ) ;
144+ }
145+ }
146+ }
147+ } ,
148+
106149 handlePaneAnimateOutEnd ( ) {
107150 this . setState ( {
108151 previousActiveKey : null
@@ -223,20 +266,23 @@ const Tabs = React.createClass({
223266 ) ;
224267 } ,
225268
226- renderTab ( child ) {
269+ renderTab ( child , index ) {
227270 if ( child . props . title == null ) {
228271 return null ;
229272 }
230273
231- let { eventKey, title, disabled} = child . props ;
274+ let { eventKey, title, disabled, onKeyDown, tabIndex = 0 } = child . props ;
275+ let isActive = this . getActiveKey ( ) === eventKey ;
232276
233277 return (
234278 < NavItem
235279 linkId = { tabId ( this . props , child ) }
236- ref = { 'tab' + eventKey }
280+ ref = { ref => ( this . _tabs || ( this . _tabs = [ ] ) ) [ index ] = ref }
237281 aria-controls = { paneId ( this . props , child ) }
282+ onKeyDown = { createChainedFunction ( this . handleKeyDown , onKeyDown ) }
238283 eventKey = { eventKey }
239- disabled = { disabled } >
284+ tabIndex = { isActive ? tabIndex : - 1 }
285+ disabled = { disabled } >
240286 { title }
241287 </ NavItem >
242288 ) ;
@@ -286,6 +332,46 @@ const Tabs = React.createClass({
286332 previousActiveKey
287333 } ) ;
288334 }
335+ } ,
336+
337+ handleKeyDown ( event ) {
338+ let keys = this . _eventKeys ( ) ;
339+ let currentKey = this . getActiveKey ( ) || keys [ 0 ] ;
340+ let next ;
341+
342+ switch ( event . keyCode ) {
343+
344+ case keycode . codes . left :
345+ case keycode . codes . up :
346+ next = move ( this . props . children , currentKey , keys , false ) ;
347+
348+ if ( next && next !== currentKey ) {
349+ event . preventDefault ( ) ;
350+ this . handleSelect ( next ) ;
351+ this . _needsRefocus = true ;
352+ }
353+ break ;
354+ case keycode . codes . right :
355+ case keycode . codes . down :
356+ next = move ( this . props . children , currentKey , keys , true ) ;
357+
358+ if ( next && next !== currentKey ) {
359+ event . preventDefault ( ) ;
360+ this . handleSelect ( next ) ;
361+ this . _needsRefocus = true ;
362+ }
363+ break ;
364+ default :
365+ }
366+ } ,
367+
368+ _eventKeys ( ) {
369+ let keys = [ ] ;
370+
371+ ValidComponentChildren . forEach ( this . props . children ,
372+ ( { props : { eventKey } } ) => keys . push ( eventKey ) ) ;
373+
374+ return keys ;
289375 }
290376} ) ;
291377
0 commit comments