From 484a3c5e4e7b0dda896478a1fba0145125e26e7c Mon Sep 17 00:00:00 2001 From: gmolki Date: Wed, 24 Jul 2024 09:54:08 +0200 Subject: [PATCH] feat: Router Sidebar refactor + new design --- .../modules/RouterSidebar/cmp.stories.tsx | 123 ++++--- src/components/modules/RouterSidebar/cmp.tsx | 235 +++++++++++--- .../modules/RouterSidebar/styles.tsx | 303 ++++++++++++------ src/themes/twentysix.ts | 6 +- src/themes/types.ts | 4 + src/types.ts | 2 + 6 files changed, 482 insertions(+), 191 deletions(-) diff --git a/src/components/modules/RouterSidebar/cmp.stories.tsx b/src/components/modules/RouterSidebar/cmp.stories.tsx index a065afbd..43027e26 100644 --- a/src/components/modules/RouterSidebar/cmp.stories.tsx +++ b/src/components/modules/RouterSidebar/cmp.stories.tsx @@ -110,70 +110,91 @@ NestedRoutes.args = { children: [ { name: 'Solutions', - href: '/solutions', + href: '/', + exact: true, children: [ { name: 'Dashboard', - href: '/solutions/dashboard', + href: '/', + exact: true, + icon: 'dashboard', }, - ], - }, - { - name: 'Computing', - href: '/computing', - children: [ - { - icon: 'console', - name: 'Functions', - href: '/computing/function', - }, - { - name: 'Instances', - href: '/computing/instance', - }, - { - name: 'Confidential', - href: '/computing/confidential', - }, - ], - }, - { - name: 'Storage', - href: '/storage', - children: [ { - name: 'Inmutable volumes', - href: '/storage/volume', + name: 'Settings', + href: '/settings', + exact: true, + icon: 'settings', }, - ], - }, - { - name: 'Tools', - href: '#', - children: [ { - name: 'VRF', - href: 'https://medium.com/aleph-im/aleph-im-verifiable-random-function-vrf-b03544a7e904', - external: true, + name: 'Web3 Hosting', + href: '/hosting', + icon: 'web3HostingBox', + children: [ + { + name: 'Manage your website', + href: '/hosting/website', + icon: 'manageWebsite', + }, + ], }, { - name: 'Indexing framework', - href: 'https://docs.aleph.im/tools/indexer/', - external: true, + name: 'Computing', + href: '/computing', + icon: 'computeSolutions', + children: [ + { + name: 'Functions', + href: '/computing/function', + icon: 'functions', + }, + { + name: 'Instances', + href: '/computing/instance', + icon: 'instance', + }, + { + name: 'Confidential', + href: '/computing/confidential', + disabled: true, + label: '(SOON)', + icon: 'confidential', + }, + ], }, - ], - }, - { - name: 'Configure', - href: '/configure', - children: [ { - name: 'Secrets', - href: '/configure/ssh', + name: 'Storage', + href: '/storage', + icon: 'storageSolutions', + children: [ + { + name: 'Volumes', + href: '/storage', + icon: 'storageSolutions', + }, + ], }, { - name: 'Custom domains', - href: '/configure/domain', + name: 'Tools', + href: '#', + icon: 'console', + children: [ + { + name: 'VRF', + href: 'https://medium.com/aleph-im/aleph-im-verifiable-random-function-vrf-b03544a7e904', + external: true, + highlighted: true, + target: '_blank', + icon: 'arrow-up-right-from-square', + }, + { + name: 'Indexer Framework', + href: 'https://docs.aleph.im/tools/indexer/', + external: true, + highlighted: true, + target: '_blank', + icon: 'arrow-up-right-from-square', + }, + ], }, ], }, diff --git a/src/components/modules/RouterSidebar/cmp.tsx b/src/components/modules/RouterSidebar/cmp.tsx index 34573ef9..97b5c87a 100644 --- a/src/components/modules/RouterSidebar/cmp.tsx +++ b/src/components/modules/RouterSidebar/cmp.tsx @@ -1,6 +1,6 @@ -import { memo, useCallback, useMemo, useState, MouseEvent } from 'react' +import React, { memo, useCallback, useMemo, useState, MouseEvent } from 'react' +import { useTransition } from 'transition-hook' import { - StyledLink, StyledLogo, StyledNav1, StyledRouterLink1, @@ -14,18 +14,25 @@ import { StyledToggleButton, StyledLogoContainer, StyledNav1Container, - StyledNav2LinkContainer, + StyledNav2Icon, + StyledNav1Link, + StyledOpenedNav2Link, + StyledClosedNav2Link, + StyledNav2TitleContainer, + StyledNav2MainTitle, + StyledOpenedNav2LinkContainer, + StyledClosedNav2LinkContainer, } from './styles' -import React from 'react' import { RouteProps, RouterSidebarProps } from './types' +import { RouterLinkProps } from '../RouterLink' -const Route = (props: RouteProps) => { - const { pathname, route, level = 0, Link, ...rest } = props +const Nav1Route = (props: RouteProps) => { + const { pathname, route, Link, ...rest } = props const isActive = route.exact ? pathname === route.href : pathname.indexOf(route.href) >= 0 - const linkProps = { + const linkProps: RouterLinkProps = { route, Link, isActive, @@ -33,41 +40,151 @@ const Route = (props: RouteProps) => { } return ( - - {level <= 0 ? ( - + + + + ) +} +Nav1Route.displayName = 'Nav1Route' + +const OpenedNav2Route = (props: RouteProps) => { + const { pathname, route, level = 1, Link, ...rest } = props + + const isActive = route.exact + ? pathname === route.href + : pathname.indexOf(route.href) >= 0 + + const routeIcon = (route: any) => { + if (route.highlighted) return route.icon + if (route.children?.length) return route.icon + + return undefined + } + + const $route = { + ...route, + icon: routeIcon(route), + } + + const linkProps: RouterLinkProps = { + route: $route, + Link, + isActive, + iconPosition: route.highlighted ? 'right' : 'left', + ...rest, + } + + return ( + + {$route.children ? ( + <> + {$route.name && ( + + {level > 1 ? ( + + {$route.icon && } + {$route.name} + + ) : ( + {$route.name} + )} + + )} + {$route?.children?.map((childrenRoute) => ( + + ))} + ) : ( + + )} + + ) +} +OpenedNav2Route.displayName = 'OpenedNav2Route' + +const ClosedNav2Route = (props: RouteProps) => { + const { pathname, route, level = 0, Link, ...rest } = props + + const isActive = route.exact + ? pathname === route.href + : pathname.indexOf(route.href) >= 0 + + const routeIcon = (route: any) => { + if (route.highlighted) return route.icon + if (!route.children?.length) return route.icon + + return undefined + } + + const routeName = (route: any) => { + if (route.highlighted) return route.name + if (route.children?.length) return route.name + + return undefined + } + + const $route = { + ...route, + icon: routeIcon(route), + name: routeName(route), + label: undefined, + } + + const linkProps: RouterLinkProps = { + route: $route, + Link, + isActive, + iconPosition: route.highlighted ? 'bottom' : undefined, + ...rest, + } + + return ( + + {$route.children ? ( <> - {route.children ? ( - <> - {route.name && ( -
- {route.name} -
+ {$route.name && ( + + {level > 1 ? ( + + {$route.icon && } + {$route.name} + + ) : ( + {$route.name} )} - {route?.children?.map((route) => ( - - ))} - - ) : ( - + )} + {$route?.children?.map((childrenRoute) => ( + + ))} + ) : ( + )} -
+ ) } -Route.displayName = 'Route' +ClosedNav2Route.displayName = 'ClosedNav2Route' // ------------------------- @@ -140,6 +257,19 @@ export const RouterSidebar = ({ const $isOpen = open const $isHover = hover && !!onToggle + const isOpenState = $isOpen || $isOpen === undefined + + const { shouldMount: $shouldMountOpened, stage: $stageOpened } = + useTransition(isOpenState, 500) + const { shouldMount: $shouldMountClosed, stage: $stageClosed } = + useTransition(!isOpenState, 500) + + const $showOpened = $shouldMountOpened && !$shouldMountClosed + const $showClosed = $shouldMountClosed && !$shouldMountOpened + + console.log('OPENED', $shouldMountOpened, $stageOpened) + console.log('CLOSED', $shouldMountClosed, $stageClosed) + console.log('\n') return ( {logo} {routes.map((route) => ( - - + {currentRoute?.children?.map((route) => ( + + ))} + + {currentRoute?.children?.map((route) => ( - ))} - +
{!!onToggle && ( @@ -217,5 +368,11 @@ export const RouterSidebar = ({ } RouterSidebar.displayName = 'RouterSidebar' -export const RouteMemo = memo(Route) as typeof Route +export const Nav1RouteMemo = memo(Nav1Route) as typeof Nav1Route +export const OpenedNav2RouteMemo = memo( + OpenedNav2Route, +) as typeof OpenedNav2Route +export const ClosedNav2RouteMemo = memo( + ClosedNav2Route, +) as typeof ClosedNav2Route export default memo(RouterSidebar) as typeof RouterSidebar diff --git a/src/components/modules/RouterSidebar/styles.tsx b/src/components/modules/RouterSidebar/styles.tsx index 71d34e78..d72a0474 100644 --- a/src/components/modules/RouterSidebar/styles.tsx +++ b/src/components/modules/RouterSidebar/styles.tsx @@ -11,17 +11,27 @@ import { StyledRouterLink, StyledRouteLinkIcon, StyledRouteLinkText, + StyledDisabledRouterLink, } from '../RouterLink/styles' +import { StyledTextGradientContainer } from '../../common/TextGradient/styles' const nav1OpenSize = 4.5 const nav1CloseSize = 0.375 const nav2OpenSize = 18.75 const nav2CloseSize = nav1OpenSize -export const StyledLink = styled.div` +export const StyledNav1Link = styled.div` ${tw`w-full`} ` +export const StyledNav2Link = styled.div` + ${tw`w-full`} +` + +export const StyledOpenedNav2Link = styled(StyledNav2Link)`` + +export const StyledClosedNav2Link = styled(StyledNav2Link)`` + export const StyledNav1 = styled.nav` ${({ theme }) => { const { nav1 } = theme.component.sidebar @@ -48,7 +58,7 @@ export const StyledRouterLink1 = styled( variant: '1', route: { ...props.route, name: undefined, flag: undefined }, } -})` +})` ${({ theme, isActive }) => { const { nav1 } = theme.component.sidebar @@ -92,25 +102,51 @@ export const StyledNav2Container = styled.div` ${tw`flex flex-col items-start h-full`} ` +export const StyledNav2TitleContainer = styled.div` + ${tw`py-2`} + position: relative; +` + export const StyledNav2Title = styled.div.attrs(addClasses('tp-nav'))` ${({ theme }) => { const { title } = theme.component.sidebar.nav2 return css` - ${tw`h-12 px-6`} ${tw`inline-flex items-center w-auto max-w-full uppercase`} color: ${title?.color}; ` }} ` -export const StyledNav2LinkContainer = styled.div` - ${tw`flex flex-col items-start cursor-auto w-full overflow-auto`} - margin-top: 6.5rem; +export const StyledNav2MainTitle = styled.div.attrs(addClasses('tp-nav'))` + ${({ theme }) => { + const { mainTitle } = theme.component.sidebar.nav2 + + return css` + ${tw`inline-flex items-center w-auto max-w-full uppercase`} + color: ${mainTitle?.color}; + ` + }} ` -export type StyledRouterLink2Props = Omit +export const StyledNav2Icon = styled(Icon).attrs((props) => { + return { + ...props, + ...addClasses('tp-nav'), + size: '1em', + } +})` + ${({ theme }) => { + const { title } = theme.component.sidebar.nav2 + return css` + ${tw`inline-flex items-center w-auto max-w-full uppercase text-center`} + color: ${title?.color}; + padding-right: 0.625rem; + ` + }} +` +export type StyledRouterLink2Props = Omit export const StyledRouterLink2 = styled( RouterLink, ).attrs((props) => { @@ -119,17 +155,75 @@ export const StyledRouterLink2 = styled( variant: '2', } })` - ${({ route: { icon } }) => { + ${() => { return css` ${StyledRouterLink} { - ${tw`h-12 px-6`} ${tw`flex max-w-full overflow-hidden`} - padding-left: ${!icon ? 3.125 : 1.5}rem; } ` }} ` +export const StyledNav2LinkContainer = styled.div` + ${tw`flex flex-col items-start cursor-auto w-full overflow-auto absolute`} + margin-top: 6.5rem; +` + +export const StyledOpenedNav2LinkContainer = styled(StyledNav2LinkContainer)` + width: ${nav2OpenSize}rem; + + & ${StyledRouterLink} { + ${tw`h-12 px-6`} + padding-left: 3.125rem; + } + + & ${StyledNav2Title} { + ${({ theme }) => { + const { title } = theme.component.sidebar.nav2 + + return css` + ${tw`h-8 mx-5 px-1`} + border-bottom: 2px solid ${title?.underline}; + ` + }} + } + + & ${StyledNav2MainTitle} { + ${tw`h-8 mx-5 px-1`} + } +` +export const StyledClosedNav2LinkContainer = styled(StyledNav2LinkContainer)` + width: ${nav2CloseSize}rem; + + & ${StyledNav2Title}, & ${StyledNav2MainTitle} { + ${tw`p-0 m-0 border-b-0 flex items-center justify-center text-center`} + font-size: 0.6rem; + min-height: 1.286rem; + padding: 0.5714rem 0; + } + + & ${StyledRouterLink} { + height: fit-content; + min-height: 2.8572rem; + padding: 0.5714rem 0; + width: 100%; + } + + & ${StyledRouterLink}, & ${StyledDisabledRouterLink} { + ${tw`flex items-center justify-center text-center`} + } + + & ${StyledNotificationBadge} { + position: absolute; + left: 66%; + } + + & ${StyledTextGradientContainer}, & ${StyledDisabledRouterLink} { + white-space: pre-wrap; + font-size: 0.6rem; + } +` + export const StyledLogoContainer = styled.div` ${({ theme }) => { const { logo } = theme.component.sidebar.nav1 @@ -204,7 +298,7 @@ export const StyledProgressBar = styled.div<{ $percent: number }>( }, ) -const fadeOutIn1 = keyframes` +const fadeOutIn1 = keyframes` 0%, 15%, 90%, 100% { opacity: 1; } @@ -215,7 +309,7 @@ const fadeOutIn1 = keyframes` ` // @note: https://stackoverflow.com/a/43575547 -const fadeOutIn1Reverse = keyframes` +const fadeOutIn1Reverse = keyframes` 0%, 10%, 80%, 100% { opacity: 1; } @@ -225,7 +319,7 @@ const fadeOutIn1Reverse = keyframes` } ` -const fadeOutIn2 = keyframes` +const fadeOutIn2 = keyframes` 0%, 6%, 80%, 100% { opacity: 1; } @@ -235,7 +329,7 @@ const fadeOutIn2 = keyframes` } ` -const fadeOutIn2Reverse = keyframes` +const fadeOutIn2Reverse = keyframes` 0%, 10%, 70%, 100% { opacity: 1; } @@ -250,6 +344,8 @@ export type StyledSidebarProps = { $isHover?: boolean $speed?: number $breakpoint: BreakpointId + $showOpened: boolean + $showClosed: boolean } export const StyledSidebar = styled.aside` @@ -273,12 +369,12 @@ export const StyledSidebar = styled.aside` transition: width ease-in-out ${0.35 / $speed}s ${0.4 / $speed}s; } - & ${StyledRouterLink1} ${StyledRouteLinkIcon}, & ${StyledLogo} { + & ${StyledLogo} { ${tw`opacity-100 visible`} transition: opacity ease-in-out ${0.2 / $speed}s ${0.55 / $speed}s, - visibility linear ${0.2 / $speed}s ${0.55 / $speed}s, - color ease-in-out 0.25s 0s !important; + visibility linear ${0.2 / $speed}s ${0.55 / $speed}s, + color ease-in-out 0.25s 0s !important; } & ${StyledLogoContainer} { @@ -286,12 +382,6 @@ export const StyledSidebar = styled.aside` ${0.2 / $speed}s; } - & ${StyledRouterLink1} ${StyledRouterLink}::after { - ${tw`-top-2 opacity-0`} - transition: opacity ease-in-out ${0.7 / $speed}s ${0.2 / $speed}s, - top ease-in-out ${0.7 / $speed}s ${0.1 / $speed}s; - } - & ${StyledNav2} { width: ${nav2OpenSize}rem; @@ -318,31 +408,6 @@ export const StyledSidebar = styled.aside` animation: ${1 / $speed}s ease-in-out 0s ${fadeOutIn1Reverse}; } - & ${StyledNav2Title}, & ${StyledRouterLink2} ${StyledRouterLink} { - ${tw`relative left-0 translate-x-0 gap-2.5`} - - transition: left linear 0s ${0.5 / $speed}s, - transform linear 0s ${0.5 / $speed}s, - font-size linear 0s ${0.5 / $speed}s, - padding linear 0s ${0.5 / $speed}s, - gap linear 0s ${0.5 / $speed}s, - background-color ease-in-out 0s ${0.5 / $speed}s, - color ease-in-out 0.25s 0s !important; - } - - & ${StyledNav2Title} { - font-size: 1.125rem; - } - - & ${StyledRouteLinkIcon} { - transition: color ease-in-out 0.25s 0s !important; - } - - & ${StyledNotificationBadge} { - ${tw`-left-0.5`} - transition: left linear 0s ${0.45 / $speed}s; - } - & ${StyledToggleButton} { transform: rotateZ(-180deg); transition: transform ease-in-out ${0.6 / $speed}s ${0.4 / $speed}s; @@ -359,13 +424,24 @@ export const StyledSidebar = styled.aside` } } - & ${StyledRouterLink2} ${StyledRouterLink}._active { - background-color: ${theme.component.sidebar.nav2.active - ?.background}; + & ${StyledNav1Link} { + & ${StyledRouterLink1} ${StyledRouteLinkIcon} { + ${tw`opacity-100 visible`} + + transition: opacity ease-in-out ${0.2 / $speed}s ${0.55 / + $speed}s, + visibility linear ${0.2 / $speed}s ${0.55 / $speed}s, + color ease-in-out 0.25s 0s !important; + } + + & ${StyledRouterLink1} ${StyledRouterLink}::after { + ${tw`-top-2 opacity-0`} + transition: opacity ease-in-out ${0.7 / $speed}s ${0.2 / $speed}s, + top ease-in-out ${0.7 / $speed}s ${0.1 / $speed}s; + } - & ${StyledRouteLinkIcon}, & ${StyledRouteLinkText} { - color: ${theme.component.sidebar.nav2.active?.color}; - transition: color ease-in-out 0s ${0.5 / $speed}s !important; + & ${StyledRouteLinkIcon} { + transition: color ease-in-out 0.25s 0s !important; } } ` @@ -375,12 +451,12 @@ export const StyledSidebar = styled.aside` transition: width ease-in-out ${0.2 / $speed}s ${0.15 / $speed}s; } - & ${StyledRouterLink1} ${StyledRouteLinkIcon}, & ${StyledLogo} { + & ${StyledLogo} { ${tw`opacity-0 invisible`} transition: opacity ease-in-out ${0.2 / $speed}s 0s, - visibility linear ${0.2 / $speed}s 0s, - color ease-in-out 0.25s 0s !important; + visibility linear ${0.2 / $speed}s 0s, + color ease-in-out 0.25s 0s !important; } & ${StyledLogoContainer} { @@ -389,12 +465,6 @@ export const StyledSidebar = styled.aside` ${0.2 / $speed}s; } - & ${StyledRouterLink1} ${StyledRouterLink}::after { - ${tw`top-0 opacity-100`} - transition: opacity ease-in-out ${0.7 / $speed}s ${0.2 / $speed}s, - top ease-in-out ${0.7 / $speed}s ${0.3 / $speed}s; - } - & ${StyledNav2} { width: ${nav2CloseSize}rem; @@ -402,14 +472,16 @@ export const StyledSidebar = styled.aside` padding-left ease-in-out ${0.4 / $speed}s 0s, box-shadow ease-in-out ${0.4 / $speed}s 0s; - ${$isHover && - css` - cursor: pointer; + ${ + $isHover && + css` + cursor: pointer; - padding-left: ${nav1CloseSize}rem; - box-shadow: ${nav1CloseSize}rem 0px 0px 0px - ${theme.component.sidebar.nav2.background}; - `} + padding-left: ${nav1CloseSize}rem; + box-shadow: ${nav1CloseSize}rem 0px 0px 0px + ${theme.component.sidebar.nav2.background}; + ` + } } & ${StyledNav1Container}, & ${StyledNav2Container} { @@ -425,34 +497,10 @@ export const StyledSidebar = styled.aside` animation: ${1 / $speed}s ease-in-out 0s ${fadeOutIn1}; } - & ${StyledNav2Title}, & ${StyledRouterLink2} ${StyledRouterLink} { - ${tw`relative left-1/2 -translate-x-1/2 gap-4`} - - transition: left linear 0s ${0.45 / $speed}s, - transform linear 0s ${0.45 / $speed}s, - font-size linear 0s ${0.45 / $speed}s, - padding linear 0s ${0.45 / $speed}s, - background-color ease-in-out 0s ${0.45 / $speed}s, - gap linear 0s ${0.45 / $speed}s; - } - - & ${StyledNav2Title} { - ${tw`px-0`} - font-size: 0.75rem; - } - - & ${StyledRouteLinkIcon} { - transition: color ease-in-out 0.25s 0s !important; - } - - & ${StyledNotificationBadge} { - ${tw`-left-8`} - transition: left linear 0s ${0.45 / $speed}s; - } - & ${StyledToggleButton} { transform: rotateZ(0deg); - transition: transform ease-in-out ${0.6 / $speed}s ${0.25 / $speed}s; + transition: transform ease-in-out ${0.6 / $speed}s + ${0.25 / $speed}s; } & ${StyledStorageContainer} { @@ -466,13 +514,68 @@ export const StyledSidebar = styled.aside` } } - & ${StyledRouterLink2} ${StyledRouterLink}._active { - & ${StyledRouteLinkIcon}, & ${StyledRouteLinkText} { - transition: color ease-in-out 0s ${0.45 / $speed}s !important; + & ${StyledNav1Link} { + & ${StyledRouterLink1} ${StyledRouteLinkIcon} { + ${tw`opacity-0 invisible`} + + transition: opacity ease-in-out ${0.2 / $speed}s 0s, + visibility linear ${0.2 / $speed}s 0s, + color ease-in-out 0.25s 0s !important; + } + + & ${StyledRouterLink1} ${StyledRouterLink}::after { + ${tw`top-0 opacity-100`} + transition: opacity ease-in-out ${0.7 / $speed}s ${0.2 / $speed}s, + top ease-in-out ${0.7 / $speed}s ${0.3 / $speed}s; + } + + & ${StyledRouteLinkIcon} { + transition: color ease-in-out 0.25s 0s !important; } - } `}; + + } + + ${({ theme, $showOpened }) => + $showOpened + ? css` + & ${StyledOpenedNav2LinkContainer} { + & ${StyledRouterLink2} ${StyledRouterLink}._active { + background-color: ${theme.component.sidebar.nav2.active + ?.background}; + + & ${StyledRouteLinkIcon}, & ${StyledRouteLinkText} { + color: ${theme.component.sidebar.nav2.active?.color}; + transition: color ease-in-out 0s !important; + } + } + } + ` + : css` + & ${StyledOpenedNav2LinkContainer} { + position: absolute; + visibility: hidden; + transition: visibility 500ms; + + & ${StyledRouterLink2} ${StyledRouterLink}._active { + & ${StyledRouteLinkIcon}, & ${StyledRouteLinkText} { + transition: color ease-in-out 0s !important; + } + } + } + `} + + ${({ $showClosed }) => + !$showClosed && + css` + & ${StyledClosedNav2LinkContainer} { + position: absolute; + visibility: hidden; + transition: visibility 500ms; + } + `} + ${({ $isOpen }) => $isOpen === undefined && css` diff --git a/src/themes/twentysix.ts b/src/themes/twentysix.ts index 4fea60d1..8cbf36f4 100644 --- a/src/themes/twentysix.ts +++ b/src/themes/twentysix.ts @@ -1033,9 +1033,13 @@ const sidebar: ThemeSidebar = { nav2: { color: color.white, background: color.dark2, - title: { + mainTitle: { color: color.light1, }, + title: { + color: color.white, + underline: color.main0, + }, progress: { color: color.main1, }, diff --git a/src/themes/types.ts b/src/themes/types.ts index 95d20aa3..6b3ac384 100644 --- a/src/themes/types.ts +++ b/src/themes/types.ts @@ -478,8 +478,12 @@ export type ThemeSidebar = { nav2: { color: string background: string + mainTitle?: { + color: string + } title?: { color: string + underline: string } progress: { color: string diff --git a/src/types.ts b/src/types.ts index d88cd991..e8e7e6ff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,9 +4,11 @@ import type { IconName } from './components/common/Icon' export type Route = { href: string external?: boolean + highlighted?: boolean target?: HTMLAnchorElement['target'] name?: string icon?: IconName + label?: string flag?: number exact?: boolean disabled?: boolean