UNPKG

8.43 kBJavaScriptView Raw
1import _extends from 'babel-runtime/helpers/extends';
2import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
3import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
4import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
5import _inherits from 'babel-runtime/helpers/inherits';
6import classNames from 'classnames';
7import React from 'react';
8import PropTypes from 'prop-types';
9import elementType from 'prop-types-extra/lib/elementType';
10import warning from 'warning';
11
12import { bsClass, getClassSet, prefix, splitBsPropsAndOmit } from './utils/bootstrapUtils';
13import createChainedFunction from './utils/createChainedFunction';
14
15import Fade from './Fade';
16
17var propTypes = {
18 /**
19 * Uniquely identify the `<TabPane>` among its siblings.
20 */
21 eventKey: PropTypes.any,
22
23 /**
24 * Use animation when showing or hiding `<TabPane>`s. Use `false` to disable,
25 * `true` to enable the default `<Fade>` animation or
26 * a react-transition-group v2 `<Transition/>` component.
27 */
28 animation: PropTypes.oneOfType([PropTypes.bool, elementType]),
29
30 /** @private * */
31 id: PropTypes.string,
32
33 /** @private * */
34 'aria-labelledby': PropTypes.string,
35
36 /**
37 * If not explicitly specified and rendered in the context of a
38 * `<TabContent>`, the `bsClass` of the `<TabContent>` suffixed by `-pane`.
39 * If otherwise not explicitly specified, `tab-pane`.
40 */
41 bsClass: PropTypes.string,
42
43 /**
44 * Transition onEnter callback when animation is not `false`
45 */
46 onEnter: PropTypes.func,
47
48 /**
49 * Transition onEntering callback when animation is not `false`
50 */
51 onEntering: PropTypes.func,
52
53 /**
54 * Transition onEntered callback when animation is not `false`
55 */
56 onEntered: PropTypes.func,
57
58 /**
59 * Transition onExit callback when animation is not `false`
60 */
61 onExit: PropTypes.func,
62
63 /**
64 * Transition onExiting callback when animation is not `false`
65 */
66 onExiting: PropTypes.func,
67
68 /**
69 * Transition onExited callback when animation is not `false`
70 */
71 onExited: PropTypes.func,
72
73 /**
74 * Wait until the first "enter" transition to mount the tab (add it to the DOM)
75 */
76 mountOnEnter: PropTypes.bool,
77
78 /**
79 * Unmount the tab (remove it from the DOM) when it is no longer visible
80 */
81 unmountOnExit: PropTypes.bool
82};
83
84var contextTypes = {
85 $bs_tabContainer: PropTypes.shape({
86 getTabId: PropTypes.func,
87 getPaneId: PropTypes.func
88 }),
89 $bs_tabContent: PropTypes.shape({
90 bsClass: PropTypes.string,
91 animation: PropTypes.oneOfType([PropTypes.bool, elementType]),
92 activeKey: PropTypes.any,
93 mountOnEnter: PropTypes.bool,
94 unmountOnExit: PropTypes.bool,
95 onPaneEnter: PropTypes.func.isRequired,
96 onPaneExited: PropTypes.func.isRequired,
97 exiting: PropTypes.bool.isRequired
98 })
99};
100
101/**
102 * We override the `<TabContainer>` context so `<Nav>`s in `<TabPane>`s don't
103 * conflict with the top level one.
104 */
105var childContextTypes = {
106 $bs_tabContainer: PropTypes.oneOf([null])
107};
108
109var TabPane = function (_React$Component) {
110 _inherits(TabPane, _React$Component);
111
112 function TabPane(props, context) {
113 _classCallCheck(this, TabPane);
114
115 var _this = _possibleConstructorReturn(this, _React$Component.call(this, props, context));
116
117 _this.handleEnter = _this.handleEnter.bind(_this);
118 _this.handleExited = _this.handleExited.bind(_this);
119
120 _this.in = false;
121 return _this;
122 }
123
124 TabPane.prototype.getChildContext = function getChildContext() {
125 return {
126 $bs_tabContainer: null
127 };
128 };
129
130 TabPane.prototype.componentDidMount = function componentDidMount() {
131 if (this.shouldBeIn()) {
132 // In lieu of the action event firing.
133 this.handleEnter();
134 }
135 };
136
137 TabPane.prototype.componentDidUpdate = function componentDidUpdate() {
138 if (this.in) {
139 if (!this.shouldBeIn()) {
140 // We shouldn't be active any more. Notify the parent.
141 this.handleExited();
142 }
143 } else if (this.shouldBeIn()) {
144 // We are the active child. Notify the parent.
145 this.handleEnter();
146 }
147 };
148
149 TabPane.prototype.componentWillUnmount = function componentWillUnmount() {
150 if (this.in) {
151 // In lieu of the action event firing.
152 this.handleExited();
153 }
154 };
155
156 TabPane.prototype.getAnimation = function getAnimation() {
157 if (this.props.animation != null) {
158 return this.props.animation;
159 }
160
161 var tabContent = this.context.$bs_tabContent;
162 return tabContent && tabContent.animation;
163 };
164
165 TabPane.prototype.handleEnter = function handleEnter() {
166 var tabContent = this.context.$bs_tabContent;
167 if (!tabContent) {
168 return;
169 }
170
171 this.in = tabContent.onPaneEnter(this, this.props.eventKey);
172 };
173
174 TabPane.prototype.handleExited = function handleExited() {
175 var tabContent = this.context.$bs_tabContent;
176 if (!tabContent) {
177 return;
178 }
179
180 tabContent.onPaneExited(this);
181 this.in = false;
182 };
183
184 TabPane.prototype.isActive = function isActive() {
185 var tabContent = this.context.$bs_tabContent;
186 var activeKey = tabContent && tabContent.activeKey;
187
188 return this.props.eventKey === activeKey;
189 };
190
191 TabPane.prototype.shouldBeIn = function shouldBeIn() {
192 return this.getAnimation() && this.isActive();
193 };
194
195 TabPane.prototype.render = function render() {
196 var _props = this.props,
197 eventKey = _props.eventKey,
198 className = _props.className,
199 onEnter = _props.onEnter,
200 onEntering = _props.onEntering,
201 onEntered = _props.onEntered,
202 onExit = _props.onExit,
203 onExiting = _props.onExiting,
204 onExited = _props.onExited,
205 propsMountOnEnter = _props.mountOnEnter,
206 propsUnmountOnExit = _props.unmountOnExit,
207 props = _objectWithoutProperties(_props, ['eventKey', 'className', 'onEnter', 'onEntering', 'onEntered', 'onExit', 'onExiting', 'onExited', 'mountOnEnter', 'unmountOnExit']);
208
209 var _context = this.context,
210 tabContent = _context.$bs_tabContent,
211 tabContainer = _context.$bs_tabContainer;
212
213 var _splitBsPropsAndOmit = splitBsPropsAndOmit(props, ['animation']),
214 bsProps = _splitBsPropsAndOmit[0],
215 elementProps = _splitBsPropsAndOmit[1];
216
217 var active = this.isActive();
218 var animation = this.getAnimation();
219
220 var mountOnEnter = propsMountOnEnter != null ? propsMountOnEnter : tabContent && tabContent.mountOnEnter;
221 var unmountOnExit = propsUnmountOnExit != null ? propsUnmountOnExit : tabContent && tabContent.unmountOnExit;
222
223 if (!active && !animation && unmountOnExit) {
224 return null;
225 }
226
227 var Transition = animation === true ? Fade : animation || null;
228
229 if (tabContent) {
230 bsProps.bsClass = prefix(tabContent, 'pane');
231 }
232
233 var classes = _extends({}, getClassSet(bsProps), {
234 active: active
235 });
236
237 if (tabContainer) {
238 process.env.NODE_ENV !== 'production' ? warning(!elementProps.id && !elementProps['aria-labelledby'], 'In the context of a `<TabContainer>`, `<TabPanes>` are given ' + 'generated `id` and `aria-labelledby` attributes for the sake of ' + 'proper component accessibility. Any provided ones will be ignored. ' + 'To control these attributes directly provide a `generateChildId` ' + 'prop to the parent `<TabContainer>`.') : void 0;
239
240 elementProps.id = tabContainer.getPaneId(eventKey);
241 elementProps['aria-labelledby'] = tabContainer.getTabId(eventKey);
242 }
243
244 var pane = React.createElement('div', _extends({}, elementProps, {
245 role: 'tabpanel',
246 'aria-hidden': !active,
247 className: classNames(className, classes)
248 }));
249
250 if (Transition) {
251 var exiting = tabContent && tabContent.exiting;
252
253 return React.createElement(
254 Transition,
255 {
256 'in': active && !exiting,
257 onEnter: createChainedFunction(this.handleEnter, onEnter),
258 onEntering: onEntering,
259 onEntered: onEntered,
260 onExit: onExit,
261 onExiting: onExiting,
262 onExited: createChainedFunction(this.handleExited, onExited),
263 mountOnEnter: mountOnEnter,
264 unmountOnExit: unmountOnExit
265 },
266 pane
267 );
268 }
269
270 return pane;
271 };
272
273 return TabPane;
274}(React.Component);
275
276TabPane.propTypes = propTypes;
277TabPane.contextTypes = contextTypes;
278TabPane.childContextTypes = childContextTypes;
279
280export default bsClass('tab-pane', TabPane);
\No newline at end of file