11import React , { cloneElement } from 'react' ;
2- import ValidComponentChildren from './utils/ValidComponentChildren' ;
2+
3+ import Col from './Col' ;
4+ import Grid from './Grid' ;
35import Nav from './Nav' ;
46import NavItem from './NavItem' ;
7+ import Row from './Row' ;
8+ import styleMaps from './styleMaps' ;
9+
10+ import ValidComponentChildren from './utils/ValidComponentChildren' ;
511
6- let panelId = ( props , child ) => child . props . id ? child . props . id : props . id && ( props . id + '___panel___ ' + child . props . eventKey ) ;
12+ let paneId = ( props , child ) => child . props . id ? child . props . id : props . id && ( props . id + '___pane___ ' + child . props . eventKey ) ;
713let tabId = ( props , child ) => child . props . id ? child . props . id + '___tab' : props . id && ( props . id + '___tab___' + child . props . eventKey ) ;
814
915function getDefaultActiveKeyFromChildren ( children ) {
@@ -22,16 +28,44 @@ const Tabs = React.createClass({
2228 propTypes : {
2329 activeKey : React . PropTypes . any ,
2430 defaultActiveKey : React . PropTypes . any ,
31+ /**
32+ * Navigation style for tabs
33+ *
34+ * If not specified, it will be treated as `'tabs'` when vertically
35+ * positioned and `'pills'` when horizontally positioned.
36+ */
2537 bsStyle : React . PropTypes . oneOf ( [ 'tabs' , 'pills' ] ) ,
2638 animation : React . PropTypes . bool ,
2739 id : React . PropTypes . string ,
28- onSelect : React . PropTypes . func
40+ onSelect : React . PropTypes . func ,
41+ position : React . PropTypes . oneOf ( [ 'top' , 'left' , 'right' ] ) ,
42+ /**
43+ * Number of grid columns for the tabs if horizontally positioned
44+ *
45+ * This accepts either a single width or a mapping of size to width.
46+ */
47+ tabWidth : React . PropTypes . oneOfType ( [
48+ React . PropTypes . number ,
49+ React . PropTypes . object
50+ ] ) ,
51+ /**
52+ * Number of grid columns for the panes if horizontally positioned
53+ *
54+ * This accepts either a single width or a mapping of size to width. If not
55+ * specified, it will be treated as `styleMaps.GRID_COLUMNS` minus
56+ * `tabWidth`.
57+ */
58+ paneWidth : React . PropTypes . oneOfType ( [
59+ React . PropTypes . number ,
60+ React . PropTypes . object
61+ ] )
2962 } ,
3063
3164 getDefaultProps ( ) {
3265 return {
33- bsStyle : 'tabs' ,
34- animation : true
66+ animation : true ,
67+ tabWidth : 2 ,
68+ position : 'top'
3569 } ;
3670 } ,
3771
@@ -73,26 +107,89 @@ const Tabs = React.createClass({
73107 id,
74108 className,
75109 style, // eslint-disable-line react/prop-types
76- ...props } = this . props ;
110+ position,
111+ bsStyle,
112+ tabWidth,
113+ paneWidth,
114+ children,
115+ ...props
116+ } = this . props ;
77117
78- function renderTabIfSet ( child ) {
79- return child . props . title != null ? this . renderTab ( child ) : null ;
118+ const isHorizontal = position === 'left' || position === 'right' ;
119+
120+ if ( bsStyle == null ) {
121+ bsStyle = isHorizontal ? 'pills' : 'tabs' ;
80122 }
81123
82- let nav = (
83- < Nav { ...props } activeKey = { this . getActiveKey ( ) } onSelect = { this . handleSelect } ref = "tabs" role = "tablist" >
84- { ValidComponentChildren . map ( this . props . children , renderTabIfSet , this ) }
85- </ Nav >
86- ) ;
124+ const containerProps = { id, className, style} ;
87125
88- return (
89- < div id = { id } className = { className } style = { style } >
90- { nav }
91- < div className = "tab-content" ref = "panes" >
92- { ValidComponentChildren . map ( this . props . children , this . renderPane ) }
126+ const tabsProps = {
127+ ...props ,
128+ bsStyle,
129+ stacked : isHorizontal ,
130+ activeKey : this . getActiveKey ( ) ,
131+ onSelect : this . handleSelect ,
132+ ref : 'tabs' ,
133+ role : 'tablist'
134+ } ;
135+ const childTabs = ValidComponentChildren . map ( children , this . renderTab ) ;
136+
137+ const panesProps = {
138+ className : 'tab-content' ,
139+ ref : 'panes'
140+ } ;
141+ const childPanes = ValidComponentChildren . map ( children , this . renderPane ) ;
142+
143+ if ( isHorizontal ) {
144+ const { tabsColProps, panesColProps} =
145+ this . getColProps ( { tabWidth, paneWidth} ) ;
146+
147+ const tabs = (
148+ < Col componentClass = { Nav } { ...tabsProps } { ...tabsColProps } >
149+ { childTabs }
150+ </ Col >
151+ ) ;
152+ const panes = (
153+ < Col { ...panesProps } { ...panesColProps } >
154+ { childPanes }
155+ </ Col >
156+ ) ;
157+
158+ let body ;
159+ if ( position === 'left' ) {
160+ body = (
161+ < Row { ...containerProps } >
162+ { tabs }
163+ { panes }
164+ </ Row >
165+ ) ;
166+ } else {
167+ body = (
168+ < Row { ...containerProps } >
169+ { panes }
170+ { tabs }
171+ </ Row >
172+ ) ;
173+ }
174+
175+ return (
176+ < Grid >
177+ { body }
178+ </ Grid >
179+ ) ;
180+ } else {
181+ return (
182+ < div { ...containerProps } >
183+ < Nav { ...tabsProps } >
184+ { childTabs }
185+ </ Nav >
186+
187+ < div { ...panesProps } >
188+ { childPanes }
189+ </ div >
93190 </ div >
94- </ div >
95- ) ;
191+ ) ;
192+ }
96193 } ,
97194
98195 getActiveKey ( ) {
@@ -111,7 +208,7 @@ const Tabs = React.createClass({
111208 child ,
112209 {
113210 active : shouldPaneBeSetActive && ( thereIsNoActivePane || ! this . props . animation ) ,
114- id : panelId ( this . props , child ) ,
211+ id : paneId ( this . props , child ) ,
115212 'aria-labelledby' : tabId ( this . props , child ) ,
116213 key : child . key ? child . key : index ,
117214 animation : this . props . animation ,
@@ -121,20 +218,47 @@ const Tabs = React.createClass({
121218 } ,
122219
123220 renderTab ( child ) {
124- let { eventKey, title, disabled } = child . props ;
221+ if ( child . props . title == null ) {
222+ return null ;
223+ }
224+
225+ let { eventKey, title, disabled} = child . props ;
125226
126227 return (
127228 < NavItem
128229 linkId = { tabId ( this . props , child ) }
129230 ref = { 'tab' + eventKey }
130- aria-controls = { panelId ( this . props , child ) }
231+ aria-controls = { paneId ( this . props , child ) }
131232 eventKey = { eventKey }
132233 disabled = { disabled } >
133234 { title }
134235 </ NavItem >
135236 ) ;
136237 } ,
137238
239+ getColProps ( { tabWidth, paneWidth} ) {
240+ let tabsColProps ;
241+ if ( tabWidth instanceof Object ) {
242+ tabsColProps = tabWidth ;
243+ } else {
244+ tabsColProps = { xs : tabWidth } ;
245+ }
246+
247+ let panesColProps ;
248+ if ( paneWidth == null ) {
249+ panesColProps = { } ;
250+ Object . keys ( tabsColProps ) . forEach ( function ( size ) {
251+ panesColProps [ size ] = styleMaps . GRID_COLUMNS - tabsColProps [ size ] ;
252+ } ) ;
253+ } else if ( paneWidth instanceof Object ) {
254+ panesColProps = paneWidth ;
255+ } else {
256+ panesColProps = { xs : paneWidth } ;
257+ }
258+
259+ return { tabsColProps, panesColProps} ;
260+ } ,
261+
138262 shouldComponentUpdate ( ) {
139263 // Defer any updates to this component during the `onSelect` handler.
140264 return ! this . _isChanging ;
0 commit comments