UNPKG

13.2 kBJavaScriptView Raw
1'use strict';
2
3exports.__esModule = true;
4
5var _extends2 = require('babel-runtime/helpers/extends');
6
7var _extends3 = _interopRequireDefault(_extends2);
8
9var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
10
11var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
12
13var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
14
15var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
16
17var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
18
19var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
20
21var _inherits2 = require('babel-runtime/helpers/inherits');
22
23var _inherits3 = _interopRequireDefault(_inherits2);
24
25var _classnames = require('classnames');
26
27var _classnames2 = _interopRequireDefault(_classnames);
28
29var _activeElement = require('dom-helpers/activeElement');
30
31var _activeElement2 = _interopRequireDefault(_activeElement);
32
33var _contains = require('dom-helpers/query/contains');
34
35var _contains2 = _interopRequireDefault(_contains);
36
37var _keycode = require('keycode');
38
39var _keycode2 = _interopRequireDefault(_keycode);
40
41var _react = require('react');
42
43var _react2 = _interopRequireDefault(_react);
44
45var _propTypes = require('prop-types');
46
47var _propTypes2 = _interopRequireDefault(_propTypes);
48
49var _reactDom = require('react-dom');
50
51var _reactDom2 = _interopRequireDefault(_reactDom);
52
53var _all = require('prop-types-extra/lib/all');
54
55var _all2 = _interopRequireDefault(_all);
56
57var _elementType = require('prop-types-extra/lib/elementType');
58
59var _elementType2 = _interopRequireDefault(_elementType);
60
61var _isRequiredForA11y = require('prop-types-extra/lib/isRequiredForA11y');
62
63var _isRequiredForA11y2 = _interopRequireDefault(_isRequiredForA11y);
64
65var _uncontrollable = require('uncontrollable');
66
67var _uncontrollable2 = _interopRequireDefault(_uncontrollable);
68
69var _warning = require('warning');
70
71var _warning2 = _interopRequireDefault(_warning);
72
73var _ButtonGroup = require('./ButtonGroup');
74
75var _ButtonGroup2 = _interopRequireDefault(_ButtonGroup);
76
77var _DropdownMenu = require('./DropdownMenu');
78
79var _DropdownMenu2 = _interopRequireDefault(_DropdownMenu);
80
81var _DropdownToggle = require('./DropdownToggle');
82
83var _DropdownToggle2 = _interopRequireDefault(_DropdownToggle);
84
85var _bootstrapUtils = require('./utils/bootstrapUtils');
86
87var _createChainedFunction = require('./utils/createChainedFunction');
88
89var _createChainedFunction2 = _interopRequireDefault(_createChainedFunction);
90
91var _PropTypes = require('./utils/PropTypes');
92
93var _ValidComponentChildren = require('./utils/ValidComponentChildren');
94
95var _ValidComponentChildren2 = _interopRequireDefault(_ValidComponentChildren);
96
97function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
98
99var TOGGLE_ROLE = _DropdownToggle2.default.defaultProps.bsRole;
100var MENU_ROLE = _DropdownMenu2.default.defaultProps.bsRole;
101
102var propTypes = {
103 /**
104 * The menu will open above the dropdown button, instead of below it.
105 */
106 dropup: _propTypes2.default.bool,
107
108 /**
109 * An html id attribute, necessary for assistive technologies, such as screen readers.
110 * @type {string|number}
111 * @required
112 */
113 id: (0, _isRequiredForA11y2.default)(_propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number])),
114
115 componentClass: _elementType2.default,
116
117 /**
118 * The children of a Dropdown may be a `<Dropdown.Toggle>` or a `<Dropdown.Menu>`.
119 * @type {node}
120 */
121 children: (0, _all2.default)((0, _PropTypes.requiredRoles)(TOGGLE_ROLE, MENU_ROLE), (0, _PropTypes.exclusiveRoles)(MENU_ROLE)),
122
123 /**
124 * Whether or not component is disabled.
125 */
126 disabled: _propTypes2.default.bool,
127
128 /**
129 * Align the menu to the right side of the Dropdown toggle
130 */
131 pullRight: _propTypes2.default.bool,
132
133 /**
134 * Whether or not the Dropdown is visible.
135 *
136 * @controllable onToggle
137 */
138 open: _propTypes2.default.bool,
139
140 defaultOpen: _propTypes2.default.bool,
141
142 /**
143 * A callback fired when the Dropdown wishes to change visibility. Called with the requested
144 * `open` value, the DOM event, and the source that fired it: `'click'`,`'keydown'`,`'rootClose'`, or `'select'`.
145 *
146 * ```js
147 * function(Boolean isOpen, Object event, { String source }) {}
148 * ```
149 * @controllable open
150 */
151 onToggle: _propTypes2.default.func,
152
153 /**
154 * A callback fired when a menu item is selected.
155 *
156 * ```js
157 * (eventKey: any, event: Object) => any
158 * ```
159 */
160 onSelect: _propTypes2.default.func,
161
162 /**
163 * If `'menuitem'`, causes the dropdown to behave like a menu item rather than
164 * a menu button.
165 */
166 role: _propTypes2.default.string,
167
168 /**
169 * Which event when fired outside the component will cause it to be closed
170 *
171 * *Note: For custom dropdown components, you will have to pass the
172 * `rootCloseEvent` to `<RootCloseWrapper>` in your custom dropdown menu
173 * component ([similarly to how it is implemented in `<Dropdown.Menu>`](https://github.com/react-bootstrap/react-bootstrap/blob/v0.31.5/src/DropdownMenu.js#L115-L119)).*
174 */
175 rootCloseEvent: _propTypes2.default.oneOf(['click', 'mousedown']),
176
177 /**
178 * @private
179 */
180 onMouseEnter: _propTypes2.default.func,
181 /**
182 * @private
183 */
184 onMouseLeave: _propTypes2.default.func
185};
186
187var defaultProps = {
188 componentClass: _ButtonGroup2.default
189};
190
191var Dropdown = function (_React$Component) {
192 (0, _inherits3.default)(Dropdown, _React$Component);
193
194 function Dropdown(props, context) {
195 (0, _classCallCheck3.default)(this, Dropdown);
196
197 var _this = (0, _possibleConstructorReturn3.default)(this, _React$Component.call(this, props, context));
198
199 _this.handleClick = _this.handleClick.bind(_this);
200 _this.handleKeyDown = _this.handleKeyDown.bind(_this);
201 _this.handleClose = _this.handleClose.bind(_this);
202
203 _this._focusInDropdown = false;
204 _this.lastOpenEventType = null;
205 return _this;
206 }
207
208 Dropdown.prototype.componentDidMount = function componentDidMount() {
209 this.focusNextOnOpen();
210 };
211
212 Dropdown.prototype.componentWillUpdate = function componentWillUpdate(nextProps) {
213 if (!nextProps.open && this.props.open) {
214 this._focusInDropdown = (0, _contains2.default)(_reactDom2.default.findDOMNode(this.menu), (0, _activeElement2.default)(document));
215 }
216 };
217
218 Dropdown.prototype.componentDidUpdate = function componentDidUpdate(prevProps) {
219 var open = this.props.open;
220
221 var prevOpen = prevProps.open;
222
223 if (open && !prevOpen) {
224 this.focusNextOnOpen();
225 }
226
227 if (!open && prevOpen) {
228 // if focus hasn't already moved from the menu let's return it
229 // to the toggle
230 if (this._focusInDropdown) {
231 this._focusInDropdown = false;
232 this.focus();
233 }
234 }
235 };
236
237 Dropdown.prototype.focus = function focus() {
238 var toggle = _reactDom2.default.findDOMNode(this.toggle);
239
240 if (toggle && toggle.focus) {
241 toggle.focus();
242 }
243 };
244
245 Dropdown.prototype.focusNextOnOpen = function focusNextOnOpen() {
246 var menu = this.menu;
247
248 if (!menu.focusNext) {
249 return;
250 }
251
252 if (this.lastOpenEventType === 'keydown' || this.props.role === 'menuitem') {
253 menu.focusNext();
254 }
255 };
256
257 Dropdown.prototype.handleClick = function handleClick(event) {
258 if (this.props.disabled) {
259 return;
260 }
261
262 this.toggleOpen(event, { source: 'click' });
263 };
264
265 Dropdown.prototype.handleClose = function handleClose(event, eventDetails) {
266 if (!this.props.open) {
267 return;
268 }
269
270 this.toggleOpen(event, eventDetails);
271 };
272
273 Dropdown.prototype.handleKeyDown = function handleKeyDown(event) {
274 if (this.props.disabled) {
275 return;
276 }
277
278 switch (event.keyCode) {
279 case _keycode2.default.codes.down:
280 if (!this.props.open) {
281 this.toggleOpen(event, { source: 'keydown' });
282 } else if (this.menu.focusNext) {
283 this.menu.focusNext();
284 }
285 event.preventDefault();
286 break;
287 case _keycode2.default.codes.esc:
288 case _keycode2.default.codes.tab:
289 this.handleClose(event, { source: 'keydown' });
290 break;
291 default:
292 }
293 };
294
295 Dropdown.prototype.toggleOpen = function toggleOpen(event, eventDetails) {
296 var open = !this.props.open;
297
298 if (open) {
299 this.lastOpenEventType = eventDetails.source;
300 }
301
302 if (this.props.onToggle) {
303 this.props.onToggle(open, event, eventDetails);
304 }
305 };
306
307 Dropdown.prototype.renderMenu = function renderMenu(child, _ref) {
308 var _this2 = this;
309
310 var id = _ref.id,
311 onSelect = _ref.onSelect,
312 rootCloseEvent = _ref.rootCloseEvent,
313 props = (0, _objectWithoutProperties3.default)(_ref, ['id', 'onSelect', 'rootCloseEvent']);
314
315 var ref = function ref(c) {
316 _this2.menu = c;
317 };
318
319 if (typeof child.ref === 'string') {
320 process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(false, 'String refs are not supported on `<Dropdown.Menu>` components. ' + 'To apply a ref to the component use the callback signature:\n\n ' + 'https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute') : void 0;
321 } else {
322 ref = (0, _createChainedFunction2.default)(child.ref, ref);
323 }
324
325 return (0, _react.cloneElement)(child, (0, _extends3.default)({}, props, {
326 ref: ref,
327 labelledBy: id,
328 bsClass: (0, _bootstrapUtils.prefix)(props, 'menu'),
329 onClose: (0, _createChainedFunction2.default)(child.props.onClose, this.handleClose),
330 onSelect: (0, _createChainedFunction2.default)(child.props.onSelect, onSelect, function (key, event) {
331 return _this2.handleClose(event, { source: 'select' });
332 }),
333 rootCloseEvent: rootCloseEvent
334 }));
335 };
336
337 Dropdown.prototype.renderToggle = function renderToggle(child, props) {
338 var _this3 = this;
339
340 var ref = function ref(c) {
341 _this3.toggle = c;
342 };
343
344 if (typeof child.ref === 'string') {
345 process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(false, 'String refs are not supported on `<Dropdown.Toggle>` components. ' + 'To apply a ref to the component use the callback signature:\n\n ' + 'https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute') : void 0;
346 } else {
347 ref = (0, _createChainedFunction2.default)(child.ref, ref);
348 }
349
350 return (0, _react.cloneElement)(child, (0, _extends3.default)({}, props, {
351 ref: ref,
352 bsClass: (0, _bootstrapUtils.prefix)(props, 'toggle'),
353 onClick: (0, _createChainedFunction2.default)(child.props.onClick, this.handleClick),
354 onKeyDown: (0, _createChainedFunction2.default)(child.props.onKeyDown, this.handleKeyDown)
355 }));
356 };
357
358 Dropdown.prototype.render = function render() {
359 var _classes,
360 _this4 = this;
361
362 var _props = this.props,
363 Component = _props.componentClass,
364 id = _props.id,
365 dropup = _props.dropup,
366 disabled = _props.disabled,
367 pullRight = _props.pullRight,
368 open = _props.open,
369 onSelect = _props.onSelect,
370 role = _props.role,
371 bsClass = _props.bsClass,
372 className = _props.className,
373 rootCloseEvent = _props.rootCloseEvent,
374 children = _props.children,
375 props = (0, _objectWithoutProperties3.default)(_props, ['componentClass', 'id', 'dropup', 'disabled', 'pullRight', 'open', 'onSelect', 'role', 'bsClass', 'className', 'rootCloseEvent', 'children']);
376
377
378 delete props.onToggle;
379
380 var classes = (_classes = {}, _classes[bsClass] = true, _classes.open = open, _classes.disabled = disabled, _classes);
381
382 if (dropup) {
383 classes[bsClass] = false;
384 classes.dropup = true;
385 }
386
387 // This intentionally forwards bsSize and bsStyle (if set) to the
388 // underlying component, to allow it to render size and style variants.
389
390 return _react2.default.createElement(
391 Component,
392 (0, _extends3.default)({}, props, { className: (0, _classnames2.default)(className, classes) }),
393 _ValidComponentChildren2.default.map(children, function (child) {
394 switch (child.props.bsRole) {
395 case TOGGLE_ROLE:
396 return _this4.renderToggle(child, {
397 id: id,
398 disabled: disabled,
399 open: open,
400 role: role,
401 bsClass: bsClass
402 });
403 case MENU_ROLE:
404 return _this4.renderMenu(child, {
405 id: id,
406 open: open,
407 pullRight: pullRight,
408 bsClass: bsClass,
409 onSelect: onSelect,
410 rootCloseEvent: rootCloseEvent
411 });
412 default:
413 return child;
414 }
415 })
416 );
417 };
418
419 return Dropdown;
420}(_react2.default.Component);
421
422Dropdown.propTypes = propTypes;
423Dropdown.defaultProps = defaultProps;
424
425(0, _bootstrapUtils.bsClass)('dropdown', Dropdown);
426
427var UncontrolledDropdown = (0, _uncontrollable2.default)(Dropdown, { open: 'onToggle' });
428
429UncontrolledDropdown.Toggle = _DropdownToggle2.default;
430UncontrolledDropdown.Menu = _DropdownMenu2.default;
431
432exports.default = UncontrolledDropdown;
433module.exports = exports['default'];
\No newline at end of file