Complete menu component

This commit is contained in:
Hazem Krimi
2021-04-22 01:52:40 +01:00
parent 8ca10aa66f
commit 7e7355de80
3 changed files with 114 additions and 4 deletions
+72 -3
View File
@@ -1,7 +1,76 @@
import { useEffect, useRef, useState } from 'react';
import { Wrapper } from './styles';
import { Text } from '..';
const Modal = () => {
return <Wrapper></Wrapper>;
type MenuProps = {
className?: string;
items: Array<{
icon: React.SVGProps<SVGSVGElement>;
avoid?: boolean;
label: string;
action: () => void;
}>;
component: string;
};
export default Modal;
const Menu = ({ items, component, className }: MenuProps) => {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const openMenu = () => setOpen(true);
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
setOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
(document.querySelector(`#${component}`) as HTMLElement)?.addEventListener(
'mouseenter',
openMenu
);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
(document.querySelector(
`#${component}`
) as HTMLElement)?.removeEventListener('mouseenter', openMenu);
};
// eslint-disable-next-line
}, [ref]);
return (
<Wrapper
ref={ref}
className={className}
top={
(document.querySelector(`#${component}`) as HTMLElement)?.offsetTop + 30
}
left={
(document.querySelector(`#${component}`) as HTMLElement)?.offsetLeft
}
>
{open && (
<ul>
{items.map(({ icon, label, avoid, action }) => (
// eslint-disable-next-line
<li
onClick={() => {
setOpen(false);
action();
}}
key={label}
>
<span className={`icon ${avoid ? 'avoid' : ''}`}>{icon}</span>
<Text color={avoid ? 'error' : undefined}>{label}</Text>
</li>
))}
</ul>
)}
</Wrapper>
);
};
export default Menu;
+40 -1
View File
@@ -1,3 +1,42 @@
import styled from 'styled-components';
export const Wrapper = styled.div``;
type WrapperProps = {
top: number;
left: number;
};
export const Wrapper = styled.div<WrapperProps>`
ul {
position: fixed;
top: ${({ top }) => top}px;
left: ${({ left }) => left}px;
background: ${({ theme }) => theme.colors.white.main};
display: grid;
grid-template-columns: auto;
row-gap: 0.5rem;
border-radius: 3px;
padding: 15px 30px 15px 15px;
box-shadow: 1px 1px 15px 0px rgba(50, 59, 105, 0.25);
li {
cursor: pointer;
display: flex;
flex-direction: row;
.icon {
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
svg path {
stroke: ${({ theme }) => theme.colors.black.main};
}
&.avoid svg path {
stroke: ${({ theme }) => theme.colors.error.main};
}
}
}
}
`;
+2
View File
@@ -12,6 +12,7 @@ import ContextMenu from './ContextMenu';
import Spinner from './Spinner';
import Alert from './Alert';
import CheckBox from './CheckBox';
import Menu from './Menu';
export {
Button,
@@ -25,6 +26,7 @@ export {
Search,
Avatar,
ContextMenu,
Menu,
Spinner,
Alert,
CheckBox,