making pins clickable + adding the corresponding panels/buttons
This commit is contained in:
94
src/lib/components/BottomSheet.svelte
Normal file
94
src/lib/components/BottomSheet.svelte
Normal file
@@ -0,0 +1,94 @@
|
||||
<script>
|
||||
import { mapStore } from '$lib/stores/mapStore.js';
|
||||
|
||||
let { message } = $props();
|
||||
</script>
|
||||
|
||||
<!-- if message exists, sheet is visible -->
|
||||
<div class="sheet" class:visible={!!message}>
|
||||
{#if message}
|
||||
<div class="handle"> </div>
|
||||
<div class="content">
|
||||
<p class="message-text">{message.text}</p>
|
||||
<p class="meta">
|
||||
left {message.lat.toFixed(4)}, {message.lng.toFixed(4)}
|
||||
</p>
|
||||
<div class="actions">
|
||||
<button class="echo-button">Echo</button>
|
||||
<button class="letgo-button" onclick={() => mapStore.set(
|
||||
{ selectedMessage: null, composing: false })}>
|
||||
let go
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.sheet {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
border-radius: 20px 20px 0 0;
|
||||
padding: 1rem 1.5rem 2rem;
|
||||
box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.sheet.visible {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.handle {
|
||||
width: 40px;
|
||||
height: 4px;
|
||||
background: #ddd;
|
||||
border-radius: 2px;
|
||||
margin: 0 auto 1rem;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 0.75rem;
|
||||
color: #999;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.echo-button {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
background: #111;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.letgo-button {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
background: transparent;
|
||||
color: #111;
|
||||
border: 1.5px solid #ddd;
|
||||
border-radius: 10px;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -2,6 +2,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { messagesStore } from '$lib/stores/messagesStore.js'; // pass the messages store here
|
||||
import { mapStore } from '$lib/stores/mapStore.js'; // use this to track interactions with da map
|
||||
|
||||
// export let latitude;
|
||||
// export let longitude;
|
||||
@@ -44,6 +45,11 @@
|
||||
map: mapDiv,
|
||||
title: message.text // firestore field for messages
|
||||
});
|
||||
|
||||
marker.addListener('click', () => {
|
||||
mapStore.set({ selectedMessage: message, composing: false}); //it updated the message object
|
||||
});
|
||||
|
||||
markers.push(marker); // add the new pin to the array
|
||||
});
|
||||
}
|
||||
|
||||
92
src/lib/components/SidePanel.svelte
Normal file
92
src/lib/components/SidePanel.svelte
Normal file
@@ -0,0 +1,92 @@
|
||||
<script>
|
||||
import { mapStore } from '$lib/stores/mapStore.js';
|
||||
|
||||
let { message } = $props();
|
||||
</script>
|
||||
|
||||
<div class = "panel">
|
||||
{#if message}
|
||||
<div class="content">
|
||||
<p class="message-text">{message.text}</p>
|
||||
<p class="meta">
|
||||
left {message.lat.toFixed(4)}, {message.lng.toFixed(4)}
|
||||
</p>
|
||||
<div class="actions">
|
||||
<button class="echo-button">Echo</button>
|
||||
<button class="letgo-button" onclick={() => mapStore.set(
|
||||
{selectedMessage: null, composing: false})}>
|
||||
Let go
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="empty">
|
||||
<p>Tap a pin to read a message</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 320px;
|
||||
height: 100vh;
|
||||
background: white;
|
||||
box-shadow: 2px 0 12px rgba(0,0,0,0.1);
|
||||
padding: 2rem 1.5rem;
|
||||
z-index: 100;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.message-text{
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
color: #111;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
color: #111;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.echo-button {
|
||||
flex: 1;
|
||||
padding: 0.75rem;
|
||||
background: #111;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.letgo-button {
|
||||
flex: 1;
|
||||
padding: 0..75rem;
|
||||
background: transparent;
|
||||
color: #111;
|
||||
border: 1.5px solid #ddd;
|
||||
border-radius: 10px;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: #999;
|
||||
font-size: 0.9rem;
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -4,12 +4,19 @@
|
||||
|
||||
import { getNearbyMessages } from '$lib/firebase/messages.js';
|
||||
import { messagesStore } from '$lib/stores/messagesStore.js';
|
||||
import { mapStore } from '$lib/stores/mapStore.js';
|
||||
|
||||
import BottomSheet from '$lib/components/BottomSheet.svelte';
|
||||
import SidePanel from '$lib/components/SidePanel.svelte';
|
||||
|
||||
let lat = $state();
|
||||
let lng = $state();
|
||||
let error = $state();
|
||||
|
||||
let windowWidth = $state(0);
|
||||
|
||||
let isMobile = $derived(windowWidth < 768);
|
||||
|
||||
onMount(() => {
|
||||
if (!navigator.geolocation) {
|
||||
error = "Your browser doesn't support geolocation :(";
|
||||
@@ -37,6 +44,8 @@
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth={windowWidth} /> <!--this sends the windowWidth to our mobile checker -->
|
||||
|
||||
{#if error}
|
||||
<p class="error">{error}</p>
|
||||
{:else if lat && lng}
|
||||
@@ -45,6 +54,18 @@
|
||||
<p class="loading">Looking for you...</p>
|
||||
{/if}
|
||||
|
||||
<!-- map must fill the whole screen-->
|
||||
{#if lat && lng}
|
||||
<MapView {lat} {lng} />
|
||||
{/if}
|
||||
|
||||
<!-- show the right panel based on mobile or desktop-->
|
||||
{#if windowWidth < 768}
|
||||
<BottomSheet message={$mapStore.selectedMessage} />
|
||||
{:else}
|
||||
<SidePanel message={$mapStore.selectedMessage} />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
|
||||
Reference in New Issue
Block a user