Skip to content

Commit

Permalink
Restyle the UI for dark/light mode (#16)
Browse files Browse the repository at this point in the history
* Numerous styling changes to bring things to more of a dark-mode compatible state.

* Add dark/light mode toggle

* Restyle the download button and bring it to the top of the UI

* Get rid of a LOT of empty/unused space

* Fix download to use a unique filename with zoom, lat, lng, and .parquet suffix.

* Add a geolocate control.

---------

Co-authored-by: Benjamin Clark <clarkben@meta.com>
  • Loading branch information
Bonkles and Benjamin Clark authored May 3, 2024
1 parent 70af9d0 commit c9d1f03
Show file tree
Hide file tree
Showing 15 changed files with 193 additions and 52 deletions.
1 change: 1 addition & 0 deletions site/public/darkmode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions site/public/download.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions site/public/lightmode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 16 additions & 6 deletions site/src/App.css
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
#root {
margin: 0;
padding: 1 rem;
font-family: Montserrat, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
width:100%;
}

.map {
width:80vw;
width:100vw;
height:80vh;
}

.header {
.wordmark {
display:flex;
flex-direction:row;
align-items: center;
}

.header,.footer {
display: flex;
flex-direction:row;
align-items: center;
justify-content:center
justify-content:space-between;
width:100%;
}

.footer {
height: 20vh;
}

.downloadBar {
display: flex;
flex-direction: row;
justify-content: end;
}

.logo {
height: 2em;
padding: .5em;
Expand Down
15 changes: 12 additions & 3 deletions site/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ import Header from './Header';
import Footer from './Footer';
import Map from './Map';
import {MapProvider} from 'react-map-gl/maplibre';
import { keepTheme } from './themeUtils';
import { useState, useEffect } from 'react';

function App() {
const [modeName, setModeName] = useState("theme-dark");

useEffect(() => {
keepTheme(setModeName);
}, [setModeName]);

return (
<MapProvider>
<Header />
<Map/>
<Footer />
<Header mode={modeName} setMode={setModeName}/>
<Map mode={modeName}/>
<Footer mode={modeName}/>
</MapProvider>
);
}
Expand Down
27 changes: 27 additions & 0 deletions site/src/DarkModeToggle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { setTheme } from './themeUtils';
import PropTypes from 'prop-types';

export default function DarkModeToggle({ mode, setMode }) {

const toggleDarkMode = () => {

if (mode === 'theme-dark') {
setTheme('theme-light', setMode);
} else {
setTheme('theme-dark', setMode);
}
};

return (
<div>
<button id="toggle" className="toggle--checkbox" onClick={toggleDarkMode} readOnly>
<img src={mode === 'theme-light' ? '/lightmode.svg' : '/darkmode.svg'} />
</button>
</div>
);
}

DarkModeToggle.propTypes = {
mode: PropTypes.string.isRequired,
setMode: PropTypes.func.isRequired
}
11 changes: 0 additions & 11 deletions site/src/DownloadBar.jsx

This file was deleted.

24 changes: 18 additions & 6 deletions site/src/DownloadButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,32 @@ function DownloadButton() {
});

//Download the blob
window.open(URL.createObjectURL(blerb));
// window.open(URL.createObjectURL(blerb));

const url = URL.createObjectURL(blerb);
var downloadLink = document.createElement("a");
downloadLink.href = url;

const center = myMap.getCenter();
const zoom = myMap.getZoom();
downloadLink.download = `overture-${zoom}-${center.lat}-${center.lng}.parquet`;

document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);

setLoading(false);
};

return (
<>
<button id="downloadButton" disabled={loading} className={loading ? "cursor-downloading" : ''} onClick={handleDownloadClick}>
Download Visible
<button id="download" disabled={loading} className={loading ? "disabled" : ''} onClick={handleDownloadClick}>

<img className={'dl-img'} src="/download.svg"/>
{loading ? 'Downloading...' : 'Download Visible'}
</button>
</>
);
}

