Merge pull request #47 from EbookFoundation/feature/mobile

breakpoints for mobile displays
pull/48/head
Theodore Kluge 2019-03-19 16:25:28 -04:00 committed by GitHub
commit 9c6c841957
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 9839 additions and 266 deletions

View File

@ -18,11 +18,16 @@ const ACTIONS = {
add_publisher: 'add_publisher',
delete_publisher: 'delete_publisher',
set_publishers: 'set_publishers',
update_publisher: 'update_publisher'
update_publisher: 'update_publisher',
toggle_menu: 'toggle_menu'
}
export default ACTIONS
export const toggleMenu = () => ({
type: ACTIONS.toggle_menu
})
export const setWorking = working => ({
type: ACTIONS.set_working,
data: working

View File

@ -7,8 +7,10 @@ import Progress from './components/Progress'
import appReducer from './reducers'
import adminReducer from './reducers/admin'
import { fetchAdminData, patchUser, patchPublisher } from './actions/admin'
import { toggleMenu } from './actions'
import Util from './lib/Util'
import Icon from './components/Icon'
import IconButton from './components/IconButton'
import '../styles/admin.scss'
import './containers/listitem.scss'
@ -28,7 +30,8 @@ class App extends React.Component {
},
users: [],
publishers: [],
working: false
working: false,
navMenu: false
}
this.dispatch = this.dispatch.bind(this)
@ -64,10 +67,10 @@ class App extends React.Component {
getRegisteredUsers () {
return this.state.users.map(user => {
return (
<li className='uri-list-item flex-container' key={`is-admin-${user.id}`}>
<li className='uri-list-item cols flex-container' key={`is-admin-${user.id}`}>
<span className='flex'>{user.email}</span>
<span className='flex'>
<label for={`is-admin-${user.id}`} className='cb-label'>Admin?</label>
<label htmlFor={`is-admin-${user.id}`} className='cb-label'>Admin?</label>
<input className='checkbox' type='checkbox' checked={user.admin} onChange={() => this.dispatch(patchUser({ id: user.id, admin: !user.admin }))} id={`is-admin-${user.id}`} />
<label htmlFor={`is-admin-${user.id}`} />
</span>
@ -84,7 +87,7 @@ class App extends React.Component {
return (
<li className='uri-list-item flex-container flex-vertical' key={`is-whitelisted-${pub.id}`}>
<header><h3>{pub.name}</h3></header>
<div className='flex flex-container'>
<div className='cols flex flex-container'>
<div className='flex flex-container flex-vertical key-value'>
<span className='flex'><span className='key'>Owner:</span><span className='value'>{pub.user.email}</span></span>
<span className='flex'><span className='key'>App ID:</span><span className='value'>{pub.appid}</span></span>
@ -111,19 +114,19 @@ class App extends React.Component {
render () {
return (
<Router basename='/admin'>
<div className='root-container flex-container admin-container'>
<div className={'root-container flex-container admin-container two-panels' + (this.state.navMenu ? ' nav-active' : '')}>
<aside className='nav nav-left'>
<header>
<h1>RoE Admin</h1>
<h1 className='flex-container'><IconButton icon='menu' className='menu-small' onClick={() => this.dispatch(toggleMenu())} /><span className='flex'>RoE Admin</span></h1>
<h2 className='flex-container'>
<span className='flex'>{this.state.user.email}</span>
<a href='/logout'>Log out</a>
</h2>
</header>
<ul>
<li><NavLink to='/users'>Users</NavLink></li>
<li><NavLink to='/publishers'>Publishers</NavLink></li>
<li><a href='/keys'>Exit admin</a></li>
<li className='flex-container'><NavLink to='/users'><Icon icon='account' /><span className='flex'>Users</span></NavLink></li>
<li className='flex-container'><NavLink to='/publishers'><Icon icon='transfer-right' /><span className='flex'>Publishers</span></NavLink></li>
<li className='flex-container'><a href='/keys'><Icon icon='exit' /><span className='flex'>Exit admin</span></a></li>
</ul>
</aside>
<section className={'content flex' + (this.state.working ? ' working' : '')}>

View File

@ -18,6 +18,12 @@ function getSVG (icon) {
case 'shield-check': return '<path d="M10,17L6,13L7.41,11.59L10,14.17L16.59,7.58L18,9M12,1L3,5V11C3,16.55 6.84,21.74 12,23C17.16,21.74 21,16.55 21,11V5L12,1Z" />'
case 'alert-circle': return '<path d="M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />'
case 'refresh': return '<path d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z" />'
case 'key': return '<path d="M22,18V22H18V19H15V16H12L9.74,13.74C9.19,13.91 8.61,14 8,14A6,6 0 0,1 2,8A6,6 0 0,1 8,2A6,6 0 0,1 14,8C14,8.61 13.91,9.19 13.74,9.74L22,18M7,5A2,2 0 0,0 5,7A2,2 0 0,0 7,9A2,2 0 0,0 9,7A2,2 0 0,0 7,5Z" />'
case 'transfer-right': return '<path d="M3,8H5V16H3V8M7,8H9V16H7V8M11,8H13V16H11V8M15,19.25V4.75L22.25,12L15,19.25Z" />'
case 'account': return '<path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" />'
case 'flash': return '<path d="M7,2V13H10V22L17,10H13L17,2H7Z" />'
case 'menu': return '<path d="M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z" />'
case 'exit': return ' <path d="M19,3H5C3.89,3 3,3.89 3,5V9H5V5H19V19H5V15H3V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M10.08,15.58L11.5,17L16.5,12L11.5,7L10.08,8.41L12.67,11H3V13H12.67L10.08,15.58Z" />'
default: return icon || 'missing icon prop'
}
}

View File

@ -28,6 +28,7 @@
width: 100%;
height: 46px;
pointer-events: none;
overflow: hidden;
label {
position: absolute;
@ -100,5 +101,9 @@
& + .underlined-input-readonly.stack-h {
margin-left: 14px;
margin-top: 0;
@include break('small') {
margin-left: 0;
}
}
}

View File

@ -34,7 +34,7 @@ class PublisherListItem extends React.Component {
<h3 className='flex'>{`${this.props.item.name}${this.props.item.whitelisted ? '' : ' (awaiting approval)'}`}</h3>
<ConfirmIconButton icon='delete' onClick={() => this.props.dispatch(removePublisher(this.props.item.id))} />
</header>
<div className='flex flex-container'>
<div className='cols flex flex-container'>
<div className='col flex flex-container flex-vertical'>
<div className='stack flex-container flex-vertical'>
<span className='label'>AppID</span>
@ -73,7 +73,7 @@ class PublisherListItem extends React.Component {
<h3 className='flex'>{this.props.item.name}</h3>
<ConfirmIconButton icon='delete' onClick={() => this.props.dispatch(removePublisher(this.props.item.id))} />
</header>
<div className='flex flex-container'>
<div className='cols flex flex-container'>
<div className='col flex flex-container flex-vertical'>
<p>
Download <span className='name'>{this.props.item.verification_key}.html</span> and upload it to the root directory of your webserver. Then, click <strong>VERIFY</strong> to verify that you own and control <span className='name'>{this.props.item.url}</span>.

View File

@ -23,12 +23,12 @@ class UriListItem extends React.Component {
}
getView () {
return (
<li className='uri-list-item flex-container' onClick={(e) => this.cancelEvent(e, this.props.item.id)}>
<div className='stack flex flex-container flex-vertical'>
<li className='cols uri-list-item flex-container' onClick={(e) => this.cancelEvent(e, this.props.item.id)}>
<div className='col stack flex flex-container flex-vertical'>
<span className='label'>Destination URL</span>
<span className='value'>{this.props.item.url}</span>
</div>
<div className='stack flex flex-container flex-vertical'>
<div className='col stack flex flex-container flex-vertical'>
<span className='label'>Filters</span>
<span className='value'>{['publisher', 'title', 'author', 'isbn'].reduce((a, x) => a + (this.props.item[x] ? 1 : 0), 0) || 'None'}</span>
</div>

View File

@ -5,10 +5,11 @@ import Progress from './components/Progress'
import UnderlineInput from './components/UnderlineInput'
import UriListItem from './containers/UriListItem'
import PublisherListItem from './containers/PublisherListItem'
import Icon from './components/Icon'
import IconButton from './components/IconButton'
import ConfirmIconButton from './containers/ConfirmIconButton'
import reducer from './reducers'
import { fetchData, createNewUrl, setEditing, editUser, createNewPublisher, regenerateSigningSecret } from './actions'
import { fetchData, createNewUrl, setEditing, editUser, createNewPublisher, regenerateSigningSecret, toggleMenu } from './actions'
import '../styles/index.scss'
@ -32,7 +33,8 @@ class App extends React.Component {
newPublisher: { name: '', url: '' },
editingUrl: null,
editingPublisher: null,
working: false
working: false,
navMenu: false
}
this.dispatch = this.dispatch.bind(this)
@ -111,21 +113,21 @@ class App extends React.Component {
render () {
return (
<Router>
<div className='root-container flex-container' onClick={() => this.dispatch(setEditing(null))}>
<div className={'root-container flex-container two-panels' + (this.state.navMenu ? ' nav-active' : '')} onClick={() => this.dispatch(setEditing(null))}>
<aside className='nav nav-left'>
<header>
<h1>River of Ebooks</h1>
<h1 className='flex-container'><IconButton icon='menu' className='menu-small' onClick={() => this.dispatch(toggleMenu())} /><span className='flex'>River of Ebooks</span></h1>
<h2 className='flex-container'>
<span className='flex'>{this.state.user.email}</span>
<a href='/logout'>Log out</a>
</h2>
</header>
<ul>
<li><NavLink to='/keys'>Publishing keys</NavLink></li>
<li><NavLink to='/targets'>Push URIs</NavLink></li>
<li><NavLink to='/account'>My account</NavLink></li>
<li><NavLink to='/keys' className='flex-container'><Icon icon='key' /><span className='flex'>Publishing keys</span></NavLink></li>
<li><NavLink to='/targets' className='flex-container'><Icon icon='transfer-right' /><span className='flex'>Push URIs</span></NavLink></li>
<li><NavLink to='/account' className='flex-container'><Icon icon='account' /><span className='flex'>My account</span></NavLink></li>
{(this.state.user.id === 1 || this.state.user.admin) &&
<li><a href='/admin'>Admin</a></li>
<li><a href='/admin' className='flex-container'><Icon icon='flash' /><span className='flex'>Admin</span></a></li>
}
</ul>
</aside>
@ -155,7 +157,7 @@ class App extends React.Component {
<h2>If you own a publishing site, generate a publishing key for it here.</h2>
</div>
</header>
<div className='creator flex-container'>
<div className='creator flex-container cols'>
<UnderlineInput
className='flex stack-h'
placeholder='Website name'

View File

@ -10,6 +10,10 @@ const reducer = (state = {}, action) => {
return {
working: data
}
case Actions.toggle_menu:
return {
navMenu: !state.navMenu
}
case Actions.set_user:
return {
user: {

View File

@ -33,10 +33,13 @@
.creator {
padding: 0 14px;
line-height: 60px;
// max-width: 500px;
.btn {
margin: 12px 0 12px 12px;
@include break('small') {
margin: 0 12px;
}
}
}
}
@ -44,7 +47,6 @@
margin: 20px 14px;
padding: 0;
list-style: none;
// overflow: hidden;
}
.inputs,
.details {
@ -105,6 +107,9 @@
padding: 0 20px;
color: $accent-2;
@include break('small') {
padding: 0 5px;
}
&:hover {
text-decoration: underline;
}
@ -115,6 +120,10 @@
background: $accent-2;
color: $text-light-1;
border-radius: 3px;
@include break('small') {
margin: 5px 5px 5px 0;
}
}
}
}
@ -164,7 +173,7 @@
background: $black-5;
padding: 10px;
border-radius: 3px;
overflow-x: scroll;
overflow-x: auto;
&:before {
display: block;

View File

@ -1,57 +1,157 @@
.nav-left {
min-width: 300px;
height: 100%;
background: $accent-1;
color: $text-light-1;
box-shadow: $shadow-1;
.two-panels {
.nav-left {
width: 300px;
height: 100%;
background: $accent-1;
color: $text-light-1;
box-shadow: $shadow-1;
overflow-y: auto;
z-index: 100;
header {
line-height: 50px;
padding: 0 14px;
h2 {
margin: -10px 0 0 0;
padding: 0;
font-weight: normal;
font-size: 12pt;
height: 36px;
line-height: 36px;
color: $white-2;
}
a {
text-decoration: none;
color: $accent-3;
}
}
ul {
list-style: none;
margin: 0;
padding: 0;
li {
height: 50px;
header {
line-height: 50px;
border-bottom: 1px solid $white-4;
padding: 0 14px;
&:hover a {
background: $white-5;
}
&:last-of-type {
border-bottom: none
}
h1 {
height: 50px;
a {
display: inline-block;
height: 100%;
width: 100%;
padding: 0 12px;
text-decoration: none !important;
.menu-small {
display: none;
margin: 5px 5px 5px 0;
path {
fill: $text-light-1;
}
@include break('medium') {
display: inline-block;
margin: 5px;
}
}
span:not(.icon) {
@include break('medium') {
display: none;
}
}
}
h2 {
margin: -10px 0 0 0;
padding: 0;
font-weight: normal;
font-size: 12pt;
height: 36px;
line-height: 36px;
color: $white-2;
}
a {
text-decoration: none;
color: $accent-3;
}
}
ul {
list-style: none;
margin: 0;
padding: 0;
&.active {
background: $white-4;
li {
height: 50px;
line-height: 50px;
overflow: hidden;
border-bottom: 1px solid $white-4;
&:hover a {
background: $white-5;
}
&:last-of-type {
border-bottom: none
}
a {
display: inline-block;
height: 100%;
width: 100%;
padding: 0 12px;
text-decoration: none !important;
color: $white-2;
&.active {
background: $white-4;
}
.icon {
margin: 5px 5px 5px 0;
vertical-align: middle;
@include break('medium') {
margin: 5px;
}
}
span:not(.icon) {
vertical-align: middle;
display: inline-block;
line-height: 50px;
@include break('medium') {
display: none;
}
@include ellip();
}
@include break('medium') {
padding: 0;
}
}
}
}
}
.content {
z-index: 1;
.cols {
@include break('small') {
flex-direction: column;
}
}
}
@include break('medium') {
.nav-left {
position: absolute;
transition: width .3s $transition;
header {
min-height: 76px;
}
}
&:not(.nav-active) {
.nav-left {
width: 50px;
header {
padding: 0;
h2 {
display: none;
}
}
}
}
&.nav-active {
span:not(.icon) {
overflow: hidden;
display: inline-block !important;
}
.nav-left {
header {
padding: 0 14px 0 0;
h2 {
display: flex;
margin-left: 50px;
}
}
}
}
> .content {
margin-left: 50px;
}
}
}

File diff suppressed because it is too large Load Diff