mirror of
https://github.com/soconnor0919/lewisburg-coffee.git
synced 2026-02-04 23:56:32 -05:00
feat: Refactor drawer for improved animations and update shadow styles across components
This commit is contained in:
@@ -31,11 +31,14 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en" className={`${ptSerif.variable}`} suppressHydrationWarning>
|
||||
<head>
|
||||
{/* only load analytics on production */}
|
||||
{process.env.NODE_ENV === "production" && (
|
||||
<script
|
||||
defer
|
||||
src="https://umami-iccw808w4wk088o0w4o8c8kg.coolify.soconnor.dev/script.js"
|
||||
data-website-id="415c64e5-98c5-4975-bf49-2c900fe6b1b5"
|
||||
/>
|
||||
)}
|
||||
</head>
|
||||
<body>
|
||||
<ThemeProvider
|
||||
|
||||
@@ -22,8 +22,6 @@ export default function HomePage() {
|
||||
|
||||
return (
|
||||
<main className="relative h-dvh w-screen overflow-hidden bg-black text-white font-serif">
|
||||
{/* Unified shadow container for navbar + drawer */}
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 pointer-events-none z-[1000]" style={{ boxShadow: 'inset 0 0 40px 10px rgb(0 0 0 / 0.3)' }}>
|
||||
{/* Navbar - always visible */}
|
||||
<Navbar isDiscoveryOpen={isDiscoveryOpen} onToggleDiscovery={() => setIsDiscoveryOpen(!isDiscoveryOpen)} />
|
||||
|
||||
@@ -38,7 +36,6 @@ export default function HomePage() {
|
||||
onToggleOpen={() => setIsDiscoveryOpen(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Map Background */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
@@ -48,6 +45,7 @@ export default function HomePage() {
|
||||
setSelectedShop(shop);
|
||||
}}
|
||||
selectedShop={selectedShop}
|
||||
isDiscoveryOpen={isDiscoveryOpen}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { X, MapPin, Globe, Phone, Coffee, ExternalLink, Search, ChevronRight } from "lucide-react";
|
||||
import { X, MapPin, Globe, Phone, Coffee, ExternalLink, Search, ChevronRight, ChevronLeft } from "lucide-react";
|
||||
import { Card } from "~/components/ui/card";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { ScrollArea } from "~/components/ui/scroll-area";
|
||||
@@ -31,10 +31,13 @@ interface DrawerProps {
|
||||
export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggleOpen }: DrawerProps) {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [imageLoading, setImageLoading] = useState(true);
|
||||
const [activeShop, setActiveShop] = useState<CoffeeShop | null>(shop);
|
||||
|
||||
// Reset loading state when shop changes
|
||||
// Update activeShop when shop changes, but only if it's not null
|
||||
// This allows us to keep displaying the shop details while animating out
|
||||
useEffect(() => {
|
||||
if (shop) {
|
||||
setActiveShop(shop);
|
||||
setImageLoading(true);
|
||||
}
|
||||
}, [shop]);
|
||||
@@ -46,21 +49,36 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`absolute top-20 left-0 h-[calc(100dvh-6rem)] w-full sm:w-[400px] z-30 p-4 pointer-events-none transition-transform duration-300 ease-in-out ${isOpen || shop ? 'translate-x-0' : '-translate-x-full'}`}
|
||||
className={`absolute top-20 left-0 h-[calc(100dvh-6rem)] w-full sm:w-[400px] z-30 px-4 pt-3 pointer-events-none transition-transform duration-300 ease-in-out ${isOpen || shop ? 'translate-x-0' : '-translate-x-full'}`}
|
||||
>
|
||||
<Card className="h-full w-full bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 overflow-hidden flex flex-col gap-0 pointer-events-auto rounded-r-xl p-0 border-0 transition-all duration-300">
|
||||
{shop ? (
|
||||
// Details View
|
||||
// Details View
|
||||
<Card className="h-full w-full bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 overflow-hidden relative shadow-xl rounded-r-xl border-0">
|
||||
{/* Details View */}
|
||||
<div
|
||||
className={`absolute inset-0 z-20 transition-transform duration-300 ease-in-out bg-background/80 backdrop-blur-3xl ${shop ? 'translate-x-0' : 'translate-x-full'}`}
|
||||
>
|
||||
{activeShop && (
|
||||
<div className="h-full flex flex-col relative">
|
||||
<div className="absolute top-4 right-4 z-50 flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={onClose}
|
||||
className="absolute top-4 right-4 z-50 bg-background/20 hover:bg-background/40 text-foreground rounded-full h-8 w-8 backdrop-blur-md border border-border/50"
|
||||
className="bg-background/20 hover:bg-background/40 text-foreground rounded-full h-8 w-8 backdrop-blur-md border border-border/50"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
onClose();
|
||||
if (onToggleOpen) onToggleOpen();
|
||||
}}
|
||||
className="bg-background/20 hover:bg-background/40 text-foreground rounded-full h-8 w-8 backdrop-blur-md border border-border/50"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="flex-1 min-h-0">
|
||||
{/* Header Image - Now part of scroll area */}
|
||||
@@ -79,8 +97,8 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
)}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<img
|
||||
src={shop.image}
|
||||
alt={shop.name}
|
||||
src={activeShop.image}
|
||||
alt={activeShop.name}
|
||||
className={`w-full h-full object-cover transition-opacity duration-500 ${imageLoading ? 'opacity-0' : 'opacity-100'}`}
|
||||
onLoad={() => setImageLoading(false)}
|
||||
style={{
|
||||
@@ -93,23 +111,23 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
|
||||
{/* Content - Overlaps image slightly or just follows */}
|
||||
<div className="p-8 -mt-12 relative z-10">
|
||||
<h2 className="text-3xl font-bold font-serif mb-4 text-primary leading-tight drop-shadow-md">{shop.name}</h2>
|
||||
<h2 className="text-3xl font-bold font-serif mb-4 text-primary leading-tight drop-shadow-md">{activeShop.name}</h2>
|
||||
|
||||
<div className="space-y-4 mb-8">
|
||||
<div className="flex items-start gap-3 text-muted-foreground font-serif text-sm">
|
||||
<MapPin className="w-5 h-5 text-primary flex-shrink-0 mt-0.5" />
|
||||
<span>{shop.address}</span>
|
||||
<span>{activeShop.address}</span>
|
||||
</div>
|
||||
{shop.phone && (
|
||||
{activeShop.phone && (
|
||||
<div className="flex items-center gap-3 text-muted-foreground font-serif text-sm">
|
||||
<Phone className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<a href={`tel:${shop.phone}`} className="hover:text-foreground transition-colors">{shop.phone}</a>
|
||||
<a href={`tel:${activeShop.phone}`} className="hover:text-foreground transition-colors">{activeShop.phone}</a>
|
||||
</div>
|
||||
)}
|
||||
{shop.website && (
|
||||
{activeShop.website && (
|
||||
<div className="flex items-center gap-3 text-muted-foreground font-serif text-sm">
|
||||
<Globe className="w-5 h-5 text-primary flex-shrink-0" />
|
||||
<a href={shop.website} target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors flex items-center gap-1">
|
||||
<a href={activeShop.website} target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors flex items-center gap-1">
|
||||
Visit Website <ExternalLink className="w-3 h-3" />
|
||||
</a>
|
||||
</div>
|
||||
@@ -122,7 +140,7 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-2 text-foreground font-serif">About</h3>
|
||||
<p className="text-muted-foreground leading-relaxed font-serif text-lg">
|
||||
{shop.description}
|
||||
{activeShop.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -132,7 +150,7 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
className="w-auto px-6 bg-primary/20 hover:bg-primary/40 text-foreground font-semibold rounded-lg shadow-lg transition-all hover:scale-[1.02] border border-primary/50 backdrop-blur-md"
|
||||
>
|
||||
<a
|
||||
href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(`${shop.name}, ${shop.address}`)}`}
|
||||
href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(`${activeShop.name}, ${activeShop.address}`)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
@@ -143,10 +161,14 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
) : (
|
||||
// List View
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="p-4 border-b border-border/50 bg-background/40 backdrop-blur-md relative">
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* List View */}
|
||||
<div
|
||||
className={`absolute inset-0 z-10 transition-all duration-300 ease-in-out flex flex-col h-full bg-background/0 ${shop ? '-translate-x-1/4 opacity-0 pointer-events-none' : 'translate-x-0 opacity-100 pointer-events-auto'}`}
|
||||
>
|
||||
<div className="p-4 border-b border-border/50 relative">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
@@ -160,7 +182,7 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
<Search className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search shops..."
|
||||
className="pl-9 bg-background/50 border-border/50 focus:bg-background transition-colors"
|
||||
className="pl-9 bg-background/20 border-border/50 focus:bg-background/40 transition-colors"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
@@ -193,7 +215,6 @@ export default function Drawer({ shop, shops, onSelect, onClose, isOpen, onToggl
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -72,7 +72,7 @@ export function LocateControl() {
|
||||
size="icon"
|
||||
onClick={handleLocate}
|
||||
disabled={loading}
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-2xl text-foreground"
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-xl text-foreground"
|
||||
>
|
||||
<Locate className={`h-5 w-5 ${loading ? 'animate-pulse' : ''}`} />
|
||||
<span className="sr-only">Locate me</span>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { MapContainer, TileLayer, Marker, Tooltip, useMap } from 'react-leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import L from 'leaflet';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useTheme } from "next-themes";
|
||||
import { MapStyleControl } from "./MapStyleControl";
|
||||
import { LocateControl } from './LocateControl';
|
||||
@@ -25,24 +25,91 @@ interface MapProps {
|
||||
shops: CoffeeShop[];
|
||||
onShopSelect: (shop: CoffeeShop) => void;
|
||||
selectedShop: CoffeeShop | null;
|
||||
isDiscoveryOpen: boolean;
|
||||
}
|
||||
|
||||
const MapController = ({ selectedShop }: { selectedShop: CoffeeShop | null }) => {
|
||||
const MapController = ({ selectedShop, isDiscoveryOpen }: { selectedShop: CoffeeShop | null, isDiscoveryOpen: boolean }) => {
|
||||
const map = useMap();
|
||||
const prevOpenRef = useRef(isDiscoveryOpen);
|
||||
|
||||
useEffect(() => {
|
||||
// Handle general panning when drawer toggles (and no shop is selected)
|
||||
const isDesktop = window.innerWidth >= 640;
|
||||
|
||||
if (prevOpenRef.current !== isDiscoveryOpen) {
|
||||
if (!selectedShop && isDesktop) {
|
||||
// If opening, we want to shift the view to the right (so panning map left? wait)
|
||||
// If drawer opens on LEFT, the center of the remaining view is to the RIGHT.
|
||||
// We want the content to move into that new center.
|
||||
// So we pan the map in the POSITIVE X direction?
|
||||
// Test: map.panBy([200, 0]) moves the map image 200px to the RIGHT relative to the viewport.
|
||||
// This means a point at x=0 moves to x=200.
|
||||
// This puts it into the open area. Correct.
|
||||
|
||||
// Inverting based on user feedback that it shifted wrong way.
|
||||
// Opening -> Shift Map Image Left (View Right?) -> offset [-200, 0]
|
||||
// Closing -> Shift Map Image Right (View Left?) -> offset [200, 0]
|
||||
const offset = isDiscoveryOpen ? [-200, 0] : [200, 0];
|
||||
map.panBy(offset as [number, number], {
|
||||
animate: true,
|
||||
duration: 0.5
|
||||
});
|
||||
}
|
||||
prevOpenRef.current = isDiscoveryOpen;
|
||||
}
|
||||
}, [isDiscoveryOpen, selectedShop, map]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedShop) {
|
||||
map.flyTo([selectedShop.lat, selectedShop.lng], 16, {
|
||||
const targetLat = selectedShop.lat;
|
||||
const targetLng = selectedShop.lng;
|
||||
|
||||
// Calculate offset if discovery panel is open and we're on desktop
|
||||
let flyToOption = {
|
||||
duration: 1.5,
|
||||
easeLinearity: 0.25,
|
||||
});
|
||||
};
|
||||
|
||||
const isDesktop = window.innerWidth >= 640;
|
||||
|
||||
if (isDiscoveryOpen && isDesktop) {
|
||||
// We need to offset the center so the point appears to the right of the drawer
|
||||
// The drawer is 400px wide. We want to shift the center left by 200px (half drawer width)
|
||||
// so that the target point appears 200px to the right of current center
|
||||
|
||||
// Get current zoom
|
||||
const zoom = 16;
|
||||
|
||||
// Project the lat/lng to point
|
||||
const point = map.project([targetLat, targetLng], zoom);
|
||||
|
||||
// Subtract offset (shift 'center' to the left, which moves 'view' to the right? Wait.)
|
||||
// If we want the point to be at x + 200 (screen coords relative to center),
|
||||
// we need the map center to be at x - 200 relative to point.
|
||||
// Actually simpler: We want the point (targetLat, targetLng) to be at screen coordinates (width/2 + 200, height/2).
|
||||
// Or simply: shift the target point by -200px in x before passing to flyTo? No, flyTo takes strict LatLng.
|
||||
|
||||
// Correct approach:
|
||||
// Find the LatLng that, when centered, puts our target LatLng at the desired pixels.
|
||||
// Center + Offset = Target -> Center = Target - Offset
|
||||
|
||||
const targetPoint = map.project([targetLat, targetLng], zoom);
|
||||
// We want the target to appear 200px (half drawer width) to the right of the map center.
|
||||
// So the new center should be 200px LEFT of the target.
|
||||
const newCenterPoint = targetPoint.subtract([200, 0]);
|
||||
const newCenter = map.unproject(newCenterPoint, zoom);
|
||||
|
||||
map.flyTo(newCenter, zoom, flyToOption);
|
||||
} else {
|
||||
map.flyTo([targetLat, targetLng], 16, flyToOption);
|
||||
}
|
||||
}, [selectedShop, map]);
|
||||
}
|
||||
}, [selectedShop, map, isDiscoveryOpen]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const Map = ({ shops, onShopSelect, selectedShop }: MapProps) => {
|
||||
const Map = ({ shops, onShopSelect, selectedShop, isDiscoveryOpen }: MapProps) => {
|
||||
useEffect(() => {
|
||||
// Fix for Leaflet default icon not found
|
||||
// @ts-expect-error Fix for Leaflet default icon not found
|
||||
@@ -122,7 +189,7 @@ const Map = ({ shops, onShopSelect, selectedShop }: MapProps) => {
|
||||
zoomControl={false}
|
||||
attributionControl={false}
|
||||
>
|
||||
<MapController selectedShop={selectedShop} />
|
||||
<MapController selectedShop={selectedShop} isDiscoveryOpen={isDiscoveryOpen} />
|
||||
<div className="absolute bottom-8 right-4 z-[1000] flex flex-col gap-2 items-end">
|
||||
<LocateControl />
|
||||
<ZoomControls />
|
||||
@@ -141,7 +208,14 @@ const Map = ({ shops, onShopSelect, selectedShop }: MapProps) => {
|
||||
click: () => onShopSelect(shop),
|
||||
}}
|
||||
>
|
||||
<Tooltip direction="top" offset={[0, -20]} opacity={0.9}>
|
||||
<Tooltip
|
||||
key={`${shop.id}-${selectedShop?.id === shop.id}`}
|
||||
direction="top"
|
||||
offset={[0, -20]}
|
||||
opacity={0.9}
|
||||
permanent={selectedShop?.id === shop.id}
|
||||
className={selectedShop?.id === shop.id ? 'force-show' : ''}
|
||||
>
|
||||
<div className="font-serif font-semibold text-sm">
|
||||
{shop.name}
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ interface MapLoaderProps {
|
||||
shops: CoffeeShop[];
|
||||
onShopSelect: (shop: CoffeeShop) => void;
|
||||
selectedShop: CoffeeShop | null;
|
||||
isDiscoveryOpen: boolean;
|
||||
}
|
||||
|
||||
// Move dynamic import outside component to prevent re-imports
|
||||
@@ -36,6 +37,6 @@ const Map = dynamic(() => import("./Map"), {
|
||||
),
|
||||
});
|
||||
|
||||
export default function MapLoader({ shops, onShopSelect, selectedShop }: MapLoaderProps) {
|
||||
return <Map shops={shops} onShopSelect={onShopSelect} selectedShop={selectedShop} />;
|
||||
export default function MapLoader({ shops, onShopSelect, selectedShop, isDiscoveryOpen }: MapLoaderProps) {
|
||||
return <Map shops={shops} onShopSelect={onShopSelect} selectedShop={selectedShop} isDiscoveryOpen={isDiscoveryOpen} />;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function MapStyleControl({ currentStyle, onStyleChange }: MapStyleControl
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-2xl text-foreground">
|
||||
<Button variant="outline" size="icon" className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-xl text-foreground">
|
||||
<Layers className="h-5 w-5" />
|
||||
<span className="sr-only">Change map style</span>
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Coffee, PanelLeft, X } from "lucide-react";
|
||||
import { Coffee, Search, X } from "lucide-react";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "~/components/ui/tooltip";
|
||||
import { useState, useEffect } from "react";
|
||||
@@ -41,7 +41,7 @@ export default function Navbar({ isDiscoveryOpen, onToggleDiscovery }: NavbarPro
|
||||
return (
|
||||
<>
|
||||
<div className="absolute top-4 left-4 right-4 z-[1000] flex justify-center pointer-events-none">
|
||||
<div className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border border-border/50 rounded-xl p-2 flex items-center justify-between w-full pointer-events-auto">
|
||||
<div className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border border-border/50 rounded-xl p-2 flex items-center justify-between w-full pointer-events-auto shadow-xl">
|
||||
<div className="flex items-center gap-2 relative">
|
||||
{/* Pulsing indicator ring - only during onboarding */}
|
||||
{isOnboarding && showTooltip && (
|
||||
@@ -61,11 +61,11 @@ export default function Navbar({ isDiscoveryOpen, onToggleDiscovery }: NavbarPro
|
||||
}}
|
||||
className={`h-10 w-10 rounded-lg hover:bg-background/40 transition-colors ${isDiscoveryOpen ? 'bg-background/40 text-primary' : 'text-muted-foreground'}`}
|
||||
>
|
||||
<PanelLeft className="h-5 w-5" />
|
||||
<Search className="h-5 w-5" />
|
||||
<span className="sr-only">Toggle Panel</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="bg-background/80 backdrop-blur-xl border-border/50 text-foreground font-semibold shadow-2xl">
|
||||
<TooltipContent side="right" className="bg-background/80 backdrop-blur-xl border-border/50 text-foreground font-semibold font-serif shadow-2xl">
|
||||
<p>Discover Coffee Shops</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
@@ -46,7 +46,7 @@ export function WelcomeModal() {
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent className="sm:max-w-md bg-background/80 backdrop-blur-xl border-border/50">
|
||||
<DialogContent className="sm:max-w-md bg-background/80 backdrop-blur-xl border-border/50 font-serif">
|
||||
<DialogHeader>
|
||||
<div className="mx-auto bg-primary/10 p-3 rounded-full mb-4 w-fit">
|
||||
<Coffee className="h-8 w-8 text-primary" />
|
||||
|
||||
@@ -13,7 +13,7 @@ export function ZoomControls() {
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => map.zoomIn()}
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-2xl text-foreground hover:bg-background/70 dark:hover:bg-background/75"
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-xl text-foreground hover:bg-background/70 dark:hover:bg-background/75"
|
||||
>
|
||||
<Plus className="h-5 w-5" />
|
||||
<span className="sr-only">Zoom in</span>
|
||||
@@ -22,7 +22,7 @@ export function ZoomControls() {
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => map.zoomOut()}
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-2xl text-foreground hover:bg-background/70 dark:hover:bg-background/75"
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-xl text-foreground hover:bg-background/70 dark:hover:bg-background/75"
|
||||
>
|
||||
<Minus className="h-5 w-5" />
|
||||
<span className="sr-only">Zoom out</span>
|
||||
@@ -31,7 +31,7 @@ export function ZoomControls() {
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => map.setView([40.9645, -76.8845], 15)}
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-2xl text-foreground hover:bg-background/70 dark:hover:bg-background/75"
|
||||
className="bg-background/60 dark:bg-background/65 backdrop-blur-2xl border-border/50 dark:border-border/50 h-10 w-10 rounded-lg shadow-xl text-foreground hover:bg-background/70 dark:hover:bg-background/75"
|
||||
>
|
||||
<Home className="h-5 w-5" />
|
||||
<span className="sr-only">Reset view</span>
|
||||
|
||||
Reference in New Issue
Block a user