Merge pull request #12 from m-tartari/refactor/home/mobile

♻️ refactor: make mobile friendly homepage
This commit is contained in:
2024-02-18 15:46:40 +01:00
committed by GitHub
20 changed files with 216 additions and 4460 deletions
+7
View File
@@ -16,3 +16,10 @@ updates:
patterns: patterns:
- 'vitest' - 'vitest'
- '@vitest*' - '@vitest*'
typescript-eslint:
patterns:
- '@typescript-eslint*'
mui:
patterns:
- '@mui/material'
- '@mui/icon-material'
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -13,7 +13,7 @@ const Hero = (props: { scrollTarget?: React.ForwardedRef<HTMLSelectElement> }) =
minHeight: '100vh', minHeight: '100vh',
boxShadow: '0 0.25em 0.5em 0 rgba(0, 0, 0, 0.25)', boxShadow: '0 0.25em 0.5em 0 rgba(0, 0, 0, 0.25)',
backgroundImage: 'url("images/banner-blur.jpg")', backgroundImage: 'url("images/banner-blur.webp")',
backgroundPosition: 'center center', backgroundPosition: 'center center',
backgroundAttachment: 'fixed', backgroundAttachment: 'fixed',
backgroundSize: 'cover', backgroundSize: 'cover',
@@ -2,6 +2,7 @@ import React from 'react'
import { ParallaxBanner, ParallaxProvider } from 'react-scroll-parallax' import { ParallaxBanner, ParallaxProvider } from 'react-scroll-parallax'
import { Box, Grid, IconButton, Typography, useTheme } from '@mui/material' import { Box, Grid, IconButton, Typography, useTheme } from '@mui/material'
import { ExpandMore } from '@mui/icons-material' import { ExpandMore } from '@mui/icons-material'
import { useViewport } from '../../utils/viewport'
type AnchorType = 'left' | 'top' | 'right' | 'bottom' type AnchorType = 'left' | 'top' | 'right' | 'bottom'
@@ -10,6 +11,7 @@ type Props = {
title: string title: string
caption: React.ReactNode caption: React.ReactNode
children: React.ReactNode children: React.ReactNode
mobileImage?: string
ref?: React.ForwardedRef<HTMLSelectElement> ref?: React.ForwardedRef<HTMLSelectElement>
anchor?: AnchorType anchor?: AnchorType
scrollTarget?: React.ForwardedRef<HTMLSelectElement> scrollTarget?: React.ForwardedRef<HTMLSelectElement>
@@ -124,7 +126,7 @@ const Description = (props: Props) => {
} }
} }
const ProjectSpotlight = React.forwardRef<HTMLSelectElement, Props>((props, ref) => { const DesktopComponent = React.forwardRef<HTMLSelectElement, Props>((props, ref) => {
return ( return (
<section ref={ref}> <section ref={ref}>
<ParallaxProvider> <ParallaxProvider>
@@ -163,4 +165,51 @@ const ProjectSpotlight = React.forwardRef<HTMLSelectElement, Props>((props, ref)
) )
}) })
const MobileComponent = React.forwardRef<HTMLSelectElement, Props>((props, ref) => {
const theme = useTheme()
return (
<>
<Box
ref={ref}
component="img"
sx={{
width: '100%',
height: '100%',
maxHeight: '60vh',
}}
src={props.mobileImage ?? props.image}
/>
<Box
sx={{
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderTopStyle: 'solid',
borderTopWidth: theme.spacing(0.5),
borderTopColor: theme.palette.primary.main,
p: `${theme.spacing(7)} ${theme.spacing(5)} ${theme.spacing(5.5)} ${theme.spacing(5)}`,
}}>
<Grid container spacing={2} maxWidth="xl">
<Grid item xs={12} md={4}>
<Typography variant="h4">{props.title}</Typography>
{props.caption}
</Grid>
<Grid item component={Typography} xs={12} md={8}>
{props.children}
</Grid>
</Grid>
</Box>
</>
)
})
const ProjectSpotlight = React.forwardRef<HTMLSelectElement, Props>((props, ref) => {
const { width } = useViewport()
const theme = useTheme()
return width < theme.breakpoints.values.md ? <MobileComponent {...props} ref={ref} /> : <DesktopComponent {...props} ref={ref} />
})
export default ProjectSpotlight export default ProjectSpotlight
@@ -5,9 +5,23 @@ import { renderWithRouter as render } from 'components/utils/tests'
import MainPage from '.' import MainPage from '.'
// Tests // Tests
describe('Main page', async () => { describe('Main page', () => {
it('Should render the page correctly', async () => { it('Should render the page correctly on desktop', async () => {
// Setup await render(<MainPage />)
// Make sure the page title is correct
expect(screen.getByText('Robotics Engineer')).toBeInTheDocument()
// Make sure the project spotlights are present
expect(screen.getByText('LINFA: An Intuitive Block-Programming Interface for Reconfigurable Robots')).toBeInTheDocument()
expect(screen.getByText("Revamping HHCM's Modular: Swift Generation of Modular Robot Models")).toBeInTheDocument()
expect(screen.getByText('Automated Actuators and Robotic Modules Calibration')).toBeInTheDocument()
expect(screen.getByText('Voice Controlled Mobile Robot for Indoor Applications')).toBeInTheDocument()
})
it('Should render the page correctly on mobile', async () => {
// Set the window size to mobile (Galaxy Note 20 Android 11)
window.innerWidth = 412
window.innerHeight = 915
await render(<MainPage />) await render(<MainPage />)
// Make sure the page title is correct // Make sure the page title is correct
+17 -11
View File
@@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import { Box, Typography } from '@mui/material' import { Box, Typography } from '@mui/material'
import { ViewportProvider } from 'components/utils/viewport'
import { AcademyIconLink, DescriptionIconLink, GitHubIconLink, VideoIconLink } from 'components/atoms' import { AcademyIconLink, DescriptionIconLink, GitHubIconLink, VideoIconLink } from 'components/atoms'
import { Footer, Hero, Toobar, ProjectSpotlight } from 'components/molecules' import { Footer, Hero, Toobar, ProjectSpotlight } from 'components/molecules'
@@ -15,6 +16,7 @@ const MainPage = () => {
const refWUTThesis = React.useRef<HTMLSelectElement>(null) const refWUTThesis = React.useRef<HTMLSelectElement>(null)
return ( return (
<ViewportProvider>
<Box aria-label="page" sx={{ flexGrow: 1 }}> <Box aria-label="page" sx={{ flexGrow: 1 }}>
<Toobar position="absolute" sx={{ backgroundColor: 'transparent' }} /> <Toobar position="absolute" sx={{ backgroundColor: 'transparent' }} />
<Hero scrollTarget={refIITLinfa} /> <Hero scrollTarget={refIITLinfa} />
@@ -23,7 +25,7 @@ const MainPage = () => {
ref={refIITLinfa} ref={refIITLinfa}
scrollTarget={refIITModular} scrollTarget={refIITModular}
anchor="bottom" anchor="bottom"
image="images/IIT_Linfa.png" image="images/IIT_Linfa.webp"
title="LINFA: An Intuitive Block-Programming Interface for Reconfigurable Robots" title="LINFA: An Intuitive Block-Programming Interface for Reconfigurable Robots"
caption={ caption={
<> <>
@@ -47,6 +49,7 @@ const MainPage = () => {
anchor="right" anchor="right"
// anchor="left" // anchor="left"
image="images/IIT_Modular.png" image="images/IIT_Modular.png"
mobileImage="images/IIT_Modular_Mobile.webp"
title="Revamping HHCM's Modular: Swift Generation of Modular Robot Models" title="Revamping HHCM's Modular: Swift Generation of Modular Robot Models"
caption={ caption={
<> <>
@@ -59,15 +62,16 @@ const MainPage = () => {
This project focused on overhauling HHCM's Modular, enhancing its graphical interface, API, and deployment pipeline. Modular serves as a web This project focused on overhauling HHCM's Modular, enhancing its graphical interface, API, and deployment pipeline. Modular serves as a web
application for the swift generation of modular robot models, with an offline approach (Building Mode) enabling users to set up robots in a application for the swift generation of modular robot models, with an offline approach (Building Mode) enabling users to set up robots in a
virtual environment by selecting and assembling reconfigurable modules, immediately visualized in 3D. Additionally, an online approach virtual environment by selecting and assembling reconfigurable modules, immediately visualized in 3D. Additionally, an online approach
(Discovery Mode) allows users to query the EtherCAT master for network topology, recognizing and displaying robots built from physical modules (Discovery Mode) allows users to query the EtherCAT master for network topology, recognizing and displaying robots built from physical
in 3D. The system seamlessly produces URDF, SRDF, and a full ROS package that can then be utilized for simulating and controlling the robot. modules in 3D. The system seamlessly produces URDF, SRDF, and a full ROS package that can then be utilized for simulating and controlling
the robot.
</ProjectSpotlight> </ProjectSpotlight>
{/* 3 IIT - Motor Calibration */} {/* 3 IIT - Motor Calibration */}
<ProjectSpotlight <ProjectSpotlight
ref={refIITCalibration} ref={refIITCalibration}
scrollTarget={refWUTThesis} scrollTarget={refWUTThesis}
anchor="left" anchor="left"
image="images/IIT_Modules.jpg" image="images/IIT_Modules.webp"
title="Automated Actuators and Robotic Modules Calibration" title="Automated Actuators and Robotic Modules Calibration"
caption={ caption={
<> <>
@@ -77,8 +81,8 @@ const MainPage = () => {
</Typography> </Typography>
</> </>
}> }>
This project focused on automating the calibration of EtherCAT actuators and robotic modules, aiming for a fully automated procedure for newly This project focused on automating the calibration of EtherCAT actuators and robotic modules, aiming for a fully automated procedure for
assembled actuators. The process involved testing the PDO, conducting phase angle calibration, torque sensor calibration, ripple and newly assembled actuators. The process involved testing the PDO, conducting phase angle calibration, torque sensor calibration, ripple and
position-dependent torque offset calibration, inertia and friction identification, and frequency response calibration. The goal was to position-dependent torque offset calibration, inertia and friction identification, and frequency response calibration. The goal was to
optimize motor performance and collect necessary data for accurate characterization, with results stored centrally and presented in a optimize motor performance and collect necessary data for accurate characterization, with results stored centrally and presented in a
comprehensive PDF report. comprehensive PDF report.
@@ -88,6 +92,7 @@ const MainPage = () => {
ref={refWUTThesis} ref={refWUTThesis}
anchor="bottom" anchor="bottom"
image="images/thesis.jpg" image="images/thesis.jpg"
mobileImage="images/thesis.webp"
title="Voice Controlled Mobile Robot for Indoor Applications" title="Voice Controlled Mobile Robot for Indoor Applications"
caption={ caption={
<> <>
@@ -103,14 +108,15 @@ const MainPage = () => {
</Typography> </Typography>
</> </>
}> }>
This thesis proposes a design for a small mobile robot controlled by voice. Firstly a preliminary analysis of the state of art of human-robot This thesis proposes a design for a small mobile robot controlled by voice. Firstly a preliminary analysis of the state of art of
interaction has been conducted. Then, an analysis of the equipment selected took place, and, later, appropriate methodologies for voice human-robot interaction has been conducted. Then, an analysis of the equipment selected took place, and, later, appropriate methodologies
recognition and user detection have been evaluated for this specific case. Finally, an overview of the complete software architecture of the for voice recognition and user detection have been evaluated for this specific case. Finally, an overview of the complete software
system was given with three possible structures and their comparison. The results obtained show both the feasibility of using low power boards architecture of the system was given with three possible structures and their comparison. The results obtained show both the feasibility of
and some of their limitations. using low power boards and some of their limitations.
</ProjectSpotlight> </ProjectSpotlight>
<Footer /> <Footer />
</Box> </Box>
</ViewportProvider>
) )
} }
@@ -0,0 +1,21 @@
import React from 'react'
import viewportContext from './viewportContext'
const ViewportProvider = (props: { children: React.ReactNode }) => {
const [width, setWidth] = React.useState(window.innerWidth)
const [height, setHeight] = React.useState(window.innerHeight)
const handleWindowResize = () => {
setWidth(window.innerWidth)
setHeight(window.innerHeight)
}
React.useEffect(() => {
window.addEventListener('resize', handleWindowResize)
return () => window.removeEventListener('resize', handleWindowResize)
}, [])
return <viewportContext.Provider value={{ width, height }}>{props.children}</viewportContext.Provider>
}
export default ViewportProvider
+3
View File
@@ -0,0 +1,3 @@
export { default as viewportContext } from './viewportContext'
export { default as ViewportProvider } from './ViewportProvider'
export { default as useViewport } from './useViewport'
@@ -0,0 +1,10 @@
import React from 'react'
import viewportContext from './viewportContext'
const useViewport = () => {
const { width, height } = React.useContext(viewportContext)
return { width, height }
}
export default useViewport
@@ -0,0 +1,6 @@
import React from 'react'
type Viewport = { width: number; height: number }
const viewportContext = React.createContext<Viewport>({ width: window.innerWidth, height: window.innerHeight })
export default viewportContext