Map Controls
terrae/controlsFreeComponent
Interactive map controls (zoom, fullscreen, etc).
Install it
npx shadcn@latest add https://www.terrae.dev/controls.jsonSource
1"use client"23import { useEffect, useRef, useState, type ReactNode } from "react"4import { Minus, Plus, Locate, Maximize, Loader2 } from "lucide-react"5import { cn } from "@/lib/utils"6import { useMap } from "./hooks"78type MapControlPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right"910type MapControlsProps = {11 children: ReactNode12 position?: MapControlPosition13 className?: string14}1516type ControlButtonProps = {17 onClick: () => void18 label: string19 children: ReactNode20 disabled?: boolean21}2223type MapGeolocateProps = {24 onLocate?: (coords: GeolocationCoordinates) => void25}2627type GeolocationCoordinates = {28 longitude: number29 latitude: number30}3132const DEFAULT_POSITION: MapControlPosition = "bottom-right"33const ZOOM_STEP = 134const ZOOM_DURATION = 30035const GEOLOCATE_ZOOM = 1436const GEOLOCATE_DURATION = 15003738const POSITION_CLASSES: Record<MapControlPosition, string> = {39 "top-left": "top-2 left-2",40 "top-right": "top-2 right-2",41 "bottom-left": "bottom-2 left-2",42 "bottom-right": "bottom-10 right-2",43}44const DEFAULT_DISABLED = false45const DEFAULT_GEOLOCATE_PROPS: MapGeolocateProps = {}4647const ControlGroup = ({ children }: { children: ReactNode }) => {48 return (49 <div className="flex flex-col rounded-md border border-border bg-background shadow-sm overflow-hidden [&>button:not(:last-child)]:border-b [&>button:not(:last-child)]:border-border">50 {children}51 </div>52 )53}5455const ControlButton = ({ onClick, label, children, disabled = DEFAULT_DISABLED }: ControlButtonProps) => {56 return (57 <button58 onClick={onClick}59 aria-label={label}60 type="button"61 className={cn(62 "flex items-center justify-center size-10 md:size-8 hover:bg-accent dark:hover:bg-accent/40 transition-colors",63 disabled && "opacity-50 pointer-events-none cursor-not-allowed"64 )}65 disabled={disabled}66 >67 {children}68 </button>69 )70}7172export const MapControls = ({ children, position = DEFAULT_POSITION, className }: MapControlsProps) => {73 const { isLoaded } = useMap()7475 if (!isLoaded) {76 return null77 }7879 return (80 <div className={cn("absolute z-10 flex flex-col gap-1.5", POSITION_CLASSES[position], className)}>{children}</div>81 )82}8384export const MapZoom = () => {85 const { map } = useMap()8687 const handleZoomIn = () => {88 map?.zoomTo(map.getZoom() + ZOOM_STEP, { duration: ZOOM_DURATION })89 }9091 const handleZoomOut = () => {92// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from terrae
All 40 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Map Animated Circleanimated-circle | terrae | Components | Free | 1 dep | |
| Map Animated Footprintanimated-footprint | terrae | Components | Free | 2 deps | |
| Map Animated Polygonanimated-polygon | terrae | Components | Free | 1 dep | |
| Map Animated Pulseanimated-pulse | terrae | Components | Free | 1 dep | |
| Map Arc Animatedarc-animated | terrae | Components | Free | 1 dep | |
| Map Blur Areablur-area | terrae | Components | Free | 1 dep | |
| Map Camera Followcamera-follow | terrae | Components | Free | 1 dep | |
| Map Choroplethchoropleth | terrae | Components | Free | 1 dep |
