Skip to content

Commit a17d847

Browse files
committed
Improve responsive UI shell behavior
1 parent c27a381 commit a17d847

6 files changed

Lines changed: 312 additions & 179 deletions

File tree

ui/src/common/ConnectionErrorBanner.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@ interface ConnectionErrorBannerProps {
88
message: string;
99
}
1010

11-
export const ConnectionErrorBanner = ({height, retry, message}: ConnectionErrorBannerProps) => (
11+
export const ConnectionErrorBanner = ({ height, retry, message }: ConnectionErrorBannerProps) => (
1212
<div
1313
style={{
1414
backgroundColor: '#e74c3c',
15-
height,
15+
minHeight: height,
1616
width: '100%',
1717
zIndex: 1300,
1818
position: 'relative',
19+
display: 'flex',
20+
alignItems: 'center',
21+
justifyContent: 'center',
22+
padding: '8px 16px',
23+
boxSizing: 'border-box',
1924
}}>
20-
<Typography align="center" variant="h6" style={{lineHeight: `${height}px`}}>
25+
<Typography align="center" variant="h6" style={{ lineHeight: 1.4 }}>
2126
{message}{' '}
2227
<Button variant="outlined" onClick={retry}>
2328
Retry

ui/src/common/ScrollUpButton.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ const ScrollUpButton = () => {
99
const currentScrollPos = Math.max(window.pageYOffset - 1000, 0);
1010
const opacity = Math.min(currentScrollPos / 1000, 1);
1111
const nextState = {display: currentScrollPos > 0 ? 'inherit' : 'none', opacity};
12-
if (state.display !== nextState.display || state.opacity !== nextState.opacity) {
13-
setState(nextState);
14-
}
12+
setState(nextState);
1513
};
14+
scrollHandler();
1615
window.addEventListener('scroll', scrollHandler);
1716
return () => window.removeEventListener('scroll', scrollHandler);
1817
}, []);

ui/src/layout/Header.tsx

Lines changed: 80 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import AppBar from '@mui/material/AppBar';
2-
import Button, {ButtonProps} from '@mui/material/Button';
2+
import Button, { ButtonProps } from '@mui/material/Button';
33
import IconButton from '@mui/material/IconButton';
4-
import {Theme} from '@mui/material/styles';
5-
import {makeStyles} from 'tss-react/mui';
4+
import { Theme } from '@mui/material/styles';
5+
import { makeStyles } from 'tss-react/mui';
66
import Toolbar from '@mui/material/Toolbar';
77
import Typography from '@mui/material/Typography';
88
import AccountCircle from '@mui/icons-material/AccountCircle';
@@ -16,10 +16,11 @@ import GitHubIcon from '@mui/icons-material/GitHub';
1616
import MenuIcon from '@mui/icons-material/Menu';
1717
import Apps from '@mui/icons-material/Apps';
1818
import SupervisorAccount from '@mui/icons-material/SupervisorAccount';
19-
import React, {CSSProperties} from 'react';
20-
import {Link} from 'react-router';
21-
import {useMediaQuery} from '@mui/material';
22-
import {ThemeKey} from './theme';
19+
import React, { CSSProperties } from 'react';
20+
import { Link } from 'react-router';
21+
import { useMediaQuery } from '@mui/material';
22+
import Tooltip from '@mui/material/Tooltip';
23+
import { ThemeKey } from './theme';
2324

