fix: styles and add zooming into location
This commit is contained in:
161
src/app.css
161
src/app.css
@@ -1,121 +1,54 @@
|
|||||||
@import 'tailwindcss';
|
@font-face {
|
||||||
|
font-family: 'Washington';
|
||||||
@import 'tw-animate-css';
|
src: url('/fonts/Washington.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
@custom-variant dark (&:is(.dark *));
|
font-style: normal;
|
||||||
|
|
||||||
:root {
|
|
||||||
--radius: 0.625rem;
|
|
||||||
--background: oklch(1 0 0);
|
|
||||||
--foreground: oklch(0.141 0.005 285.823);
|
|
||||||
--card: oklch(1 0 0);
|
|
||||||
--card-foreground: oklch(0.141 0.005 285.823);
|
|
||||||
--popover: oklch(1 0 0);
|
|
||||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
||||||
--primary: oklch(0.21 0.006 285.885);
|
|
||||||
--primary-foreground: oklch(0.985 0 0);
|
|
||||||
--secondary: oklch(0.967 0.001 286.375);
|
|
||||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
||||||
--muted: oklch(0.967 0.001 286.375);
|
|
||||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
|
||||||
--accent: oklch(0.967 0.001 286.375);
|
|
||||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
||||||
--destructive: oklch(0.577 0.245 27.325);
|
|
||||||
--border: oklch(0.92 0.004 286.32);
|
|
||||||
--input: oklch(0.92 0.004 286.32);
|
|
||||||
--ring: oklch(0.705 0.015 286.067);
|
|
||||||
--chart-1: oklch(0.646 0.222 41.116);
|
|
||||||
--chart-2: oklch(0.6 0.118 184.704);
|
|
||||||
--chart-3: oklch(0.398 0.07 227.392);
|
|
||||||
--chart-4: oklch(0.828 0.189 84.429);
|
|
||||||
--chart-5: oklch(0.769 0.188 70.08);
|
|
||||||
--sidebar: oklch(0.985 0 0);
|
|
||||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
|
||||||
--sidebar-primary: oklch(0.21 0.006 285.885);
|
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
||||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
|
||||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
|
||||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
|
||||||
--sidebar-ring: oklch(0.705 0.015 286.067);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
* {
|
||||||
--background: oklch(0.141 0.005 285.823);
|
box-sizing: border-box;
|
||||||
--foreground: oklch(0.985 0 0);
|
margin: 0;
|
||||||
--card: oklch(0.21 0.006 285.885);
|
padding: 0;
|
||||||
--card-foreground: oklch(0.985 0 0);
|
|
||||||
--popover: oklch(0.21 0.006 285.885);
|
|
||||||
--popover-foreground: oklch(0.985 0 0);
|
|
||||||
--primary: oklch(0.92 0.004 286.32);
|
|
||||||
--primary-foreground: oklch(0.21 0.006 285.885);
|
|
||||||
--secondary: oklch(0.274 0.006 286.033);
|
|
||||||
--secondary-foreground: oklch(0.985 0 0);
|
|
||||||
--muted: oklch(0.274 0.006 286.033);
|
|
||||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
|
||||||
--accent: oklch(0.274 0.006 286.033);
|
|
||||||
--accent-foreground: oklch(0.985 0 0);
|
|
||||||
--destructive: oklch(0.704 0.191 22.216);
|
|
||||||
--border: oklch(1 0 0 / 10%);
|
|
||||||
--input: oklch(1 0 0 / 15%);
|
|
||||||
--ring: oklch(0.552 0.016 285.938);
|
|
||||||
--chart-1: oklch(0.488 0.243 264.376);
|
|
||||||
--chart-2: oklch(0.696 0.17 162.48);
|
|
||||||
--chart-3: oklch(0.769 0.188 70.08);
|
|
||||||
--chart-4: oklch(0.627 0.265 303.9);
|
|
||||||
--chart-5: oklch(0.645 0.246 16.439);
|
|
||||||
--sidebar: oklch(0.21 0.006 285.885);
|
|
||||||
--sidebar-foreground: oklch(0.985 0 0);
|
|
||||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
||||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
|
||||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
||||||
--sidebar-border: oklch(1 0 0 / 10%);
|
|
||||||
--sidebar-ring: oklch(0.552 0.016 285.938);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
body {
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
font-family:
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
system-ui,
|
||||||
--radius-lg: var(--radius);
|
-apple-system,
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
BlinkMacSystemFont,
|
||||||
--color-background: var(--background);
|
'Segoe UI',
|
||||||
--color-foreground: var(--foreground);
|
Roboto,
|
||||||
--color-card: var(--card);
|
sans-serif;
|
||||||
--color-card-foreground: var(--card-foreground);
|
line-height: 1.5;
|
||||||
--color-popover: var(--popover);
|
background-color: #f8f8f8;
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
color: #333;
|
||||||
--color-primary: var(--primary);
|
margin: 0 auto;
|
||||||
--color-primary-foreground: var(--primary-foreground);
|
|
||||||
--color-secondary: var(--secondary);
|
|
||||||
--color-secondary-foreground: var(--secondary-foreground);
|
|
||||||
--color-muted: var(--muted);
|
|
||||||
--color-muted-foreground: var(--muted-foreground);
|
|
||||||
--color-accent: var(--accent);
|
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
|
||||||
--color-destructive: var(--destructive);
|
|
||||||
--color-border: var(--border);
|
|
||||||
--color-input: var(--input);
|
|
||||||
--color-ring: var(--ring);
|
|
||||||
--color-chart-1: var(--chart-1);
|
|
||||||
--color-chart-2: var(--chart-2);
|
|
||||||
--color-chart-3: var(--chart-3);
|
|
||||||
--color-chart-4: var(--chart-4);
|
|
||||||
--color-chart-5: var(--chart-5);
|
|
||||||
--color-sidebar: var(--sidebar);
|
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
h1 {
|
||||||
* {
|
font-family: 'Washington', serif;
|
||||||
@apply border-border outline-ring/50;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
body {
|
|
||||||
@apply bg-background text-foreground;
|
h2,
|
||||||
}
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-family: 'Times New Roman', Times, serif;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { MapLibre } from 'svelte-maplibre';
|
import { MapLibre, Marker } from 'svelte-maplibre';
|
||||||
import type { StyleSpecification } from 'svelte-maplibre';
|
import type { StyleSpecification } from 'svelte-maplibre';
|
||||||
import { coordinates, getMapCenter, getMapZoom } from '$lib/stores/location';
|
import {
|
||||||
|
coordinates,
|
||||||
|
getMapCenter,
|
||||||
|
getMapZoom,
|
||||||
|
shouldZoomToLocation,
|
||||||
|
locationActions
|
||||||
|
} from '$lib/stores/location';
|
||||||
import LocationButton from './LocationButton.svelte';
|
import LocationButton from './LocationButton.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -41,16 +47,46 @@
|
|||||||
|
|
||||||
// Reactive center and zoom based on location or props
|
// Reactive center and zoom based on location or props
|
||||||
const mapCenter = $derived(
|
const mapCenter = $derived(
|
||||||
$coordinates && autoCenter
|
$coordinates && (autoCenter || $shouldZoomToLocation)
|
||||||
? ([$coordinates.longitude, $coordinates.latitude] as [number, number])
|
? ([$coordinates.longitude, $coordinates.latitude] as [number, number])
|
||||||
: center || $getMapCenter
|
: center || $getMapCenter
|
||||||
);
|
);
|
||||||
|
|
||||||
const mapZoom = $derived($coordinates && autoCenter ? zoom || $getMapZoom : zoom || 13);
|
const mapZoom = $derived(() => {
|
||||||
|
if ($shouldZoomToLocation && $coordinates) {
|
||||||
|
// Force zoom to calculated level when location button is clicked
|
||||||
|
return $getMapZoom;
|
||||||
|
}
|
||||||
|
if ($coordinates && autoCenter) {
|
||||||
|
return $getMapZoom;
|
||||||
|
}
|
||||||
|
return zoom || 13;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Effect to clear zoom trigger after it's been used
|
||||||
|
$effect(() => {
|
||||||
|
if ($shouldZoomToLocation) {
|
||||||
|
// Use a timeout to ensure the map has updated before clearing the trigger
|
||||||
|
setTimeout(() => {
|
||||||
|
locationActions.clearZoomTrigger();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="map-container {className}">
|
<div class="map-container {className}">
|
||||||
<MapLibre {style} center={mapCenter} zoom={mapZoom} />
|
<MapLibre {style} center={mapCenter} zoom={mapZoom()}>
|
||||||
|
{#if $coordinates}
|
||||||
|
<Marker lngLat={[$coordinates.longitude, $coordinates.latitude]}>
|
||||||
|
<div class="location-marker">
|
||||||
|
<div class="marker-pulse"></div>
|
||||||
|
<div class="marker-outer">
|
||||||
|
<div class="marker-inner"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Marker>
|
||||||
|
{/if}
|
||||||
|
</MapLibre>
|
||||||
|
|
||||||
{#if showLocationButton}
|
{#if showLocationButton}
|
||||||
<div class="location-controls">
|
<div class="location-controls">
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ interface LocationState {
|
|||||||
error: LocationError | null;
|
error: LocationError | null;
|
||||||
isWatching: boolean;
|
isWatching: boolean;
|
||||||
lastUpdated: Date | null;
|
lastUpdated: Date | null;
|
||||||
|
shouldZoomToLocation: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: LocationState = {
|
const initialState: LocationState = {
|
||||||
@@ -19,7 +20,8 @@ const initialState: LocationState = {
|
|||||||
status: 'idle',
|
status: 'idle',
|
||||||
error: null,
|
error: null,
|
||||||
isWatching: false,
|
isWatching: false,
|
||||||
lastUpdated: null
|
lastUpdated: null,
|
||||||
|
shouldZoomToLocation: false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Main location store
|
// Main location store
|
||||||
@@ -37,6 +39,10 @@ export const hasLocationAccess = derived(
|
|||||||
locationStore,
|
locationStore,
|
||||||
($location) => $location.coordinates !== null
|
($location) => $location.coordinates !== null
|
||||||
);
|
);
|
||||||
|
export const shouldZoomToLocation = derived(
|
||||||
|
locationStore,
|
||||||
|
($location) => $location.shouldZoomToLocation
|
||||||
|
);
|
||||||
|
|
||||||
// Location actions
|
// Location actions
|
||||||
export const locationActions = {
|
export const locationActions = {
|
||||||
@@ -58,7 +64,8 @@ export const locationActions = {
|
|||||||
coordinates,
|
coordinates,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
error: null,
|
error: null,
|
||||||
lastUpdated: new Date()
|
lastUpdated: new Date(),
|
||||||
|
shouldZoomToLocation: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return coordinates;
|
return coordinates;
|
||||||
@@ -169,6 +176,16 @@ export const locationActions = {
|
|||||||
|
|
||||||
const ageInMinutes = (Date.now() - currentState.lastUpdated.getTime()) / (1000 * 60);
|
const ageInMinutes = (Date.now() - currentState.lastUpdated.getTime()) / (1000 * 60);
|
||||||
return ageInMinutes > maxAgeMinutes;
|
return ageInMinutes > maxAgeMinutes;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the zoom trigger flag
|
||||||
|
*/
|
||||||
|
clearZoomTrigger(): void {
|
||||||
|
locationStore.update((state) => ({
|
||||||
|
...state,
|
||||||
|
shouldZoomToLocation: false
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -182,14 +199,17 @@ export const getMapCenter = derived(coordinates, ($coordinates) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Utility function to get appropriate zoom level based on accuracy
|
// Utility function to get appropriate zoom level based on accuracy
|
||||||
export const getMapZoom = derived(coordinates, ($coordinates) => {
|
export const getMapZoom = derived([coordinates, shouldZoomToLocation], ([$coordinates, $shouldZoom]) => {
|
||||||
if ($coordinates?.accuracy) {
|
if ($coordinates?.accuracy) {
|
||||||
|
// More aggressive zoom levels when location button is clicked
|
||||||
|
const baseZoom = $shouldZoom ? 2 : 0; // Add 2 zoom levels when triggered by button
|
||||||
|
|
||||||
// Adjust zoom based on accuracy (lower accuracy = lower zoom)
|
// Adjust zoom based on accuracy (lower accuracy = lower zoom)
|
||||||
if ($coordinates.accuracy < 10) return 18; // Very accurate
|
if ($coordinates.accuracy < 10) return Math.min(20, 18 + baseZoom); // Very accurate
|
||||||
if ($coordinates.accuracy < 50) return 16; // Good accuracy
|
if ($coordinates.accuracy < 50) return Math.min(19, 16 + baseZoom); // Good accuracy
|
||||||
if ($coordinates.accuracy < 100) return 14; // Moderate accuracy
|
if ($coordinates.accuracy < 100) return Math.min(18, 14 + baseZoom); // Moderate accuracy
|
||||||
if ($coordinates.accuracy < 500) return 12; // Low accuracy
|
if ($coordinates.accuracy < 500) return Math.min(16, 12 + baseZoom); // Low accuracy
|
||||||
return 10; // Very low accuracy
|
return Math.min(15, 10 + baseZoom); // Very low accuracy
|
||||||
}
|
}
|
||||||
return 13; // Default zoom level
|
return $shouldZoom ? 16 : 13; // More aggressive default when triggered by button
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
<style>
|
<style>
|
||||||
.home-container {
|
.home-container {
|
||||||
background-color: #f8f8f8;
|
background-color: #f8f8f8;
|
||||||
min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
@@ -29,7 +28,7 @@
|
|||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-section :global(.map-container) {
|
.map-section :global(.map-container) {
|
||||||
|
|||||||
Reference in New Issue
Block a user