CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pigeon-maps

ReactJS maps without external dependencies - performance-first React-centric extendable map engine

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

controls.mddocs/

Map Controls

UI controls for map interaction including zoom buttons and other control elements. Controls are positioned overlays that provide user interface elements for map interaction without interfering with map events.

Capabilities

ZoomControl Component

Zoom in/out button controls with customizable styling and positioning.

/**
 * Zoom in/out button controls for map interaction
 * @param props - Zoom control configuration and styling
 * @returns JSX.Element representing the zoom controls
 */
function ZoomControl(props: ZoomProps): JSX.Element;

interface ZoomProps extends PigeonProps {
  style?: React.CSSProperties;
  buttonStyle?: React.CSSProperties;
}

Usage Examples:

import React from "react";
import { Map, ZoomControl } from "pigeon-maps";

// Basic zoom controls
function MapWithZoomControls() {
  return (
    <Map height={400} center={[50.879, 4.6997]} zoom={11}>
      <ZoomControl />
    </Map>
  );
}

// Styled zoom controls
function StyledZoomControls() {
  return (
    <Map height={400} center={[50.879, 4.6997]} zoom={11}>
      <ZoomControl
        style={{
          position: 'absolute',
          top: 20,
          right: 20,
          left: 'auto' // Override default left positioning
        }}
        buttonStyle={{
          backgroundColor: '#007bff',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
          fontSize: '18px'
        }}
      />
    </Map>
  );
}

Custom Positioned Controls

function CustomPositionedControls() {
  return (
    <Map height={400} center={[50.879, 4.6997]} zoom={11}>
      {/* Top-right zoom controls */}
      <ZoomControl
        style={{
          position: 'absolute',
          top: 10,
          right: 10,
          left: 'auto'
        }}
      />
      
      {/* Custom control overlay */}
      <div style={{
        position: 'absolute',
        bottom: 10,
        left: 10,
        background: 'white',
        padding: '8px',
        borderRadius: '4px',
        boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
      }}>
        Custom Control
      </div>
    </Map>
  );
}

Zoom Control Features

Button Functionality

The zoom control provides two buttons:

  • Zoom In (+): Increases zoom level by 1, respecting maxZoom limit
  • Zoom Out (–): Decreases zoom level by 1, respecting minZoom limit

Automatic Integration

Zoom controls automatically:

  • Access current map state (zoom level, bounds)
  • Respect map zoom limits (minZoom, maxZoom)
  • Use map's zoom animation settings
  • Call the map's setCenterZoom function

Default Styling

// Default container style
const commonStyle: React.CSSProperties = {
  position: 'absolute',
  top: 10,
  left: 10,
};

// Default button style
const commonButtonStyle: React.CSSProperties = {
  width: 28,
  height: 28,
  borderRadius: 2,
  boxShadow: '0 1px 4px -1px rgba(0,0,0,.3)',
  background: 'white',
  lineHeight: '26px',
  fontSize: '20px',
  fontWeight: 700,
  color: '#666',
  marginBottom: 1,
  cursor: 'pointer',
  border: 'none',
  display: 'block',
  outline: 'none',
};

CSS Classes

Default Classes

// Container classes
className="pigeon-zoom-buttons pigeon-drag-block"

// Button classes
className="pigeon-zoom-in"    // Zoom in button
className="pigeon-zoom-out"   // Zoom out button

The pigeon-drag-block class prevents map interactions when clicking on controls.

Custom Styling Examples

/* Custom zoom control styling */
.pigeon-zoom-buttons {
  background: rgba(255, 255, 255, 0.9);
  border-radius: 8px;
  padding: 4px;
}

