aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/Gallery/Gallery.module.css63
-rw-r--r--components/Gallery/Gallery.tsx80
-rw-r--r--components/Map/Map.module.css28
-rw-r--r--components/Map/Map.tsx121
-rw-r--r--components/Sidebar/Sidebar.module.css54
-rw-r--r--components/Sidebar/Sidebar.tsx71
6 files changed, 0 insertions, 417 deletions
diff --git a/components/Gallery/Gallery.module.css b/components/Gallery/Gallery.module.css
deleted file mode 100644
index 01e8037..0000000
--- a/components/Gallery/Gallery.module.css
+++ /dev/null
@@ -1,63 +0,0 @@
-.galleryContainer {
- padding: 20px;
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 99999;
- background: rgba(0, 0, 0, 0.8);
-}
-
-.photoContainer {
- width: 100%;
- height: 100%;
- padding: 20px;
- position: relative;
-}
-
-.photo {
- max-width: 100%;
- max-height: 100%;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
-}
-
-.btn {
- position: absolute;
- z-index: 99999;
- font-size: 2rem;
- color: #eee;
- background: none;
- border: none;
- cursor: pointer;
- outline: currentcolor none medium;
-}
-
-.btn:hover {
- color: #fff;
-}
-
-.arrowLeft {
- top: 50%;
- left: 0;
- transform: translateY(-50%);
-}
-
-.arrowRight {
- top: 50%;
- right: 0;
- transform: translateY(-50%);
-}
-
-.closeBtn {
- width: 64px;
- height: 64px;
- top: 0;
- right: 0;
- font-size: 4rem;
- line-height: 4rem;
- font-weight: 300;
-}
diff --git a/components/Gallery/Gallery.tsx b/components/Gallery/Gallery.tsx
deleted file mode 100644
index dfadbc0..0000000
--- a/components/Gallery/Gallery.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import { useEffect, useRef } from 'react';
-
-import { Photo } from 'models';
-import useEvent from 'lib/useEvent';
-import styles from './Gallery.module.css';
-
-interface Props {
- currentPhoto: Photo;
- handleClose: () => void;
- handlePhotoChange: (direction: boolean) => void;
-}
-
-export default function Gallery({
- currentPhoto,
- handleClose,
- handlePhotoChange,
-}: Props): JSX.Element {
- const wrapperRef = useRef(null);
-
- useEffect(() => {
- function handleClickOutside(e: MouseEvent) {
- if (
- wrapperRef.current &&
- !wrapperRef.current.contains(e.target) &&
- (e.target as Node).nodeName !== 'BUTTON'
- ) {
- handleClose();
- }
- }
-
- document.addEventListener('mousedown', handleClickOutside);
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- };
- }, [wrapperRef]);
-
- useEvent('keydown', (e: KeyboardEvent) => {
- if (e.key === 'ArrowLeft') {
- handlePhotoChange(false);
- } else if (e.key === 'ArrowRight') {
- handlePhotoChange(true);
- } else if (e.key === 'Escape') {
- handleClose();
- }
- });
-
- return (
- <div className={styles.galleryContainer} role="region">
- <button
- type="button"
- className={`${styles.arrowLeft} ${styles.btn}`}
- onClick={() => handlePhotoChange(false)}
- >
- 〈
- </button>
- <div className={styles.photoContainer}>
- <img
- ref={wrapperRef}
- src={currentPhoto.src}
- alt={currentPhoto.name}
- className={styles.photo}
- />
- </div>
- <button
- type="button"
- className={`${styles.arrowRight} ${styles.btn}`}
- onClick={() => handlePhotoChange(true)}
- >
- 〉
- </button>
- <button
- type="button"
- className={`${styles.closeBtn} ${styles.btn}`}
- onClick={() => handleClose()}
- >
- ×
- </button>
- </div>
- );
-}
diff --git a/components/Map/Map.module.css b/components/Map/Map.module.css
deleted file mode 100644
index b13f5d2..0000000
--- a/components/Map/Map.module.css
+++ /dev/null
@@ -1,28 +0,0 @@
-.popup {
- width: auto;
- min-height: 90%;
-}
-
-.popup .leaflet-popup-content {
- margin: 0;
-}
-
-.markerIcon {
- border: 2px solid #fff;
- border-radius: 2px;
- overflow: hidden;
-}
-
-.markerItemCount {
- width: 18px;
- height: 18px;
- display: block;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- border-radius: 10px;
- background: #fff;
- text-align: center;
- font-weight: bold;
-}
diff --git a/components/Map/Map.tsx b/components/Map/Map.tsx
deleted file mode 100644
index 8ecde47..0000000
--- a/components/Map/Map.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { LatLngBounds, DivIcon, Icon } from 'leaflet';
-import { useEffect, useState } from 'react';
-import ReactDom from 'react-dom/server';
-import { MapContainer, Marker, TileLayer, GeoJSON, ZoomControl } from 'react-leaflet';
-import MarkerClusterGroup from 'react-leaflet-markercluster';
-
-import { Trip, Photo } from 'models';
-import { distanceBetween } from 'lib/util';
-
-import 'leaflet/dist/leaflet.css';
-import styles from './Map.module.css';
-
-interface Props {
- trip: Trip;
- handleMarkerClick: (photo: string) => void;
-}
-
-export default function Map({ trip, handleMarkerClick }: Props): JSX.Element {
- const [map, setMap] = useState(null);
-
- const bounds = trip
- ? new LatLngBounds(
- [trip.track.bbox[1], trip.track.bbox[0]],
- [trip.track.bbox[3], trip.track.bbox[2]],
- )
- : new LatLngBounds([75, -145], [-52, 145]);
-
- function createMarkers(photos: Photo[]): JSX.Element[] {
- // cluster photos that are close to each other
- const clusters: Photo[][] = [];
- for (let i = 0; i < photos.length; i += 1) {
- if (clusters.filter((c) => c.includes(photos[i])).length === 0) {
- const cluster = [photos[i]];
- for (let j = 0; j < photos.length; j += 1) {
- if (photos[i] !== photos[j]) {
- const a = [photos[i].latitude, photos[i].longitude];
- const b = [photos[j].latitude, photos[j].longitude];
- const distance = distanceBetween(a, b);
- if (distance < 10) cluster.push(photos[j]);
- }
- }
- clusters.push(cluster);
- }
- }
-
- // create React elements based on the clusters
- const markers = clusters.map((cluster) => {
- let Wrapper = ({ children }) => <>{children}</>;
- if (cluster.length > 1) {
- Wrapper = ({ children }) => (
- <MarkerClusterGroup
- iconCreateFunction={(markerCluster) =>
- new DivIcon({
- html: ReactDom.renderToString(
- <>
- <img
- style={{ width: 36, height: 36 }}
- src={cluster[0].thumbnail}
- alt=""
- />
- <span className={styles.markerItemCount}>{markerCluster.getChildCount()}</span>
- </>,
- ),
- iconSize: [36, 36],
- className: styles.markerIcon,
- })
- }
- >
- {children}
- </MarkerClusterGroup>
- );
- }
-
- const children = cluster.map((photo) => (
- <Marker
- key={photo.name}
- position={[photo.latitude, photo.longitude]}
- icon={
- new Icon({
- iconUrl: photo.thumbnail,
- iconSize: [36, 36],
- className: styles.markerIcon,
- })
- }
- eventHandlers={{ click: () => handleMarkerClick(photo.name) }}
- />
- ));
-
- return <Wrapper key={cluster[0].name}>{children}</Wrapper>;
- });
-
- return markers;
- }
-
- useEffect(() => {
- if (map) {
- map.setView(bounds.getCenter());
- map.fitBounds(bounds);
- }
- }, [trip]);
-
- return (
- <MapContainer
- center={bounds.getCenter()}
- bounds={bounds}
- scrollWheelZoom
- whenCreated={setMap}
- style={{ height: '100%', width: '100%' }}
- keyboard
- zoomControl={false}
- >
- <ZoomControl position="bottomright" />
- <TileLayer
- attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
- url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
- />
- {trip && <GeoJSON key={trip.name} data={trip.track} />}
- {trip && createMarkers(trip.photos)}
- </MapContainer>
- );
-}
diff --git a/components/Sidebar/Sidebar.module.css b/components/Sidebar/Sidebar.module.css
deleted file mode 100644
index a3c8a1a..0000000
--- a/components/Sidebar/Sidebar.module.css
+++ /dev/null
@@ -1,54 +0,0 @@
-.aside {
- min-width: 272px;
- height: 100%;
- padding: 5px;
- display: flex;
- flex-direction: column;
- border-right: 1px solid #e5e5e5;
- box-shadow: -2px 0 10px #000;
- z-index: 9999;
- background-color: #fff;
-}
-
-.aside h2 {
- padding-left: 1.5rem;
-}
-
-.list {
- margin: 0;
- padding: 0;
- list-style: none;
- overflow-y: auto;
-}
-
-.listItem {
- margin-bottom: 5px;
- padding: 1rem 2rem 1rem 1.5rem;
- display: block;
- border-radius: 10px;
- cursor: pointer;
- user-select: none;
- outline: currentcolor none medium;
-}
-
-.listItem:hover {
- background-color: #f5f5f5;
-}
-
-.listItemActive {
- background-color: #e9f3ff !important;
-}
-
-@media only screen and (max-width: 500px) {
- .aside {
- position: absolute;
- top: 0;
- bottom: 0;
- left: -100vw;
- transition: left 0.3s ease-in;
- }
-
- .asideOpen {
- left: 0;
- }
-}
diff --git a/components/Sidebar/Sidebar.tsx b/components/Sidebar/Sidebar.tsx
deleted file mode 100644
index 6b0473a..0000000
--- a/components/Sidebar/Sidebar.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { useEffect, useRef } from 'react';
-
-import { Trip } from 'models';
-import { secondsToTimeString } from 'lib/util';
-
-import styles from './Sidebar.module.css';
-
-interface Props {
- trips: Trip[];
- currentTrip: Trip;
- asideOpen: boolean;
- handleClose: () => void;
- setCurrentTrip: (trip: Trip) => void;
-}
-
-export default function Sidebar({
- trips,
- currentTrip,
- asideOpen,
- handleClose,
- setCurrentTrip,
-}: Props): JSX.Element {
- const wrapperRef = useRef(null);
-
- useEffect(() => {
- function handleClickOutside(e: MouseEvent) {
- if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
- handleClose();
- }
- }
-
- document.addEventListener('mousedown', handleClickOutside);
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- };
- }, [wrapperRef]);
-
- function handleTripChange(trip: Trip): void {
- setCurrentTrip(trip);
- handleClose();
- }
-
- return (
- <aside ref={wrapperRef} className={`${styles.aside} ${asideOpen && styles.asideOpen}`}>
- <h2>Trips</h2>
- <ul className={styles.list}>
- {trips.map((t) => (
- <li key={t.name}>
- <a
- onClick={() => handleTripChange(t)}
- onKeyPress={() => handleTripChange(t)}
- tabIndex={0}
- role="menuitem"
- className={`${styles.listItem} ${
- t.name === currentTrip.name && styles.listItemActive
- }`}
- >
- <b>{new Date(t.start).toDateString()}</b>
- <br />
- Total distance: {t.distance} km
- <br />
- Duration: {secondsToTimeString(t.duration)}
- <br />
- Average speed: {t.speed} km/h
- </a>
- </li>
- ))}
- </ul>
- </aside>
- );
-}