UNPKG

6.37 kBJavaScriptView Raw
1import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2import _extends from "@babel/runtime/helpers/esm/extends";
3import PropTypes from 'prop-types';
4import React, { useContext, useRef } from 'react';
5import useCallbackRef from '@restart/hooks/useCallbackRef';
6import DropdownContext from './DropdownContext';
7import usePopper from './usePopper';
8import useRootClose from './useRootClose';
9import mergeOptionsWithPopperConfig from './mergeOptionsWithPopperConfig';
10
11var noop = function noop() {};
12/**
13 * @memberOf Dropdown
14 * @param {object} options
15 * @param {boolean} options.flip Automatically adjust the menu `drop` position based on viewport edge detection
16 * @param {[number, number]} options.offset Define an offset distance between the Menu and the Toggle
17 * @param {boolean} options.show Display the menu manually, ignored in the context of a `Dropdown`
18 * @param {boolean} options.usePopper opt in/out of using PopperJS to position menus. When disabled you must position it yourself.
19 * @param {string} options.rootCloseEvent The pointer event to listen for when determining "clicks outside" the menu for triggering a close.
20 * @param {object} options.popperConfig Options passed to the [`usePopper`](/api/usePopper) hook.
21 */
22
23
24export function useDropdownMenu(options) {
25 if (options === void 0) {
26 options = {};
27 }
28
29 var context = useContext(DropdownContext);
30
31 var _useCallbackRef = useCallbackRef(),
32 arrowElement = _useCallbackRef[0],
33 attachArrowRef = _useCallbackRef[1];
34
35 var hasShownRef = useRef(false);
36 var _options = options,
37 flip = _options.flip,
38 offset = _options.offset,
39 rootCloseEvent = _options.rootCloseEvent,
40 _options$fixed = _options.fixed,
41 fixed = _options$fixed === void 0 ? false : _options$fixed,
42 _options$popperConfig = _options.popperConfig,
43 popperConfig = _options$popperConfig === void 0 ? {} : _options$popperConfig,
44 _options$usePopper = _options.usePopper,
45 shouldUsePopper = _options$usePopper === void 0 ? !!context : _options$usePopper;
46 var show = (context == null ? void 0 : context.show) == null ? !!options.show : context.show;
47 var alignEnd = (context == null ? void 0 : context.alignEnd) == null ? options.alignEnd : context.alignEnd;
48
49 if (show && !hasShownRef.current) {
50 hasShownRef.current = true;
51 }
52
53 var handleClose = function handleClose(e) {
54 context == null ? void 0 : context.toggle(false, e);
55 };
56
57 var _ref = context || {},
58 drop = _ref.drop,
59 setMenu = _ref.setMenu,
60 menuElement = _ref.menuElement,
61 toggleElement = _ref.toggleElement;
62
63 var placement = alignEnd ? 'bottom-end' : 'bottom-start';
64 if (drop === 'up') placement = alignEnd ? 'top-end' : 'top-start';else if (drop === 'right') placement = alignEnd ? 'right-end' : 'right-start';else if (drop === 'left') placement = alignEnd ? 'left-end' : 'left-start';
65 var popper = usePopper(toggleElement, menuElement, mergeOptionsWithPopperConfig({
66 placement: placement,
67 enabled: !!(shouldUsePopper && show),
68 enableEvents: show,
69 offset: offset,
70 flip: flip,
71 fixed: fixed,
72 arrowElement: arrowElement,
73 popperConfig: popperConfig
74 }));
75
76 var menuProps = _extends({
77 ref: setMenu || noop,
78 'aria-labelledby': toggleElement == null ? void 0 : toggleElement.id
79 }, popper.attributes.popper, {
80 style: popper.styles.popper
81 });
82
83 var metadata = {
84 show: show,
85 alignEnd: alignEnd,
86 hasShown: hasShownRef.current,
87 toggle: context == null ? void 0 : context.toggle,
88 popper: shouldUsePopper ? popper : null,
89 arrowProps: shouldUsePopper ? _extends({
90 ref: attachArrowRef
91 }, popper.attributes.arrow, {
92 style: popper.styles.arrow
93 }) : {}
94 };
95 useRootClose(menuElement, handleClose, {
96 clickTrigger: rootCloseEvent,
97 disabled: !show
98 });
99 return [menuProps, metadata];
100}
101var propTypes = {
102 /**
103 * A render prop that returns a Menu element. The `props`
104 * argument should spread through to **a component that can accept a ref**.
105 *
106 * @type {Function ({
107 * show: boolean,
108 * alignEnd: boolean,
109 * close: (?SyntheticEvent) => void,
110 * placement: Placement,
111 * update: () => void,
112 * forceUpdate: () => void,
113 * props: {
114 * ref: (?HTMLElement) => void,
115 * style: { [string]: string | number },
116 * aria-labelledby: ?string
117 * },
118 * arrowProps: {
119 * ref: (?HTMLElement) => void,
120 * style: { [string]: string | number },
121 * },
122 * }) => React.Element}
123 */
124 children: PropTypes.func.isRequired,
125
126 /**
127 * Controls the visible state of the menu, generally this is
128 * provided by the parent `Dropdown` component,
129 * but may also be specified as a prop directly.
130 */
131 show: PropTypes.bool,
132
133 /**
134 * Aligns the dropdown menu to the 'end' of it's placement position.
135 * Generally this is provided by the parent `Dropdown` component,
136 * but may also be specified as a prop directly.
137 */
138 alignEnd: PropTypes.bool,
139
140 /**
141 * Enables the Popper.js `flip` modifier, allowing the Dropdown to
142 * automatically adjust it's placement in case of overlap with the viewport or toggle.
143 * Refer to the [flip docs](https://popper.js.org/popper-documentation.html#modifiers..flip.enabled) for more info
144 */
145 flip: PropTypes.bool,
146 usePopper: PropTypes.oneOf([true, false]),
147
148 /**
149 * A set of popper options and props passed directly to react-popper's Popper component.
150 */
151 popperConfig: PropTypes.object,
152
153 /**
154 * Override the default event used by RootCloseWrapper.
155 */
156 rootCloseEvent: PropTypes.string
157};
158var defaultProps = {
159 usePopper: true
160};
161
162/**
163 * Also exported as `<Dropdown.Menu>` from `Dropdown`.
164 *
165 * @displayName DropdownMenu
166 * @memberOf Dropdown
167 */
168function DropdownMenu(_ref2) {
169 var children = _ref2.children,
170 options = _objectWithoutPropertiesLoose(_ref2, ["children"]);
171
172 var _useDropdownMenu = useDropdownMenu(options),
173 props = _useDropdownMenu[0],
174 meta = _useDropdownMenu[1];
175
176 return /*#__PURE__*/React.createElement(React.Fragment, null, meta.hasShown ? children(props, meta) : null);
177}
178
179DropdownMenu.displayName = 'ReactOverlaysDropdownMenu';
180DropdownMenu.propTypes = propTypes;
181DropdownMenu.defaultProps = defaultProps;
182/** @component */
183
184export default DropdownMenu;
\No newline at end of file