Lint 4-space indent

This commit is contained in:
Tom Russell 2019-05-27 16:39:16 +01:00
parent 907afa29f0
commit af9c23d883
25 changed files with 622 additions and 603 deletions

View File

@ -21,7 +21,8 @@
"no-console": "off", "no-console": "off",
"strict": ["error", "global"], "strict": ["error", "global"],
"curly": "warn", "curly": "warn",
"quotes": ["warn", "single"] "quotes": ["warn", "single"],
"indent": ["warn", 4]
}, },
"plugins": [ "plugins": [
"react" "react"

View File

@ -11,12 +11,12 @@ import App from './frontend/app';
const data = window.__PRELOADED_STATE__; const data = window.__PRELOADED_STATE__;
hydrate( hydrate(
<BrowserRouter> <BrowserRouter>
<App user={data.user} building={data.building} building_like={data.building_like} /> <App user={data.user} building={data.building} building_like={data.building_like} />
</BrowserRouter>, </BrowserRouter>,
document.getElementById('root') document.getElementById('root')
); );
if (module.hot) { if (module.hot) {
module.hot.accept(); module.hot.accept();
} }

View File

@ -5,51 +5,51 @@ import './about.css';
const AboutPage = () => ( const AboutPage = () => (
<article> <article>
<section className="main-col"> <section className="main-col">
<h1 className="h2"> <h1 className="h2">
Can you help us capture information on every building in London? Can you help us capture information on every building in London?
</h1> </h1>
<p> <p>
How many buildings are there in London? What are their characteristics? Where How many buildings are there in London? What are their characteristics? Where
are they located and how do they contribute to the city? How adaptable are are they located and how do they contribute to the city? How adaptable are
they? How long will they last, and what are the environmental and they? How long will they last, and what are the environmental and
socio-economic implications of demolition? socio-economic implications of demolition?
</p> </p>
<p> <p>
Colouring London is being designed to address these questions by crowdsourcing Colouring London is being designed to address these questions by crowdsourcing
and visualising information on Londons buildings. Were releasing the and visualising information on Londons buildings. Were releasing the
prototype for testing in the late summer. See the slideshow below for what it prototype for testing in the late summer. See the slideshow below for what it
will look like. will look like.
</p> </p>
<div className="buttons-container btn-center"> <div className="buttons-container btn-center">
<a className="btn btn-outline-dark btn-lg" href="#sign-up">Sign up for updates</a> <a className="btn btn-outline-dark btn-lg" href="#sign-up">Sign up for updates</a>
</div> </div>
<div className="carousel"> <div className="carousel">
<button className="carousel-control offscreen-text back">Back</button> <button className="carousel-control offscreen-text back">Back</button>
<ul className="carousel-content"> <ul className="carousel-content">
<li><img src="images/slide-1-welcome.png" alt="Welcome" /></li> <li><img src="images/slide-1-welcome.png" alt="Welcome" /></li>
<li><img src="images/slide-2-categories.png" alt="Categories" /></li> <li><img src="images/slide-2-categories.png" alt="Categories" /></li>
<li><img src="images/slide-3-edit.png" alt="Add/edit data" /></li> <li><img src="images/slide-3-edit.png" alt="Add/edit data" /></li>
<li><img src="images/slide-4-view.png" alt="View maps" /></li> <li><img src="images/slide-4-view.png" alt="View maps" /></li>
<li><img src="images/slide-5-download.png" alt="Download data" /></li> <li><img src="images/slide-5-download.png" alt="Download data" /></li>
<li><img src="images/slide-6-showcase.png" alt="Showcase" /></li> <li><img src="images/slide-6-showcase.png" alt="Showcase" /></li>
<li><img src="images/slide-7-partners.png" alt="Partners" /></li> <li><img src="images/slide-7-partners.png" alt="Partners" /></li>
</ul> </ul>
<button className="carousel-control offscreen-text next">Next</button> <button className="carousel-control offscreen-text next">Next</button>
</div> </div>
<div className="buttons-container btn-center"> <div className="buttons-container btn-center">
<a className="btn btn-outline-dark btn-lg" <a className="btn btn-outline-dark btn-lg"
href="files/colouring-london-online-exhibition.pdf"> href="files/colouring-london-online-exhibition.pdf">
View online exhibition</a> View online exhibition</a>
</div> </div>
</section> </section>
<hr/> <hr/>
<section className="main-col"> <section className="main-col">
<p> <p>
Colouring London is being designed and built by the Centre for Advanced Spatial Colouring London is being designed and built by the Centre for Advanced Spatial
Analysis (CASA), University College London and funded by Historic England. Analysis (CASA), University College London and funded by Historic England.
@ -57,159 +57,159 @@ const AboutPage = () => (
facilitated by the GLA, and giving access to its API and technical support. It facilitated by the GLA, and giving access to its API and technical support. It
will launch in 2019. will launch in 2019.
</p> </p>
<SupporterLogos /> <SupporterLogos />
</section> </section>
<hr/> <hr/>
<div className="main-col"> <div className="main-col">
<h2 className="h1">Data Categories</h2> <h2 className="h1">Data Categories</h2>
<p> <p>
12 categories have been chosen in consultation with specialists working in a 12 categories have been chosen in consultation with specialists working in a
range of areas, from energy analysis and sustainable urban planning and design range of areas, from energy analysis and sustainable urban planning and design
to building conservation, community planning, architecture and historical to building conservation, community planning, architecture and historical
research. research.
</p> </p>
<ol className="data-category-list"> <ol className="data-category-list">
<li className="bold-yellow"> <li className="bold-yellow">
<h3 className="category">Location</h3> <h3 className="category">Location</h3>
<p className="description">Where is it?</p> <p className="description">Where is it?</p>
</li> </li>
<li className="bright-yellow"> <li className="bright-yellow">
<h3 className="category">Use</h3> <h3 className="category">Use</h3>
<p className="description">How is it used?</p> <p className="description">How is it used?</p>
</li> </li>
<li className="bold-orange"> <li className="bold-orange">
<h3 className="category">Type</h3> <h3 className="category">Type</h3>
<p className="description">How was it first used?</p> <p className="description">How was it first used?</p>
</li> </li>
<li className="red"> <li className="red">
<h3 className="category">Age</h3> <h3 className="category">Age</h3>
<p className="description">When was it built?</p> <p className="description">When was it built?</p>
</li> </li>
<li className="pastel-pink"> <li className="pastel-pink">
<h3 className="category">Size</h3> <h3 className="category">Size</h3>
<p className="description">How big is it?</p> <p className="description">How big is it?</p>
</li> </li>
<li className="pastel-purple"> <li className="pastel-purple">
<h3 className="category">Construction</h3> <h3 className="category">Construction</h3>
<p className="description">How is it built?</p> <p className="description">How is it built?</p>
</li> </li>
<li className="blue-grey"> <li className="blue-grey">
<h3 className="category">Design/Build</h3> <h3 className="category">Design/Build</h3>
<p className="description">Who built it?</p> <p className="description">Who built it?</p>
</li> </li>
<li className="bright-green"> <li className="bright-green">
<h3 className="category">Street Front</h3> <h3 className="category">Street Front</h3>
<p className="description">How does it relate to the street?</p> <p className="description">How does it relate to the street?</p>
</li> </li>
<li className="pastel-green"> <li className="pastel-green">
<h3 className="category">Greenery</h3> <h3 className="category">Greenery</h3>
<p className="description">Is it near a tree or park?</p> <p className="description">Is it near a tree or park?</p>
</li> </li>
<li className="bright-blue"> <li className="bright-blue">
<h3 className="category">Protection</h3> <h3 className="category">Protection</h3>
<p className="description">Is it designated?</p> <p className="description">Is it designated?</p>
</li> </li>
<li className="pale-grey"> <li className="pale-grey">
<h3 className="category">Demolitions</h3> <h3 className="category">Demolitions</h3>
<p className="description">How many rebuilds on the site?</p> <p className="description">How many rebuilds on the site?</p>
</li> </li>
<li className="pale-brown"> <li className="pale-brown">
<h3 className="category">Like Me?</h3> <h3 className="category">Like Me?</h3>
<p className="description">Do you like it?</p> <p className="description">Do you like it?</p>
</li> </li>
</ol> </ol>
</div> </div>
<hr/> <hr/>
<div className="main-col">
<h2 className="h1">Once built, our platform will allow you to:</h2>
</div>
<section className="pale-pink color-block">
<div className="main-col"> <div className="main-col">
<h3 className="h2">View maps</h3> <h2 className="h1">Once built, our platform will allow you to:</h2>
<p> </div>
<section className="pale-pink color-block">
<div className="main-col">
<h3 className="h2">View maps</h3>
<p>
To view the data, navigate to the View Maps page and find the category that To view the data, navigate to the View Maps page and find the category that
interests you. interests you.
</p> </p>
<img className="border-image" src="images/slide-4-view.png" <img className="border-image" src="images/slide-4-view.png"
alt="Preview of view maps page" /> alt="Preview of view maps page" />
</div> </div>
</section> </section>
<section className="pale-yellow color-block"> <section className="pale-yellow color-block">
<div className="main-col"> <div className="main-col">
<h3 className="h2">Add and edit data</h3> <h3 className="h2">Add and edit data</h3>
<p> <p>
Find a building and add or edit data for any of the 12 core categories. Find a building and add or edit data for any of the 12 core categories.
</p> </p>
<img className="border-image" src="images/slide-3-edit.png" <img className="border-image" src="images/slide-3-edit.png"
alt="Preview of add/edit data page" /> alt="Preview of add/edit data page" />
</div> </div>
</section> </section>
<section className="pale-orange color-block"> <section className="pale-orange color-block">
<div className="main-col"> <div className="main-col">
<h3 className="h2">See how people are using our data</h3> <h3 className="h2">See how people are using our data</h3>
<p> <p>
Find links to visualisations and analysis, art projects and applications Find links to visualisations and analysis, art projects and applications
relating to the evolution of London, housing, energy, planning, heritage and relating to the evolution of London, housing, energy, planning, heritage and
history&mdash;or something we havent imagined yet. history&mdash;or something we havent imagined yet.
</p> </p>
<img className="border-image" src="images/slide-6-showcase.png" <img className="border-image" src="images/slide-6-showcase.png"
alt="Preview of data showcase page" /> alt="Preview of data showcase page" />
</div> </div>
</section> </section>
<section className="pale-green color-block"> <section className="pale-green color-block">
<div className="main-col"> <div className="main-col">
<h3 className="h2">Download, remix and reuse</h3> <h3 className="h2">Download, remix and reuse</h3>
<p> <p>
Access bulk downloads of data created through the project to use and reuse Access bulk downloads of data created through the project to use and reuse
under a liberal open data license. Let us know and well feature showcase under a liberal open data license. Let us know and well feature showcase
projects on the Colouring London site. projects on the Colouring London site.
</p> </p>
<img className="border-image" src="images/slide-5-download.png" <img className="border-image" src="images/slide-5-download.png"
alt="Preview of download page" /> alt="Preview of download page" />
</div> </div>
</section> </section>
<div className="main-col"> <div className="main-col">
<form id="sign-up" action="https://tinyletter.com/colouringlondon" method="post" <form id="sign-up" action="https://tinyletter.com/colouringlondon" method="post"
target="popupwindow" target="popupwindow"
onSubmit={function() {window.open( onSubmit={function() {window.open(
'https://tinyletter.com/colouringlondon', 'https://tinyletter.com/colouringlondon',
'popupwindow', 'popupwindow',
'scrollbars=yes,width=800,height=600'); return true}}> 'scrollbars=yes,width=800,height=600'); return true}}>
<h3 className="h1">Keep in touch</h3> <h3 className="h1">Keep in touch</h3>
<p> <p>
Receive occasional newsletters about the project as it develops. You can Receive occasional newsletters about the project as it develops. You can
read previous newsletters in our <a read previous newsletters in our <a
href="https://tinyletter.com/colouringlondon/archive">newsletter archive</a>. href="https://tinyletter.com/colouringlondon/archive">newsletter archive</a>.
</p> </p>
<label htmlFor="tlemail">Enter your email address:</label> <label htmlFor="tlemail">Enter your email address:</label>
<input className="form-control" type="email" name="email" id="tlemail" placeholder="name@example.com" /> <input className="form-control" type="email" name="email" id="tlemail" placeholder="name@example.com" />
<input type="hidden" value="1" name="embed"/> <input type="hidden" value="1" name="embed"/>
<small className="form-text text-muted"> <small className="form-text text-muted">
<a href="https://tinyletter.com"> <a href="https://tinyletter.com">
powered by TinyLetter</a>. powered by TinyLetter</a>.
We&rsquo;ll never share your email address. We&rsquo;ll never share your email address.
</small> </small>
<div className="buttons-container"> <div className="buttons-container">
<input className="btn btn-outline-dark btn-block" type="submit" value="Sign up for updates" /> <input className="btn btn-outline-dark btn-block" type="submit" value="Sign up for updates" />
</div>
</form>
</div> </div>
</form>
</div>
</article> </article>
); );

View File

@ -59,11 +59,11 @@ class App extends React.Component {
} }
selectBuilding(building) { selectBuilding(building) {
// get UPRNs and update // get UPRNs and update
fetch(`/building/${building.building_id}/uprns.json`, { fetch(`/building/${building.building_id}/uprns.json`, {
method: 'GET', method: 'GET',
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
@ -84,7 +84,7 @@ class App extends React.Component {
fetch(`/building/${building.building_id}/like.json`, { fetch(`/building/${building.building_id}/like.json`, {
method: 'GET', method: 'GET',
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
@ -114,13 +114,13 @@ class App extends React.Component {
<Overview <Overview
{...props} {...props}
mode='view' user={this.state.user} mode='view' user={this.state.user}
/> />
) } /> ) } />
<Route exact path="/edit/:cat.html" render={(props) => ( <Route exact path="/edit/:cat.html" render={(props) => (
<Overview <Overview
{...props} {...props}
mode='edit' user={this.state.user} mode='edit' user={this.state.user}
/> />
) } /> ) } />
<Route exact path="/view/:cat/building/:building.html" render={(props) => ( <Route exact path="/view/:cat/building/:building.html" render={(props) => (
<BuildingView <BuildingView
@ -128,7 +128,7 @@ class App extends React.Component {
{...this.state.building} {...this.state.building}
user={this.state.user} user={this.state.user}
building_like={this.state.building_like} building_like={this.state.building_like}
/> />
) } /> ) } />
<Route exact path="/edit/:cat/building/:building.html" render={(props) => ( <Route exact path="/edit/:cat/building/:building.html" render={(props) => (
<BuildingEdit <BuildingEdit
@ -137,7 +137,7 @@ class App extends React.Component {
user={this.state.user} user={this.state.user}
building_like={this.state.building_like} building_like={this.state.building_like}
selectBuilding={this.selectBuilding} selectBuilding={this.selectBuilding}
/> />
) } /> ) } />
</Switch> </Switch>
<Switch> <Switch>
@ -146,7 +146,7 @@ class App extends React.Component {
{...props} {...props}
building={this.state.building} building={this.state.building}
selectBuilding={this.selectBuilding} selectBuilding={this.selectBuilding}
/> />
) } /> ) } />
<Route exact path="/about.html" component={AboutPage} /> <Route exact path="/about.html" component={AboutPage} />
<Route exact path="/login.html"> <Route exact path="/login.html">
@ -160,7 +160,7 @@ class App extends React.Component {
user={this.state.user} user={this.state.user}
updateUser={this.updateUser} updateUser={this.updateUser}
logout={this.logout} logout={this.logout}
/> />
</Route> </Route>
<Route component={NotFound} /> <Route component={NotFound} />
</Switch> </Switch>

View File

@ -5,13 +5,13 @@ import { MemoryRouter } from 'react-router-dom';
import App from './app'; import App from './app';
describe('<App />', () => { describe('<App />', () => {
test('renders without exploding', () => { test('renders without exploding', () => {
const div = document.createElement('div'); const div = document.createElement('div');
ReactDOM.render( ReactDOM.render(
<MemoryRouter> <MemoryRouter>
<App /> <App />
</MemoryRouter>, </MemoryRouter>,
div div
); );
}); });
}); });

View File

@ -121,7 +121,7 @@ class EditForm extends Component {
fetch(`/building/${this.props.building_id}/like.json`, { fetch(`/building/${this.props.building_id}/like.json`, {
method: 'POST', method: 'POST',
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
credentials: 'same-origin', credentials: 'same-origin',
body: JSON.stringify({like: like}) body: JSON.stringify({like: like})
@ -149,7 +149,7 @@ class EditForm extends Component {
method: 'POST', method: 'POST',
body: JSON.stringify(this.state), body: JSON.stringify(this.state),
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
@ -179,82 +179,82 @@ class EditForm extends Component {
<h3 className="h3">{this.props.title}</h3> <h3 className="h3">{this.props.title}</h3>
</NavLink> </NavLink>
<nav className="icon-buttons"> <nav className="icon-buttons">
{ {
this.props.help? this.props.help?
<a className="icon-button help" title="Find out more" href={this.props.help}> <a className="icon-button help" title="Find out more" href={this.props.help}>
Info Info
</a> </a>
: null : null
} }
{ {
(match && !this.props.inactive && this.props.slug !== 'like')? // special-case for likes (match && !this.props.inactive && this.props.slug !== 'like')? // special-case for likes
<NavLink className="icon-button save" title="Save Changes" <NavLink className="icon-button save" title="Save Changes"
onClick={this.handleSubmit} onClick={this.handleSubmit}
to={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}> to={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}>
Save Save
<SaveIcon /> <SaveIcon />
</NavLink> </NavLink>
: null : null
} }
</nav> </nav>
</header> </header>
{ {
match? ( match? (
!this.props.inactive? !this.props.inactive?
<form action={`/edit/${this.props.slug}/building/${this.props.building_id}.html`} <form action={`/edit/${this.props.slug}/building/${this.props.building_id}.html`}
method="GET" onSubmit={this.handleSubmit}> method="GET" onSubmit={this.handleSubmit}>
{ {
this.props.slug === 'location'? this.props.slug === 'location'?
<InfoBox msg="Text-based address fields are disabled at the moment. We're looking into how best to collect this data." /> <InfoBox msg="Text-based address fields are disabled at the moment. We're looking into how best to collect this data." />
: null : null
} }
<ErrorBox msg={this.state.error} /> <ErrorBox msg={this.state.error} />
{ {
this.props.fields.map((props) => { this.props.fields.map((props) => {
switch (props.type) { switch (props.type) {
case 'text': case 'text':
return <TextInput {...props} handleChange={this.handleChange} return <TextInput {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
case 'text_list': case 'text_list':
return <TextListInput {...props} handleChange={this.handleChange} return <TextListInput {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
case 'text_long': case 'text_long':
return <LongTextInput {...props} handleChange={this.handleChange} return <LongTextInput {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
case 'number': case 'number':
return <NumberInput {...props} handleChange={this.handleChange} return <NumberInput {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
case 'year_estimator': case 'year_estimator':
return <YearEstimator {...props} handleChange={this.handleChange} return <YearEstimator {...props} handleChange={this.handleChange}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
case 'text_multi': case 'text_multi':
return <MultiTextInput {...props} handleChange={this.handleUpdate} return <MultiTextInput {...props} handleChange={this.handleUpdate}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
case 'checkbox': case 'checkbox':
return <CheckboxInput {...props} handleChange={this.handleCheck} return <CheckboxInput {...props} handleChange={this.handleCheck}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
case 'like': case 'like':
return <LikeButton {...props} handleLike={this.handleLike} return <LikeButton {...props} handleLike={this.handleLike}
building_like={building_like} building_like={building_like}
value={this.state[props.slug]} key={props.slug} /> value={this.state[props.slug]} key={props.slug} />
default: default:
return null return null
}
})
} }
}) <InfoBox msg="Colouring may take a few seconds - try zooming the map or hitting refresh after saving (we're working on making this smoother)." />
}
<InfoBox msg="Colouring may take a few seconds - try zooming the map or hitting refresh after saving (we're working on making this smoother)." />
{ {
(this.props.slug === 'like')? // special-case for likes (this.props.slug === 'like')? // special-case for likes
null : null :
<div className="buttons-container"> <div className="buttons-container">
<button type="submit" className="btn btn-primary">Save</button> <button type="submit" className="btn btn-primary">Save</button>
</div> </div>
} }
</form> </form>
: <form><InfoBox msg={`We're not collection data on ${this.props.title.toLowerCase()} yet - check back soon.`} /></form> : <form><InfoBox msg={`We're not collection data on ${this.props.title.toLowerCase()} yet - check back soon.`} /></form>
) : null ) : null
} }
</section> </section>
) )
} }
@ -270,7 +270,7 @@ const TextInput = (props) => (
disabled={props.disabled} disabled={props.disabled}
placeholder={props.placeholder} placeholder={props.placeholder}
onChange={props.handleChange} onChange={props.handleChange}
/> />
</Fragment> </Fragment>
); );
@ -327,30 +327,30 @@ class MultiTextInput extends Component {
render() { render() {
const values = this.getValues(); const values = this.getValues();
return ( return (
<Fragment> <Fragment>
<Label slug={this.props.slug} title={this.props.title} tooltip={this.props.tooltip} /> <Label slug={this.props.slug} title={this.props.title} tooltip={this.props.tooltip} />
{ {
values.map((item, i) => ( values.map((item, i) => (
<div className="input-group" key={i}> <div className="input-group" key={i}>
<input className="form-control" type="text" <input className="form-control" type="text"
key={`${this.props.slug}-${i}`} name={`${this.props.slug}-${i}`} key={`${this.props.slug}-${i}`} name={`${this.props.slug}-${i}`}
data-index={i} data-index={i}
value={item || ''} value={item || ''}
placeholder={this.props.placeholder} placeholder={this.props.placeholder}
disabled={this.props.disabled} disabled={this.props.disabled}
onChange={this.edit} onChange={this.edit}
/> />
<div className="input-group-append"> <div className="input-group-append">
<button type="button" onClick={this.remove} <button type="button" onClick={this.remove}
title="Remove" title="Remove"
data-index={i} className="btn btn-outline-dark"></button> data-index={i} className="btn btn-outline-dark"></button>
</div> </div>
</div> </div>
)) ))
} }
<button type="button" title="Add" onClick={this.add} <button type="button" title="Add" onClick={this.add}
className="btn btn-outline-dark">+</button> className="btn btn-outline-dark">+</button>
</Fragment> </Fragment>
) )
} }
} }
@ -382,7 +382,7 @@ const NumberInput = (props) => (
value={props.value || ''} value={props.value || ''}
disabled={props.disabled} disabled={props.disabled}
onChange={props.handleChange} onChange={props.handleChange}
/> />
</Fragment> </Fragment>
); );
@ -403,7 +403,7 @@ class YearEstimator extends Component {
render() { render() {
return ( return (
<NumberInput {...this.props} handleChange={this.props.handleChange} <NumberInput {...this.props} handleChange={this.props.handleChange}
value={this.props.value} key={this.props.slug} /> value={this.props.value} key={this.props.slug} />
) )
} }
} }
@ -415,7 +415,7 @@ const CheckboxInput = (props) => (
checked={!!props.value} checked={!!props.value}
disabled={props.disabled} disabled={props.disabled}
onChange={props.handleChange} onChange={props.handleChange}
/> />
<label htmlFor={props.slug} className="form-check-label"> <label htmlFor={props.slug} className="form-check-label">
{props.title} {props.title}
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null } { props.tooltip? <Tooltip text={ props.tooltip } /> : null }
@ -432,7 +432,7 @@ const LikeButton = (props) => (
checked={!!props.building_like} checked={!!props.building_like}
disabled={props.disabled} disabled={props.disabled}
onChange={props.handleLike} onChange={props.handleLike}
/> />
<label htmlFor={props.slug} className="form-check-label"> <label htmlFor={props.slug} className="form-check-label">
I like this building and think it contributes to the city! I like this building and think it contributes to the city!
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null } { props.tooltip? <Tooltip text={ props.tooltip } /> : null }

View File

@ -35,31 +35,31 @@ const BuildingView = (props) => {
section_props.fields.map(field_props => { section_props.fields.map(field_props => {
switch (field_props.type) { switch (field_props.type) {
case 'uprn_list': case 'uprn_list':
return <UPRNsDataEntry return <UPRNsDataEntry
key={field_props.slug} key={field_props.slug}
title={field_props.title} title={field_props.title}
value={props.uprns} value={props.uprns}
tooltip={field_props.tooltip} /> tooltip={field_props.tooltip} />
case 'text_multi': case 'text_multi':
return <MultiDataEntry return <MultiDataEntry
key={field_props.slug} key={field_props.slug}
title={field_props.title} title={field_props.title}
value={props[field_props.slug]} value={props[field_props.slug]}
tooltip={field_props.tooltip} /> tooltip={field_props.tooltip} />
case 'like': case 'like':
return <LikeDataEntry return <LikeDataEntry
key={field_props.slug} key={field_props.slug}
title={field_props.title} title={field_props.title}
value={props[field_props.slug]} value={props[field_props.slug]}
user_building_like={props.building_like} user_building_like={props.building_like}
tooltip={field_props.tooltip} /> tooltip={field_props.tooltip} />
default: default:
return <DataEntry return <DataEntry
key={field_props.slug} key={field_props.slug}
title={field_props.title} title={field_props.title}
value={props[field_props.slug]} value={props[field_props.slug]}
tooltip={field_props.tooltip} /> tooltip={field_props.tooltip} />
} }
}) })
} }
@ -84,30 +84,30 @@ const DataSection = (props) => {
<h3 className="h3">{props.title}</h3> <h3 className="h3">{props.title}</h3>
</NavLink> </NavLink>
<nav className="icon-buttons"> <nav className="icon-buttons">
{ {
props.help? props.help?
<a className="icon-button help" title="Find out more" href={props.help}> <a className="icon-button help" title="Find out more" href={props.help}>
Info Info
</a> </a>
: null : null
} }
{ {
!props.inactive? !props.inactive?
<NavLink className="icon-button edit" title="Edit data" <NavLink className="icon-button edit" title="Edit data"
to={`/edit/${props.slug}/building/${props.building_id}.html`}> to={`/edit/${props.slug}/building/${props.building_id}.html`}>
Edit Edit
<EditIcon /> <EditIcon />
</NavLink> </NavLink>
: null : null
} }
</nav> </nav>
</header> </header>
{ {
match? match?
!props.inactive? !props.inactive?
<dl className="data-list">{props.children}</dl> <dl className="data-list">{props.children}</dl>
: <p className="data-intro">{props.intro}</p> : <p className="data-intro">{props.intro}</p>
: null : null
} }
</section> </section>
); );
@ -135,13 +135,13 @@ const LikeDataEntry = (props) => (
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null } { props.tooltip? <Tooltip text={ props.tooltip } /> : null }
</dt> </dt>
<dd> <dd>
{ {
(props.value != null)? (props.value != null)?
(props.value === 1)? (props.value === 1)?
`${props.value} person likes this building` `${props.value} person likes this building`
: `${props.value} people like this building` : `${props.value} people like this building`
: '\u00A0' : '\u00A0'
} }
</dd> </dd>
{ {
(props.user_building_like)? <dd>&hellip;including you!</dd> : null (props.user_building_like)? <dd>&hellip;including you!</dd> : null
@ -215,33 +215,33 @@ const UPRNsDataEntry = (props) => {
const with_parent = uprns.filter(uprn => uprn.parent_uprn != null); const with_parent = uprns.filter(uprn => uprn.parent_uprn != null);
return ( return (
<Fragment> <Fragment>
<dt> <dt>
{ props.title } { props.title }
{ props.tooltip? <Tooltip text={ props.tooltip } /> : null } { props.tooltip? <Tooltip text={ props.tooltip } /> : null }
</dt> </dt>
<dd><ul className="uprn-list"> <dd><ul className="uprn-list">
<Fragment>{ <Fragment>{
no_parent.length? no_parent.length?
no_parent.map(uprn => ( no_parent.map(uprn => (
<li key={uprn.uprn}>{uprn.uprn}</li> <li key={uprn.uprn}>{uprn.uprn}</li>
))
: '\u00A0'
}</Fragment>
{
with_parent.length?
<details>
<summary>Children</summary>
{
with_parent.map(uprn => (
<li key={uprn.uprn}>{uprn.uprn} (child of {uprn.parent_uprn})</li>
)) ))
} : '\u00A0'
</details> }</Fragment>
: null {
} with_parent.length?
</ul></dd> <details>
</Fragment> <summary>Children</summary>
{
with_parent.map(uprn => (
<li key={uprn.uprn}>{uprn.uprn} (child of {uprn.parent_uprn})</li>
))
}
</details>
: null
}
</ul></dd>
</Fragment>
) )
} }

View File

@ -8,15 +8,15 @@ function ErrorBox(props){
<Fragment> <Fragment>
{ {
(props.msg)? (props.msg)?
( (
<div className="alert alert-danger" role="alert"> <div className="alert alert-danger" role="alert">
{ {
(typeof props.msg === 'string' || props.msg instanceof String)? (typeof props.msg === 'string' || props.msg instanceof String)?
props.msg props.msg
: 'Unexpected error' : 'Unexpected error'
} }
</div> </div>
) : null ) : null
} }
</Fragment> </Fragment>
); );

View File

@ -8,78 +8,96 @@ import './header.css';
* Render the main header using a responsive design * Render the main header using a responsive design
*/ */
class Header extends React.Component { class Header extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {collapseMenu: true}; this.state = {collapseMenu: true};
this.handleClick = this.handleClick.bind(this); this.handleClick = this.handleClick.bind(this);
} }
handleClick() { handleClick() {
this.setState(state => ({ this.setState(state => ({
collapseMenu: !state.collapseMenu collapseMenu: !state.collapseMenu
})); }));
} }
render() { render() {
return ( return (
<header className="main-header"> <header className="main-header">
<nav className="navbar navbar-light navbar-expand-md"> <nav className="navbar navbar-light navbar-expand-md">
<span className="navbar-brand"> <span className="navbar-brand">
<Logo/> <Logo/>
</span> </span>
<button className="navbar-toggler navbar-toggler-right" type="button" <button className="navbar-toggler navbar-toggler-right" type="button"
onClick={this.handleClick} aria-expanded="false" aria-label="Toggle navigation"> onClick={this.handleClick} aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span> <span className="navbar-toggler-icon"></span>
</button> </button>
<div className={this.state.collapseMenu ? 'collapse navbar-collapse' : 'navbar-collapse'}> <div className={this.state.collapseMenu ? 'collapse navbar-collapse' : 'navbar-collapse'}>
<ul className="navbar-nav ml-auto"> <ul className="navbar-nav ml-auto">
<li className="nav-item"> <li className="nav-item">
<a className="nav-link" href="https://pages.colouring.london">Hello</a> <a className="nav-link" href="https://pages.colouring.london">
</li> Hello
<li className="nav-item"> </a>
<a className="nav-link" href="https://pages.colouring.london/buildingcategories">Data Categories</a> </li>
</li> <li className="nav-item">
<li className="nav-item"> <a className="nav-link" href="https://pages.colouring.london/buildingcategories">
<NavLink to="/view/age.html" className="nav-link">View Maps</NavLink> Data Categories
</li> </a>
<li className="nav-item"> </li>
<NavLink to="/edit/age.html" className="nav-link">Add/Edit Data</NavLink> <li className="nav-item">
</li> <NavLink to="/view/age.html" className="nav-link">
<li className="nav-item"> View Maps
<a className="nav-link" href="https://pages.colouring.london/about">More about</a> </NavLink>
</li> </li>
<li className="nav-item"> <li className="nav-item">
<a className="nav-link" href="https://pages.colouring.london/whoisinvolved">Who&rsquo;s Involved?</a> <NavLink to="/edit/age.html" className="nav-link">
</li> Add/Edit Data
<li className="nav-item"> </NavLink>
<a className="nav-link" href="https://discuss.colouring.london">Discuss</a> </li>
</li> <li className="nav-item">
{ <a className="nav-link" href="https://pages.colouring.london/about">
this.props.user? More about
( </a>
<li className="nav-item"> </li>
<NavLink to="/my-account.html" className="nav-link"> <li className="nav-item">
My account (Logged in as {this.props.user.username}) <a className="nav-link" href="https://pages.colouring.london/whoisinvolved">
</NavLink> Who&rsquo;s Involved?
</li> </a>
): </li>
( <li className="nav-item">
<Fragment> <a className="nav-link" href="https://discuss.colouring.london">
<li className="nav-item"> Discuss
<NavLink to="/login.html" className="nav-link">Log in</NavLink> </a>
</li> </li>
<li className="nav-item"> {
<NavLink to="/sign-up.html" className="nav-link">Sign up</NavLink> this.props.user?
</li> (
</Fragment> <li className="nav-item">
) <NavLink to="/my-account.html" className="nav-link">
} My account (Logged in as {this.props.user.username})
</ul> </NavLink>
</div> </li>
</nav> ):
</header> (
); <Fragment>
} <li className="nav-item">
<NavLink to="/login.html" className="nav-link">
Log in
</NavLink>
</li>
<li className="nav-item">
<NavLink to="/sign-up.html" className="nav-link">
Sign up
</NavLink>
</li>
</Fragment>
)
}
</ul>
</div>
</nav>
</header>
);
}
} }
export default Header; export default Header;

View File

@ -5,7 +5,7 @@ import React from 'react'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faQuestionCircle, faPaintBrush, faInfoCircle, faTimes, faCheck, faCheckDouble, import { faQuestionCircle, faPaintBrush, faInfoCircle, faTimes, faCheck, faCheckDouble,
faAngleLeft, faCaretDown } from '@fortawesome/free-solid-svg-icons' faAngleLeft, faCaretDown } from '@fortawesome/free-solid-svg-icons'
library.add( library.add(
faQuestionCircle, faQuestionCircle,

View File

@ -4,18 +4,18 @@ const InfoBox = (props) => (
<Fragment> <Fragment>
{ {
(props.msg || props.children)? (props.msg || props.children)?
( (
<div className="alert alert-info" role="alert"> <div className="alert alert-info" role="alert">
{ {
(typeof props.msg === 'string' || props.msg instanceof String)? (typeof props.msg === 'string' || props.msg instanceof String)?
props.msg props.msg
: 'Enjoy the colouring! Usual service should resume shortly.' : 'Enjoy the colouring! Usual service should resume shortly.'
} }
{ {
props.children props.children
} }
</div> </div>
) : null ) : null
} }
</Fragment> </Fragment>
); );

View File

@ -18,46 +18,46 @@ const LEGEND_CONFIG = {
age: { age: {
title: 'Age', title: 'Age',
elements: [ elements: [
{ color: '#f0eaba', text: '≥2000' }, { color: '#f0eaba', text: '≥2000' },
{ color: '#fae269', text: '19802000' }, { color: '#fae269', text: '19802000' },
{ color: '#fbaf27', text: '19601980' }, { color: '#fbaf27', text: '19601980' },
{ color: '#e6711d', text: '19401960' }, { color: '#e6711d', text: '19401960' },
{ color: '#d73d3a', text: '19201940' }, { color: '#d73d3a', text: '19201940' },
{ color: '#ba221c', text: '19001920' }, { color: '#ba221c', text: '19001920' },
{ color: '#bb859b', text: '18801900' }, { color: '#bb859b', text: '18801900' },
{ color: '#8b3654', text: '18601880' }, { color: '#8b3654', text: '18601880' },
{ color: '#8f5385', text: '18401860' }, { color: '#8f5385', text: '18401860' },
{ color: '#56619b', text: '18201840' }, { color: '#56619b', text: '18201840' },
{ color: '#6793b2', text: '18001820' }, { color: '#6793b2', text: '18001820' },
{ color: '#83c3b3', text: '17801800' }, { color: '#83c3b3', text: '17801800' },
{ color: '#adc88f', text: '17601780' }, { color: '#adc88f', text: '17601780' },
{ color: '#83a663', text: '17401760' }, { color: '#83a663', text: '17401760' },
{ color: '#77852d', text: '17201740' }, { color: '#77852d', text: '17201740' },
{ color: '#69814e', text: '17001720' }, { color: '#69814e', text: '17001720' },
{ color: '#d0c291', text: '16801700' }, { color: '#d0c291', text: '16801700' },
{ color: '#918158', text: '16601680' }, { color: '#918158', text: '16601680' },
{ color: '#7a5732', text: '<1660' }, { color: '#7a5732', text: '<1660' },
] ]
}, },
size: { size: {
title: 'Number of storeys', title: 'Number of storeys',
elements: [ elements: [
{ color: '#ffffcc', text: '≥40' }, { color: '#ffffcc', text: '≥40' },
{ color: '#fed976', text: '2039' }, { color: '#fed976', text: '2039' },
{ color: '#fd8d3c', text: '1019' }, { color: '#fd8d3c', text: '1019' },
{ color: '#e31a1c', text: '69' }, { color: '#e31a1c', text: '69' },
{ color: '#800026', text: '15' }, { color: '#800026', text: '15' },
] ]
}, },
like: { like: {
title: 'Like Me', title: 'Like Me',
elements: [ elements: [
{ color: '#bd0026', text: '👍👍👍 ≥10' }, { color: '#bd0026', text: '👍👍👍 ≥10' },
{ color: '#e31a1c', text: '👍👍 510' }, { color: '#e31a1c', text: '👍👍 510' },
{ color: '#fc4e2a', text: '👍 4' }, { color: '#fc4e2a', text: '👍 4' },
{ color: '#fd8d3c', text: '👍 3' }, { color: '#fd8d3c', text: '👍 3' },
{ color: '#feb24c', text: '👍 2' }, { color: '#feb24c', text: '👍 2' },
{ color: '#fed976', text: '👍 1' }, { color: '#fed976', text: '👍 1' },
] ]
}, },
use: { use: {
@ -87,7 +87,7 @@ const LEGEND_CONFIG = {
planning: { planning: {
title: 'Planning', title: 'Planning',
elements: [ elements: [
{ color: '#73ebaf', text: 'within conservation area' }, { color: '#73ebaf', text: 'within conservation area' },
] ]
}, },
demolition: { demolition: {
@ -136,14 +136,14 @@ const Legend = (props) => {
} }
{ {
elements.length? elements.length?
(<ul className="data-legend"> (<ul className="data-legend">
{ {
elements.map((data_item) => ( elements.map((data_item) => (
<LegendItem {...data_item} key={data_item.color} /> <LegendItem {...data_item} key={data_item.color} />
)) ))
} }
</ul>) </ul>)
: (<p className="data-intro">Coming soon</p>) : (<p className="data-intro">Coming soon</p>)
} }
</div> </div>
); );

View File

@ -37,7 +37,7 @@ class Login extends Component {
method: 'POST', method: 'POST',
body: JSON.stringify(this.state), body: JSON.stringify(this.state),
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
@ -76,32 +76,32 @@ class Login extends Component {
<InfoBox msg="Welcome to Colouring London. You're one of the first people to use the site! "> <InfoBox msg="Welcome to Colouring London. You're one of the first people to use the site! ">
<br/>Please <a href="https://discuss.colouring.london/">discuss <br/>Please <a href="https://discuss.colouring.london/">discuss
suggestions for improvements</a> and <a suggestions for improvements</a> and <a
href="https://github.com/tomalrussell/colouring-london/issues"> href="https://github.com/tomalrussell/colouring-london/issues">
report issues or problems</a>. report issues or problems</a>.
</InfoBox> </InfoBox>
<ErrorBox msg={this.state.error} /> <ErrorBox msg={this.state.error} />
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<label htmlFor="username">Username*</label> <label htmlFor="username">Username*</label>
<input name="username" id="username" <input name="username" id="username"
className="form-control" type="text" className="form-control" type="text"
value={this.state.username} onChange={this.handleChange} value={this.state.username} onChange={this.handleChange}
placeholder="not-your-real-name" required placeholder="not-your-real-name" required
/> />
<label htmlFor="password">Password</label> <label htmlFor="password">Password</label>
<input name="password" id="password" <input name="password" id="password"
className="form-control" className="form-control"
type={(this.state.show_password)? 'text': 'password'} type={(this.state.show_password)? 'text': 'password'}
value={this.state.password} onChange={this.handleChange} value={this.state.password} onChange={this.handleChange}
required required
/> />
<div className="form-check"> <div className="form-check">
<input id="show_password" name="show_password" <input id="show_password" name="show_password"
className="form-check-input" type="checkbox" className="form-check-input" type="checkbox"
checked={this.state.show_password} checked={this.state.show_password}
onChange={this.handleChange} onChange={this.handleChange}
/> />
<label htmlFor="show_password" className="form-check-label">Show password?</label> <label htmlFor="show_password" className="form-check-label">Show password?</label>
</div> </div>

View File

@ -126,7 +126,7 @@ class ColouringMap extends Component {
zoomControl={false} zoomControl={false}
attributionControl={false} attributionControl={false}
onClick={this.handleClick} onClick={this.handleClick}
> >
<TileLayer url={url} attribution={attribution} /> <TileLayer url={url} attribution={attribution} />
<TileLayer url={base_layer_url} minZoom={14} /> <TileLayer url={base_layer_url} minZoom={14} />
{ dataLayer } { dataLayer }
@ -143,11 +143,11 @@ class ColouringMap extends Component {
} }
{ {
this.props.match.url !== '/'? ( this.props.match.url !== '/'? (
<Fragment> <Fragment>
<Legend slug={cat} /> <Legend slug={cat} />
<ThemeSwitcher onSubmit={this.themeSwitch} currentTheme={this.state.theme} /> <ThemeSwitcher onSubmit={this.themeSwitch} currentTheme={this.state.theme} />
<SearchBox onLocate={this.handleLocate} is_building={is_building} /> <SearchBox onLocate={this.handleLocate} is_building={is_building} />
</Fragment> </Fragment>
) : null ) : null
} }
</Fragment> </Fragment>

View File

@ -63,7 +63,7 @@ class MyAccountPage extends Component {
Colouring London is under active development, Please <a href="https://discuss.colouring.london/">discuss Colouring London is under active development, Please <a href="https://discuss.colouring.london/">discuss
suggestions for improvements</a> and <a suggestions for improvements</a> and <a
href="https://github.com/tomalrussell/colouring-london/issues"> href="https://github.com/tomalrussell/colouring-london/issues">
report issues or problems</a>. report issues or problems</a>.
</p> </p>

View File

@ -47,37 +47,37 @@ const OverviewSection = (props) => {
<nav className="icon-buttons"> <nav className="icon-buttons">
{ {
props.help? props.help?
<a className="icon-button help" href={props.help}> <a className="icon-button help" href={props.help}>
Info Info
</a> </a>
: null : null
} }
{ {
props.mode === 'view'? props.mode === 'view'?
<NavLink className="icon-button edit" title="Edit data" <NavLink className="icon-button edit" title="Edit data"
to={`/edit/${props.slug}.html`}> to={`/edit/${props.slug}.html`}>
Edit Edit
<EditIcon /> <EditIcon />
</NavLink> </NavLink>
: null : null
} }
</nav> </nav>
</header> </header>
{ {
(match && props.intro)? (match && props.intro)?
( (
<Fragment> <Fragment>
<p className="data-intro">{props.intro}</p> <p className="data-intro">{props.intro}</p>
<ul> <ul>
{ {
props.fields.map((field) => { props.fields.map((field) => {
return (<li key={field.slug}>{field.title}</li>) return (<li key={field.slug}>{field.title}</li>)
}) })
} }
</ul> </ul>
</Fragment> </Fragment>
) )
: null : null
} }
</section> </section>
) )

View File

@ -28,7 +28,7 @@ class SearchBox extends Component {
}); });
// If the clear icon has been clicked, clear results list as well // If the clear icon has been clicked, clear results list as well
if(e.target.value === '') { if(e.target.value === '') {
this.clearResults(); this.clearResults();
} }
} }
@ -79,41 +79,41 @@ class SearchBox extends Component {
}) })
} }
}).catch((err) => { }).catch((err) => {
console.error(err) console.error(err)
this.setState({ this.setState({
results: [], results: [],
fetching: false fetching: false
}) })
}) })
} }
render() { render() {
const resultsList = this.state.results.length? const resultsList = this.state.results.length?
<ul className="search-box-results"> <ul className="search-box-results">
{ {
this.state.results.map((result) => { this.state.results.map((result) => {
const label = result.attributes.label; const label = result.attributes.label;
const lng = result.geometry.coordinates[0]; const lng = result.geometry.coordinates[0];
const lat = result.geometry.coordinates[1]; const lat = result.geometry.coordinates[1];
const zoom = result.attributes.zoom; const zoom = result.attributes.zoom;
const href = `?lng=${lng}&lat=${lat}&zoom=${zoom}` const href = `?lng=${lng}&lat=${lat}&zoom=${zoom}`
return ( return (
<li key={result.attributes.label}> <li key={result.attributes.label}>
<a <a
className="search-box-result" className="search-box-result"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
this.props.onLocate(lat, lng, zoom); this.props.onLocate(lat, lng, zoom);
}} }}
href={href} href={href}
>{`${label.substring(0,4)} ${label.substring(4, 7)}`}</a> >{`${label.substring(0,4)} ${label.substring(4, 7)}`}</a>
</li> </li>
) )
}) })
} }
</ul> </ul>
: null; : null;
return ( return (
<div className={`search-box ${this.props.is_building? 'building' : ''}`} onKeyDown={this.handleKeyPress}> <div className={`search-box ${this.props.is_building? 'building' : ''}`} onKeyDown={this.handleKeyPress}>
<form action="/search" method="GET" onSubmit={this.search} <form action="/search" method="GET" onSubmit={this.search}
@ -127,7 +127,7 @@ class SearchBox extends Component {
placeholder="Search for a postcode" placeholder="Search for a postcode"
aria-label="Search for a postcode" aria-label="Search for a postcode"
onChange={this.handleChange} onChange={this.handleChange}
/> />
<button className="btn btn-outline-dark" type="submit">Search</button> <button className="btn btn-outline-dark" type="submit">Search</button>
</form> </form>
{ resultsList } { resultsList }

View File

@ -12,7 +12,7 @@ const Sidebar = (props) => (
<Link className="icon-button back" to={props.back}> <Link className="icon-button back" to={props.back}>
<BackIcon /> <BackIcon />
</Link> </Link>
: null : null
} }
<h2 className="h2">{props.title}</h2> <h2 className="h2">{props.title}</h2>
</header> </header>

View File

@ -40,7 +40,7 @@ class SignUp extends Component {
method: 'POST', method: 'POST',
body: JSON.stringify(this.state), body: JSON.stringify(this.state),
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
@ -75,7 +75,7 @@ class SignUp extends Component {
<InfoBox msg="Welcome to Colouring London. You're one of the first people to sign up! "> <InfoBox msg="Welcome to Colouring London. You're one of the first people to sign up! ">
<br/>Please <a href="https://discuss.colouring.london/">discuss <br/>Please <a href="https://discuss.colouring.london/">discuss
suggestions for improvements</a> and <a suggestions for improvements</a> and <a
href="https://github.com/tomalrussell/colouring-london/issues"> href="https://github.com/tomalrussell/colouring-london/issues">
report issues or problems</a>. report issues or problems</a>.
</InfoBox> </InfoBox>
<p> <p>
@ -85,38 +85,38 @@ class SignUp extends Component {
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<label htmlFor="username">Username*</label> <label htmlFor="username">Username*</label>
<input name="username" id="username" <input name="username" id="username"
className="form-control" type="text" className="form-control" type="text"
value={this.state.username} onChange={this.handleChange} value={this.state.username} onChange={this.handleChange}
placeholder="not-your-real-name" required placeholder="not-your-real-name" required
/> />
<label htmlFor="email">Email (optional)</label> <label htmlFor="email">Email (optional)</label>
<input name="email" id="email" <input name="email" id="email"
className="form-control" type="email" className="form-control" type="email"
value={this.state.email} onChange={this.handleChange} value={this.state.email} onChange={this.handleChange}
placeholder="someone@example.com" placeholder="someone@example.com"
/> />
<label htmlFor="confirm_email">Confirm email (optional)</label> <label htmlFor="confirm_email">Confirm email (optional)</label>
<input name="confirm_email" id="confirm_email" <input name="confirm_email" id="confirm_email"
className="form-control" type="email" className="form-control" type="email"
value={this.state.confirm_email} onChange={this.handleChange} value={this.state.confirm_email} onChange={this.handleChange}
/> />
<label htmlFor="password">Password (at least 8 characters)</label> <label htmlFor="password">Password (at least 8 characters)</label>
<input name="password" id="password" <input name="password" id="password"
className="form-control" className="form-control"
type={(this.state.show_password)? 'text': 'password'} type={(this.state.show_password)? 'text': 'password'}
value={this.state.password} onChange={this.handleChange} value={this.state.password} onChange={this.handleChange}
required required
/> />
<div className="form-check"> <div className="form-check">
<input id="show_password" name="show_password" <input id="show_password" name="show_password"
className="form-check-input" type="checkbox" className="form-check-input" type="checkbox"
checked={this.state.show_password} checked={this.state.show_password}
onChange={this.handleChange} onChange={this.handleChange}
/> />
<label className="form-check-label" htmlFor="show_password"> <label className="form-check-label" htmlFor="show_password">
Show password? Show password?
</label> </label>
@ -124,14 +124,14 @@ class SignUp extends Component {
<div className="form-check"> <div className="form-check">
<input id="confirm_conditions" name="confirm_conditions" <input id="confirm_conditions" name="confirm_conditions"
className="form-check-input" type="checkbox" className="form-check-input" type="checkbox"
checked={this.state.confirm_conditions} checked={this.state.confirm_conditions}
onChange={this.handleChange} onChange={this.handleChange}
required /> required />
<label className="form-check-label" htmlFor="confirm_conditions"> <label className="form-check-label" htmlFor="confirm_conditions">
I confirm that I have read and agree to the <a I confirm that I have read and agree to the <a
href="/privacy-policy">privacy policy</a> and <a href="/privacy-policy">privacy policy</a> and <a
href="/user-agreement">contributor agreement</a>. href="/user-agreement">contributor agreement</a>.
</label> </label>
</div> </div>

View File

@ -5,7 +5,7 @@ import './theme-switcher.css';
const ThemeSwitcher = (props) => ( const ThemeSwitcher = (props) => (
<form className={`theme-switcher ${props.currentTheme}`} onSubmit={props.onSubmit}> <form className={`theme-switcher ${props.currentTheme}`} onSubmit={props.onSubmit}>
<button className="btn btn-outline btn-outline-dark" <button className="btn btn-outline btn-outline-dark"
type="submit"> type="submit">
Switch theme ({(props.currentTheme === 'light')? 'Light' : 'Night'}) Switch theme ({(props.currentTheme === 'light')? 'Light' : 'Night'})
</button> </button>
</form> </form>

View File

@ -23,20 +23,20 @@ class Tooltip extends Component {
return ( return (
<div className="tooltip-wrap"> <div className="tooltip-wrap">
<button className={(this.state.active? 'active ': '') + 'tooltip-hint icon-button'} <button className={(this.state.active? 'active ': '') + 'tooltip-hint icon-button'}
title={this.props.text} title={this.props.text}
onClick={this.handleClick}> onClick={this.handleClick}>
Hint Hint
<InfoIcon /> <InfoIcon />
</button> </button>
{ {
this.state.active? this.state.active?
( (
<div className="tooltip bs-tooltip-bottom"> <div className="tooltip bs-tooltip-bottom">
<div className="arrow"></div> <div className="arrow"></div>
<div className="tooltip-inner">{this.props.text}</div> <div className="tooltip-inner">{this.props.text}</div>
</div> </div>
) )
: null : null
} }
</div> </div>
); );

View File

@ -12,22 +12,22 @@ const server = http.createServer(app);
let currentApp = app; let currentApp = app;
server.listen(process.env.PORT || 3000, error => { server.listen(process.env.PORT || 3000, error => {
if (error) { if (error) {
console.log(error); console.log(error);
} }
console.log('🚀 started'); console.log('🚀 started');
}); });
// In development mode, enable hot module reloading (HMR) // In development mode, enable hot module reloading (HMR)
if (module.hot) { if (module.hot) {
console.log('✅ Server-side HMR Enabled!'); console.log('✅ Server-side HMR Enabled!');
module.hot.accept('./server', () => { module.hot.accept('./server', () => {
console.log('🔁 HMR Reloading x`./server`...'); console.log('🔁 HMR Reloading x`./server`...');
server.removeListener('request', currentApp); server.removeListener('request', currentApp);
const newApp = require('./server').default; const newApp = require('./server').default;
server.on('request', newApp); server.on('request', newApp);
currentApp = newApp; currentApp = newApp;
}); });
} }

View File

@ -141,15 +141,15 @@ function renderHTML(context, data, req, res) {
} }
</style> </style>
${ ${
assets.client.css assets.client.css
? `<link rel="stylesheet" href="${assets.client.css}">` ? `<link rel="stylesheet" href="${assets.client.css}">`
: '' : ''
} }
${ ${
process.env.NODE_ENV === 'production' process.env.NODE_ENV === 'production'
? `<script src="${assets.client.js}" defer></script>` ? `<script src="${assets.client.js}" defer></script>`
: `<script src="${assets.client.js}" defer crossorigin></script>` : `<script src="${assets.client.js}" defer crossorigin></script>`
} }
</head> </head>
<body> <body>
<div id="root">${markup}</div> <div id="root">${markup}</div>

View File

@ -157,11 +157,11 @@ function cache_location(tileset, z, x, y) {
*/ */
function should_try_cache(tileset, z) { function should_try_cache(tileset, z) {
if (tileset === 'date_year') { if (tileset === 'date_year') {
// cache high zoom because of front page hits // cache high zoom because of front page hits
return z <= 16 return z <= 16
} }
if (tileset === 'base_light' || tileset === 'base_night') { if (tileset === 'base_light' || tileset === 'base_night') {
// cache for higher zoom levels (unlikely to change) // cache for higher zoom levels (unlikely to change)
return z <= 17 return z <= 17
} }
// else cache for lower zoom levels (change slowly) // else cache for lower zoom levels (change slowly)

View File

@ -138,7 +138,7 @@ function stitch_tile(tileset, z, x, y) {
const next_xy = get_xyz(bbox, next_z) const next_xy = get_xyz(bbox, next_z)
return Promise.all([ return Promise.all([
// recurse down through zoom levels, using cache if available... // recurse down through zoom levels, using cache if available...
load_tile(tileset, next_z, next_xy.minX, next_xy.minY), load_tile(tileset, next_z, next_xy.minX, next_xy.minY),
load_tile(tileset, next_z, next_xy.maxX, next_xy.minY), load_tile(tileset, next_z, next_xy.maxX, next_xy.minY),
load_tile(tileset, next_z, next_xy.minX, next_xy.maxY), load_tile(tileset, next_z, next_xy.minX, next_xy.maxY),
@ -149,9 +149,9 @@ function stitch_tile(tileset, z, x, y) {
bottom_left, bottom_left,
bottom_right bottom_right
]) => { ]) => {
// not possible to chain overlays in a single pipeline, but there may still be a better // not possible to chain overlays in a single pipeline, but there may still be a better
// way to create image buffer here (four tiles resize to one at the next zoom level) // way to create image buffer here (four tiles resize to one at the next zoom level)
// instead of repeatedly creating `sharp` objects, to png, to buffer... // instead of repeatedly creating `sharp` objects, to png, to buffer...
return sharp({ return sharp({
create: { create: {
width: TILE_SIZE * 2, width: TILE_SIZE * 2,