Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile layout #134

Merged
merged 7 commits into from
Nov 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# CHANGELOG
## Unreleased
- Adapt layout for mobile [#134](https://github.com/CartoDB/carto-react-template/pull/134)

## 1.0.0-beta4 (2020-11-25)
- Update @carto/react dependency to 1.0.0-beta5
Expand All @@ -10,4 +12,4 @@
- Fix routes with missing slash [#125](https://github.com/CartoDB/carto-react-template/pull/125)

## 1.0.0-beta1 (2020-11-20)
- Initial version: 2 templates, simple and full app
- Initial version: 2 templates, simple and full app
10 changes: 10 additions & 0 deletions template-sample-app/template/public/logo-xs.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion template-sample-app/template/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Grid container direction='column' className={classes.root}>
<Grid container direction='column' alignItems='stretch' className={classes.root}>
<Header></Header>
{routing}
</Grid>
Expand Down
138 changes: 107 additions & 31 deletions template-sample-app/template/src/components/common/Header.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import {
AppBar,
Tab,
Tabs,
Toolbar,
Drawer,
Divider,
Hidden,
Grid,
IconButton,
Link,
makeStyles,
Tab,
Tabs,
Toolbar,
Typography,
Divider,
} from '@material-ui/core';
import MenuIcon from '@material-ui/icons/Menu';
import UserMenu from 'components/views/UserMenu';
import { NavLink, useLocation } from 'react-router-dom';

const useStyles = makeStyles((theme) => ({
navBar: {
boxShadow: 'none',
zIndex: theme.zIndex.modal + 1,
overflow: 'hidden',
},
navTabs: {
'& .MuiTabs-indicator': {
Expand All @@ -25,6 +31,12 @@ const useStyles = makeStyles((theme) => ({
divider: {
margin: theme.spacing(0, 3),
},
menuButton: {
marginRight: theme.spacing(2),
},
drawer: {
minWidth: 260,
},
title: {
'& h1': {
fontWeight: theme.typography.fontWeightRegular,
Expand All @@ -35,48 +47,112 @@ const useStyles = makeStyles((theme) => ({
marginRight: theme.spacing(2),
verticalAlign: 'bottom',
},

'& img + hr': {
display: 'inline-block',
height: '1em',
marginRight: theme.spacing(2),
verticalAlign: 'text-bottom'
}
},
},
}));

const NavigationMenu = (props) => {
const { location, column: vertical } = props;
const classes = useStyles();

return (
<React.Fragment>
<Grid
container
direction={vertical ? 'column' : 'row'}
className={!vertical ? classes.navTabs : ''}
>
<Tabs
value={location.pathname.split('/')[1] || 'stores'}
textColor='inherit'
orientation={vertical ? 'vertical' : 'horizontal'}
variant={vertical ? 'fullWidth' : 'standard'}
>
<Tab label='Stores' value='stores' component={NavLink} to='/stores' />
<Tab label='KPI' value='kpi' component={NavLink} to='/kpi' />
<Tab label='Datasets' value='datasets' component={NavLink} to='/datasets' />
{/* Auto import links */}
</Tabs>
</Grid>
</React.Fragment>
);
};

export function Header() {
const classes = useStyles();
const location = useLocation();
const [drawerOpen, setDrawerOpen] = useState(false);

useEffect(() => {
setDrawerOpen(false);
}, [location]);

const handleDrawerToggle = () => {
setDrawerOpen(!drawerOpen);
};

return (
<AppBar position='static' className={classes.navBar}>
<Toolbar variant='dense'>
<Hidden smUp>
<IconButton
edge='start'
className={classes.menuButton}
color='inherit'
aria-label='menu'
onClick={handleDrawerToggle}
>
<MenuIcon />
</IconButton>
</Hidden>
<Link component={NavLink} to='/' className={classes.title}>
<Typography component='h1' variant='subtitle1' noWrap>
<img src='/logo.svg' alt='CARTO ' />
<Hidden xsDown>
<img src='/logo.svg' alt='CARTO ' />
</Hidden>
<Hidden smUp>
<img src='/logo-xs.svg' alt='CARTO ' />
<Divider orientation="vertical" light />
</Hidden>
<strong>React</strong> Demo
</Typography>
</Link>
<Divider orientation='vertical' className={classes.divider} light></Divider>
<Grid container className={classes.navTabs}>
<Tabs value={location.pathname.split('/')[1] || 'stores'} textColor='inherit'>
<Tab
label='Stores'
value='stores'
component={NavLink}
to='/stores'
/>
<Tab
label='KPI'
value='kpi'
component={NavLink}
to='/kpi'
/>
<Tab
label='Datasets'
value='datasets'
component={NavLink}
to='/datasets'
/>
{/* Auto import links */}
</Tabs>
</Grid>
<Grid container item xs={3}>
<Hidden xsDown>
<Divider orientation='vertical' className={classes.divider} light></Divider>
<NavigationMenu location={location}/>
</Hidden>
<Hidden smUp>
<Drawer
variant='temporary'
anchor='left'
open={drawerOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
PaperProps={{
className: classes.drawer,
}}
>
<Toolbar variant='dense' />
<Grid container direction='column' justify='space-between' item xs>
<NavigationMenu location={location} column={true} />
</Grid>
</Drawer>
</Hidden>
<Grid
container
item
xs
justify='flex-end'
>
<UserMenu />
</Grid>
</Toolbar>
Expand Down
129 changes: 96 additions & 33 deletions template-sample-app/template/src/components/views/Main.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,138 @@
import React from 'react';
import React, { useState } from 'react';
import { Outlet } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { Box, Drawer, Grid, Hidden, Paper, Portal, Snackbar, Toolbar, useTheme, useMediaQuery } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { Grid, Snackbar } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import {
ExpandLess as ExpandLessIcon,
ExpandMore as ExpandMoreIcon,
} from '@material-ui/icons';
import { GeocoderWidget } from '@carto/react/widgets';
import { Map } from 'components/common/Map';
import { Legend } from 'components/legends/Legend';
import { GeocoderWidget } from '@carto/react/widgets';
import { getLayers } from 'components/layers';
import { useDispatch, useSelector } from 'react-redux';
import { Alert } from '@material-ui/lab';
import { setError } from 'config/appSlice';

const drawerWidth = 350;

const useStyles = makeStyles((theme) => ({
contentWrapper: {
display: 'flex',
flexDirection: 'row',
flex: 1,
alignItems: 'start',
justifyContent: 'space-between',
overflow: 'hidden',
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
},
sidebarWrapper: {
position: 'relative',
width: '350px',
height: '100%',
backgroundColor: theme.palette.common.white,
boxShadow: theme.shadows[3],
overflow: 'auto',
drawerPaper: {
width: drawerWidth,
},
widgetDrawerToggle: {
position: 'absolute',
bottom: 0,
width: '100%',
zIndex: 1,
textAlign: 'center',
},
bottomSheet: {
maxHeight: 'calc(100% - 48px)',
},
bottomSheetContent: {
minHeight: theme.spacing(16)
},
mapWrapper: {
position: 'relative',
width: 'calc(100% - 350px)',
height: '100%',
flex: 1,
overflow: 'hidden',
},
legend: {
position: 'absolute',
bottom: theme.spacing(3.5),
right: theme.spacing(4),

[theme.breakpoints.down('sm')]: {
bottom: theme.spacing(9),
right: theme.spacing(2),
},
},
geocoder: {
position: 'absolute',
top: theme.spacing(3),
left: theme.spacing(3),
},

[theme.breakpoints.down('xs')]: {
width: `calc(100% - ${theme.spacing(6)}px)`
}
}
}));

export default function Main() {
const dispatch = useDispatch();
const error = useSelector((state) => state.app.error);
const [widgetsDrawerOpen, setWidgetsDrawerOpen] = useState(false);
const classes = useStyles();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('xs'));
const mobileContainer = React.useRef(null);
const desktopContainer = React.useRef(null);

const handleClose = () => {
dispatch(setError(null));
};

const handleWidgetsDrawerToggle = () => {
setWidgetsDrawerOpen(!widgetsDrawerOpen);
};

const onGeocoderWidgetError = (error) => {
dispatch(setError(`Geocoding error: ${error.message}`));
};

return (
<Grid container direction='row' className={classes.contentWrapper}>
<Grid
container
direction='column'
alignItems='stretch'
item
className={classes.sidebarWrapper}
>
<Outlet />
</Grid>
<Grid container direction='row' alignItems='stretch' item xs>
<nav className={classes.drawer}>
<Portal container={isMobile ? mobileContainer.current : desktopContainer.current }>
<Outlet />
</Portal>
<Hidden xsDown>
<Drawer
classes={{
paper: classes.drawerPaper,
}}
variant='permanent'
open
>
<Toolbar variant='dense' />
<div ref={desktopContainer}></div>
</Drawer>
</Hidden>
<Hidden smUp>
<Paper
className={classes.widgetDrawerToggle}
elevation={4}
square
onClick={handleWidgetsDrawerToggle}
>
<ExpandLessIcon />
</Paper>
<Drawer
variant='persistent'
anchor='bottom'
open={widgetsDrawerOpen}
onClose={handleWidgetsDrawerToggle}
PaperProps={{ className: classes.bottomSheet }}
>
<Box
display='flex'
justifyContent='center'
onClick={handleWidgetsDrawerToggle}
>
<ExpandMoreIcon />
</Box>
<div ref={mobileContainer} className={classes.bottomSheetContent}></div>
</Drawer>
</Hidden>
</nav>

<Grid item className={classes.mapWrapper}>
<Map layers={getLayers()} />
<GeocoderWidget className={classes.geocoder} onError={onGeocoderWidgetError} />
Expand All @@ -80,4 +143,4 @@ export default function Main() {
</Snackbar>
</Grid>
);
}
};
Loading