aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/LocationsMap.svelte
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-12-06 20:33:45 -0800
committerAdam Soltys <[email protected]>2023-12-06 20:33:45 -0800
commit6b3ff247e4125941a611369ed9bb74a2ffca57d8 (patch)
tree91da939350398188834e5e002bc78011a2a8e5bf /src/components/LocationsMap.svelte
parentbb59ac3e4e7fcbf7f8a48820eab0ee21558183fc (diff)
parentd69c34cf389639eac82d24d1f1b02f038ef42399 (diff)
downloadcoinos-ui-6b3ff247e4125941a611369ed9bb74a2ffca57d8.tar.gz
coinos-ui-6b3ff247e4125941a611369ed9bb74a2ffca57d8.zip
Merge branch 'master' into swapee
Diffstat (limited to 'src/components/LocationsMap.svelte')
-rw-r--r--src/components/LocationsMap.svelte368
1 files changed, 257 insertions, 111 deletions
diff --git a/src/components/LocationsMap.svelte b/src/components/LocationsMap.svelte
index 7876659a..b41b1a73 100644
--- a/src/components/LocationsMap.svelte
+++ b/src/components/LocationsMap.svelte
@@ -1,141 +1,287 @@
<script>
- import { onMount, onDestroy } from 'svelte';
- import { browser } from '$app/environment';
- import html2canvas from 'html2canvas';
+ import { tick, onMount, onDestroy } from "svelte";
+ import { browser } from "$app/environment";
+ import Icon from "$comp/Icon.svelte";
+ import Popup from "$comp/Popup.svelte";
+ import { shuffleArray } from "$lib/utils";
- import 'maplibre-gl/dist/maplibre-gl.css';
+ import "maplibre-gl/dist/maplibre-gl.css";
- export let locations;
- let map;
- let mapContainer;
- let markers = [];
+ export let locations;
+ let map;
+ let mapContainer, mapWrapper;
+ let markers = [];
- let checkAddress = (location) => {
- if (location['addr:housenumber'] && location['addr:street'] && location['addr:city']) {
- return `${
- location['addr:housenumber'] + ' ' + location['addr:street'] + ', ' + location['addr:city']
- }`;
- } else if (location['addr:street'] && location['addr:city']) {
- return `${location['addr:street'] + ', ' + location['addr:city']}`;
- } else if (location['addr:city']) {
- return `${location['addr:city']}`;
- } else {
- return '';
- }
- };
+ let currentIndex = -1;
+ let timeout;
- function updateLabelVisibility() {
- if (!map) return;
- let zoom = map.getZoom();
- let bounds = map.getBounds();
+ let full = () => {
+ if (!document.fullscreenElement) {
+ if (mapWrapper.requestFullscreen) {
+ mapWrapper.requestFullscreen();
+ } else if (mapWrapper.mozRequestFullScreen) {
+ /* Firefox */
+ mapWrapper.mozRequestFullScreen();
+ } else if (mapWrapper.webkitRequestFullscreen) {
+ /* Chrome, Safari & Opera */
+ mapWrapper.webkitRequestFullscreen();
+ } else if (mapWrapper.msRequestFullscreen) {
+ /* IE/Edge */
+ mapWrapper.msRequestFullscreen();
+ }
- for (let marker of markers) {
- let isInView = bounds.contains(marker.getLngLat());
- let popup = marker.getPopup();
- let { tags } = marker;
+ map.resize();
+ } else {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ /* Firefox */
+ document.mozCancelFullScreen();
+ } else if (document.webkitExitFullscreen) {
+ /* Chrome, Safari and Opera */
+ document.webkitExitFullscreen();
+ } else if (document.msExitFullscreen) {
+ /* IE/Edge */
+ document.msExitFullscreen();
+ }
+ }
+ };
- if (zoom > 13 && isInView) {
- popup.setHTML(`<span class='block font-bold'>${tags.name}</span>`).addTo(map);
- } else {
- popup.remove();
- popup.setHTML(html(tags));
- }
- }
- }
+ function select(m) {
+ stopFlying();
+ currentIndex = inview.indexOf(m);
+ flyToMarker(m);
+ }
- let html = (tags) => `${
- tags && tags.name ? `<span class='block font-bold'>${tags.name}</span>` : ''
- }
+ async function flyToMarker(marker) {
+ map.flyTo({
+ zoom: 12,
+ center: marker.getLngLat(),
+ essential: true,
+ easing: function (t) {
+ return 1 - Math.pow(1 - t, 3);
+ },
+ duration: 3000,
+ });
- <span class='block'>${tags && checkAddress(tags)}</span>
+ await new Promise((r) => setTimeout(r, 3000));
+ marker.getElement().click();
+ await new Promise((r) => setTimeout(r, 1500));
- ${tags && tags.phone ? `<a href='tel:${tags.phone}' class='block'>${tags.phone}</a>` : ''}
+ if (!timeout) return;
- ${
- tags && tags.website
- ? `<a href=${tags.website} target="_blank" rel="noreferrer" class='block text-blue-400'>${tags.website}</a>`
- : ''
- }`;
+ map.flyTo({
+ zoom: 10.5,
+ center: marker.getLngLat(),
+ essential: true,
+ easing: function (t) {
+ return 1 - Math.pow(1 - t, 3);
+ },
+ duration: 1500,
+ });
+ }
- onMount(() => {
- if (browser) {
- import('maplibre-gl').then(({ default: maplibre }) => {
- map = new maplibre.Map({
- container: mapContainer,
- style: 'https://tiles.stadiamaps.com/styles/stamen_toner.json',
- center: [-123.05, 49.26],
- zoom: 10.5
- });
+ let toggle = () => {
+ if (timeout) stopFlying();
+ else startFlying();
+ };
- map.on('load', () => {
- locations.forEach((location) => {
- if (location['deleted_at']) return;
+ let currentPopup;
+ function startFlying() {
+ currentIndex++;
- let { lat, lon, tags } = location['osm_json'];
- if (!(lat && lon)) return;
+ for (let p in popups) popups[p].remove();
+ if (currentIndex >= inview.length) currentIndex = 0;
+ flyToMarker(inview[currentIndex]);
- let element = document.createElement('div');
- element.className = 'marker';
+ timeout = setTimeout(startFlying, 6000);
+ }
- let marker = new maplibre.Marker({ color: '#F7931A' })
- .setLngLat([lon, lat])
- .setPopup(new maplibre.Popup().setHTML(html(tags)))
- .addTo(map);
+ function stopFlying() {
+ if (!timeout) return;
+ timeout = clearTimeout(timeout);
- marker.getElement().addEventListener('click', () => {
- let h = html(marker.tags);
- let popup = marker.getPopup();
- popup.setHTML(h);
- });
+ if (currentPopup) {
+ currentPopup.remove();
+ }
- marker.tags = tags;
+ map.jumpTo({ center: map.getCenter(), zoom: map.getZoom() });
+ map.off("moveend"); // Remove moveend listener if set
+ }
- markers.push(marker);
- });
+ let inview = [];
- // updateLabelVisibility();
- // map.on('moveend', updateLabelVisibility);
- // map.on('zoomend', updateLabelVisibility);
- });
- });
- }
- });
+ function updateLabelVisibility() {
+ inview = [];
+ if (!map) return;
+ let zoom = map.getZoom();
+ let bounds = map.getBounds();
- onDestroy(() => {
- if (map) {
- // map.off('moveend', updateLabelVisibility);
- // map.off('zoomend', updateLabelVisibility);
- map.remove();
- }
- });
+ for (let marker of markers) {
+ let isInView = bounds.contains(marker.getLngLat());
+ let { tags } = marker;
+
+ if (isInView) {
+ inview.push(marker);
+ }
+ }
+
+ inview = inview.sort((a, b) => a.tags.name.localeCompare(b.tags.name));
+ shuffleArray(inview);
+ }
+
+ let popups = {};
+ let counter = 0;
+
+ onMount(() => {
+ if (browser) {
+ import("maplibre-gl").then(({ default: maplibre }) => {
+ map = new maplibre.Map({
+ container: mapContainer,
+ style: "https://tiles.stadiamaps.com/styles/stamen_toner.json",
+ center: [-123.05, 49.26],
+ zoom: 10.5,
+ });
+
+ map.on("load", () => {
+ locations.forEach((location) => {
+ if (location["deleted_at"]) return;
+
+ let { lat, lon, tags } = location["osm_json"];
+ if (!(lat && lon)) return;
+
+ let element = document.createElement("div");
+ element.className = "marker";
+
+ let popupContainer = document.createElement("div");
+
+ let popup = new Popup({
+ target: popupContainer,
+ props: { tags },
+ });
+
+ let marker = new maplibre.Marker({ color: "#F7931A" })
+ .setLngLat([lon, lat])
+ .setPopup(new maplibre.Popup().setDOMContent(popupContainer))
+ .addTo(map);
+
+ marker.id = counter++;
+
+ marker.getElement().addEventListener("click", () => {
+ let p = marker.getPopup();
+ popups[marker.id] = p;
+ p.setDOMContent(popupContainer);
+ p.on("close", () => {
+ delete popups[marker.id];
+ });
+ });
+
+ marker.tags = tags;
+
+ markers.push(marker);
+ });
+
+ updateLabelVisibility();
+
+ let observer = new IntersectionObserver(
+ (entries, observer) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) startFlying();
+ else stopFlying();
+ });
+ },
+ { threshold: 0.5 }
+ );
+
+ observer.observe(mapContainer);
+ });
+
+ map.on("mousedown", stopFlying);
+ map.on("touchstart", stopFlying);
+ map.on("wheel", stopFlying);
+ map.on("dragstart", stopFlying);
+
+ let resize = () => map && map.resize();
+
+ document.addEventListener("fullscreenchange", resize, false);
+ document.addEventListener("webkitfullscreenchange", resize, false);
+ document.addEventListener("mozfullscreenchange", resize, false);
+ document.addEventListener("MSFullscreenChange", resize, false);
+ });
+ }
+ });
+
+ onDestroy(() => map && map.remove());
</script>
-<div class="container mx-auto max-w-4xl">
- <div class="flex w-full">
- <div class="mx-auto h-[550px] w-full z-0" bind:this={mapContainer} />
- </div>
+<div class="container mx-auto p-2 space-y-5">
+ <div class="flex w-full h-[60vh]" id="map" bind:this={mapWrapper}>
+ <div
+ id="map-container"
+ class="mx-auto h-full w-full z-0"
+ bind:this={mapContainer}
+ />
+ <div class="relative">
+ <div class="flex absolute flex top-2 right-2 gap-2">
+ <button
+ class="rounded-full border-2 border-black bg-white w-10 h-10"
+ on:click={toggle}
+ >
+ {#if timeout}
+ <Icon icon="pause" style="w-4 m-auto" />
+ {:else}
+ <Icon icon="play" style="w-4 m-auto" />
+ {/if}
+ </button>
+ <button
+ class="rounded-full border-2 border-black bg-white w-10 h-10"
+ on:click={full}
+ >
+ <Icon icon="full" style="w-4 m-auto" />
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <!-- <div class="hidden lg:grid grid-cols-3 justify-items-center"> -->
+ <!-- {#each inview as m} -->
+ <!-- <button -->
+ <!-- on:click={() => select(m)} -->
+ <!-- class="text-left" -->
+ <!-- class:font-bold={m.id === inview[currentIndex]?.id} -->
+ <!-- id={`marker-${m.id}`}>{m.tags.name}</button -->
+ <!-- > -->
+ <!-- {/each} -->
+ <!-- </div> -->
</div>
<style>
- :global(.maplibregl-popup-close-button) {
- font-size: 18px;
- padding: 8px;
- }
+ #map:fullscreen,
+ #map-container:fullscreen {
+ width: 100% !important;
+ height: 100% !important;
+ }
+
+ :global(.maplibregl-popup-close-button) {
+ font-size: 24px;
+ padding: 8px;
+ }
- :global(.maplibregl-popup :focus) {
- outline: none;
- }
+ :global(.maplibregl-popup :focus) {
+ outline: none;
+ }
- :global(.maplibregl-ctrl-attrib) {
- display: none;
- }
+ :global(.maplibregl-ctrl-attrib) {
+ display: none;
+ }
- :global(.maplibregl-popup-content) {
- padding: 0.5em 1em;
- border-radius: 4px;
- font-size: 14px;
- color: black;
- background-color: white;
- overflow: hidden;
- }
+ :global(.maplibregl-popup-content) {
+ padding: 1em 2em;
+ border-radius: 4px;
+ font-size: 14px;
+ color: black;
+ background-color: white;
+ overflow: hidden;
+ width: 280px;
+ }
</style>