Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Dropdown.js 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. 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; }
  2. function _extends() { _extends = Object.assign || 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); }
  3. function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
  4. import matches from 'dom-helpers/query/matches';
  5. import qsa from 'dom-helpers/query/querySelectorAll';
  6. import React from 'react';
  7. import ReactDOM from 'react-dom';
  8. import PropTypes from 'prop-types';
  9. import uncontrollable from 'uncontrollable';
  10. import * as Popper from 'react-popper';
  11. import DropdownContext from './DropdownContext';
  12. import DropdownMenu from './DropdownMenu';
  13. import DropdownToggle from './DropdownToggle';
  14. var propTypes = {
  15. /**
  16. * A render prop that returns the root dropdown element. The `props`
  17. * argument should spread through to an element containing _both_ the
  18. * menu and toggle in order to handle keyboard events for focus management.
  19. *
  20. * @type {Function ({
  21. * props: {
  22. * onKeyDown: (SyntheticEvent) => void,
  23. * },
  24. * }) => React.Element}
  25. */
  26. children: PropTypes.func.isRequired,
  27. /**
  28. * Determines the direction and location of the Menu in relation to it's Toggle.
  29. */
  30. drop: PropTypes.oneOf(['up', 'left', 'right', 'down']),
  31. /**
  32. * Controls the focus behavior for when the Dropdown is opened. Set to
  33. * `true` to always focus the first menu item, `keyboard` to focus only when
  34. * navigating via the keyboard, or `false` to disable completely
  35. *
  36. * The Default behavior is `false` **unless** the Menu has a `role="menu"`
  37. * where it will default to `keyboard` to match the recommended [ARIA Authoring practices](https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton).
  38. */
  39. focusFirstItemOnShow: PropTypes.oneOf([false, true, 'keyboard']),
  40. /**
  41. * A css slector string that will return __focusable__ menu items.
  42. * Selectors should be relative to the menu component:
  43. * e.g. ` > li:not('.disabled')`
  44. */
  45. itemSelector: PropTypes.string.isRequired,
  46. /**
  47. * Align the menu to the 'end' side of the placement side of the Dropdown toggle. The default placement is `top-start` or `bottom-start`.
  48. */
  49. alignEnd: PropTypes.bool,
  50. /**
  51. * Whether or not the Dropdown is visible.
  52. *
  53. * @controllable onToggle
  54. */
  55. show: PropTypes.bool,
  56. /**
  57. * A callback fired when the Dropdown wishes to change visibility. Called with the requested
  58. * `show` value, the DOM event, and the source that fired it: `'click'`,`'keydown'`,`'rootClose'`, or `'select'`.
  59. *
  60. * ```js
  61. * function(
  62. * isOpen: boolean,
  63. * event: SyntheticEvent,
  64. * ): void
  65. * ```
  66. *
  67. * @controllable show
  68. */
  69. onToggle: PropTypes.func
  70. };
  71. var defaultProps = {
  72. itemSelector: '* > *'
  73. };
  74. /**
  75. * `Dropdown` is set of structural components for building, accessible dropdown menus with close-on-click,
  76. * keyboard navigation, and correct focus handling. As with all the react-overlay's
  77. * components its BYOS (bring your own styles). Dropdown is primarily
  78. * built from three base components, you should compose to build your Dropdowns.
  79. *
  80. * - `Dropdown`, which wraps the menu and toggle, and handles keyboard navigation
  81. * - `Dropdown.Toggle` generally a button that triggers the menu opening
  82. * - `Dropdown.Menu` The overlaid, menu, positioned to the toggle with PopperJs
  83. */
  84. var Dropdown =
  85. /*#__PURE__*/
  86. function (_React$Component) {
  87. _inheritsLoose(Dropdown, _React$Component);
  88. Dropdown.getDerivedStateFromProps = function getDerivedStateFromProps(_ref, prevState) {
  89. var drop = _ref.drop,
  90. alignEnd = _ref.alignEnd,
  91. show = _ref.show;
  92. var lastShow = prevState.context.show;
  93. return {
  94. lastShow: lastShow,
  95. context: _extends({}, prevState.context, {
  96. drop: drop,
  97. show: show,
  98. alignEnd: alignEnd
  99. })
  100. };
  101. };
  102. function Dropdown(props, context) {
  103. var _this;
  104. _this = _React$Component.call(this, props, context) || this;
  105. _this.handleClick = function (event) {
  106. _this.toggleOpen(event);
  107. };
  108. _this.handleKeyDown = function (event) {
  109. var key = event.key,
  110. target = event.target; // Second only to https://github.com/twbs/bootstrap/blob/8cfbf6933b8a0146ac3fbc369f19e520bd1ebdac/js/src/dropdown.js#L400
  111. // in inscrutability
  112. var isInput = /input|textarea/i.test(target.tagName);
  113. if (isInput && (key === ' ' || key !== 'Escape' && _this.menu.contains(target))) {
  114. return;
  115. }
  116. _this._lastSourceEvent = event.type;
  117. switch (key) {
  118. case 'ArrowUp':
  119. {
  120. var next = _this.getNextFocusedChild(target, -1);
  121. if (next && next.focus) next.focus();
  122. event.preventDefault();
  123. return;
  124. }
  125. case 'ArrowDown':
  126. event.preventDefault();
  127. if (!_this.props.show) {
  128. _this.toggleOpen(event);
  129. } else {
  130. var _next = _this.getNextFocusedChild(target, 1);
  131. if (_next && _next.focus) _next.focus();
  132. }
  133. return;
  134. case 'Escape':
  135. case 'Tab':
  136. _this.props.onToggle(false, event);
  137. break;
  138. default:
  139. }
  140. };
  141. _this._focusInDropdown = false;
  142. _this.menu = null;
  143. _this.state = {
  144. context: {
  145. close: _this.handleClose,
  146. toggle: _this.handleClick,
  147. menuRef: function menuRef(r) {
  148. _this.menu = r;
  149. },
  150. toggleRef: function toggleRef(r) {
  151. var toggleNode = r && ReactDOM.findDOMNode(r);
  152. _this.setState(function (_ref2) {
  153. var context = _ref2.context;
  154. return {
  155. context: _extends({}, context, {
  156. toggleNode: toggleNode
  157. })
  158. };
  159. });
  160. }
  161. }
  162. };
  163. return _this;
  164. }
  165. var _proto = Dropdown.prototype;
  166. _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
  167. var show = this.props.show;
  168. var prevOpen = prevProps.show;
  169. if (show && !prevOpen) {
  170. this.maybeFocusFirst();
  171. }
  172. this._lastSourceEvent = null;
  173. if (!show && prevOpen) {
  174. // if focus hasn't already moved from the menu let's return it
  175. // to the toggle
  176. if (this._focusInDropdown) {
  177. this._focusInDropdown = false;
  178. this.focus();
  179. }
  180. }
  181. };
  182. _proto.getNextFocusedChild = function getNextFocusedChild(current, offset) {
  183. if (!this.menu) return null;
  184. var itemSelector = this.props.itemSelector;
  185. var items = qsa(this.menu, itemSelector);
  186. var index = items.indexOf(current) + offset;
  187. index = Math.max(0, Math.min(index, items.length));
  188. return items[index];
  189. };
  190. _proto.hasMenuRole = function hasMenuRole() {
  191. return this.menu && matches(this.menu, '[role=menu]');
  192. };
  193. _proto.focus = function focus() {
  194. var toggleNode = this.state.context.toggleNode;
  195. if (toggleNode && toggleNode.focus) {
  196. toggleNode.focus();
  197. }
  198. };
  199. _proto.maybeFocusFirst = function maybeFocusFirst() {
  200. var type = this._lastSourceEvent;
  201. var focusFirstItemOnShow = this.props.focusFirstItemOnShow;
  202. if (focusFirstItemOnShow == null) {
  203. focusFirstItemOnShow = this.hasMenuRole() ? 'keyboard' : false;
  204. }
  205. if (focusFirstItemOnShow === false || focusFirstItemOnShow === 'keyboard' && !/^key.+$/.test(type)) {
  206. return;
  207. }
  208. var itemSelector = this.props.itemSelector;
  209. var first = qsa(this.menu, itemSelector)[0];
  210. if (first && first.focus) first.focus();
  211. };
  212. _proto.toggleOpen = function toggleOpen(event) {
  213. var show = !this.props.show;
  214. this.props.onToggle(show, event);
  215. };
  216. _proto.render = function render() {
  217. var _this$props = this.props,
  218. children = _this$props.children,
  219. props = _objectWithoutPropertiesLoose(_this$props, ["children"]);
  220. delete props.onToggle;
  221. if (this.menu && this.state.lastShow && !this.props.show) {
  222. this._focusInDropdown = this.menu.contains(document.activeElement);
  223. }
  224. return React.createElement(DropdownContext.Provider, {
  225. value: this.state.context
  226. }, React.createElement(Popper.Manager, null, children({
  227. props: {
  228. onKeyDown: this.handleKeyDown
  229. }
  230. })));
  231. };
  232. return Dropdown;
  233. }(React.Component);
  234. Dropdown.displayName = 'ReactOverlaysDropdown';
  235. Dropdown.propTypes = propTypes;
  236. Dropdown.defaultProps = defaultProps;
  237. var UncontrolledDropdown = uncontrollable(Dropdown, {
  238. show: 'onToggle'
  239. });
  240. UncontrolledDropdown.Menu = DropdownMenu;
  241. UncontrolledDropdown.Toggle = DropdownToggle;
  242. export default UncontrolledDropdown;