UNPKG

4.65 kBJavaScriptView Raw
1import classNames from 'classnames';
2import * as React from 'react';
3import { useContext } from 'react';
4import { useDropdownMenu } from '@restart/ui/DropdownMenu';
5import useIsomorphicEffect from '@restart/hooks/useIsomorphicEffect';
6import useMergedRefs from '@restart/hooks/useMergedRefs';
7import warning from 'warning';
8import DropdownContext from './DropdownContext';
9import InputGroupContext from './InputGroupContext';
10import NavbarContext from './NavbarContext';
11import { useBootstrapPrefix } from './ThemeProvider';
12import useWrappedRefWithWarning from './useWrappedRefWithWarning';
13import { alignPropType } from './types';
14import { jsx as _jsx } from "react/jsx-runtime";
15const defaultProps = {
16 flip: true
17};
18export function getDropdownMenuPlacement(alignEnd, dropDirection, isRTL) {
19 const topStart = isRTL ? 'top-end' : 'top-start';
20 const topEnd = isRTL ? 'top-start' : 'top-end';
21 const bottomStart = isRTL ? 'bottom-end' : 'bottom-start';
22 const bottomEnd = isRTL ? 'bottom-start' : 'bottom-end';
23 const leftStart = isRTL ? 'right-start' : 'left-start';
24 const leftEnd = isRTL ? 'right-end' : 'left-end';
25 const rightStart = isRTL ? 'left-start' : 'right-start';
26 const rightEnd = isRTL ? 'left-end' : 'right-end';
27 let placement = alignEnd ? bottomEnd : bottomStart;
28 if (dropDirection === 'up') placement = alignEnd ? topEnd : topStart;else if (dropDirection === 'end') placement = alignEnd ? rightEnd : rightStart;else if (dropDirection === 'start') placement = alignEnd ? leftEnd : leftStart;
29 return placement;
30}
31const DropdownMenu = /*#__PURE__*/React.forwardRef(({
32 bsPrefix,
33 className,
34 align,
35 rootCloseEvent,
36 flip,
37 show: showProps,
38 renderOnMount,
39 // Need to define the default "as" during prop destructuring to be compatible with styled-components github.com/react-bootstrap/react-bootstrap/issues/3595
40 as: Component = 'div',
41 popperConfig,
42 variant,
43 ...props
44}, ref) => {
45 let alignEnd = false;
46 const isNavbar = useContext(NavbarContext);
47 const prefix = useBootstrapPrefix(bsPrefix, 'dropdown-menu');
48 const {
49 align: contextAlign,
50 drop,
51 isRTL
52 } = useContext(DropdownContext);
53 align = align || contextAlign;
54 const isInputGroup = useContext(InputGroupContext);
55 const alignClasses = [];
56
57 if (align) {
58 if (typeof align === 'object') {
59 const keys = Object.keys(align);
60 process.env.NODE_ENV !== "production" ? warning(keys.length === 1, 'There should only be 1 breakpoint when passing an object to `align`') : void 0;
61
62 if (keys.length) {
63 const brkPoint = keys[0];
64 const direction = align[brkPoint]; // .dropdown-menu-end is required for responsively aligning
65 // left in addition to align left classes.
66
67 alignEnd = direction === 'start';
68 alignClasses.push(`${prefix}-${brkPoint}-${direction}`);
69 }
70 } else if (align === 'end') {
71 alignEnd = true;
72 }
73 }
74
75 const placement = getDropdownMenuPlacement(alignEnd, drop, isRTL);
76 const [menuProps, {
77 hasShown,
78 popper,
79 show,
80 toggle
81 }] = useDropdownMenu({
82 flip,
83 rootCloseEvent,
84 show: showProps,
85 usePopper: !isNavbar && alignClasses.length === 0,
86 offset: [0, 2],
87 popperConfig,
88 placement
89 });
90 menuProps.ref = useMergedRefs(useWrappedRefWithWarning(ref, 'DropdownMenu'), menuProps.ref);
91 useIsomorphicEffect(() => {
92 // Popper's initial position for the menu is incorrect when
93 // renderOnMount=true. Need to call update() to correct it.
94 if (show) popper == null ? void 0 : popper.update();
95 }, [show]);
96 if (!hasShown && !renderOnMount && !isInputGroup) return null; // For custom components provide additional, non-DOM, props;
97
98 if (typeof Component !== 'string') {
99 menuProps.show = show;
100
101 menuProps.close = () => toggle == null ? void 0 : toggle(false);
102
103 menuProps.align = align;
104 }
105
106 let style = props.style;
107
108 if (popper != null && popper.placement) {
109 // we don't need the default popper style,
110 // menus are display: none when not shown.
111 style = { ...props.style,
112 ...menuProps.style
113 };
114 props['x-placement'] = popper.placement;
115 }
116
117 return /*#__PURE__*/_jsx(Component, { ...props,
118 ...menuProps,
119 style: style // Bootstrap css requires this data attrib to style responsive menus.
120 ,
121 ...((alignClasses.length || isNavbar) && {
122 'data-bs-popper': 'static'
123 }),
124 className: classNames(className, prefix, show && 'show', alignEnd && `${prefix}-end`, variant && `${prefix}-${variant}`, ...alignClasses)
125 });
126});
127DropdownMenu.displayName = 'DropdownMenu';
128DropdownMenu.defaultProps = defaultProps;
129export default DropdownMenu;
\No newline at end of file