No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

272 líneas
8.7 KiB

  1. "use strict";
  2. var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
  3. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  4. exports.__esModule = true;
  5. exports["default"] = void 0;
  6. var _matches = _interopRequireDefault(require("dom-helpers/matches"));
  7. var _querySelectorAll = _interopRequireDefault(require("dom-helpers/querySelectorAll"));
  8. var _react = _interopRequireWildcard(require("react"));
  9. var _propTypes = _interopRequireDefault(require("prop-types"));
  10. var _uncontrollable = require("uncontrollable");
  11. var _usePrevious = _interopRequireDefault(require("@restart/hooks/usePrevious"));
  12. var _useCallbackRef2 = _interopRequireDefault(require("@restart/hooks/useCallbackRef"));
  13. var _useForceUpdate = _interopRequireDefault(require("@restart/hooks/useForceUpdate"));
  14. var _useEventCallback = _interopRequireDefault(require("@restart/hooks/useEventCallback"));
  15. var _DropdownContext = _interopRequireDefault(require("./DropdownContext"));
  16. var _DropdownMenu = _interopRequireDefault(require("./DropdownMenu"));
  17. var _DropdownToggle = _interopRequireDefault(require("./DropdownToggle"));
  18. var propTypes = {
  19. /**
  20. * A render prop that returns the root dropdown element. The `props`
  21. * argument should spread through to an element containing _both_ the
  22. * menu and toggle in order to handle keyboard events for focus management.
  23. *
  24. * @type {Function ({
  25. * props: {
  26. * onKeyDown: (SyntheticEvent) => void,
  27. * },
  28. * }) => React.Element}
  29. */
  30. children: _propTypes["default"].func.isRequired,
  31. /**
  32. * Determines the direction and location of the Menu in relation to it's Toggle.
  33. */
  34. drop: _propTypes["default"].oneOf(['up', 'left', 'right', 'down']),
  35. /**
  36. * Controls the focus behavior for when the Dropdown is opened. Set to
  37. * `true` to always focus the first menu item, `keyboard` to focus only when
  38. * navigating via the keyboard, or `false` to disable completely
  39. *
  40. * The Default behavior is `false` **unless** the Menu has a `role="menu"`
  41. * where it will default to `keyboard` to match the recommended [ARIA Authoring practices](https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton).
  42. */
  43. focusFirstItemOnShow: _propTypes["default"].oneOf([false, true, 'keyboard']),
  44. /**
  45. * A css slector string that will return __focusable__ menu items.
  46. * Selectors should be relative to the menu component:
  47. * e.g. ` > li:not('.disabled')`
  48. */
  49. itemSelector: _propTypes["default"].string.isRequired,
  50. /**
  51. * Align the menu to the 'end' side of the placement side of the Dropdown toggle. The default placement is `top-start` or `bottom-start`.
  52. */
  53. alignEnd: _propTypes["default"].bool,
  54. /**
  55. * Whether or not the Dropdown is visible.
  56. *
  57. * @controllable onToggle
  58. */
  59. show: _propTypes["default"].bool,
  60. /**
  61. * Sets the initial show position of the Dropdown.
  62. */
  63. defaultShow: _propTypes["default"].bool,
  64. /**
  65. * A callback fired when the Dropdown wishes to change visibility. Called with the requested
  66. * `show` value, the DOM event, and the source that fired it: `'click'`,`'keydown'`,`'rootClose'`, or `'select'`.
  67. *
  68. * ```js
  69. * function(
  70. * isOpen: boolean,
  71. * event: SyntheticEvent,
  72. * ): void
  73. * ```
  74. *
  75. * @controllable show
  76. */
  77. onToggle: _propTypes["default"].func
  78. };
  79. var defaultProps = {
  80. itemSelector: '* > *'
  81. };
  82. /**
  83. * `Dropdown` is set of structural components for building, accessible dropdown menus with close-on-click,
  84. * keyboard navigation, and correct focus handling. As with all the react-overlay's
  85. * components its BYOS (bring your own styles). Dropdown is primarily
  86. * built from three base components, you should compose to build your Dropdowns.
  87. *
  88. * - `Dropdown`, which wraps the menu and toggle, and handles keyboard navigation
  89. * - `Dropdown.Toggle` generally a button that triggers the menu opening
  90. * - `Dropdown.Menu` The overlaid, menu, positioned to the toggle with PopperJs
  91. */
  92. function Dropdown(_ref) {
  93. var drop = _ref.drop,
  94. alignEnd = _ref.alignEnd,
  95. defaultShow = _ref.defaultShow,
  96. rawShow = _ref.show,
  97. rawOnToggle = _ref.onToggle,
  98. itemSelector = _ref.itemSelector,
  99. focusFirstItemOnShow = _ref.focusFirstItemOnShow,
  100. children = _ref.children;
  101. var forceUpdate = (0, _useForceUpdate["default"])();
  102. var _useUncontrolled = (0, _uncontrollable.useUncontrolled)({
  103. defaultShow: defaultShow,
  104. show: rawShow,
  105. onToggle: rawOnToggle
  106. }, {
  107. show: 'onToggle'
  108. }),
  109. show = _useUncontrolled.show,
  110. onToggle = _useUncontrolled.onToggle;
  111. var _useCallbackRef = (0, _useCallbackRef2["default"])(),
  112. toggleElement = _useCallbackRef[0],
  113. setToggle = _useCallbackRef[1]; // We use normal refs instead of useCallbackRef in order to populate the
  114. // the value as quickly as possible, otherwise the effect to focus the element
  115. // may run before the state value is set
  116. var menuRef = (0, _react.useRef)();
  117. var menuElement = menuRef.current;
  118. var setMenu = (0, _react.useCallback)(function (ref) {
  119. menuRef.current = ref; // ensure that a menu set triggers an update for consumers
  120. forceUpdate();
  121. }, [forceUpdate]);
  122. var lastShow = (0, _usePrevious["default"])(show);
  123. var lastSourceEvent = (0, _react.useRef)(null);
  124. var focusInDropdown = (0, _react.useRef)(false);
  125. var toggle = (0, _react.useCallback)(function (event) {
  126. onToggle(!show, event);
  127. }, [onToggle, show]);
  128. var context = (0, _react.useMemo)(function () {
  129. return {
  130. toggle: toggle,
  131. drop: drop,
  132. show: show,
  133. alignEnd: alignEnd,
  134. menuElement: menuElement,
  135. toggleElement: toggleElement,
  136. setMenu: setMenu,
  137. setToggle: setToggle
  138. };
  139. }, [toggle, drop, show, alignEnd, menuElement, toggleElement, setMenu, setToggle]);
  140. if (menuElement && lastShow && !show) {
  141. focusInDropdown.current = menuElement.contains(document.activeElement);
  142. }
  143. var focusToggle = (0, _useEventCallback["default"])(function () {
  144. if (toggleElement && toggleElement.focus) {
  145. toggleElement.focus();
  146. }
  147. });
  148. var maybeFocusFirst = (0, _useEventCallback["default"])(function () {
  149. var type = lastSourceEvent.current;
  150. var focusType = focusFirstItemOnShow;
  151. if (focusType == null) {
  152. focusType = menuRef.current && (0, _matches["default"])(menuRef.current, '[role=menu]') ? 'keyboard' : false;
  153. }
  154. if (focusType === false || focusType === 'keyboard' && !/^key.+$/.test(type)) {
  155. return;
  156. }
  157. var first = (0, _querySelectorAll["default"])(menuRef.current, itemSelector)[0];
  158. if (first && first.focus) first.focus();
  159. });
  160. (0, _react.useEffect)(function () {
  161. if (show) maybeFocusFirst();else if (focusInDropdown.current) {
  162. focusInDropdown.current = false;
  163. focusToggle();
  164. } // only `show` should be changing
  165. }, [show, focusInDropdown, focusToggle, maybeFocusFirst]);
  166. (0, _react.useEffect)(function () {
  167. lastSourceEvent.current = null;
  168. });
  169. var getNextFocusedChild = function getNextFocusedChild(current, offset) {
  170. if (!menuRef.current) return null;
  171. var items = (0, _querySelectorAll["default"])(menuRef.current, itemSelector);
  172. var index = items.indexOf(current) + offset;
  173. index = Math.max(0, Math.min(index, items.length));
  174. return items[index];
  175. };
  176. var handleKeyDown = function handleKeyDown(event) {
  177. var key = event.key,
  178. target = event.target; // Second only to https://github.com/twbs/bootstrap/blob/8cfbf6933b8a0146ac3fbc369f19e520bd1ebdac/js/src/dropdown.js#L400
  179. // in inscrutability
  180. var isInput = /input|textarea/i.test(target.tagName);
  181. if (isInput && (key === ' ' || key !== 'Escape' && menuRef.current && menuRef.current.contains(target))) {
  182. return;
  183. }
  184. lastSourceEvent.current = event.type;
  185. switch (key) {
  186. case 'ArrowUp':
  187. {
  188. var next = getNextFocusedChild(target, -1);
  189. if (next && next.focus) next.focus();
  190. event.preventDefault();
  191. return;
  192. }
  193. case 'ArrowDown':
  194. event.preventDefault();
  195. if (!show) {
  196. toggle(event);
  197. } else {
  198. var _next = getNextFocusedChild(target, 1);
  199. if (_next && _next.focus) _next.focus();
  200. }
  201. return;
  202. case 'Escape':
  203. case 'Tab':
  204. onToggle(false, event);
  205. break;
  206. default:
  207. }
  208. };
  209. return _react["default"].createElement(_DropdownContext["default"].Provider, {
  210. value: context
  211. }, children({
  212. props: {
  213. onKeyDown: handleKeyDown
  214. }
  215. }));
  216. }
  217. Dropdown.displayName = 'ReactOverlaysDropdown';
  218. Dropdown.propTypes = propTypes;
  219. Dropdown.defaultProps = defaultProps;
  220. Dropdown.Menu = _DropdownMenu["default"];
  221. Dropdown.Toggle = _DropdownToggle["default"];
  222. var _default = Dropdown;
  223. exports["default"] = _default;
  224. module.exports = exports.default;