1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 | var _react = _interopRequireDefault(require("react"));
|
8 | var _propTypes = _interopRequireDefault(require("prop-types"));
|
9 | var _reactPopper = require("react-popper");
|
10 | var _classnames = _interopRequireDefault(require("classnames"));
|
11 | var _DropdownContext = require("./DropdownContext");
|
12 | var _utils = require("./utils");
|
13 | var _InputGroupContext = require("./InputGroupContext");
|
14 | const _excluded = ["className", "cssModule", "direction", "isOpen", "group", "size", "nav", "setActiveFromChild", "active", "tag", "menuRole"];
|
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
16 | function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
17 | function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
18 | function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
19 | const propTypes = {
|
20 | a11y: _propTypes.default.bool,
|
21 | disabled: _propTypes.default.bool,
|
22 | direction: _propTypes.default.oneOf(['up', 'down', 'start', 'end', 'left', 'right']),
|
23 | group: _propTypes.default.bool,
|
24 | isOpen: _propTypes.default.bool,
|
25 | nav: _propTypes.default.bool,
|
26 | active: _propTypes.default.bool,
|
27 | size: _propTypes.default.string,
|
28 | tag: _utils.tagPropType,
|
29 | toggle: _propTypes.default.func,
|
30 | children: _propTypes.default.node,
|
31 | className: _propTypes.default.string,
|
32 | cssModule: _propTypes.default.object,
|
33 | dropup: _propTypes.default.bool,
|
34 | inNavbar: _propTypes.default.bool,
|
35 | setActiveFromChild: _propTypes.default.bool,
|
36 | menuRole: _propTypes.default.oneOf(['listbox', 'menu'])
|
37 | };
|
38 | const defaultProps = {
|
39 | a11y: true,
|
40 | isOpen: false,
|
41 | direction: 'down',
|
42 | nav: false,
|
43 | active: false,
|
44 | inNavbar: false,
|
45 | setActiveFromChild: false
|
46 | };
|
47 | const preventDefaultKeys = [_utils.keyCodes.space, _utils.keyCodes.enter, _utils.keyCodes.up, _utils.keyCodes.down, _utils.keyCodes.end, _utils.keyCodes.home];
|
48 | class Dropdown extends _react.default.Component {
|
49 | constructor(props) {
|
50 | super(props);
|
51 | this.addEvents = this.addEvents.bind(this);
|
52 | this.handleDocumentClick = this.handleDocumentClick.bind(this);
|
53 | this.handleKeyDown = this.handleKeyDown.bind(this);
|
54 | this.removeEvents = this.removeEvents.bind(this);
|
55 | this.toggle = this.toggle.bind(this);
|
56 | this.handleMenuRef = this.handleMenuRef.bind(this);
|
57 | this.handleToggleRef = this.handleToggleRef.bind(this);
|
58 | this.containerRef = _react.default.createRef();
|
59 | this.menuRef = _react.default.createRef();
|
60 | this.toggleRef = _react.default.createRef();
|
61 |
|
62 | }
|
63 |
|
64 | componentDidMount() {
|
65 | this.handleProps();
|
66 | }
|
67 | componentDidUpdate(prevProps) {
|
68 | if (this.props.isOpen !== prevProps.isOpen) {
|
69 | this.handleProps();
|
70 | }
|
71 | }
|
72 | componentWillUnmount() {
|
73 | this.removeEvents();
|
74 | }
|
75 | handleMenuRef(menuRef) {
|
76 | this.menuRef.current = menuRef;
|
77 | }
|
78 | handleToggleRef(toggleRef) {
|
79 | this.toggleRef.current = toggleRef;
|
80 | }
|
81 | handleDocumentClick(e) {
|
82 | if (e && (e.which === 3 || e.type === 'keyup' && e.which !== _utils.keyCodes.tab)) return;
|
83 | const container = this.getContainer();
|
84 | const menu = this.getMenu();
|
85 | const toggle = this.getToggle();
|
86 |
|
87 |
|
88 |
|
89 | if (!toggle) {
|
90 | return;
|
91 | }
|
92 | const targetIsToggle = toggle.contains(e.target);
|
93 | const clickIsInMenu = menu && menu.contains(e.target) && menu !== e.target;
|
94 | let clickIsInInput = false;
|
95 | if (container) {
|
96 |
|
97 | clickIsInInput = container.classList.contains('input-group') && container.classList.contains('dropdown') && e.target.tagName === 'INPUT';
|
98 | }
|
99 | if ((targetIsToggle && !clickIsInInput || clickIsInMenu) && (e.type !== 'keyup' || e.which === _utils.keyCodes.tab)) {
|
100 | return;
|
101 | }
|
102 | this.toggle(e);
|
103 | }
|
104 | handleKeyDown(e) {
|
105 | const isTargetMenuItem = e.target.getAttribute('role') === 'menuitem' || e.target.getAttribute('role') === 'option';
|
106 | const isTargetMenuCtrl = this.getMenuCtrl() === e.target;
|
107 | const isTab = _utils.keyCodes.tab === e.which;
|
108 | if (/input|textarea/i.test(e.target.tagName) || isTab && !this.props.a11y || isTab && !(isTargetMenuItem || isTargetMenuCtrl)) {
|
109 | return;
|
110 | }
|
111 | if (preventDefaultKeys.indexOf(e.which) !== -1 || e.which >= 48 && e.which <= 90) {
|
112 | e.preventDefault();
|
113 | }
|
114 | if (this.props.disabled) return;
|
115 | if (isTargetMenuCtrl) {
|
116 | if ([_utils.keyCodes.space, _utils.keyCodes.enter, _utils.keyCodes.up, _utils.keyCodes.down].indexOf(e.which) > -1) {
|
117 |
|
118 | if (!this.props.isOpen) {
|
119 | this.toggle(e);
|
120 | }
|
121 | setTimeout(() => this.getMenuItems()[0]?.focus());
|
122 | } else if (this.props.isOpen && isTab) {
|
123 |
|
124 |
|
125 |
|
126 | e.preventDefault();
|
127 | this.getMenuItems()[0]?.focus();
|
128 | } else if (this.props.isOpen && e.which === _utils.keyCodes.esc) {
|
129 | this.toggle(e);
|
130 | }
|
131 | }
|
132 | if (this.props.isOpen && isTargetMenuItem) {
|
133 | if ([_utils.keyCodes.tab, _utils.keyCodes.esc].indexOf(e.which) > -1) {
|
134 | this.toggle(e);
|
135 | this.getMenuCtrl().focus();
|
136 | } else if ([_utils.keyCodes.space, _utils.keyCodes.enter].indexOf(e.which) > -1) {
|
137 | e.target.click();
|
138 | this.getMenuCtrl().focus();
|
139 | } else if ([_utils.keyCodes.down, _utils.keyCodes.up].indexOf(e.which) > -1 || [_utils.keyCodes.n, _utils.keyCodes.p].indexOf(e.which) > -1 && e.ctrlKey) {
|
140 | const $menuitems = this.getMenuItems();
|
141 | let index = $menuitems.indexOf(e.target);
|
142 | if (_utils.keyCodes.up === e.which || _utils.keyCodes.p === e.which && e.ctrlKey) {
|
143 | index = index !== 0 ? index - 1 : $menuitems.length - 1;
|
144 | } else if (_utils.keyCodes.down === e.which || _utils.keyCodes.n === e.which && e.ctrlKey) {
|
145 | index = index === $menuitems.length - 1 ? 0 : index + 1;
|
146 | }
|
147 | $menuitems[index].focus();
|
148 | } else if (_utils.keyCodes.end === e.which) {
|
149 | const $menuitems = this.getMenuItems();
|
150 | $menuitems[$menuitems.length - 1].focus();
|
151 | } else if (_utils.keyCodes.home === e.which) {
|
152 | const $menuitems = this.getMenuItems();
|
153 | $menuitems[0].focus();
|
154 | } else if (e.which >= 48 && e.which <= 90) {
|
155 | const $menuitems = this.getMenuItems();
|
156 | const charPressed = String.fromCharCode(e.which).toLowerCase();
|
157 | for (let i = 0; i < $menuitems.length; i += 1) {
|
158 | const firstLetter = $menuitems[i].textContent && $menuitems[i].textContent[0].toLowerCase();
|
159 | if (firstLetter === charPressed) {
|
160 | $menuitems[i].focus();
|
161 | break;
|
162 | }
|
163 | }
|
164 | }
|
165 | }
|
166 | }
|
167 | handleProps() {
|
168 | if (this.props.isOpen) {
|
169 | this.addEvents();
|
170 | } else {
|
171 | this.removeEvents();
|
172 | }
|
173 | }
|
174 | getContextValue() {
|
175 | return {
|
176 | toggle: this.toggle,
|
177 | isOpen: this.props.isOpen,
|
178 | direction: this.props.direction === 'down' && this.props.dropup ? 'up' : this.props.direction,
|
179 | inNavbar: this.props.inNavbar,
|
180 | disabled: this.props.disabled,
|
181 |
|
182 |
|
183 | onMenuRef: this.handleMenuRef,
|
184 | onToggleRef: this.handleToggleRef,
|
185 | menuRole: this.props.menuRole
|
186 | };
|
187 | }
|
188 | getContainer() {
|
189 | return this.containerRef.current;
|
190 | }
|
191 | getMenu() {
|
192 | return this.menuRef.current;
|
193 | }
|
194 | getToggle() {
|
195 | return this.toggleRef.current;
|
196 | }
|
197 | getMenuCtrl() {
|
198 | if (this._$menuCtrl) return this._$menuCtrl;
|
199 | this._$menuCtrl = this.getToggle();
|
200 | return this._$menuCtrl;
|
201 | }
|
202 | getItemType() {
|
203 | if (this.props.menuRole === 'listbox') {
|
204 | return 'option';
|
205 | }
|
206 | return 'menuitem';
|
207 | }
|
208 | getMenuItems() {
|
209 |
|
210 |
|
211 |
|
212 | const menuContainer = this.getMenu() || this.getContainer();
|
213 | return [].slice.call(menuContainer.querySelectorAll(`[role="${this.getItemType()}"]`));
|
214 | }
|
215 | addEvents() {
|
216 | ['click', 'touchstart', 'keyup'].forEach(event => document.addEventListener(event, this.handleDocumentClick, true));
|
217 | }
|
218 | removeEvents() {
|
219 | ['click', 'touchstart', 'keyup'].forEach(event => document.removeEventListener(event, this.handleDocumentClick, true));
|
220 | }
|
221 | toggle(e) {
|
222 | if (this.props.disabled) {
|
223 | return e && e.preventDefault();
|
224 | }
|
225 | return this.props.toggle(e);
|
226 | }
|
227 | render() {
|
228 | const _omit = (0, _utils.omit)(this.props, ['toggle', 'disabled', 'inNavbar', 'a11y']),
|
229 | {
|
230 | className,
|
231 | cssModule,
|
232 | direction,
|
233 | isOpen,
|
234 | group,
|
235 | size,
|
236 | nav,
|
237 | setActiveFromChild,
|
238 | active,
|
239 | tag,
|
240 | menuRole
|
241 | } = _omit,
|
242 | attrs = _objectWithoutProperties(_omit, _excluded);
|
243 | const Tag = tag || (nav ? 'li' : 'div');
|
244 | let subItemIsActive = false;
|
245 | if (setActiveFromChild) {
|
246 | _react.default.Children.map(this.props.children[1].props.children, dropdownItem => {
|
247 | if (dropdownItem && dropdownItem.props.active) subItemIsActive = true;
|
248 | });
|
249 | }
|
250 | const classes = (0, _utils.mapToCssModules)((0, _classnames.default)(className, nav && active ? 'active' : false, setActiveFromChild && subItemIsActive ? 'active' : false, {
|
251 | 'btn-group': group,
|
252 | [`btn-group-${size}`]: !!size,
|
253 | dropdown: !group,
|
254 | dropup: direction === 'up',
|
255 | dropstart: direction === 'start' || direction === 'left',
|
256 | dropend: direction === 'end' || direction === 'right',
|
257 | show: isOpen,
|
258 | 'nav-item': nav
|
259 | }), cssModule);
|
260 | if (this.context.insideInputGroup) {
|
261 | return _react.default.createElement(_DropdownContext.DropdownContext.Provider, {
|
262 | value: this.getContextValue()
|
263 | }, _react.default.createElement(_reactPopper.Manager, null, _react.default.Children.map(this.props.children, child => _react.default.cloneElement(child, {
|
264 | onKeyDown: this.handleKeyDown
|
265 | }))));
|
266 | }
|
267 | return _react.default.createElement(_DropdownContext.DropdownContext.Provider, {
|
268 | value: this.getContextValue()
|
269 | }, _react.default.createElement(_reactPopper.Manager, null, _react.default.createElement(Tag, _extends({}, attrs, {
|
270 | [typeof Tag === 'string' ? 'ref' : 'innerRef']: this.containerRef,
|
271 | onKeyDown: this.handleKeyDown,
|
272 | className: classes
|
273 | }))));
|
274 | }
|
275 | }
|
276 | Dropdown.propTypes = propTypes;
|
277 | Dropdown.defaultProps = defaultProps;
|
278 | Dropdown.contextType = _InputGroupContext.InputGroupContext;
|
279 | var _default = Dropdown;
|
280 | exports.default = _default; |
\ | No newline at end of file |