mirror of https://github.com/halo-dev/halo
266 lines
6.5 KiB
TypeScript
266 lines
6.5 KiB
TypeScript
import type { MenuItem, MenuItemSpec } from "@halo-dev/api-client";
|
|
import { cloneDeep } from "lodash-es";
|
|
|
|
export interface MenuTreeItemSpec extends Omit<MenuItemSpec, "children"> {
|
|
children: MenuTreeItem[];
|
|
}
|
|
|
|
export interface MenuTreeItem extends Omit<MenuItem, "spec"> {
|
|
spec: MenuTreeItemSpec;
|
|
}
|
|
|
|
/**
|
|
* Convert a flat array of menu items into flattens a menu tree.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```json
|
|
* [
|
|
* {
|
|
* "spec": {
|
|
* "displayName": "文章分类",
|
|
* "href": "https://ryanc.cc/categories",
|
|
* "children": [
|
|
* "caeef383-3828-4039-9114-6f9ad3b4a37e",
|
|
* "ded1943d-9fdb-4563-83ee-2f04364872e0"
|
|
* ]
|
|
* },
|
|
* "apiVersion": "v1alpha1",
|
|
* "kind": "MenuItem",
|
|
* "metadata": {
|
|
* "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
|
|
* "version": 12,
|
|
* "creationTimestamp": "2022-08-05T04:19:37.252228Z"
|
|
* }
|
|
* },
|
|
* {
|
|
* "spec": {
|
|
* "displayName": "Halo",
|
|
* "href": "https://ryanc.cc/categories/halo",
|
|
* "children": []
|
|
* },
|
|
* "apiVersion": "v1alpha1",
|
|
* "kind": "MenuItem",
|
|
* "metadata": {
|
|
* "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
|
|
* "version": 4,
|
|
* "creationTimestamp": "2022-07-28T06:50:32.777556Z"
|
|
* }
|
|
* },
|
|
* {
|
|
* "spec": {
|
|
* "displayName": "Java",
|
|
* "href": "https://ryanc.cc/categories/java",
|
|
* "children": []
|
|
* },
|
|
* "apiVersion": "v1alpha1",
|
|
* "kind": "MenuItem",
|
|
* "metadata": {
|
|
* "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
|
|
* "version": 1,
|
|
* "creationTimestamp": "2022-08-05T04:22:03.377364Z"
|
|
* }
|
|
* }
|
|
* ]
|
|
* ```
|
|
*
|
|
* will be transformed to:
|
|
*
|
|
* ```json
|
|
* [
|
|
* {
|
|
* "spec": {
|
|
* "displayName": "文章分类",
|
|
* "href": "https://ryanc.cc/categories",
|
|
* "children": [
|
|
* {
|
|
* "spec": {
|
|
* "displayName": "Halo",
|
|
* "href": "https://ryanc.cc/categories/halo",
|
|
* "children": []
|
|
* },
|
|
* "apiVersion": "v1alpha1",
|
|
* "kind": "MenuItem",
|
|
* "metadata": {
|
|
* "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
|
|
* "version": 4,
|
|
* "creationTimestamp": "2022-07-28T06:50:32.777556Z"
|
|
* }
|
|
* },
|
|
* {
|
|
* "spec": {
|
|
* "displayName": "Java",
|
|
* "href": "https://ryanc.cc/categories/java",
|
|
* "children": []
|
|
* },
|
|
* "apiVersion": "v1alpha1",
|
|
* "kind": "MenuItem",
|
|
* "metadata": {
|
|
* "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
|
|
* "version": 1,
|
|
* "creationTimestamp": "2022-08-05T04:22:03.377364Z"
|
|
* }
|
|
* }
|
|
* ]
|
|
* },
|
|
* "apiVersion": "v1alpha1",
|
|
* "kind": "MenuItem",
|
|
* "metadata": {
|
|
* "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
|
|
* "version": 12,
|
|
* "creationTimestamp": "2022-08-05T04:19:37.252228Z"
|
|
* }
|
|
* }
|
|
* ]
|
|
* ```
|
|
*
|
|
* @param menuItems
|
|
*/
|
|
export function buildMenuItemsTree(menuItems: MenuItem[]): MenuTreeItem[] {
|
|
const menuItemsToUpdate = cloneDeep(menuItems);
|
|
|
|
const menuItemsMap = {};
|
|
const parentMap = {};
|
|
|
|
menuItemsToUpdate.forEach((menuItem) => {
|
|
menuItemsMap[menuItem.metadata.name] = menuItem;
|
|
// @ts-ignore
|
|
menuItem.spec.children.forEach((child) => {
|
|
parentMap[child] = menuItem.metadata.name;
|
|
});
|
|
menuItem.spec.children = [];
|
|
});
|
|
|
|
menuItemsToUpdate.forEach((menuItem) => {
|
|
const parentName = parentMap[menuItem.metadata.name];
|
|
if (parentName && menuItemsMap[parentName]) {
|
|
menuItemsMap[parentName].spec.children.push(menuItem);
|
|
}
|
|
});
|
|
|
|
const menuTreeItems = menuItemsToUpdate.filter(
|
|
(node) => parentMap[node.metadata.name] === undefined
|
|
);
|
|
|
|
return sortMenuItemsTree(menuTreeItems);
|
|
}
|
|
|
|
/**
|
|
* Sort a menu tree by priority.
|
|
*
|
|
* @param menuTreeItems
|
|
*/
|
|
export function sortMenuItemsTree(
|
|
menuTreeItems: MenuTreeItem[] | MenuItem[]
|
|
): MenuTreeItem[] {
|
|
return menuTreeItems
|
|
.sort((a, b) => {
|
|
if (a.spec.priority < b.spec.priority) {
|
|
return -1;
|
|
}
|
|
if (a.spec.priority > b.spec.priority) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
})
|
|
.map((menuTreeItem) => {
|
|
if (menuTreeItem.spec.children.length) {
|
|
return {
|
|
...menuTreeItem,
|
|
spec: {
|
|
...menuTreeItem.spec,
|
|
children: sortMenuItemsTree(menuTreeItem.spec.children),
|
|
},
|
|
};
|
|
}
|
|
return menuTreeItem;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reset the menu tree item's priority.
|
|
*
|
|
* @param menuItems
|
|
*/
|
|
export function resetMenuItemsTreePriority(
|
|
menuItems: MenuTreeItem[]
|
|
): MenuTreeItem[] {
|
|
for (let i = 0; i < menuItems.length; i++) {
|
|
menuItems[i].spec.priority = i;
|
|
if (menuItems[i].spec.children) {
|
|
resetMenuItemsTreePriority(menuItems[i].spec.children);
|
|
}
|
|
}
|
|
return menuItems;
|
|
}
|
|
|
|
/**
|
|
* Convert a menu tree items into a flat array of menu.
|
|
*
|
|
* @param menuTreeItems
|
|
*/
|
|
export function convertTreeToMenuItems(menuTreeItems: MenuTreeItem[]) {
|
|
const menuItems: MenuItem[] = [];
|
|
const menuItemsMap = new Map<string, MenuItem>();
|
|
const convertMenuItem = (node: MenuTreeItem | undefined) => {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
const children = node.spec.children || [];
|
|
menuItemsMap.set(node.metadata.name, {
|
|
...node,
|
|
spec: {
|
|
...node.spec,
|
|
children: children.map((child) => child.metadata.name),
|
|
},
|
|
});
|
|
children.forEach((child) => {
|
|
convertMenuItem(child);
|
|
});
|
|
};
|
|
menuTreeItems.forEach((node) => {
|
|
convertMenuItem(node);
|
|
});
|
|
menuItemsMap.forEach((node) => {
|
|
menuItems.push(node);
|
|
});
|
|
return menuItems;
|
|
}
|
|
|
|
export function getChildrenNames(menuTreeItem: MenuTreeItem): string[] {
|
|
const childrenNames: string[] = [];
|
|
|
|
function getChildrenNamesRecursive(menuTreeItem: MenuTreeItem) {
|
|
if (menuTreeItem.spec.children) {
|
|
menuTreeItem.spec.children.forEach((child) => {
|
|
childrenNames.push(child.metadata.name);
|
|
getChildrenNamesRecursive(child);
|
|
});
|
|
}
|
|
}
|
|
|
|
getChildrenNamesRecursive(menuTreeItem);
|
|
|
|
return childrenNames;
|
|
}
|
|
|
|
/**
|
|
* Convert {@link MenuTreeItem} to {@link MenuItem} with flat children name array.
|
|
*
|
|
* @param menuTreeItem
|
|
*/
|
|
export function convertMenuTreeItemToMenuItem(
|
|
menuTreeItem: MenuTreeItem
|
|
): MenuItem {
|
|
const childNames = menuTreeItem.spec.children.map(
|
|
(child) => child.metadata.name
|
|
);
|
|
return {
|
|
...menuTreeItem,
|
|
spec: {
|
|
...menuTreeItem.spec,
|
|
children: childNames,
|
|
},
|
|
};
|
|
}
|