saleor-dashboard/src/utils/menu/menu.ts

179 lines
5 KiB
TypeScript
Raw Normal View History

interface IBaseMenuItem<TMenuData = {}, TValue = string> {
2019-06-19 14:40:52 +00:00
label: React.ReactNode;
value?: TValue;
2019-06-19 14:40:52 +00:00
data: TMenuData | null;
}
export type IFlatMenuItem<TMenuData = {}, TValue = string> = IBaseMenuItem<
TMenuData,
TValue
> & {
2019-06-19 14:40:52 +00:00
id: string;
parent: string | null;
sort: number;
};
export type IMenuItem<TMenuData = {}, TValue = string> = IBaseMenuItem<
TMenuData,
TValue
> & {
children: Array<IMenuItem<TMenuData, TValue>>;
2019-06-19 14:40:52 +00:00
};
export type IMenu<TMenuData = {}, TValue = string> = Array<
IMenuItem<TMenuData, TValue>
>;
export type IFlatMenu<TMenuData = {}, TValue = string> = Array<
IFlatMenuItem<TMenuData, TValue>
>;
export function validateMenuOptions<TMenuData = {}, TValue = string>(
menu: IMenu<TMenuData, TValue>
2019-06-19 14:40:52 +00:00
): boolean {
const values: TValue[] = toFlat(menu)
2019-06-19 14:40:52 +00:00
.map(menuItem => menuItem.value)
.filter(value => value !== undefined);
const uniqueValues = Array.from(new Set(values));
return uniqueValues.length === values.length;
}
function _getMenuItemByPath<TMenuData = {}, TValue = string>(
menuItem: IMenuItem<TMenuData, TValue>,
2019-06-19 14:40:52 +00:00
path: number[]
): IMenuItem<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
if (path.length === 0) {
return menuItem;
}
return _getMenuItemByPath(menuItem.children[path[0]], path.slice(1));
}
export function getMenuItemByPath<TMenuData = {}, TValue = string>(
menu: IMenu<TMenuData, TValue>,
2019-06-19 14:40:52 +00:00
path: number[]
): IMenuItem<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
return _getMenuItemByPath(menu[path[0]], path.slice(1));
}
export function getMenuItemByValue<TMenuData = {}, TValue = string>(
menu: IMenu<TMenuData, TValue>,
value: TValue
): IMenuItem<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
const flatMenu = toFlat(menu);
const flatMenuItem: IFlatMenuItem<TMenuData, TValue> = flatMenu.find(
2019-06-19 14:40:52 +00:00
menuItem => menuItem.value === value
);
if (flatMenuItem === undefined) {
throw new Error(`Value ${value} does not exist in menu`);
}
2019-06-19 14:40:52 +00:00
return _fromFlat(flatMenu, flatMenuItem);
}
function _walkToMenuItem<TMenuData = {}, TValue = string>(
menuItem: IMenuItem<TMenuData, TValue>,
2019-06-19 14:40:52 +00:00
path: number[]
): IMenu<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
const node = menuItem.children[path[0]];
if (path.length === 1) {
return [node];
}
return [node, ..._walkToMenuItem(node, path.slice(1))];
}
export function walkToMenuItem<TMenuData = {}, TValue = string>(
menu: IMenu<TMenuData, TValue>,
2019-06-19 14:40:52 +00:00
path: number[]
): IMenu<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
const walkByNode = menu[path[0]];
return [walkByNode, ..._walkToMenuItem(walkByNode, path.slice(1))];
}
function _walkToRoot<TMenuData = {}, TValue = string>(
flatMenu: IFlatMenu<TMenuData, TValue>,
2019-06-19 14:40:52 +00:00
parent: string
): IFlatMenu<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
const menuItem = flatMenu.find(menuItem => menuItem.id === parent);
if (menuItem.parent === null) {
return [menuItem];
}
return [menuItem, ..._walkToRoot(flatMenu, menuItem.parent)];
}
export function walkToRoot<TMenuData = {}, TValue = string>(
menu: IMenu<TMenuData, TValue>,
value: TValue
): IMenu<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
const flatMenu = toFlat(menu);
const menuItem = flatMenu.find(menuItem => menuItem.value === value);
return (menuItem.parent === null
? [menuItem]
: [menuItem, ..._walkToRoot(flatMenu, menuItem.parent)]
).map(flatMenuItem => _fromFlat(flatMenu, flatMenuItem));
}
function _toFlat<TMenuData = {}, TValue = string>(
menuItem: IMenuItem<TMenuData, TValue>,
2019-06-19 14:40:52 +00:00
sort: number,
parent: string
): IFlatMenu<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
const id = parent ? [parent, sort].join(":") : sort.toString();
const flatMenuItem: IFlatMenuItem<TMenuData, TValue> = {
2019-06-19 14:40:52 +00:00
data: menuItem.data,
id,
label: menuItem.label,
parent,
sort,
value: menuItem.value
};
return [
flatMenuItem,
...menuItem.children
.map((child, childIndex) => _toFlat(child, childIndex, id))
.reduce((acc, curr) => [...acc, ...curr], [] as IFlatMenu<
TMenuData,
TValue
>)
2019-06-19 14:40:52 +00:00
];
}
export function toFlat<TMenuData = {}, TValue = string>(
menu: IMenu<TMenuData, TValue>
): IFlatMenu<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
return menu
.map((menuItem, menuItemIndex) => _toFlat(menuItem, menuItemIndex, null))
.reduce((acc, curr) => [...acc, ...curr], [] as IFlatMenu<
TMenuData,
TValue
>);
2019-06-19 14:40:52 +00:00
}
function _fromFlat<TMenuData = {}, TValue = string>(
menu: IFlatMenu<TMenuData, TValue>,
flatMenuItem: IFlatMenuItem<TMenuData, TValue>
): IMenuItem<TMenuData, TValue> {
const children: Array<IMenuItem<TMenuData, TValue>> = menu
2019-06-19 14:40:52 +00:00
.filter(menuItem => menuItem.parent === flatMenuItem.id)
.map(menuItem => _fromFlat(menu, menuItem));
return {
children,
data: flatMenuItem.data,
label: flatMenuItem.label,
value: flatMenuItem.value
};
}
export function fromFlat<TMenuData = {}, TValue = string>(
menu: IFlatMenu<TMenuData, TValue>
): IMenu<TMenuData, TValue> {
2019-06-19 14:40:52 +00:00
return menu
.filter(menuItem => menuItem.parent === null)
.map(menuItem => _fromFlat(menu, menuItem));
}
export function isLeaf<TMenuData = {}, TValue = string>(
menuItem: IMenuItem<TMenuData, TValue>
2019-06-19 14:40:52 +00:00
): boolean {
return menuItem.children.length === 0;
}