refactor context menu data structure

This commit is contained in:
maxichrome
2022-05-28 05:37:52 -05:00
parent f3733b486b
commit fe2cd9be6b
3 changed files with 57 additions and 58 deletions

View File

@@ -29,7 +29,7 @@ export const MenuContext = React.createContext<MenuContextData & MenuContextActi
export const useMenu = () => React.useContext(MenuContext);
export const WithContextMenu: React.FC<{
menu: ContextMenuProps['sections'];
menu: ContextMenuProps['items'];
children: React.ReactElement<{ onContextMenu: MouseEventHandler }>;
}> = (props) => {
const { menu: sections = [], children } = props;
@@ -45,7 +45,7 @@ export const WithContextMenu: React.FC<{
e.stopPropagation();
menu.showMenu(
<ContextMenu sections={sections} />,
<ContextMenu items={sections} />,
{ x: e.clientX, y: e.clientY },
e.target as HTMLElement
);

View File

@@ -16,30 +16,26 @@ const Template: ComponentStory<typeof ContextMenu> = (args) => <ContextMenu {...
export const Default = Template.bind({});
Default.args = {
sections: [
{
items: [
{
label: 'New Item',
icon: Plus,
onClick: () => {}
}
]
},
{
items: [
{
label: 'View Info',
icon: FileText,
onClick: () => {}
},
{
label: 'Delete',
icon: Trash,
danger: true,
onClick: () => {}
}
]
}
items: [
[
{
label: 'New Item',
icon: Plus,
onClick: () => {}
}
],
[
{
label: 'View Info',
icon: FileText,
onClick: () => {}
},
{
label: 'Delete',
icon: Trash,
danger: true,
onClick: () => {}
}
]
]
};

View File

@@ -11,56 +11,59 @@ export interface ContextMenuItem {
}
export interface ContextMenuProps {
sections?: {
heading?: string;
items: ContextMenuItem[];
}[];
items?: (ContextMenuItem | string)[][];
className?: string;
}
export const ContextMenu: React.FC<ContextMenuProps> = (props) => {
const { sections = [], className, ...rest } = props;
const { items = [], className, ...rest } = props;
return (
<div
role="menu"
className={clsx(
'shadow-2xl min-w-[15rem] shadow-gray-300 dark:shadow-gray-600 flex flex-col select-none cursor-default bg-gray-50 text-gray-800 border-gray-200 dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500 text-left text-sm rounded gap-1.5 border py-1.5',
'shadow-2xl min-w-[15rem] shadow-gray-300 dark:shadow-gray-750 flex flex-col select-none cursor-default bg-gray-50 text-gray-800 border-gray-200 dark:bg-gray-650 dark:text-gray-100 dark:border-gray-550 text-left text-sm rounded gap-1.5 border py-1.5',
className
)}
{...rest}
>
{sections.map((sec, i) => (
{items.map((sec, i) => (
<>
{i !== 0 && (
<hr className="border-0 border-b border-b-gray-300 dark:border-b-gray-500 mx-2" />
<hr className="border-0 border-b border-b-gray-300 dark:border-b-gray-550 mx-2" />
)}
<section key={i} className="flex items-stretch flex-col gap-0.5">
{sec.heading && (
<span className="text-xs ml-2 mt-1 uppercase text-gray-400">{sec.heading}</span>
)}
<ul>
{sec.items.map(({ icon: ItemIcon = Question, ...item }) => (
<li key={item.label} className="flex">
<button
style={{
font: 'inherit',
textAlign: 'inherit'
}}
className={clsx('group cursor-default flex-1 px-1.5 py-0 group-first:pt-1.5', {
'text-red-600 dark:text-red-400': item.danger
})}
onClick={item.onClick}
>
<div className="px-1.5 py-[0.4em] group-focus-visible:bg-gray-150 group-hover:bg-gray-150 dark:group-focus-visible:bg-gray-500 dark:group-hover:bg-gray-500 flex flex-row gap-2.5 items-center rounded-sm">
{<ItemIcon size={18} />}
<span className="leading-snug text-[14px] font-normal">{item.label}</span>
</div>
</button>
</li>
))}
{sec.map((item) => {
if (typeof item === 'string')
return <span className="text-xs ml-2 mt-1 uppercase text-gray-400">{item}</span>;
const { icon: ItemIcon = Question } = item;
return (
<li key={item.label} className="flex">
<button
style={{
font: 'inherit',
textAlign: 'inherit'
}}
className={clsx(
'group cursor-default flex-1 px-1.5 py-0 group-first:pt-1.5',
{
'text-red-600 dark:text-red-400': item.danger
}
)}
onClick={item.onClick}
>
<div className="px-1.5 py-[0.4em] group-focus-visible:bg-gray-150 group-hover:bg-gray-150 dark:group-focus-visible:bg-gray-550 dark:group-hover:bg-gray-550 flex flex-row gap-2.5 items-center rounded-sm">
{<ItemIcon size={18} />}
<span className="leading-snug text-[14px] font-normal">{item.label}</span>
</div>
</button>
</li>
);
})}
</ul>
</section>
</>