@samitouri / QOSami-HFS / commits / 2335d3c5

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 }) => {