From c3be3471fe96c810f013c9755f9b903b893ae84e Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Thu, 8 Apr 2021 21:25:05 +0800 Subject: [PATCH 1/2] feat: show more menu in navbar --- .../core/public/locales/en/common.json | 3 + .../core/public/locales/zh/common.json | 3 + .../packages/core/src/components/Navbar.tsx | 207 +++++++++++++----- .../core/src/hooks/useAvailableComponents.ts | 118 ++++++++++ .../packages/core/src/hooks/useNavItems.ts | 84 ------- frontend/packages/core/src/pages/index.tsx | 20 +- frontend/packages/core/src/routes/index.ts | 7 +- frontend/packages/mock/data/components.ts | 3 +- 8 files changed, 291 insertions(+), 154 deletions(-) create mode 100644 frontend/packages/core/src/hooks/useAvailableComponents.ts delete mode 100644 frontend/packages/core/src/hooks/useNavItems.ts diff --git a/frontend/packages/core/public/locales/en/common.json b/frontend/packages/core/public/locales/en/common.json index c9e7817c1..d4480febc 100644 --- a/frontend/packages/core/public/locales/en/common.json +++ b/frontend/packages/core/public/locales/en/common.json @@ -9,8 +9,11 @@ "graph": "Graphs", "high-dimensional": "High Dimensional", "histogram": "Histogram", + "hyper-parameter": "Hyper Parameters", "image": "Image", + "inactive": "Inactive", "loading": "Please wait while loading data", + "more": "More", "next-page": "Next Page", "pr-curve": "PR Curve", "previous-page": "Prev Page", diff --git a/frontend/packages/core/public/locales/zh/common.json b/frontend/packages/core/public/locales/zh/common.json index 5c62b2315..df165c5ac 100644 --- a/frontend/packages/core/public/locales/zh/common.json +++ b/frontend/packages/core/public/locales/zh/common.json @@ -9,8 +9,11 @@ "graph": "网络结构", "high-dimensional": "高维数据映射", "histogram": "直方图", + "hyper-parameter": "超参可视化", "image": "图像", + "inactive": "待使用", "loading": "数据载入中,请稍等", + "more": "更多", "next-page": "下一页", "pr-curve": "PR曲线", "previous-page": "上一页", diff --git a/frontend/packages/core/src/components/Navbar.tsx b/frontend/packages/core/src/components/Navbar.tsx index 14c14ef69..68f7f1d8c 100644 --- a/frontend/packages/core/src/components/Navbar.tsx +++ b/frontend/packages/core/src/components/Navbar.tsx @@ -14,9 +14,11 @@ * limitations under the License. */ +// cspell:words cimode + import {Link, LinkProps, useLocation} from 'react-router-dom'; import React, {FunctionComponent, useCallback, useEffect, useMemo, useState} from 'react'; -import {border, borderRadius, rem, size, transitionProps} from '~/utils/style'; +import {border, borderRadius, rem, size, transitionProps, triangle} from '~/utils/style'; import Icon from '~/components/Icon'; import Language from '~/components/Language'; @@ -28,17 +30,40 @@ import {getApiToken} from '~/utils/fetch'; import logo from '~/assets/images/logo.svg'; import queryString from 'query-string'; import styled from 'styled-components'; -import useNavItems from '~/hooks/useNavItems'; +import useAvailableComponents from '~/hooks/useAvailableComponents'; import {useTranslation} from 'react-i18next'; const BASE_URI: string = import.meta.env.SNOWPACK_PUBLIC_BASE_URI; const PUBLIC_PATH: string = import.meta.env.SNOWPACK_PUBLIC_PATH; const API_TOKEN_KEY: string = import.meta.env.SNOWPACK_PUBLIC_API_TOKEN_KEY; -interface NavbarItemProps extends Route { +const MAX_ITEM_COUNT_IN_NAVBAR = 5; + +const flatten = (routes: T[]) => { + const result: Omit[] = []; + routes.forEach(route => { + if (route.children) { + result.push(...flatten(route.children)); + } else { + result.push(route); + } + }); + return result; +}; + +interface NavbarItemProps { + active: boolean; + path?: Route['path']; + showDropdownIcon?: boolean; +} + +interface NavbarItemType { + id: string; cid?: string; + name: string; active: boolean; - children?: ({active: boolean} & NonNullable[number])[]; + path?: Route['path']; + children?: NavbarItemType[]; } function appendApiToken(url: string) { @@ -129,10 +154,27 @@ const NavItem = styled.div<{active?: boolean}>` ${props => border('bottom', rem(3), 'solid', props.active ? 'var(--navbar-highlight-color)' : 'transparent')} ${transitionProps('border-bottom')} text-transform: uppercase; + + &.dropdown-icon { + &::after { + content: ''; + display: inline-block; + width: 0; + height: 0; + margin-left: 0.5rem; + vertical-align: middle; + ${triangle({ + pointingDirection: 'bottom', + width: rem(8), + height: rem(5), + foregroundColor: 'currentColor' + })} + } + } } `; -const SubNav = styled.div` +const SubNavWrapper = styled.div` overflow: hidden; border-radius: ${borderRadius}; `; @@ -156,38 +198,65 @@ const NavItemChild = styled.div<{active?: boolean}>` } `; -const NavbarLink: FunctionComponent<{to?: string} & Omit> = ({to, children, ...props}) => { - return ( - - {children} - - ); -}; - -const NavbarItem = React.forwardRef(({id, cid, path, active}, ref) => { - const {t} = useTranslation('common'); - - const name = useMemo(() => (cid ? `${t(id)} - ${t(cid)}` : t(id)), [t, id, cid]); +const NavbarLink: FunctionComponent<{to?: string} & Omit> = ({to, children, ...props}) => ( + + {children} + +); + +// FIXME: why we need to add children type here... that's weird... +const NavbarItem = React.forwardRef( + ({path, active, showDropdownIcon, children}, ref) => { + if (path) { + return ( + + + {children} + + + ); + } - if (path) { return ( - - {name} - + {children} ); } - - return ( - - {name} - - ); -}); +); NavbarItem.displayName = 'NavbarItem'; +const SubNav: FunctionComponent<{ + menu: Omit[]; + active?: boolean; + path?: string; + showDropdownIcon?: boolean; +}> = ({menu, active, path, showDropdownIcon, children}) => ( + + {menu.map(item => ( + + {item.name} + + ))} + + } + > + + {children} + + +); + const Navbar: FunctionComponent = () => { const {t, i18n} = useTranslation('common'); const {pathname} = useLocation(); @@ -202,11 +271,32 @@ const Navbar: FunctionComponent = () => { const currentPath = useMemo(() => pathname.replace(BASE_URI, ''), [pathname]); - const [navItems] = useNavItems(); - const [items, setItems] = useState([]); + const [components, inactiveComponents] = useAvailableComponents(); + + const componentsInNavbar = useMemo(() => components.slice(0, MAX_ITEM_COUNT_IN_NAVBAR), [components]); + const flattenMoreComponents = useMemo(() => flatten(components.slice(MAX_ITEM_COUNT_IN_NAVBAR)), [components]); + const flattenInactiveComponents = useMemo(() => flatten(inactiveComponents), [inactiveComponents]); + const componentsInMoreMenu = useMemo( + () => + flattenMoreComponents.map(item => ({ + ...item, + active: currentPath === item.path + })), + [currentPath, flattenMoreComponents] + ); + const componentsInInactiveMenu = useMemo( + () => + flattenInactiveComponents.map(item => ({ + ...item, + active: currentPath === item.path + })), + [currentPath, flattenInactiveComponents] + ); + + const [navItemsInNavbar, setNavItemsInNavbar] = useState([]); useEffect(() => { - setItems(oldItems => - navItems.map(item => { + setNavItemsInNavbar(oldItems => + componentsInNavbar.map(item => { const children = item.children?.map(child => ({ ...child, active: child.path === currentPath @@ -217,6 +307,7 @@ const Navbar: FunctionComponent = () => { return { ...item, cid: child.id, + name: child.name, path: currentPath, active: true, children @@ -227,6 +318,7 @@ const Navbar: FunctionComponent = () => { return { ...item, ...oldItem, + name: item.children?.find(c => c.id === oldItem.cid)?.name ?? item.name, active: false, children }; @@ -240,7 +332,7 @@ const Navbar: FunctionComponent = () => { }; }) ); - }, [navItems, currentPath]); + }, [componentsInNavbar, currentPath]); return (