-
Notifications
You must be signed in to change notification settings - Fork 0
Menu
Menu is a component with the name of the app, isDev badge and buttons with different purposes.
It is rendered in Header component like this:
export function Header() {
return (
<TopBar>
<Menu></Menu>
<div className="header__right-side">
<NotificationBell></NotificationBell>
<Avatar></Avatar>
</div>
</TopBar>
);
}
Menu buttons are handled by a special context provider called MenuProvider. This way, the application can have some initial default buttons, or it can also add buttons based on the current page/component etc.
Use useMenu hook to get access to the functions which change the menu state. Currently, there are these functions that can be utilised:
menu: Menu;
setMenu: React.Dispatch<React.SetStateAction<Menu>>;
addRootMenuItem: (item: RootMenuItem) => void;
deleteRootMenuItem: (id: string) => RootMenuItem | undefined;
retrieveIdByTitle: (name: string) => string | undefined;
addMenuItemIntoSection: (
rootMenuItemId: string,
sectionId: string,
newItem: MenuItem
) => void;
A menu is a tree-like structure in its nature. The roots are so-called RootMenuItem objects. They can be added/deleted using addRootMenuItem and deleteRootMenuItem functions. It is worth noting that RootMenuItem is just a ordinary MenuItem with a order priority:
export type Priority = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
export type RootMenuItem = MenuItem & { priority: Priority };
The highest priority is 1. Root menu items are ordered based on priority. As for MenuItem, it is a general object which has id, title and task, and may have an icon. The most important is task - this tells what the item is: if it is an action, such as a button with a click handler or a dropdown containing other menu items.
export type MenuItem = {
id: string;
title: string;
icon?: MenuIcon;
task: Dropdown | Action;
};
The Action is quite straigh-forward:
/**
* The action can be either:
* - `direct`: the action is executed immediately.
* - `secondary`: the action triggers an intermediate step,
* such as opening a file explorer or another dialog, before
* the actual action runs.
*/
export type ActionType = "direct" | "secondary";
export type Action = {
action: () => void;
type: ActionType;
};
As for Dropdown, it is actually an array of Section. The Section object is a tool which can contain, recursively, other MenuItem. It can also have a label:
export type Section = {
id: string;
title?: string;
items: MenuItem[];
visible?: () => boolean;
};
This way, app menu can be made complex and structured based on app's needs.
Add page/component-specific root menu item:
const clearViewerItem: MenuItem = {
id: "clear-viewer",
title: t("menu.pageSpecific.viewer.Clear viewer"),
icon: { icon: BroomIcon, position: "left" },
task: {
action: () => {
clearViewer();
},
type: "direct",
},
};
const section: Section = {
id: "general-edit",
items: [clearViewerItem],
};
const edit: RootMenuItem = {
id: "edit",
title: "Edit",
task: [section],
priority: 3,
};
useEffect(() => {
addRootMenuItem(edit);
return () => {
deleteRootMenuItem(edit.id);
};
}, []);
Notice that the useEffect clean-up function deletes the root menu item after the page is unmounted.
Add MenuItem into Section:
useEffect(() => {
addMenuItemIntoSection("file", "general-file", {
id: "open-file-in-viewer",
title: "Open file in viewer",
icon: { icon: IconFolderOpen, position: "left" },
task: {
action: () => {
loadAndHandleFile({ regime: "toView" }, actions);
},
type: "secondary",
},
});
}, []);
In this example, the button will remain in the menu for the lifetime of MenuProvider - which means probably for the app lifetime.