2425
const themeIcons: Record<ThemeKey, React.ReactElement> = {
2526
dark: <Brightness4 />,
@@ -36,9 +37,6 @@ const useStyles = makeStyles()((theme: Theme) => ({
3637
},
3738
toolbar: {
3839
justifyContent: 'space-between',
39-
[theme.breakpoints.down('sm')]: {
40-
flexWrap: 'wrap',
41-
},
4240
},
4341
menuButtons: {
4442
display: 'flex',
@@ -47,12 +45,9 @@ const useStyles = makeStyles()((theme: Theme) => ({
4745
},
4846
justifyContent: 'center',
4947
[theme.breakpoints.down('sm')]: {
50-
flexBasis: '100%',
51-
marginTop: 5,
52-
order: 1,
53-
height: 50,
54-
justifyContent: 'space-between',
55-
alignItems: 'center',
48+
flex: 'none',
49+
height: 'auto',
50+
margin: 0,
5651
},
5752
},
5853
title: {
@@ -81,6 +76,7 @@ interface IProps {
8176
showSettings: VoidFunction;
8277
logout: VoidFunction;
8378
style: CSSProperties;
79+
navOpen: boolean;
8480
setNavOpen: (open: boolean) => void;
8581
}
8682

@@ -92,16 +88,17 @@ const Header = ({
9288
toggleTheme,
9389
logout,
9490
style,
91+
navOpen,
9592
setNavOpen,
9693
showSettings,
9794
themeMode,
9895
}: IProps) => {
99-
const {classes} = useStyles();
96+
const { classes } = useStyles();
10097
const themeLabel = `Toggle theme (current: ${themeMode})`;
10198
const themeIcon = themeIcons[themeMode];
10299
return (
103100
<AppBar
104-
sx={{position: {xs: 'sticky', sm: 'fixed'}}}
101+
sx={{ position: { xs: 'sticky', sm: 'fixed' } }}
105102
style={style}
106103
className={classes.appBar}>
107104
<Toolbar className={classes.toolbar}>
@@ -128,29 +125,34 @@ const Header = ({
128125
admin={admin}
129126
name={name}
130127
logout={logout}
128+
navOpen={navOpen}
131129
setNavOpen={setNavOpen}
132130
showSettings={showSettings}
133131
/>
134132
)}
135133
<div>
136-
<IconButton
137-
onClick={toggleTheme}
138-
color="inherit"
139-
size="large"
140-
title={themeLabel}
141-
aria-label={themeLabel}>
142-
{themeIcon}
143-
</IconButton>
144-
145-
<a
146-
href="https://github.com/gotify/server"
147-
className={classes.link}
148-
target="_blank"
149-
rel="noopener noreferrer">
150-
<IconButton color="inherit" size="large">
151-
<GitHubIcon />
134+
<Tooltip title={themeLabel} arrow>
135+
<IconButton
136+
onClick={toggleTheme}
137+
color="inherit"
138+
size="large"
139+
aria-label={themeLabel}>
140+
{themeIcon}
152141
</IconButton>
153-
</a>
142+
</Tooltip>
143+
144+
<Tooltip title="Gotify on GitHub" arrow>
145+
<a
146+
href="https://github.com/gotify/server"
147+
className={classes.link}
148+
target="_blank"
149+
rel="noopener noreferrer"
150+
aria-label="Gotify on GitHub">
151+
<IconButton color="inherit" size="large" aria-label="Gotify on GitHub">
152+
<GitHubIcon />
153+
</IconButton>
154+
</a>
155+
</Tooltip>
154156
</div>
155157
</Toolbar>
156158
</AppBar>
@@ -162,53 +164,60 @@ const Buttons = ({
162164
name,
163165
admin,
164166
logout,
167+
navOpen,
165168
setNavOpen,
166169
}: {
167170
name: string;
168171
admin: boolean;
169172
logout: VoidFunction;
173+
navOpen: boolean;
170174
setNavOpen: (open: boolean) => void;
171175
showSettings: VoidFunction;
172176
}) => {
173-
const {classes} = useStyles();
177+
const { classes } = useStyles();
178+
const mobile = useMediaQuery('(max-width:600px)');
174179

175180
return (
176181
<div className={classes.menuButtons}>
177182
<ResponsiveButton
178-
sx={{display: {sm: 'none', xs: 'block'}}}
183+
sx={{ display: { sm: 'none', xs: 'block' } }}
179184
icon={<MenuIcon />}
180-
onClick={() => setNavOpen(true)}
185+
onClick={() => setNavOpen(!navOpen)}
181186
label="menu"
182187
color="inherit"
183188
/>
184-
{admin && (
185-
<Link className={classes.link} to="/users" id="navigate-users">
186-
<ResponsiveButton icon={<SupervisorAccount />} label="users" color="inherit" />
187-
</Link>
189+
{!mobile && (
190+
<>
191+
{admin && (
192+
<Link className={classes.link} to="/users" id="navigate-users">
193+
<ResponsiveButton icon={<SupervisorAccount />} label="users" color="inherit" />
194+
</Link>
195+
)}
196+
<Link className={classes.link} to="/applications" id="navigate-apps">
197+
<ResponsiveButton icon={<Chat />} label="apps" color="inherit" />
198+
</Link>
199+
<Link className={classes.link} to="/clients" id="navigate-clients">
200+
<ResponsiveButton icon={<DevicesOther />} label="clients" color="inherit" />
201+
</Link>
202+
<Link className={classes.link} to="/plugins" id="navigate-plugins">
203+
<ResponsiveButton icon={<Apps />} label="plugins" color="inherit" />
204+
</Link>
205+
<ResponsiveButton
206+
icon={<AccountCircle />}
207+
label={name}
208+
onClick={showSettings}
209+
id="changepw"
210+
color="inherit"
211+
/>
212+
<ResponsiveButton
213+
icon={<ExitToApp />}
214+
label="Logout"
215+
onClick={logout}
216+
id="logout"
217+
color="inherit"
218+
/>
219+
</>
188220
)}
189-
<Link className={classes.link} to="/applications" id="navigate-apps">
190-
<ResponsiveButton icon={<Chat />} label="apps" color="inherit" />
191-
</Link>
192-
<Link className={classes.link} to="/clients" id="navigate-clients">
193-
<ResponsiveButton icon={<DevicesOther />} label="clients" color="inherit" />
194-
</Link>
195-
<Link className={classes.link} to="/plugins" id="navigate-plugins">
196-
<ResponsiveButton icon={<Apps />} label="plugins" color="inherit" />
197-
</Link>
198-
<ResponsiveButton
199-
icon={<AccountCircle />}
200-
label={name}
201-
onClick={showSettings}
202-
id="changepw"
203-
color="inherit"
204-
/>
205-
<ResponsiveButton
206-
icon={<ExitToApp />}
207-
label="Logout"
208-
onClick={logout}
209-
id="logout"
210-
color="inherit"
211-
/>
212221
</div>
213222
);
214223
};
@@ -220,13 +229,15 @@ const ResponsiveButton: React.FC<{
220229
id?: string;
221230
onClick?: () => void;
222231
icon: React.ReactNode;
223-
}> = ({icon, label, ...rest}) => {
232+
}> = ({ icon, label, ...rest }) => {
224233
const matches = useMediaQuery('(max-width:1000px)');
225234
if (matches) {
226235
return (
227-
<IconButton {...rest} size="large">
228-
{icon}
229-
</IconButton>
236+
<Tooltip title={label} arrow>
237+
<IconButton {...rest} size="large" aria-label={label}>
238+
{icon}
239+
</IconButton>
240+
</Tooltip>
230241
);
231242
}
232243
return (

0 commit comments

Comments
 (0)