Added swipable menu

This commit is contained in:
Alejandro Celaya 2018-08-14 20:28:46 +02:00
parent 42d718960f
commit cb9dc9d65e
11 changed files with 139 additions and 48 deletions

View file

@ -29,6 +29,7 @@
"react-moment": "^0.7.6", "react-moment": "^0.7.6",
"react-redux": "^5.0.7", "react-redux": "^5.0.7",
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",
"react-swipeable": "^4.3.0",
"react-tagsinput": "^3.19.0", "react-tagsinput": "^3.19.0",
"reactstrap": "^6.0.1", "reactstrap": "^6.0.1",
"redux": "^4.0.0", "redux": "^4.0.0",

View file

@ -9,7 +9,7 @@ import CreateServer from './servers/CreateServer';
export default class App extends React.Component { export default class App extends React.Component {
render() { render() {
return ( return (
<div className="container-fluid"> <div className="container-fluid app-container">
<MainHeader/> <MainHeader/>
<div className="app"> <div className="app">

View file

@ -1,5 +1,10 @@
@import './utils/base'; @import './utils/base';
.app-container {
height: 100%;
}
.app { .app {
padding-top: $headerHeight; padding-top: $headerHeight;
height: 100%;
} }

View file

@ -6,12 +6,23 @@ import { NavLink } from 'react-router-dom';
import DeleteServerButton from '../servers/DeleteServerButton'; import DeleteServerButton from '../servers/DeleteServerButton';
import './AsideMenu.scss'; import './AsideMenu.scss';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { serverType } from '../servers/prop-types';
export default function AsideMenu({ selectedServer }) { const defaultProps = {
className: '',
showOnMobile: false,
};
const propTypes = {
selectedServer: serverType,
className: PropTypes.string,
showOnMobile: PropTypes.bool,
};
export default function AsideMenu({ selectedServer, className, showOnMobile }) {
const serverId = selectedServer ? selectedServer.id : ''; const serverId = selectedServer ? selectedServer.id : '';
return ( return (
<aside className="aside-menu col-lg-2 col-md-3"> <aside className={`aside-menu ${!showOnMobile ? 'aside-menu--hidden' : ''} ${className}`}>
<nav className="nav flex-column aside-menu__nav"> <nav className="nav flex-column aside-menu__nav">
<NavLink <NavLink
className="aside-menu__item" className="aside-menu__item"
@ -39,11 +50,5 @@ export default function AsideMenu({ selectedServer }) {
); );
} }
AsideMenu.propTypes = { AsideMenu.defaultProps = defaultProps;
selectedServer: PropTypes.shape({ AsideMenu.propTypes = propTypes;
id: PropTypes.string,
name: PropTypes.string,
url: PropTypes.string,
apiKey: PropTypes.string,
}),
};

View file

@ -1,22 +1,38 @@
@import '../utils/base'; @import '../utils/base';
@import '../utils/mixins/box-shadow';
$asideMenuMobileWidth: 280px;
.aside-menu { .aside-menu {
background-color: #f7f7f7; background-color: #f7f7f7;
padding-top: 10px;
padding-bottom: 10px;
@media (min-width: $mdMin) {
position: fixed !important; position: fixed !important;
top: $headerHeight; padding-top: 13px;
padding-bottom: 10px;
top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: 1000;
display: block; display: block;
padding: 30px 15px 15px; z-index: 1050;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
@media (min-width: $mdMin) {
top: $headerHeight;
padding: 30px 15px 15px;
border-right: 1px solid #eee; border-right: 1px solid #eee;
} }
@media (max-width: $smMax) {
width: $asideMenuMobileWidth !important;
@include box-shadow(-10px 0px 50px 11px rgba(0, 0, 0, 0.55));
transition: left 300ms;
}
}
.aside-menu--hidden {
@media (max-width: $smMax) {
left: -($asideMenuMobileWidth + 35px);
}
} }
.aside-menu__nav { .aside-menu__nav {

View file

@ -1,27 +1,54 @@
import React from 'react'; import React from 'react';
import { Route, Switch } from 'react-router-dom'; import { Route, Switch, withRouter } from 'react-router-dom';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { compose } from 'redux';
import { selectServer } from '../servers/reducers/selectedServer'; import { selectServer } from '../servers/reducers/selectedServer';
import CreateShortUrl from '../short-urls/CreateShortUrl'; import CreateShortUrl from '../short-urls/CreateShortUrl';
import ShortUrls from '../short-urls/ShortUrls'; import ShortUrls from '../short-urls/ShortUrls';
import ShortUrlsVisits from '../short-urls/ShortUrlVisits'; import ShortUrlsVisits from '../short-urls/ShortUrlVisits';
import AsideMenu from './AsideMenu'; import AsideMenu from './AsideMenu';
import { pick } from 'ramda'; import { pick } from 'ramda';
import Swipeable from 'react-swipeable';
import './MenuLayout.scss';
export class MenuLayout extends React.Component { export class MenuLayout extends React.Component {
state = { showSideBar: false };
// FIXME Shouldn't use componentWillMount, but this code has to be run before children components are rendered // FIXME Shouldn't use componentWillMount, but this code has to be run before children components are rendered
componentWillMount() { componentWillMount() {
const { serverId } = this.props.match.params; const { serverId } = this.props.match.params;
this.props.selectServer(serverId); this.props.selectServer(serverId);
} }
componentDidUpdate(prevProps) {
const { location } = this.props;
// Hide sidebar when location changes
if (location !== prevProps.location) {
this.setState({ showSideBar: false });
}
}
render() { render() {
const { selectedServer } = this.props; const { selectedServer } = this.props;
return ( return (
<div className="row"> <Swipeable
<AsideMenu selectedServer={selectedServer} /> delta={40}
<div className="col-lg-10 offset-lg-2 col-md-9 offset-md-3"> onSwipedLeft={() => this.setState({ showSideBar: false })}
onSwipedRight={() => this.setState({ showSideBar: true })}
className="menu-layout__swipeable"
>
<div className="row menu-layout__swipeable-inner">
<AsideMenu
className="col-lg-2 col-md-3"
selectedServer={selectedServer}
showOnMobile={this.state.showSideBar}
/>
<div
className="col-lg-10 offset-lg-2 col-md-9 offset-md-3"
onClick={() => this.setState({ showSideBar: false })}
>
<Switch> <Switch>
<Route <Route
exact exact
@ -41,8 +68,12 @@ export class MenuLayout extends React.Component {
</Switch> </Switch>
</div> </div>
</div> </div>
</Swipeable>
); );
} }
} }
export default connect(pick(['selectedServer', 'shortUrlsListParams']), { selectServer })(MenuLayout); export default compose(
connect(pick(['selectedServer', 'shortUrlsListParams']), { selectServer }),
withRouter
)(MenuLayout);

View file

@ -0,0 +1,12 @@
.menu-layout__swipeable {
$offset: 15px;
height: 100%;
margin-right: -$offset;
margin-left: -$offset;
padding-left: $offset;
padding-right: $offset;
}
.menu-layout__swipeable-inner {
height: 100%;
}

View file

@ -1,5 +1,11 @@
@import './utils/base'; @import './utils/base';
html,
body,
#root {
height: 100%
}
* { * {
outline: none !important; outline: none !important;
} }

View file

@ -5,6 +5,7 @@ import { withRouter } from 'react-router-dom';
import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap';
import { compose } from 'redux'; import { compose } from 'redux';
import { deleteServer } from './reducers/server'; import { deleteServer } from './reducers/server';
import { serverType } from './prop-types';
export const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }) => { export const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }) => {
const closeModal = () => { const closeModal = () => {
@ -34,12 +35,7 @@ export const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, histor
DeleteServerModal.propTypes = { DeleteServerModal.propTypes = {
toggle: PropTypes.func.isRequired, toggle: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
server: PropTypes.shape({ server: serverType,
id: PropTypes.string,
name: PropTypes.string,
url: PropTypes.string,
apiKey: PropTypes.string,
}),
}; };
export default compose( export default compose(

View file

@ -0,0 +1,8 @@
import PropTypes from 'prop-types';
export const serverType = PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
url: PropTypes.string,
apiKey: PropTypes.string,
});

View file

@ -2160,6 +2160,10 @@ detect-node@^2.0.3:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127"
detect-passive-events@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/detect-passive-events/-/detect-passive-events-1.0.4.tgz#6ed477e6e5bceb79079735dcd357789d37f9a91a"
detect-port-alt@1.1.6: detect-port-alt@1.1.6:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
@ -6228,6 +6232,13 @@ react-router@^4.3.1:
prop-types "^15.6.1" prop-types "^15.6.1"
warning "^4.0.1" warning "^4.0.1"
react-swipeable@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-4.3.0.tgz#818c0818ac565f5b169348f442153f343abff8bc"
dependencies:
detect-passive-events "^1.0.4"
prop-types "^15.5.8"
react-tagsinput@^3.19.0: react-tagsinput@^3.19.0:
version "3.19.0" version "3.19.0"
resolved "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.19.0.tgz#6e3b45595f2d295d4657bf194491988f948caabf" resolved "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.19.0.tgz#6e3b45595f2d295d4657bf194491988f948caabf"