1
1
import React , { cloneElement } from 'react' ;
2
- import ValidComponentChildren from './utils/ValidComponentChildren' ;
2
+
3
+ import Col from './Col' ;
4
+ import Grid from './Grid' ;
3
5
import Nav from './Nav' ;
4
6
import NavItem from './NavItem' ;
7
+ import Row from './Row' ;
8
+ import styleMaps from './styleMaps' ;
9
+
10
+ import ValidComponentChildren from './utils/ValidComponentChildren' ;
5
11
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 ) ;
7
13
let tabId = ( props , child ) => child . props . id ? child . props . id + '___tab' : props . id && ( props . id + '___tab___' + child . props . eventKey ) ;
8
14
9
15
function getDefaultActiveKeyFromChildren ( children ) {
@@ -22,16 +28,44 @@ const Tabs = React.createClass({
22
28
propTypes : {
23
29
activeKey : React . PropTypes . any ,
24
30
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
+ */
25
37
bsStyle : React . PropTypes . oneOf ( [ 'tabs' , 'pills' ] ) ,
26
38
animation : React . PropTypes . bool ,
27
39
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
+ ] )
29
62
} ,
30
63
31
64
getDefaultProps ( ) {
32
65
return {
33
- bsStyle : 'tabs' ,
34
- animation : true
66
+ animation : true ,
67
+ tabWidth : 2 ,
68
+ position : 'top'
35
69
} ;
36
70
} ,
37
71
@@ -73,26 +107,89 @@ const Tabs = React.createClass({
73
107
id,
74
108
className,
75
109
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 ;
77
117
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' ;
80
122
}
81
123
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} ;
87
125
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 >
93
190
</ div >
94
- </ div >
95
- ) ;
191
+ ) ;
192
+ }
96
193
} ,
97
194
98
195
getActiveKey ( ) {
@@ -111,7 +208,7 @@ const Tabs = React.createClass({
111
208
child ,
112
209
{
113
210
active : shouldPaneBeSetActive && ( thereIsNoActivePane || ! this . props . animation ) ,
114
- id : panelId ( this . props , child ) ,
211
+ id : paneId ( this . props , child ) ,
115
212
'aria-labelledby' : tabId ( this . props , child ) ,
116
213
key : child . key ? child . key : index ,
117
214
animation : this . props . animation ,
@@ -121,20 +218,47 @@ const Tabs = React.createClass({
121
218
} ,
122
219
123
220
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 ;
125
226
126
227
return (
127
228
< NavItem
128
229
linkId = { tabId ( this . props , child ) }
129
230
ref = { 'tab' + eventKey }
130
- aria-controls = { panelId ( this . props , child ) }
231
+ aria-controls = { paneId ( this . props , child ) }
131
232
eventKey = { eventKey }
132
233
disabled = { disabled } >
133
234
{ title }
134
235
</ NavItem >
135
236
) ;
136
237
} ,
137
238
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
+
138
262
shouldComponentUpdate ( ) {
139
263
// Defer any updates to this component during the `onSelect` handler.
140
264
return ! this . _isChanging ;
0 commit comments