.pigeon-zoom-in,
.pigeon-zoom-out {
  background: linear-gradient(to bottom, #f8f8f8, #e8e8e8);
  border: 1px solid #ccc;
  transition: all 0.2s;
}

.pigeon-zoom-in:hover,
.pigeon-zoom-out:hover {
  background: linear-gradient(to bottom, #fff, #f0f0f0);
  border-color: #999;
}

.pigeon-zoom-in:active,
.pigeon-zoom-out:active {
  background: linear-gradient(to bottom, #e8e8e8, #f8f8f8);
  box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
}

Custom Controls

Creating Custom Controls

import React from "react";
import { Map } from "pigeon-maps";

function CustomMapControls() {
  const [mapRef, setMapRef] = useState(null);

  // Custom zoom to specific location
  const zoomToLocation = (center, zoom) => {
    if (mapRef && mapRef.setCenterZoom) {
      mapRef.setCenterZoom(center, zoom);
    }
  };

  return (
    <Map 
      height={400} 
      center={[50.879, 4.6997]} 
      zoom={11}
      ref={setMapRef}
    >
      {/* Custom location buttons */}
      <div style={{
        position: 'absolute',
        top: 10,
        right: 10,
        display: 'flex',
        flexDirection: 'column',
        gap: '4px'
      }}>
        <button
          className="pigeon-drag-block"
          style={{
            padding: '8px 12px',
            background: 'white',
            border: '1px solid #ccc',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
          onClick={() => zoomToLocation([50.879, 4.6997], 15)}
        >
          Brussels
        </button>
        <button
          className="pigeon-drag-block"
          style={{
            padding: '8px 12px',
            background: 'white',
            border: '1px solid #ccc',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
          onClick={() => zoomToLocation([48.8566, 2.3522], 12)}
        >
          Paris
        </button>
      </div>
    </Map>
  );
}

Control with Map State

function StatefulControls() {
  const [zoom, setZoom] = useState(11);
  const [center, setCenter] = useState([50.879, 4.6997]);

  return (
    <Map 
      height={400} 
      center={center} 
      zoom={zoom}
      onBoundsChanged={({ center, zoom }) => {
        setCenter(center);
        setZoom(zoom);
      }}
    >
      {/* Display current state */}
      <div style={{
        position: 'absolute',
        top: 10,
        left: 10,
        background: 'rgba(255, 255, 255, 0.9)',
        padding: '8px',
        borderRadius: '4px',
        fontSize: '12px',
        fontFamily: 'monospace'
      }}>
        <div>Zoom: {zoom.toFixed(2)}</div>
        <div>Lat: {center[0].toFixed(4)}</div>
        <div>Lng: {center[1].toFixed(4)}</div>
      </div>
      
      <ZoomControl />
    </Map>
  );
}

Toggle Controls

function ToggleControls() {
  const [showSatellite, setShowSatellite] = useState(false);
  const [showTraffic, setShowTraffic] = useState(false);

  return (
    <Map height={400} center={[50.879, 4.6997]} zoom={11}>
      {/* Toggle control panel */}
      <div style={{
        position: 'absolute',
        bottom: 10,
        left: 10,
        background: 'white',
        padding: '12px',
        borderRadius: '4px',
        boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
      }}>
        <label style={{ display: 'block', marginBottom: '8px' }}>
          <input
            type="checkbox"
            checked={showSatellite}
            onChange={(e) => setShowSatellite(e.target.checked)}
            style={{ marginRight: '8px' }}
          />
          Satellite View
        </label>
        <label style={{ display: 'block' }}>
          <input
            type="checkbox"
            checked={showTraffic}
            onChange={(e) => setShowTraffic(e.target.checked)}
            style={{ marginRight: '8px' }}
          />
          Traffic Layer
        </label>
      </div>
      
      <ZoomControl />
    </Map>
  );
}

Accessibility

Keyboard Support

function AccessibleControls() {
  const handleKeyDown = (event, action) => {
    if (event.key === 'Enter' || event.key === ' ') {
      event.preventDefault();
      action();
    }
  };

  return (
    <Map height={400} center={[50.879, 4.6997]} zoom={11}>
      <div style={{
        position: 'absolute',
        top: 10,
        right: 10,
        display: 'flex',
        flexDirection: 'column'
      }}>
        <button
          className="pigeon-drag-block"
          aria-label="Zoom in"
          tabIndex={0}
          onKeyDown={(e) => handleKeyDown(e, () => console.log('zoom in'))}
          style={{
            width: 32,
            height: 32,
            fontSize: '18px',
            marginBottom: 2
          }}
        >
          +
        </button>
        <button
          className="pigeon-drag-block"
          aria-label="Zoom out"
          tabIndex={0}
          onKeyDown={(e) => handleKeyDown(e, () => console.log('zoom out'))}
          style={{
            width: 32,
            height: 32,
            fontSize: '18px'
          }}
        >
          –
        </button>
      </div>
    </Map>
  );
}

Performance Considerations

  • Controls are lightweight DOM elements with minimal re-rendering
  • Use pigeon-drag-block class to prevent event conflicts with map
  • Position controls absolutely to avoid affecting map layout
  • Consider control visibility at different screen sizes
  • Group related controls to minimize DOM complexity

Install with Tessl CLI

npx tessl i tessl/npm-pigeon-maps

docs

controls.md

draggable.md

geojson.md

index.md

map.md

markers-overlays.md

providers.md

tile.json