Categories everywhere

This commit is contained in:
Tom Russell 2020-02-03 22:35:32 +00:00
parent ca6ba7f217
commit 1a8e035fea
8 changed files with 75 additions and 64 deletions

View File

@ -44,7 +44,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
return <UseContainer return <UseContainer
{...props} {...props}
inactive={false} inactive={false}
title="Land Use" title="Current Use"
intro="How are buildings used, and how does use change over time? Coming soon…" intro="How are buildings used, and how does use change over time? Coming soon…"
help="https://pages.colouring.london/use" help="https://pages.colouring.london/use"
/>; />;
@ -52,7 +52,7 @@ const BuildingView: React.FunctionComponent<BuildingViewProps> = (props) => {
return <TypeContainer return <TypeContainer
{...props} {...props}
inactive={false} inactive={false}
title="Type" title="Original Use"
intro="How were buildings previously used?" intro="How were buildings previously used?"
help="https://www.pages.colouring.london/buildingtypology" help="https://www.pages.colouring.london/buildingtypology"
/>; />;

View File

@ -1,25 +1,36 @@
/** /**
* Data categories * Data categories
*/ */
.data-category-list { .data-category-list {
padding: 0 0 0.75rem; padding: 0px 0 10px 9px;
text-align: center;
list-style: none; list-style: none;
margin: 0 0 0 0.2rem; margin: 0;
text-align: center; text-align: left;
max-width: 480px;
}
.navbar .data-category-list {
padding: 0px 0 0px 15px;
} }
.data-category-list li { .data-category-list li {
position: relative; position: relative;
display: inline-block; display: inline-block;
vertical-align: bottom; vertical-align: bottom;
width: 10rem; width: 110px;
height: 10rem; height: 110px;
margin: 0.375rem; margin: 2px;
box-shadow: 0 0 2px 5px #ffffff; box-shadow: 0 0 2px 3px #ffffff;
transition: box-shadow 0.2s; transition: box-shadow 0.2s;
} }
.navbar .data-category-list li {
width: 105px;
height: 105px;
}
.data-category-list li:nth-child(4n) {
margin-right: 0;
}
.data-category-list li:hover { .data-category-list li:hover {
box-shadow: 0 0 2px 5px #00ffff; box-shadow: 0 0 2px 3px #00ffff;
z-index: 1;
} }
.data-category-list a { .data-category-list a {
color: #222; color: #222;
@ -41,6 +52,6 @@
.data-category-list .category { .data-category-list .category {
text-align: center; text-align: center;
font-size: 1.4em; font-size: 1em;
margin: 0 0 0.5em; margin: 0 0 0.5em;
} }

View File

@ -1,7 +1,4 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom';
import { BackIcon }from '../components/icons';
interface ContainerHeaderProps { interface ContainerHeaderProps {
cat?: string; cat?: string;
@ -11,9 +8,6 @@ interface ContainerHeaderProps {
const ContainerHeader: React.FunctionComponent<ContainerHeaderProps> = (props) => ( const ContainerHeader: React.FunctionComponent<ContainerHeaderProps> = (props) => (
<header className={`section-header view ${props.cat ? props.cat : ''} ${props.cat ? `background-${props.cat}` : ''}`}> <header className={`section-header view ${props.cat ? props.cat : ''} ${props.cat ? `background-${props.cat}` : ''}`}>
<Link className="icon-button back" to={props.backLink}>
<BackIcon />
</Link>
<h2 className="h2">{props.title}</h2> <h2 className="h2">{props.title}</h2>
<nav className="icon-buttons"> <nav className="icon-buttons">
{props.children} {props.children}

View File

@ -1,16 +1,15 @@
import { parse } from 'query-string';
import React from 'react'; import React from 'react';
import { Link, Redirect, RouteComponentProps } from 'react-router-dom'; import { Link, Redirect } from 'react-router-dom';
import { parseJsonOrDefault } from '../../helpers'; import { parseJsonOrDefault } from '../../helpers';
import ErrorBox from '../components/error-box'; import ErrorBox from '../components/error-box';
import { BackIcon } from '../components/icons';
import InfoBox from '../components/info-box'; import InfoBox from '../components/info-box';
import { dataFields } from '../data_fields'; import { dataFields } from '../data_fields';
import { User } from '../models/user'; import { User } from '../models/user';
import DataEntry from './data-components/data-entry'; import DataEntry from './data-components/data-entry';
import Sidebar from './sidebar'; import Sidebar from './sidebar';
import Categories from './categories';
interface MultiEditProps { interface MultiEditProps {
user?: User; user?: User;
@ -26,6 +25,7 @@ const MultiEdit: React.FC<MultiEditProps> = (props) => {
// special case for likes // special case for likes
return ( return (
<Sidebar> <Sidebar>
<Categories mode={'view'} />
<section className='data-section'> <section className='data-section'>
<header className={`section-header view ${props.category} background-${props.category}`}> <header className={`section-header view ${props.category} background-${props.category}`}>
<h2 className="h2">Like me!</h2> <h2 className="h2">Like me!</h2>
@ -54,39 +54,37 @@ const MultiEdit: React.FC<MultiEditProps> = (props) => {
return ( return (
<Sidebar> <Sidebar>
<Categories mode={'view'} />
<section className='data-section'> <section className='data-section'>
<header className={`section-header view ${props.category} background-${props.category}`}> <header className={`section-header view ${props.category} background-${props.category}`}>
<Link
className="icon-button back"
to={`/edit/${props.category}`}>
<BackIcon />
</Link>
<h2 className="h2">Copy {props.category} data</h2> <h2 className="h2">Copy {props.category} data</h2>
</header> </header>
<form> <div className="section-body">
<form>
{ {
error ? error ?
<ErrorBox msg={error} /> : <ErrorBox msg={error} /> :
<InfoBox msg='Click buildings one at a time to colour using the data below' /> <InfoBox msg='Click buildings one at a time to colour using the data below' />
} }
{ {
Object.keys(data).map((key => { Object.keys(data).map((key => {
const info = dataFields[key] || {}; const info = dataFields[key] || {};
return ( return (
<DataEntry <DataEntry
title={info.title || `Unknown field (${key})`} title={info.title || `Unknown field (${key})`}
slug={key} slug={key}
disabled={true} disabled={true}
value={data[key]} value={data[key]}
/> />
); );
})) }))
} }
</form> </form>
<form className='buttons-container'> <form className='buttons-container'>
<Link to={`/view/${props.category}`} className='btn btn-secondary'>Back to view</Link> <Link to={`/view/${props.category}`} className='btn btn-secondary'>Back to view</Link>
<Link to={`/edit/${props.category}`} className='btn btn-secondary'>Back to edit</Link> <Link to={`/edit/${props.category}`} className='btn btn-secondary'>Back to edit</Link>
</form> </form>
</div>
</section> </section>
</Sidebar> </Sidebar>
); );

View File

@ -8,6 +8,7 @@
bottom: 0; bottom: 0;
width: 95%; width: 95%;
width: calc(100% - 40px); width: calc(100% - 40px);
border-right: 1px solid #000;
z-index: 1001; z-index: 1001;
transition: transform 0.3s; transition: transform 0.3s;
transform: translateX(0); transform: translateX(0);
@ -49,15 +50,10 @@
clear: both; clear: both;
text-decoration: none; text-decoration: none;
color: #222; color: #222;
padding: 0.75rem 0.25rem 0.5rem 0; padding: 0.75rem 0.25rem 0.5rem 0.75rem;
z-index: 1000; z-index: 1000;
} position: sticky;
top: 0;
@media (min-width: 990px) {
.section-header {
position: sticky;
top: 0;
}
} }
.section-header h2, .section-header h2,
.section-header .icon-buttons { .section-header .icon-buttons {
@ -168,11 +164,11 @@
/** /**
* Data list sections * Data list sections
*/ */
.section-body {
.section-body { margin-top: 0.75em;
margin-top: 0.75em; padding: 0 0.75em 5em 0.75em;
padding: 0 0.75em; min-height: 100vh;
} }
.data-section .h3 { .data-section .h3 {
margin: 0; margin: 0;
} }
@ -218,6 +214,10 @@
.data-section select { .data-section select {
margin: 0 0 0.5em 0; margin: 0 0 0.5em 0;
} }
.data-section input[type="checkbox"] {
position: static;
margin-right: 0.5em;
}
.data-list dd { .data-list dd {
margin: 0 0 0.5rem; margin: 0 0 0.5rem;
line-height: 1.5; line-height: 1.5;

View File

@ -53,6 +53,9 @@
background: #fff; background: #fff;
border-right: 1px solid #000; border-right: 1px solid #000;
} }
.main-header .navbar-collapse > ul:last-child {
padding-bottom: 5rem;
}
.navbar-collapse.collapse { .navbar-collapse.collapse {
transform: translateY(-100vh); transform: translateY(-100vh);
} }

View File

@ -5,6 +5,7 @@ import './header.css';
import { Logo } from './components/logo'; import { Logo } from './components/logo';
import { User } from './models/user'; import { User } from './models/user';
import Categories from './building/categories';
interface HeaderProps { interface HeaderProps {
@ -132,6 +133,8 @@ class Header extends React.Component<HeaderProps, HeaderState> {
</li> </li>
</ul> </ul>
<hr /> <hr />
<Categories mode='view' />
<hr />
<ul className="navbar-nav flex-column"> <ul className="navbar-nav flex-column">
<li className="nav-item"> <li className="nav-item">
<NavLink to="/contact.html" className="nav-link" onClick={this.handleNavigate}> <NavLink to="/contact.html" className="nav-link" onClick={this.handleNavigate}>

View File

@ -66,7 +66,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
async fetchLatestRevision() { async fetchLatestRevision() {
try { try {
const {latestRevisionId} = await apiGet(`/api/buildings/revision`); const {latestRevisionId} = await apiGet(`/api/buildings/revision`);
this.increaseRevision(latestRevisionId); this.increaseRevision(latestRevisionId);
} catch(error) { } catch(error) {
console.error(error); console.error(error);
@ -74,7 +74,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
} }
/** /**
* Fetches building data if a building is selected but no data provided through * Fetches building data if a building is selected but no data provided through
* props (from server-side rendering) * props (from server-side rendering)
*/ */
async fetchBuildingData() { async fetchBuildingData() {
@ -173,13 +173,13 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
*/ */
colourBuilding(building: Building) { colourBuilding(building: Building) {
const cat = this.props.match.params.category; const cat = this.props.match.params.category;
if (cat === 'like') { if (cat === 'like') {
this.likeBuilding(building.building_id); this.likeBuilding(building.building_id);
} else { } else {
const data = parseJsonOrDefault(this.getMultiEditDataString()); const data = parseJsonOrDefault(this.getMultiEditDataString());
if (data != undefined && !Object.values(data).some(x => x == undefined)) { if (data != undefined && !Object.values(data).some(x => x == undefined)) {
this.updateBuilding(building.building_id, data); this.updateBuilding(building.building_id, data);
} }
@ -240,6 +240,7 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
)} /> )} />
<Route exact path="/:mode/:cat/:building?"> <Route exact path="/:mode/:cat/:building?">
<Sidebar> <Sidebar>
<Categories mode={mode || 'view'} building_id={building_id} />
<BuildingView <BuildingView
mode={viewEditMode} mode={viewEditMode}
cat={category} cat={category}
@ -252,11 +253,12 @@ class MapApp extends React.Component<MapAppProps, MapAppState> {
</Route> </Route>
<Route exact path="/:mode/:cat/:building/history"> <Route exact path="/:mode/:cat/:building/history">
<Sidebar> <Sidebar>
<Categories mode={mode || 'view'} building_id={building_id} />
<EditHistory building={this.state.building} /> <EditHistory building={this.state.building} />
</Sidebar> </Sidebar>
</Route> </Route>
<Route exact path="/:mode(view|edit|multi-edit)" <Route exact path="/:mode(view|edit|multi-edit)"
render={props => (<Redirect to={`/${props.match.params.mode}/categories`} />)} render={props => (<Redirect to={`/${props.match.params.mode}/categories`} />)}
/> />
</Switch> </Switch>
<ColouringMap <ColouringMap