// DownloadButton.propTypes = {
// onClick: PropTypes.func.isRequired
// }
export default DownloadButton;
9 changes: 7 additions & 2 deletions site/src/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export default function Footer() {
import PropTypes from 'prop-types';
export default function Footer({mode}) {
return (
<>
<div className="footer">
<div className={`footer ${mode}`}>
<p className="read-the-docs">
Head on over to{' '}
<a href="https://docs.overturemaps.org/how-to" target="_blank">
Expand All @@ -14,3 +15,7 @@ export default function Footer() {
</>
);
}

Footer.propTypes = {
mode: PropTypes.string.isRequired
}
25 changes: 14 additions & 11 deletions site/src/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import overtureLogo from '/omf_logo_transparent.png';
import DownloadButton from './DownloadButton';
import OvertureWordmark from './OvertureWordmark';
import DarkModeToggle from './DarkModeToggle';
import PropTypes from 'prop-types';

export default function Header() {
export default function Header({ mode, setMode }) {
return (
<>
<div className="header">
<a href="https://overturemaps.org" target="_blank">
<img
src={overtureLogo}
className="logo overture"
alt="Overture Maps logo"
/>
</a>
<div>Overture Maps Explorer</div>
<div className={`header ${mode}` }>
<OvertureWordmark></OvertureWordmark>
<DarkModeToggle mode={mode} setMode={setMode}></DarkModeToggle>
<DownloadButton></DownloadButton>
</div>
</>
);
}

Header.propTypes = {
mode: PropTypes.string.isRequired,
setMode: PropTypes.func.isRequired
}
27 changes: 21 additions & 6 deletions site/src/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import 'maplibre-gl/dist/maplibre-gl.css';
import * as pmtiles from 'pmtiles';
import maplibregl from 'maplibre-gl';
import { useState, useEffect } from 'react';
import { Layer } from 'react-map-gl/maplibre';
import DownloadBar from './DownloadBar';
import { Layer, GeolocateControl } from 'react-map-gl/maplibre';
import PropTypes from 'prop-types';

const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';
const DARK_MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json'

const PLACES_PMTILES_URL = 'pmtiles://https://r2-public.protomaps.com/overture-tiles/2023-10-19-alpha.0/places.pmtiles';
const PLACES_MAP_STYLE = {
Expand Down Expand Up @@ -54,7 +55,7 @@ const INITIAL_VIEW_STATE = {
bearing: 0,
pitch: 0
};
export default function Map() {
export default function Map({mode}) {

const [pmTilesReady, setPmTilesReady] = useState(false)

Expand All @@ -64,23 +65,37 @@ export default function Map() {
setPmTilesReady(true)
}, []);

function getMapStyle() {
let mapStyle;

if (pmTilesReady){
mapStyle = mode === 'theme-light' ? MAP_STYLE : DARK_MAP_STYLE;
} else {
mapStyle = undefined;
}
return mapStyle;
}

return (
<>
<div className="map">
<div className={`map ${mode}`}>
<MapLibreMap
id="myMap"
hash={true}
initialViewState={INITIAL_VIEW_STATE}
mapStyle={pmTilesReady ? MAP_STYLE : undefined}
mapStyle={getMapStyle()}
>
<Source id="overture-places" type="vector" url={PLACES_PMTILES_URL}>
<Layer {...PLACES_MAP_STYLE} />
</Source>
<NavigationControl position='top-right'></NavigationControl>
<GeolocateControl />
</MapLibreMap>
</div>
<DownloadBar />
</>
);
}

Map.propTypes = {
mode: PropTypes.string.isRequired
}
18 changes: 18 additions & 0 deletions site/src/OvertureWordmark.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import overtureLogo from '/omf_logo_transparent.png';

export default function OvertureWordmark() {
return (
<>
<div className='wordmark'>
<a href="https://overturemaps.org" target="_blank">
<img
src={overtureLogo}
className="logo overture"
alt="Overture Maps logo"
/>
</a>
<div>Overture Maps</div>
</div>
</>
);
}
40 changes: 34 additions & 6 deletions site/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

width:100%;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
Expand All @@ -11,6 +11,9 @@
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
padding: 0 rem;
font-family: Montserrat, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
}

a {
Expand All @@ -35,22 +38,39 @@ h1 {
font-size: 3.2em;
line-height: 1.1;
}
button.disabled {
opacity: 50%;
}

button {
border-radius: 4px;
border: 2px solid gray;
button#download {
background-color: rgb(0,255,255);
border-radius: 16px;
color: rgba(0, 0, 0, 0.87);
display: flex;
justify-content:space-between;
align-items: center;
padding: 0.6em 1.2em;
margin: 2px;
font-size: 1em;
font-weight: 400;
margin: 2px;
border: 2px solid gray;
}

img.dl-img {
margin-right: 5px;
width: 1.5rem;
}

button {
font-family: inherit;
background-color: aliceblue;
cursor: pointer;
transition: border-color 0.25s;
}

button:hover {
border-color: #646cff;
}

button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
Expand All @@ -68,3 +88,11 @@ button:focus-visible {
background-color: #f9f9f9;
}
}

div.theme-light button {
background-color: #ffffff;
}
div.theme-light {
color: #213547;
background-color: #ffffff;
}
2 changes: 1 addition & 1 deletion site/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import initWasm from "@geoarrow/geoarrow-wasm/esm/index.js";
import initWasm from "@geoarrow/geoarrow-wasm/esm/index.js";

//TODO: Make this async and parallelize with the startup of the map component, rather than blocking in.
await initWasm();
Expand Down
Loading

0 comments on commit c9d1f03

Please sign in to comment.