mirror of
https://github.com/hazemKrimi/crimson-quirks-ui.git
synced 2026-05-02 02:30:29 +00:00
Complete menu component
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user