diff --git a/packages/ui-elements/src/components/Menu/Menu.js b/packages/ui-elements/src/components/Menu/Menu.js
index 5ad82f5c2d..b2aba4699f 100644
--- a/packages/ui-elements/src/components/Menu/Menu.js
+++ b/packages/ui-elements/src/components/Menu/Menu.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect, useMemo, useRef, useState } from 'react';
import { css } from '@emotion/react';
import useTheme from '../../hooks/useTheme';
import { Box } from '../Box';
@@ -9,6 +9,11 @@ import { appendClassNames } from '../../lib/appendClassNames';
import { Tooltip } from '../Tooltip';
import { getMenuStyles } from './Menu.styles';
+const MOBILE_BREAKPOINT = 499;
+
+const getIsMobileViewport = () =>
+ typeof window !== 'undefined' && window.innerWidth <= MOBILE_BREAKPOINT;
+
const Menu = ({
options = [],
className = '',
@@ -43,17 +48,39 @@ const Menu = ({
useComponentOverrides('MenuWrapper');
const [isOpen, setOpen] = useState(false);
+ const [isMobile, setIsMobile] = useState(getIsMobileViewport);
+ const wrapperRef = useRef(null);
const onClick = (action, disabled) => () => {
if (!disabled) {
action();
- setOpen(!isOpen);
+ setOpen(false);
}
};
+ useEffect(() => {
+ if (typeof window === 'undefined') {
+ return undefined;
+ }
+
+ const onResize = () => {
+ setIsMobile(getIsMobileViewport());
+ };
+
+ window.addEventListener('resize', onResize);
+
+ return () => {
+ window.removeEventListener('resize', onResize);
+ };
+ }, []);
+
useEffect(() => {
const onBodyClick = (e) => {
- if (isOpen && !e.target.classList.contains('ec-menu-wrapper')) {
+ if (
+ isOpen &&
+ wrapperRef.current &&
+ !wrapperRef.current.contains(e.target)
+ ) {
setOpen(false);
}
};
@@ -65,32 +92,61 @@ const Menu = ({
};
}, [isOpen]);
+ const menuItems = options.map((option, idx) => (
+
+ ));
+
+ const triggerButton = tooltip.isToolTip ? (
+
+ {
+ e.stopPropagation();
+ setOpen((prev) => !prev);
+ }}
+ />
+
+ ) : (
+ {
+ e.stopPropagation();
+ setOpen((prev) => !prev);
+ }}
+ />
+ );
+
const optionJsx = (
<>
- {tooltip.isToolTip ? (
-
- {
- e.stopPropagation();
- setOpen((prev) => !prev);
- }}
- />
-
- ) : (
- {
- e.stopPropagation();
- setOpen((prev) => !prev);
- }}
- />
- )}
- {isOpen ? (
+ {triggerButton}
+ {isOpen && isMobile ? (
+ <>
+ setOpen(false)} />
+ e.stopPropagation()}
+ >
+ {menuItems}
+
+ >
+ ) : null}
+ {isOpen && !isMobile ? (
- {options.map((option, idx) => (
-
- ))}
+ {menuItems}
) : null}
>
);
return useWrapper ? (
) : (
- optionJsx
+ {optionJsx}
);
};
diff --git a/packages/ui-elements/src/components/Menu/Menu.styles.js b/packages/ui-elements/src/components/Menu/Menu.styles.js
index 2537aaaef5..b2b680225d 100644
--- a/packages/ui-elements/src/components/Menu/Menu.styles.js
+++ b/packages/ui-elements/src/components/Menu/Menu.styles.js
@@ -21,6 +21,28 @@ export const getMenuStyles = (theme) => {
box-shadow: ${theme.shadows[1]};
background-color: ${theme.colors.background};
`,
+
+ backdrop: css`
+ position: fixed;
+ inset: 0;
+ z-index: ${theme.zIndex?.menu || 1300};
+ background: transparent;
+ `,
+
+ sheet: css`
+ position: fixed;
+ left: 0.5rem;
+ right: 0.5rem;
+ bottom: 0.5rem;
+ display: flex;
+ flex-direction: column;
+ max-height: min(70vh, calc(100vh - 6rem));
+ overflow-y: auto;
+ z-index: ${(theme.zIndex?.menu || 1300) + 1};
+ border-radius: 0.75rem;
+ padding: 0.75rem 0;
+ background-color: ${theme.colors.background};
+ `,
};
return styles;
@@ -30,6 +52,7 @@ export const getMenuItemStyles = ({ theme, mode }) => {
const styles = {
item: css`
font-size: 14px;
+ font-family: inherit;
display: flex;
flex-direction: row;
align-items: center;
@@ -46,6 +69,28 @@ export const getMenuItemStyles = ({ theme, mode }) => {
}
`,
+ itemMobile: css`
+ font-size: 14px;
+ font-family: inherit;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-start;
+ gap: 0.5rem;
+ padding: 0.75rem 1rem;
+ white-space: nowrap;
+ color: ${theme.colors.foreground};
+ &:hover {
+ background-color: ${mode === 'light'
+ ? darken(theme.colors.background, 0.05)
+ : lighten(theme.colors.background, 2)};
+ cursor: pointer;
+ }
+ & + & {
+ border-top: 1px solid ${theme.colors.border};
+ }
+ `,
+
disabled: css`
cursor: not-allowed !important;
color: ${theme.colors.mutedForeground};
diff --git a/packages/ui-elements/src/components/Menu/MenuItem.js b/packages/ui-elements/src/components/Menu/MenuItem.js
index f0c69b4e53..4fe7f55982 100644
--- a/packages/ui-elements/src/components/Menu/MenuItem.js
+++ b/packages/ui-elements/src/components/Menu/MenuItem.js
@@ -7,7 +7,7 @@ import { appendClassNames } from '../../lib/appendClassNames';
import { getMenuItemStyles } from './Menu.styles';
import { useTheme } from '../../hooks';
-const MenuItem = ({ icon, label, action, disabled }) => {
+const MenuItem = ({ icon, label, action, disabled, isMobile = false }) => {
const { classNames, styleOverrides } = useComponentOverrides(
'MenuItem',
disabled && 'disabled'
@@ -17,12 +17,15 @@ const MenuItem = ({ icon, label, action, disabled }) => {
return (
-
+
{label}
);