admin: highlight selected entry
Massimo Melina committed
Apr 27, 2026 at 14:46 UTC
2335d3c5b7fbd92b51e1b5c9f6ed7a3ba664646a
2 files changed
+8
-35
admin/src/MainMenu.ts
+7
-4
@@ -7,7 +7,7 @@ import {
7
SvgIconComponent
8
} from '@mui/icons-material'
9
import _ from 'lodash'
10
-import { NavLink } from './router'
10
+import { Link, useLocation } from './router'
11
import MonitorPage from './MonitorPage'
12
import OptionsPage from './OptionsPage';
13
import VfsPage from './VfsPage';
@@ -52,6 +52,7 @@ export default function Menu({ onSelect, itemTitle }: { onSelect: ()=>void, item
52
const { VERSION } = getHFS()
53
const logo = 'hfs-logo.svg'
54
const short = useWindowSize().height < 700
55
+ const currentPath = useLocation().pathname.slice(1)
56
return h(Box, { sx: { display: 'flex', flexDirection: 'column', bgcolor: 'primary.main', minHeight: '100%' } },
57
h(List, {
58
sx:{
@@ -75,11 +76,13 @@ export default function Menu({ onSelect, itemTitle }: { onSelect: ()=>void, item
76
),
77
mainMenu.map((it, idx) => hTooltip( itemTitle(idx), getMenuLabel(it) + ' ' + itemTitle(idx),
78
h(ListItemButton, {
79
+ // @ts-expect-error mui createElement overload does not infer custom Link props
80
+ component: Link,
81
to: it.path,
79
- component: NavLink,
82
onClick: onSelect,
81
- // @ts-ignore
82
- style: ({ isActive }) => isActive ? { textDecoration: 'underline' } : {},
83
+ // home has to match exactly because every path starts with an empty prefix
84
+ selected: it.path ? currentPath === it.path || currentPath.startsWith(it.path + '/') : !currentPath,
85
+ sx: { '&.Mui-selected': { '&,&:hover': { bgcolor: 'primary.dark', textDecoration: 'underline' } } },
86
children: undefined, // shut up ts
87
},
88
it.icon && h(ListItemIcon, { sx: { color: 'primary.contrastText', minWidth: 48 } }, h(it.icon)),
admin/src/router.ts
+1
-31
@@ -1,5 +1,5 @@
1
import { Children, cloneElement, createElement as h, forwardRef, Fragment, isValidElement } from 'react'
2
-import type { AnchorHTMLAttributes, ComponentType, CSSProperties, ReactElement, ReactNode } from 'react'
2
+import type { AnchorHTMLAttributes, ComponentType, ReactElement, ReactNode } from 'react'
3
import { Link as WouterLink, Route as WouterRoute, Router, Switch, useLocation as useWouterLocation } from 'wouter'
4
import { useHashLocation } from 'wouter/use-hash-location'
5
@@ -7,17 +7,6 @@ export type LinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> &
7
to: string
8
}
9
10
-type NavLinkRenderProps = {
11
- isActive: boolean
12
-}
13
-
14
-type NavLinkProps = Omit<LinkProps, 'children' | 'className' | 'style'> & {
15
- children?: ReactNode | ((props: NavLinkRenderProps) => ReactNode)
16
- className?: string | ((props: NavLinkRenderProps) => string | undefined)
17
- end?: boolean
18
- style?: CSSProperties | ((props: NavLinkRenderProps) => CSSProperties | undefined)
19
-}
20
-
10
type RouteProps = {
11
path?: string
12
element?: ReactElement
@@ -43,25 +32,6 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link({ to,
32
return h(WouterLink as unknown as ComponentType<any>, { ...rest, ref, href: normalizePath(to) })
33
})
34
46
-export const NavLink = forwardRef<HTMLAnchorElement, NavLinkProps>(function NavLink({ to, end, className, style, children, ...rest }, ref) {
47
- const [pathname] = useWouterLocation()
48
- const targetPath = normalizePath(to)
49
- const isActive = end
50
- ? pathname === targetPath
51
- : targetPath === '/'
52
- ? pathname === '/'
53
- : pathname === targetPath || pathname.startsWith(`${targetPath}/`)
54
- const activeProps = { isActive }
55
- return h(Link, {
56
- ref,
57
- to: targetPath,
58
- ...rest,
59
- className: typeof className === 'function' ? className(activeProps) : className,
60
- style: typeof style === 'function' ? style(activeProps) : style,
61
- children: typeof children === 'function' ? children(activeProps) : children,
62
- })
63
-})
64
-
35
export function useNavigate() {
36
const [, setLocation] = useWouterLocation()
37
return (to: string, options?: { replace?: boolean }) => {