main
ts 104 lines 4.97 KB
Raw
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import { createElement as h, FC } from 'react';
4 import { List, ListItemButton, ListItemIcon, ListItemText, Box } from '@mui/material'
5 import {
6 AccountTree, Extension, History, Home, Logout, ManageAccounts, Monitor, Public, Settings, Translate, Code,
7 SvgIconComponent
8 } from '@mui/icons-material'
9 import _ from 'lodash'
10 import { Link, useLocation } from 'wouter'
11 import MonitorPage from './MonitorPage'
12 import OptionsPage from './OptionsPage';
13 import VfsPage from './VfsPage';
14 import AccountsPage from './AccountsPage';
15 import HomePage from './HomePage'
16 import LogoutPage from './LogoutPage';
17 import LangPage from './LangPage'
18 import LogsPage from './LogsPage';
19 import PluginsPage from './PluginsPage';
20 import { getHFS, HIDE_IN_TESTS, replaceStringToReact, WEBSITE } from '@hfs/shared'
21 import CustomHtmlPage from './CustomHtmlPage';
22 import InternetPage from './InternetPage'
23 import { useWindowSize } from 'usehooks-ts'
24 import { hTooltip } from './mui'
25 import { PageProps } from './App'
26 import { confirmDialog } from './dialog'
27
28 export interface MenuEntry {
29 path: `/${string}`
30 icon: SvgIconComponent
31 label?: string
32 title?: string
33 comp: FC<PageProps>
34 noPaddingOnMobile?: true
35 subRoutes?: true
36 }
37
38 export const mainMenu: MenuEntry[] = [
39 { path: '/', icon: Home, label: "Home", comp: HomePage },
40 { path: '/fs', icon: AccountTree, label: "Shared files", comp: VfsPage },
41 { path: '/accounts', icon: ManageAccounts, comp: AccountsPage },
42 { path: '/options', icon: Settings, comp: OptionsPage },
43 { path: '/internet', icon: Public, comp: InternetPage },
44 { path: '/monitoring', icon: Monitor, comp: MonitorPage, noPaddingOnMobile: true },
45 { path: '/logs', icon: History, comp: LogsPage, noPaddingOnMobile: true, subRoutes: true },
46 { path: '/language', icon: Translate, comp: LangPage },
47 { path: '/plugins', icon: Extension, comp: PluginsPage, noPaddingOnMobile: true, subRoutes: true },
48 { path: '/html', icon: Code, label: "Custom HTML", comp: CustomHtmlPage },
49 { path: '/logout', icon: Logout, comp: LogoutPage }
50 ]
51
52 export default function Menu({ onSelect, itemTitle }: { onSelect: ()=>void, itemTitle: (idx: number) => string }) {
53 const { VERSION } = getHFS()
54 const logo = 'hfs-logo.svg'
55 const short = useWindowSize().height < 700
56 const currentPath = useLocation()[0]
57 return h(Box, { sx: { display: 'flex', flexDirection: 'column', bgcolor: 'primary.main', minHeight: '100%' } },
58 h(List, {
59 sx:{
60 pr: 1, py: 0, color: 'primary.contrastText',
61 height: '100vh', boxSizing: 'border-box', // grow as screen permits, so we know the extra space for the logo
62 overflowY: 'auto', // ...and account for clipping
63 position: 'sticky', top: 0, // be independent (scrolling-wise)
64 display: 'flex', flexDirection: 'column', '&>a': { flex: '0' },
65 }
66 },
67 h(Box, { id: 'hfs-name', sx: { display: 'flex', px: 2, py: .5, gap: 2, alignItems: 'center' } },
68 h(Box, {
69 sx: { color: 'primary.contrastText', fontSize: 'min(3rem, max(5vw, 4vh))', cursor: 'pointer' },
70 async onClick() {
71 if (await confirmDialog("Open HFS website?"))
72 window.open(WEBSITE)
73 }
74 }, 'HFS'),
75 h(Box, { sx: { fontSize: 'small' }, className: HIDE_IN_TESTS }, replaceStringToReact(VERSION||'', /-/, () => h('br'))),
76 short && h('img', { src: logo, style: { height: '2.5em' } }),
77 ),
78 mainMenu.map((it, idx) => hTooltip( itemTitle(idx), getMenuLabel(it) + ' ' + itemTitle(idx),
79 h(ListItemButton, {
80 // @ts-expect-error mui createElement overload does not infer custom Link props
81 component: Link,
82 href: it.path,
83 onClick: onSelect,
84 selected: matchesMenuPath(it, 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)),
89 h(ListItemText, { sx: { whiteSpace: 'nowrap' }, primary: getMenuLabel(it) })
90 ),
91 { key: it.path, placement: 'right' }
92 )),
93 !short && h(Box, { id: 'hfs-logo', sx: { flex: 1, opacity: .7, background: `url(${logo}) no-repeat bottom`, backgroundSize: 'contain', margin: 2 } }),
94 )
95 )
96 }
97
98 export function getMenuLabel(it: MenuEntry) {
99 return it && (it.label ?? _.capitalize(it.path.slice(1)))
100 }
101
102 export function matchesMenuPath(it: MenuEntry, path: string) {
103 return path === it.path || path.startsWith(it.path + '/')
104 }