Skip to content

Commit d005d79

Browse files
author
Oskar Widmark
committed
feat: dynamic toolbar resizing for mobile
1 parent 0a47995 commit d005d79

File tree

7 files changed

+121
-43
lines changed

7 files changed

+121
-43
lines changed

TODO.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ Features:
66
- [x] 2D color matrix visualization
77
- [ ] Spiral visualization
88
- [x] Highlight color based on action type
9-
- [ ] Mobile browser support
9+
- [x] Mobile browser support
1010

1111
Fixes:
1212

1313
- [ ] Heap sort wrong when unsorted first element is smallest
1414
- [x] Improve Bully Sort
1515
- [x] Cleaner draw logic with no gaps
16-
- [ ] Handle resizing to smaller height correctly
16+
- [x] Handle resizing to smaller height correctly
1717
- [ ] Make parallel highlight work for recursive sorting networks
1818
- [ ] Separate highlight types for parallel highlighting to work properly
1919
- [ ] Optimize current 2D color matrix visualization

src/App.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
flex: 1 1 auto;
3434
width: 100%;
3535
line-height: 0;
36+
min-height: 0vh;
3637
}
3738

3839
.chevron-wrapper {

src/AppBar.tsx

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,52 @@
1-
import { PlayCircle, StopCircle } from '@mui/icons-material';
1+
import {
2+
BarChart,
3+
Brush,
4+
MusicNote,
5+
MusicOff,
6+
PlayCircle,
7+
Refresh,
8+
StopCircle,
9+
} from '@mui/icons-material';
210
import {
311
Toolbar,
412
Button,
513
FormControlLabel,
6-
Switch,
714
Typography,
815
CircularProgress,
916
IconButton,
1017
AppBar,
18+
useTheme,
19+
useMediaQuery,
20+
Checkbox,
21+
Grid2,
22+
ButtonProps,
1123
} from '@mui/material';
1224
import MenuIcon from '@mui/icons-material/Menu';
1325
import { AppState, SortSettings, SortValue } from './types';
1426

27+
type AppBarButtonProps = {
28+
isCompact: boolean;
29+
text: string;
30+
icon: React.ReactElement;
31+
} & ButtonProps;
32+
33+
const AppBarButton = ({
34+
isCompact,
35+
icon,
36+
text,
37+
...props
38+
}: AppBarButtonProps) => (
39+
<Button
40+
variant="contained"
41+
color="secondary"
42+
disableElevation
43+
startIcon={!isCompact ? icon : undefined}
44+
{...props}
45+
>
46+
{!isCompact ? text : icon}
47+
</Button>
48+
);
49+
1550
interface AppBarProps {
1651
startSorting: (arr: SortValue[]) => Promise<void>;
1752
arr: SortValue[];
@@ -44,70 +79,78 @@ export function SortAppBar({
4479
shouldPlaySound,
4580
},
4681
}: AppBarProps) {
82+
const theme = useTheme();
83+
const isSm = useMediaQuery(theme.breakpoints.down('md'));
84+
const isM = useMediaQuery(theme.breakpoints.down('lg'));
85+
86+
const sortIcon = !isSorting ? <PlayCircle /> : <StopCircle />;
87+
4788
return (
4889
<AppBar position="relative">
49-
<Toolbar className="toolbar" onClick={onClick}>
90+
<Toolbar variant="dense" className="toolbar" onClick={onClick}>
5091
<div>
51-
<Button
52-
variant="contained"
53-
color="secondary"
92+
<AppBarButton
93+
isCompact={isM}
94+
text="Sort"
95+
icon={sortIcon}
5496
onClick={() => startSorting(arr)}
55-
disableElevation
56-
startIcon={!isSorting ? <PlayCircle /> : <StopCircle />}
57-
>
58-
Sort
59-
</Button>
97+
/>
6098
</div>
6199
<div>
62-
<Button
63-
variant="contained"
64-
color="secondary"
100+
<AppBarButton
101+
isCompact={isM}
102+
text="Shuffle"
103+
icon={<BarChart />}
65104
onClick={shuffleAndRedraw}
66-
disableElevation
67-
>
68-
Shuffle
69-
</Button>
105+
/>
70106
</div>
71107
<div>
72-
<Button
73-
variant="contained"
74-
color="secondary"
108+
<AppBarButton
109+
isCompact={isM}
110+
text="Reset"
111+
icon={<Refresh />}
75112
onClick={resetAndDraw}
76-
disableElevation
77-
>
78-
Reset
79-
</Button>
113+
/>
80114
</div>
81-
<div>
115+
<Grid2
116+
container
117+
direction="row"
118+
sx={{ flexWrap: 'nowrap', alignItems: 'center', flexShrink: 0 }}
119+
>
82120
<FormControlLabel
83121
control={
84-
<Switch
122+
<Checkbox
85123
checked={canDraw}
86124
onChange={toggleCanDraw}
87125
name="canDraw"
88126
color="secondary"
127+
icon={<Brush />}
128+
checkedIcon={<Brush />}
89129
/>
90130
}
91-
sx={{ marginRight: '0px' }}
92-
label="Draw Mode"
131+
sx={{ marginRight: isSm ? '0px' : undefined, marginLeft: '0px' }}
132+
label={!isSm ? 'Draw Mode' : ''}
133+
slotProps={{ typography: { whiteSpace: 'nowrap' } }}
93134
/>
94-
</div>
95-
<div>
96135
<FormControlLabel
97136
control={
98-
<Switch
137+
<Checkbox
99138
checked={shouldPlaySound}
100139
onChange={togglePlaySound}
101140
name="shouldPlaySound"
102141
color="secondary"
142+
icon={<MusicOff />}
143+
checkedIcon={<MusicNote />}
103144
/>
104145
}
105-
label="Play Sound"
146+
sx={{ marginRight: isSm ? '0px' : undefined, marginLeft: '0px' }}
147+
label={!isSm ? 'Play Sound' : ''}
148+
slotProps={{ typography: { whiteSpace: 'nowrap' } }}
106149
/>
107-
</div>
150+
</Grid2>
108151
<div>
109-
<Typography className="counter" align="left" color="white">
110-
Swaps:{' '}
152+
<Typography className="counter" align="left" color="white" noWrap>
153+
{!isM ? 'Swaps:' : 'S:'}{' '}
111154
{swapTime || !isSorting ? (
112155
nbrOfSwaps
113156
) : (
@@ -121,8 +164,8 @@ export function SortAppBar({
121164
</Typography>
122165
</div>
123166
<div>
124-
<Typography className="counter" align="left" color="white">
125-
Comparisons:{' '}
167+
<Typography className="counter" align="left" color="white" noWrap>
168+
{!isM ? 'Comparisons:' : 'C:'}{' '}
126169
{compareTime || !isSorting ? (
127170
nbrOfComparisons
128171
) : (
@@ -136,8 +179,8 @@ export function SortAppBar({
136179
</Typography>
137180
</div>
138181
<div>
139-
<Typography className="counter" align="left" color="white">
140-
Aux. writes:{' '}
182+
<Typography className="counter" align="left" color="white" noWrap>
183+
{!isM ? 'Aux. writes:' : 'AW:'}{' '}
141184
{auxWriteTime || !isSorting ? (
142185
nbrOfAuxWrites
143186
) : (

src/AppWithSound.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import React from 'react';
22
import App from './App';
3-
import { useSoundPlayer } from './useSoundPlayer';
3+
import { useSoundPlayer } from './hooks/useSoundPlayer';
44
import { NonCustomOscillatorType } from 'tone/build/esm/source/oscillator/OscillatorInterface';
55
import { gainToDb } from 'tone';
66
import { DEFAULT_SOUND_TYPE, DEFAULT_SOUND_VOLUME } from './constants';
7+
import { RotationRequest } from './RotationRequest';
8+
import { useIsPortrait } from './hooks/useIsPortrait';
79

810
export function AppWithSound(): React.ReactElement {
911
const [soundType, setSoundType] =
@@ -16,6 +18,12 @@ export function AppWithSound(): React.ReactElement {
1618
volume: gainToDb(volume / 100), // Convert volume percentage to decibels
1719
});
1820

21+
const isPortrait = useIsPortrait();
22+
23+
if (isPortrait) {
24+
return <RotationRequest />;
25+
}
26+
1927
return (
2028
<App
2129
playSound={play}

src/RotationRequest.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { RotateLeft } from '@mui/icons-material';
2+
import { Box, Typography } from '@mui/material';
3+
4+
export const RotationRequest = () => (
5+
<Box
6+
className="App"
7+
sx={(theme) => ({
8+
alignItems: 'center',
9+
justifyContent: 'center',
10+
display: 'flex',
11+
height: '100vh',
12+
textAlign: 'center',
13+
p: 2,
14+
backgroundColor: theme.palette.background.default,
15+
})}
16+
>
17+
<RotateLeft color="primary" sx={{ marginRight: '4px' }} />
18+
<Typography color="primary">Please rotate to landscape mode.</Typography>
19+
</Box>
20+
);

src/hooks/useIsPortrait.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { useMediaQuery, useTheme } from '@mui/material';
2+
3+
export const useIsPortrait = () => {
4+
const theme = useTheme();
5+
return useMediaQuery(theme.breakpoints.down('sm'));
6+
};

0 commit comments

Comments
 (0)