diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4575fc2e..85d9e536 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
@@ -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
\ No newline at end of file
+- Initial version: 2 templates, simple and full app
diff --git a/template-sample-app/template/public/logo-xs.svg b/template-sample-app/template/public/logo-xs.svg
new file mode 100644
index 00000000..3e6a95a1
--- /dev/null
+++ b/template-sample-app/template/public/logo-xs.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/template-sample-app/template/src/App.js b/template-sample-app/template/src/App.js
index 0c01aaa6..6a07e786 100644
--- a/template-sample-app/template/src/App.js
+++ b/template-sample-app/template/src/App.js
@@ -50,7 +50,7 @@ function App() {
return (
-
+
{routing}
diff --git a/template-sample-app/template/src/components/common/Header.js b/template-sample-app/template/src/components/common/Header.js
index 4a1cba33..236478a7 100644
--- a/template-sample-app/template/src/components/common/Header.js
+++ b/template-sample-app/template/src/components/common/Header.js
@@ -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': {
@@ -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,
@@ -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 (
+
+
+
+
+
+
+ {/* Auto import links */}
+
+
+
+ );
+};
+
export function Header() {
const classes = useStyles();
const location = useLocation();
+ const [drawerOpen, setDrawerOpen] = useState(false);
+
+ useEffect(() => {
+ setDrawerOpen(false);
+ }, [location]);
+
+ const handleDrawerToggle = () => {
+ setDrawerOpen(!drawerOpen);
+ };
return (
+
+
+
+
+
-
+
+
+
+
+
+
+
React Demo
-
-
-
-
-
-
- {/* Auto import links */}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template-sample-app/template/src/components/views/Main.js b/template-sample-app/template/src/components/views/Main.js
index 1979e3d7..e16daf18 100644
--- a/template-sample-app/template/src/components/views/Main.js
+++ b/template-sample-app/template/src/components/views/Main.js
@@ -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 (
-
-
-
-
+
+
+
@@ -80,4 +143,4 @@ export default function Main() {
);
-}
+};
\ No newline at end of file
diff --git a/template-sample-app/template/src/components/views/UserMenu.js b/template-sample-app/template/src/components/views/UserMenu.js
index c545a68d..d34cbf12 100644
--- a/template-sample-app/template/src/components/views/UserMenu.js
+++ b/template-sample-app/template/src/components/views/UserMenu.js
@@ -2,22 +2,17 @@ import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { makeStyles } from '@material-ui/core/styles';
-import { Avatar, Grid, Link, Menu, MenuItem, Typography } from '@material-ui/core';
+import { Avatar, Grid, Hidden, Link, Menu, MenuItem, Typography } from '@material-ui/core';
import { OAuthLogin } from '@carto/react/oauth';
import { logout } from '@carto/react/redux';
const useStyles = makeStyles((theme) => ({
- root: {
- flexGrow: 1,
- },
- menuButton: {
- marginRight: theme.spacing(2),
- },
avatar: {
cursor: 'pointer',
width: theme.spacing(4.5),
height: theme.spacing(4.5),
+ marginLeft: theme.spacing(1),
},
}));
@@ -61,51 +56,44 @@ function UserMenu() {
// Display User menu, with name, avatar + an attached menu for user-related options
return (
-
-
+
-
-
- {user.username}
-
+
+
+
+ {user.username}
+
+
+
-
-
-
-
-
-
-
-
+
+
+
);
}
diff --git a/template-skeleton/template/public/logo-xs.svg b/template-skeleton/template/public/logo-xs.svg
new file mode 100644
index 00000000..3e6a95a1
--- /dev/null
+++ b/template-skeleton/template/public/logo-xs.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/template-skeleton/template/src/components/common/Header.js b/template-skeleton/template/src/components/common/Header.js
index 4429a6f2..e44c82f3 100644
--- a/template-skeleton/template/src/components/common/Header.js
+++ b/template-skeleton/template/src/components/common/Header.js
@@ -1,30 +1,42 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import {
AppBar,
+ Drawer,
+ Divider,
+ Hidden,
+ Grid,
+ IconButton,
Tab,
Tabs,
Toolbar,
- Grid,
Link,
makeStyles,
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': {
backgroundColor: theme.palette.common.white,
- }
+ },
},
divider: {
margin: theme.spacing(0, 3),
},
+ menuButton: {
+ marginRight: theme.spacing(2),
+ },
+ drawer: {
+ minWidth: 260,
+ },
title: {
'& h1': {
fontWeight: theme.typography.fontWeightRegular,
@@ -35,39 +47,105 @@ 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 (
+
+
+
+
+ {/* Auto import links */}
+
+
+
+ );
+};
+
export function Header() {
const classes = useStyles();
const location = useLocation();
+ const [drawerOpen, setDrawerOpen] = useState(false);
+
+ useEffect(() => {
+ setDrawerOpen(false);
+ }, [location]);
+
+ const handleDrawerToggle = () => {
+ setDrawerOpen(!drawerOpen);
+ };
return (
+
+
+
+
+
-
+
+
+
+
+
+
+
React Demo
-
-
-
+
+
+
+
+
-
- {/* Auto import links */}
-
-
-
+
+
+
+
+
+
+
diff --git a/template-skeleton/template/src/components/views/Main.js b/template-skeleton/template/src/components/views/Main.js
index 7e9d78ff..5fa2340a 100644
--- a/template-skeleton/template/src/components/views/Main.js
+++ b/template-skeleton/template/src/components/views/Main.js
@@ -1,69 +1,138 @@
-import React from 'react';
+import React, { useState } from 'react';
import { Outlet } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
import { makeStyles } from '@material-ui/core/styles';
-import { Grid, Snackbar } from '@material-ui/core';
-import { Map } from 'components/common/Map';
+import {
+ Box,
+ Drawer,
+ Grid,
+ Hidden,
+ Paper,
+ Portal,
+ Snackbar,
+ Toolbar,
+ useTheme,
+ useMediaQuery,
+} 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 { 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',
},
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 (
-
-
-
-
+
+
+
diff --git a/template-skeleton/template/src/components/views/UserMenu.js b/template-skeleton/template/src/components/views/UserMenu.js
index c545a68d..667ea9d4 100644
--- a/template-skeleton/template/src/components/views/UserMenu.js
+++ b/template-skeleton/template/src/components/views/UserMenu.js
@@ -2,22 +2,25 @@ import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { makeStyles } from '@material-ui/core/styles';
-import { Avatar, Grid, Link, Menu, MenuItem, Typography } from '@material-ui/core';
+import {
+ Avatar,
+ Grid,
+ Hidden,
+ Link,
+ Menu,
+ MenuItem,
+ Typography,
+} from '@material-ui/core';
import { OAuthLogin } from '@carto/react/oauth';
import { logout } from '@carto/react/redux';
const useStyles = makeStyles((theme) => ({
- root: {
- flexGrow: 1,
- },
- menuButton: {
- marginRight: theme.spacing(2),
- },
avatar: {
cursor: 'pointer',
width: theme.spacing(4.5),
height: theme.spacing(4.5),
+ marginLeft: theme.spacing(1),
},
}));
@@ -61,51 +64,43 @@ function UserMenu() {
// Display User menu, with name, avatar + an attached menu for user-related options
return (
-
-
+
-
-
- {user.username}
-
+
+
+
+ {user.username}
+
+
+
-
-
-
-
-
-
-
-
+
+
+
);
}