diff --git a/docs-src/compass.md b/docs-src/compass.md
new file mode 100644
index 0000000..9ad62dd
--- /dev/null
+++ b/docs-src/compass.md
@@ -0,0 +1,147 @@
+# 3D Compass
+
+The `Compass3D` component displays a 3D compass that shows cardinal directions (N, S, E, W) and vertical orientation (Up, Down). It helps users maintain spatial awareness when navigating 3D map views.
+
+## Basic Usage
+
+The simplest way to add a compass is to use the default overlay mode, which automatically positions the compass in the corner of your canvas:
+
+```tsx
+import { Canvas, Compass3D } from 'react-three-map/maplibre';
+import Map from 'react-map-gl/maplibre';
+
+function App() {
+ return (
+
+ );
+}
+```
+
+When `overlay={true}` (the default), the compass:
+- Renders as a HUD element that stays fixed on screen
+- Automatically syncs its orientation with the camera
+- Uses the `alignment` and `margin` props for positioning
+
+## Screen Position
+
+Control where the compass appears using `alignment` and `margin`:
+
+```tsx
+// Bottom-left corner with 40px margin
+
+
+// Top-center with default margin
+
+
+// Available alignments:
+// 'top-left', 'top-right', 'bottom-left', 'bottom-right'
+// 'top-center', 'bottom-center', 'center-left', 'center-right'
+```
+
+## Customizing Appearance
+
+Adjust the compass size and proportions:
+
+```tsx
+
+```
+
+## World-Space Compass
+
+For a compass that exists in 3D world space rather than as a screen overlay, disable overlay mode and provide manual bearing/pitch values:
+
+```tsx
+import { Canvas, Compass3D, useMap } from 'react-three-map/maplibre';
+import { useState, useEffect } from 'react';
+
+function WorldSpaceCompass() {
+ const map = useMap();
+ const [bearing, setBearing] = useState(0);
+ const [pitch, setPitch] = useState(0);
+
+ useEffect(() => {
+ if (!map) return;
+
+ const update = () => {
+ setBearing(map.getBearing());
+ setPitch(map.getPitch());
+ };
+
+ update();
+ map.on('move', update);
+ map.on('rotate', update);
+ map.on('pitch', update);
+
+ return () => {
+ map.off('move', update);
+ map.off('rotate', update);
+ map.off('pitch', update);
+ };
+ }, [map]);
+
+ return (
+
+ );
+}
+```
+
+## Axis Convention
+
+The compass follows the library's axis convention:
+
+| Axis | Color | Direction |
+|------|-------|-----------|
+| X | Red | East (+X) / West (-X) |
+| Y | Green | Up (+Y) / Down (-Y) |
+| Z | Blue | South (+Z) / North (-Z) |
+
+## CompassOverlay Component
+
+For advanced use cases, the `CompassOverlay` component renders the compass in a separate React Three Fiber canvas that floats above the map. This can be useful when you want complete control over the compass rendering:
+
+```tsx
+import { CompassOverlay } from 'react-three-map';
+import Map, { useMap } from 'react-map-gl/maplibre';
+
+function App() {
+ return (
+
+ );
+}
+```
+
+The `CompassOverlay`:
+- Creates its own R3F canvas layered above the map
+- Syncs bearing and pitch with the MapLibre camera
+- Provides a semi-transparent background
+
+## Props Reference
+
+See the full API documentation for all configuration options in the API reference section.
diff --git a/src/components/compass-overlay.tsx b/src/components/compass-overlay.tsx
index 09f71b8..4431249 100644
--- a/src/components/compass-overlay.tsx
+++ b/src/components/compass-overlay.tsx
@@ -1,22 +1,91 @@
-import React, { useEffect, useMemo, useRef, useState } from 'react';
+/**
+ * @packageDocumentation
+ * Screen-space compass overlay component.
+ */
+import { useEffect, useMemo, useRef, useState } from 'react';
import { Canvas } from '@react-three/fiber';
import { useMap } from 'react-map-gl/maplibre';
import { Compass3D } from './compass-3d';
+/**
+ * Props for the CompassOverlay component.
+ *
+ * @example Basic usage
+ * ```tsx
+ *
+ * ```
+ *
+ * @example Custom size and position
+ * ```tsx
+ *
+ * ```
+ */
export interface CompassOverlayProps {
- /** Size of the overlay square in px */
+ /**
+ * Size of the overlay square in pixels.
+ * @defaultValue 200
+ */
size?: number;
- /** CSS inset from bottom/left */
+
+ /**
+ * CSS inset from bottom-left corner in pixels.
+ * @defaultValue \{ x: 20, y: 20 \}
+ */
offset?: { x: number; y: number };
- /** Optional className for the outer div */
+
+ /**
+ * Optional className for the outer container div.
+ */
className?: string;
- /** Respect external overlay toggle */
+
+ /**
+ * Controls visibility of the overlay.
+ * Set to false to hide the compass.
+ * @defaultValue true
+ */
overlay?: boolean;
}
/**
- * Screen-space compass overlay that syncs to the MapLibre camera bearing/pitch.
- * Renders its own R3F canvas layered above the map.
+ * A screen-space compass overlay that renders in its own React Three Fiber canvas.
+ *
+ * This component creates a separate R3F canvas that floats above the map and displays
+ * a 3D compass synchronized with the MapLibre camera's bearing and pitch.
+ *
+ * Use this when you want the compass in a separate rendering context from your main
+ * 3D scene, or when you need precise control over the overlay's position and size.
+ *
+ * @example Basic usage inside a Map
+ * ```tsx
+ * import Map from 'react-map-gl/maplibre';
+ * import { CompassOverlay } from 'react-three-map/maplibre';
+ *
+ * function App() {
+ * return (
+ *
+ * );
+ * }
+ * ```
+ *
+ * @example Custom positioning
+ * ```tsx
+ *
+ * ```
+ *
+ * @see {@link Compass3D} for the in-canvas compass component
+ * @see {@link CompassOverlayProps} for available configuration options
*/
export function CompassOverlay({
size = 200,
diff --git a/src/mapbox.index.ts b/src/mapbox.index.ts
index 8fd9f16..e8852b6 100644
--- a/src/mapbox.index.ts
+++ b/src/mapbox.index.ts
@@ -40,6 +40,8 @@ export { EnhancedPivotControls } from './components/enhanced-pivot-controls';
export type { PivotControlsProps } from './components/enhanced-pivot-controls';
export { Compass3D } from './components/compass-3d';
export type { Compass3DProps } from './components/compass-3d';
+export { CompassOverlay } from './components/compass-overlay';
+export type { CompassOverlayProps } from './components/compass-overlay';
/**
* Hook to access the Mapbox GL JS map instance from within a Canvas.
diff --git a/src/maplibre.index.ts b/src/maplibre.index.ts
index 9975554..84c228a 100644
--- a/src/maplibre.index.ts
+++ b/src/maplibre.index.ts
@@ -39,6 +39,8 @@ export { EnhancedPivotControls } from './components/enhanced-pivot-controls';
export type { PivotControlsProps } from './components/enhanced-pivot-controls';
export { Compass3D } from './components/compass-3d';
export type { Compass3DProps } from './components/compass-3d';
+export { CompassOverlay } from './components/compass-overlay';
+export type { CompassOverlayProps } from './components/compass-overlay';
/**
* Hook to access the MapLibre GL JS map instance from within a Canvas.
diff --git a/stories/src/compass-3d.stories.tsx b/stories/src/compass-3d.stories.tsx
index 058183f..784597a 100644
--- a/stories/src/compass-3d.stories.tsx
+++ b/stories/src/compass-3d.stories.tsx
@@ -1,114 +1,8 @@
-// Removed unused import
import { Canvas } from "@react-three/fiber";
import { useControls } from "leva";
-import { Fragment, useEffect, useState } from "react";
-import { useMap } from "react-map-gl/maplibre";
-import { Compass3D } from "react-three-map";
-import { CompassOverlay } from "../../src/components/compass-overlay";
+import { Compass3D, CompassOverlay } from "react-three-map";
import { StoryMap } from "./story-map-storybook";
-// Component to sync compass rotation with map camera
-function MapCompass({ overlay }: { overlay?: boolean }) {
- const { current: map } = useMap();
- const [bearing, setBearing] = useState(0);
- const [pitch, setPitch] = useState(0);
-
- const {
- cylinderLength,
- cylinderRadius,
- sphereRadius,
- compassScale
- } = useControls({
- cylinderLength: { value: 2, min: 1, max: 5, step: 0.1 },
- cylinderRadius: { value: 0.05, min: 0.01, max: 0.2, step: 0.01 },
- sphereRadius: { value: 0.2, min: 0.1, max: 0.5, step: 0.05 },
- compassScale: { value: 0.5, min: 0.1, max: 2, step: 0.1 }
- });
-
- useEffect(() => {
- if (!map) return;
-
- const updateRotation = () => {
- setBearing(map.getBearing());
- setPitch(map.getPitch());
- };
-
- // Initial update
- updateRotation();
-
- // Listen for map movements
- map.on('move', updateRotation);
- map.on('rotate', updateRotation);
- map.on('pitch', updateRotation);
-
- return () => {
- map.off('move', updateRotation);
- map.off('rotate', updateRotation);
- map.off('pitch', updateRotation);
- };
- }, [map]);
-
- return (
-
-
-
- {/* Display bearing and pitch outside of Canvas */}
-
-
Bearing: {Math.round(bearing)}°
-
Pitch: {Math.round(pitch)}°
-
-
- );
-}
-
// Main story component
export function TerrainWith3DCompass() {
const origin = useControls({
diff --git a/stories/src/story-map-storybook.tsx b/stories/src/story-map-storybook.tsx
index a3e96ea..7fbe817 100644
--- a/stories/src/story-map-storybook.tsx
+++ b/stories/src/story-map-storybook.tsx
@@ -4,7 +4,7 @@ import { useControls } from 'leva';
import MapboxGl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import 'maplibre-gl/dist/maplibre-gl.css';
-import { FC, PropsWithChildren, ReactNode, cloneElement, isValidElement } from "react";
+import { FC, PropsWithChildren, ReactElement, ReactNode, cloneElement, isValidElement } from "react";
import MapboxMap from 'react-map-gl/mapbox';
import MaplibreMap from 'react-map-gl/maplibre';
import { Canvas as MapboxCanvas, CanvasProps } from 'react-three-map/mapbox';
@@ -65,16 +65,16 @@ export const StoryMap: FC = (props) => {
const canvasProps = { overlay, ...canvas };
const mapChildrenWithOverlay = isValidElement(mapChildren)
- ? cloneElement(mapChildren, { overlay })
+ ? cloneElement(mapChildren as ReactElement<{ overlay?: boolean }>, { overlay })
: mapChildren;
const mapboxChildrenWithOverlay = isValidElement(mapboxChildren)
- ? cloneElement(mapboxChildren, { overlay })
+ ? cloneElement(mapboxChildren as ReactElement<{ overlay?: boolean }>, { overlay })
: mapboxChildren;
const maplibreChildrenWithOverlay = isValidElement(maplibreChildren)
- ? cloneElement(maplibreChildren, { overlay })
+ ? cloneElement(maplibreChildren as ReactElement<{ overlay?: boolean }>, { overlay })
: maplibreChildren;
const childrenWithOverlay = isValidElement(children)
- ? cloneElement(children, { overlay })
+ ? cloneElement(children as ReactElement<{ overlay?: boolean }>, { overlay })
: children;
// Set Mapbox token
diff --git a/typedoc.json b/typedoc.json
index 2a59000..01e1f22 100644
--- a/typedoc.json
+++ b/typedoc.json
@@ -22,7 +22,8 @@
"./docs-src/getting-started.md",
"./docs-src/multiple-locations.md",
"./docs-src/interacting-with-map.md",
- "./docs-src/pivot-controls.md"
+ "./docs-src/pivot-controls.md",
+ "./docs-src/compass.md"
],
"navigationLinks": {
"Storybook": "https://wendylabsinc.github.io/react-three-map/storybook/",