Kenneth Bruen
3 years ago
commit
5a4c97ba58
11 changed files with 1776 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||||||
|
/node_modules/ |
||||||
|
/public/build/ |
||||||
|
|
||||||
|
.DS_Store |
||||||
|
|
||||||
|
.vscode |
@ -0,0 +1,32 @@ |
|||||||
|
{ |
||||||
|
"name": "svelte-app", |
||||||
|
"version": "1.0.0", |
||||||
|
"private": true, |
||||||
|
"scripts": { |
||||||
|
"build": "rollup -c", |
||||||
|
"dev": "rollup -c -w", |
||||||
|
"start": "sirv public --no-clear", |
||||||
|
"check": "svelte-check --tsconfig ./tsconfig.json" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@rollup/plugin-commonjs": "^17.0.0", |
||||||
|
"@rollup/plugin-node-resolve": "^11.0.0", |
||||||
|
"@rollup/plugin-typescript": "^8.0.0", |
||||||
|
"@tsconfig/svelte": "^2.0.0", |
||||||
|
"@types/leaflet": "^1.7.4", |
||||||
|
"eslint": "^7.32.0", |
||||||
|
"rollup": "^2.3.4", |
||||||
|
"rollup-plugin-css-only": "^3.1.0", |
||||||
|
"rollup-plugin-livereload": "^2.0.0", |
||||||
|
"rollup-plugin-svelte": "^7.0.0", |
||||||
|
"rollup-plugin-terser": "^7.0.0", |
||||||
|
"svelte": "^3.0.0", |
||||||
|
"svelte-check": "^2.0.0", |
||||||
|
"svelte-preprocess": "^4.0.0", |
||||||
|
"tslib": "^2.0.0", |
||||||
|
"typescript": "^4.0.0" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"sirv-cli": "^1.0.0" |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1,63 @@ |
|||||||
|
html, body { |
||||||
|
position: relative; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
color: #333; |
||||||
|
margin: 0; |
||||||
|
padding: 8px; |
||||||
|
box-sizing: border-box; |
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; |
||||||
|
} |
||||||
|
|
||||||
|
a { |
||||||
|
color: rgb(0,100,200); |
||||||
|
text-decoration: none; |
||||||
|
} |
||||||
|
|
||||||
|
a:hover { |
||||||
|
text-decoration: underline; |
||||||
|
} |
||||||
|
|
||||||
|
a:visited { |
||||||
|
color: rgb(0,80,160); |
||||||
|
} |
||||||
|
|
||||||
|
label { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
input, button, select, textarea { |
||||||
|
font-family: inherit; |
||||||
|
font-size: inherit; |
||||||
|
-webkit-padding: 0.4em 0; |
||||||
|
padding: 0.4em; |
||||||
|
margin: 0 0 0.5em 0; |
||||||
|
box-sizing: border-box; |
||||||
|
border: 1px solid #ccc; |
||||||
|
border-radius: 2px; |
||||||
|
} |
||||||
|
|
||||||
|
input:disabled { |
||||||
|
color: #ccc; |
||||||
|
} |
||||||
|
|
||||||
|
button { |
||||||
|
color: #333; |
||||||
|
background-color: #f4f4f4; |
||||||
|
outline: none; |
||||||
|
} |
||||||
|
|
||||||
|
button:disabled { |
||||||
|
color: #999; |
||||||
|
} |
||||||
|
|
||||||
|
button:not(:disabled):active { |
||||||
|
background-color: #ddd; |
||||||
|
} |
||||||
|
|
||||||
|
button:focus { |
||||||
|
border-color: #666; |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset='utf-8'> |
||||||
|
<meta name='viewport' content='width=device-width,initial-scale=1'> |
||||||
|
|
||||||
|
<title>Location Preview</title> |
||||||
|
|
||||||
|
<link rel='icon' type='image/png' href='/favicon.png'> |
||||||
|
<link rel='stylesheet' href='/global.css'> |
||||||
|
<link rel='stylesheet' href='/build/bundle.css'> |
||||||
|
|
||||||
|
<!-- leaflet --> |
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" |
||||||
|
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" |
||||||
|
crossorigin=""/> |
||||||
|
|
||||||
|
<script defer src='/build/bundle.js'></script> |
||||||
|
|
||||||
|
<!-- leaflet --> |
||||||
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" |
||||||
|
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" |
||||||
|
crossorigin=""></script> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,84 @@ |
|||||||
|
import svelte from 'rollup-plugin-svelte'; |
||||||
|
import commonjs from '@rollup/plugin-commonjs'; |
||||||
|
import resolve from '@rollup/plugin-node-resolve'; |
||||||
|
import livereload from 'rollup-plugin-livereload'; |
||||||
|
import { terser } from 'rollup-plugin-terser'; |
||||||
|
import sveltePreprocess from 'svelte-preprocess'; |
||||||
|
import typescript from '@rollup/plugin-typescript'; |
||||||
|
import css from 'rollup-plugin-css-only'; |
||||||
|
|
||||||
|
const production = !process.env.ROLLUP_WATCH; |
||||||
|
|
||||||
|
function serve() { |
||||||
|
let server; |
||||||
|
|
||||||
|
function toExit() { |
||||||
|
if (server) server.kill(0); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
writeBundle() { |
||||||
|
if (server) return; |
||||||
|
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { |
||||||
|
stdio: ['ignore', 'inherit', 'inherit'], |
||||||
|
shell: true |
||||||
|
}); |
||||||
|
|
||||||
|
process.on('SIGTERM', toExit); |
||||||
|
process.on('exit', toExit); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export default { |
||||||
|
input: 'src/main.ts', |
||||||
|
output: { |
||||||
|
sourcemap: true, |
||||||
|
format: 'iife', |
||||||
|
name: 'app', |
||||||
|
file: 'public/build/bundle.js', |
||||||
|
}, |
||||||
|
plugins: [ |
||||||
|
svelte({ |
||||||
|
preprocess: sveltePreprocess({ sourceMap: !production }), |
||||||
|
compilerOptions: { |
||||||
|
// enable run-time checks when not in production
|
||||||
|
dev: !production |
||||||
|
} |
||||||
|
}), |
||||||
|
// we'll extract any component CSS out into
|
||||||
|
// a separate file - better for performance
|
||||||
|
css({ output: 'bundle.css' }), |
||||||
|
|
||||||
|
// If you have external dependencies installed from
|
||||||
|
// npm, you'll most likely need these plugins. In
|
||||||
|
// some cases you'll need additional configuration -
|
||||||
|
// consult the documentation for details:
|
||||||
|
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||||
|
resolve({ |
||||||
|
browser: true, |
||||||
|
dedupe: ['svelte'] |
||||||
|
}), |
||||||
|
commonjs(), |
||||||
|
typescript({ |
||||||
|
sourceMap: !production, |
||||||
|
inlineSources: !production |
||||||
|
}), |
||||||
|
|
||||||
|
// In dev mode, call `npm run start` once
|
||||||
|
// the bundle has been generated
|
||||||
|
!production && serve(), |
||||||
|
|
||||||
|
// Watch the `public` directory and refresh the
|
||||||
|
// browser on changes when not in production
|
||||||
|
!production && livereload('public'), |
||||||
|
|
||||||
|
// If we're building for production (npm run build
|
||||||
|
// instead of npm run dev), minify
|
||||||
|
production && terser() |
||||||
|
], |
||||||
|
watch: { |
||||||
|
clearScreen: false |
||||||
|
}, |
||||||
|
external: ['leaflet'], |
||||||
|
}; |
@ -0,0 +1,219 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import L from 'leaflet'; |
||||||
|
import { onMount } from 'svelte'; |
||||||
|
|
||||||
|
let MAPBOX_ACCESS_TOKEN: string | undefined = void 0; |
||||||
|
|
||||||
|
let error: string | undefined = void 0; |
||||||
|
let followLocation = true; |
||||||
|
|
||||||
|
let darkMode = false; |
||||||
|
let map: L.Map | undefined = void 0; |
||||||
|
const mapLayers = { |
||||||
|
dark: L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { |
||||||
|
attribution: 'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', |
||||||
|
maxZoom: 18, |
||||||
|
id: 'mapbox/dark-v10', |
||||||
|
tileSize: 512, |
||||||
|
zoomOffset: -1, |
||||||
|
accessToken: MAPBOX_ACCESS_TOKEN, |
||||||
|
}), |
||||||
|
light: L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { |
||||||
|
attribution: 'Map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', |
||||||
|
maxZoom: 18, |
||||||
|
id: 'mapbox/light-v10', |
||||||
|
tileSize: 512, |
||||||
|
zoomOffset: -1, |
||||||
|
accessToken: MAPBOX_ACCESS_TOKEN, |
||||||
|
}), |
||||||
|
}; |
||||||
|
const locationCircleLayerGroup = L.layerGroup(); |
||||||
|
$: if (map) { |
||||||
|
locationCircleLayerGroup.addTo(map); |
||||||
|
} |
||||||
|
|
||||||
|
const geolocation = navigator.geolocation; |
||||||
|
if (!geolocation) { |
||||||
|
error = 'Geolocation not available'; |
||||||
|
} |
||||||
|
let geoWatchHandle: ReturnType<InstanceType<typeof Geolocation>["watchPosition"]> | undefined = void 0; |
||||||
|
|
||||||
|
if (window.matchMedia) { |
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { |
||||||
|
if (!darkMode && e.matches) { |
||||||
|
darkMode = true; |
||||||
|
} |
||||||
|
else if (darkMode && !e.matches) { |
||||||
|
darkMode = false; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
onMount(async () => { |
||||||
|
const matResp = await fetch('/MAPBOX_ACCESS_TOKEN.txt'); |
||||||
|
MAPBOX_ACCESS_TOKEN = (await matResp.text()).trim(); |
||||||
|
|
||||||
|
map = L.map('mapid', { |
||||||
|
zoomDelta: 1, |
||||||
|
zoomSnap: 0.25, |
||||||
|
}).setView([0, 0], 1); |
||||||
|
}); |
||||||
|
|
||||||
|
// Set light or dark mode MapBox layer |
||||||
|
$: { |
||||||
|
if (map) { |
||||||
|
if (!darkMode) { |
||||||
|
mapLayers.light.addTo(map); |
||||||
|
} |
||||||
|
else { |
||||||
|
mapLayers.dark.addTo(map); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let coords: GeolocationCoordinates | undefined = void 0; |
||||||
|
$: if (coords) { |
||||||
|
locationCircleLayerGroup.clearLayers(); |
||||||
|
L.circle({lat: coords.latitude, lng: coords.longitude, alt: coords.altitude}, { |
||||||
|
radius: coords.accuracy, |
||||||
|
opacity: 0.2, |
||||||
|
}).addTo(locationCircleLayerGroup); |
||||||
|
L.circle([coords.latitude, coords.longitude], { |
||||||
|
radius: 10, |
||||||
|
opacity: 0.75, |
||||||
|
}).addTo(locationCircleLayerGroup); |
||||||
|
} |
||||||
|
else { |
||||||
|
locationCircleLayerGroup.clearLayers(); |
||||||
|
} |
||||||
|
$: if (coords && followLocation) { |
||||||
|
map.fitBounds( |
||||||
|
L.latLng(coords.latitude, coords.longitude).toBounds(coords.accuracy * 2), |
||||||
|
{ |
||||||
|
animate: true, |
||||||
|
duration: 1, |
||||||
|
}, |
||||||
|
); |
||||||
|
} else if (map) { |
||||||
|
map.setZoom(1, { |
||||||
|
animate: true, |
||||||
|
duration: 5, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function onPosition(e: GeolocationPosition) { |
||||||
|
error = void 0; |
||||||
|
coords = e.coords; |
||||||
|
} |
||||||
|
|
||||||
|
function onLocationButtonClick() { |
||||||
|
if (geoWatchHandle === void 0) { |
||||||
|
geoWatchHandle = geolocation.watchPosition(onPosition, (e) => { |
||||||
|
setTimeout(() => {geoWatchHandle = void 0;}, 0); |
||||||
|
if (e.code === e.PERMISSION_DENIED) { |
||||||
|
error = 'Geolocation Permission Denied'; |
||||||
|
} |
||||||
|
else if (e.code === e.POSITION_UNAVAILABLE) { |
||||||
|
error = 'Geolocation Unavailable'; |
||||||
|
} |
||||||
|
else { |
||||||
|
error = `Unknown Error: ${e}`; |
||||||
|
} |
||||||
|
}, { |
||||||
|
enableHighAccuracy: true, |
||||||
|
}); |
||||||
|
} |
||||||
|
else { |
||||||
|
geolocation.clearWatch(geoWatchHandle); |
||||||
|
geoWatchHandle = void 0; |
||||||
|
setTimeout(() => { |
||||||
|
coords = void 0; |
||||||
|
}, 1000); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function toggleFollowLocation() { |
||||||
|
followLocation = !followLocation; |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<main> |
||||||
|
<h1>Location Preview</h1> |
||||||
|
{#if error} |
||||||
|
<p class="error">{error}</p> |
||||||
|
{/if} |
||||||
|
{#if coords} |
||||||
|
<div id="coordsDiv"> |
||||||
|
<table> |
||||||
|
<tr> |
||||||
|
<th>Field</th> |
||||||
|
<th>Value</th> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td>Latitude</td> |
||||||
|
<td>{coords.latitude}</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td>Longitude</td> |
||||||
|
<td>{coords.longitude}</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td>Accuracy</td> |
||||||
|
<td>{Math.round(coords.accuracy)} m</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
{/if} |
||||||
|
<div id="button-area"> |
||||||
|
{#key geoWatchHandle} |
||||||
|
<button on:click={onLocationButtonClick}>{geoWatchHandle ? "Stop Watching" : "Find Location"}</button> |
||||||
|
{/key} |
||||||
|
{#key followLocation} |
||||||
|
<button on:click={toggleFollowLocation}>{followLocation ? "Stop Zooming to Location" : "Zoom to Location"}</button> |
||||||
|
{/key} |
||||||
|
</div> |
||||||
|
|
||||||
|
<div id="mapid"></div> |
||||||
|
</main> |
||||||
|
|
||||||
|
<style> |
||||||
|
main { |
||||||
|
text-align: center; |
||||||
|
padding: 1em; |
||||||
|
margin: 0 auto; |
||||||
|
} |
||||||
|
|
||||||
|
h1 { |
||||||
|
color: #ff3e00; |
||||||
|
text-transform: uppercase; |
||||||
|
font-size: 2em; |
||||||
|
font-weight: 100; |
||||||
|
} |
||||||
|
|
||||||
|
p.error { |
||||||
|
font-size: 1.25em; |
||||||
|
color: #D32F2F; |
||||||
|
border-left: 5px #D32F2F solid; |
||||||
|
margin-left: auto; |
||||||
|
margin-right: auto; |
||||||
|
} |
||||||
|
|
||||||
|
#mapid { |
||||||
|
height: 300px; |
||||||
|
max-width: 700px; |
||||||
|
margin-left: auto; |
||||||
|
margin-right: auto; |
||||||
|
} |
||||||
|
|
||||||
|
#coordsDiv { |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
#coordsDiv table { |
||||||
|
border-collapse: collapse; |
||||||
|
} |
||||||
|
#coordsDiv td { |
||||||
|
border: 1px solid black; |
||||||
|
padding: 8px; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,9 @@ |
|||||||
|
import App from './App.svelte'; |
||||||
|
|
||||||
|
const app = new App({ |
||||||
|
target: document.body, |
||||||
|
props: { |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
export default app; |
@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"extends": "@tsconfig/svelte/tsconfig.json", |
||||||
|
|
||||||
|
"include": ["src/**/*"], |
||||||
|
"exclude": ["node_modules/*", "__sapper__/*", "public/*"], |
||||||
|
"compilerOptions": { |
||||||
|
"sourceMap": true |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue