Updates: Example update

This commit is contained in:
Anmoldeep Singh
2026-01-07 00:56:53 +05:30
parent bf96f5f129
commit c0e13fdbcc
6 changed files with 208 additions and 10 deletions

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,13 @@ import {
} from "@/components/ui/table";
import { cn } from "@/lib/utils";
function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)/g, "");
}
// DocsHeader - Page title and description
interface DocsHeaderProps {
title: string;
@@ -89,8 +96,9 @@ interface DocsSectionProps {
}
export function DocsSection({ title, children }: DocsSectionProps) {
const id = title ? slugify(title) : undefined;
return (
<section className="space-y-5">
<section className="space-y-5 scroll-mt-20" id={id}>
{title && (
<h2 className="text-xl font-semibold tracking-tight text-primary">
{title}

View File

@@ -0,0 +1,155 @@
"use client";
import { useEffect, useState, useId } from "react";
import { Map, MapPopup, useMap } from "@/registry/map";
// Generate random points around NYC
function generateRandomPoints(count: number) {
const center = { lng: -73.98, lat: 40.75 };
const features = [];
for (let i = 0; i < count; i++) {
const lng = center.lng + (Math.random() - 0.5) * 0.15;
const lat = center.lat + (Math.random() - 0.5) * 0.1;
features.push({
type: "Feature" as const,
properties: {
id: i,
name: `Location ${i + 1}`,
category: ["Restaurant", "Cafe", "Bar", "Shop"][
Math.floor(Math.random() * 4)
],
},
geometry: {
type: "Point" as const,
coordinates: [lng, lat],
},
});
}
return {
type: "FeatureCollection" as const,
features,
};
}
// 200 markers - would be slow with DOM markers, but fast with layers
const pointsData = generateRandomPoints(200);
interface SelectedPoint {
id: number;
name: string;
category: string;
coordinates: [number, number];
}
function MarkersLayer() {
const { map, isLoaded } = useMap();
const id = useId();
const sourceId = `markers-source-${id}`;
const layerId = `markers-layer-${id}`;
const [selectedPoint, setSelectedPoint] = useState<SelectedPoint | null>(
null
);
useEffect(() => {
if (!map || !isLoaded) return;
map.addSource(sourceId, {
type: "geojson",
data: pointsData,
});
map.addLayer({
id: layerId,
type: "circle",
source: sourceId,
paint: {
"circle-radius": 6,
"circle-color": "#3b82f6",
"circle-stroke-width": 2,
"circle-stroke-color": "#ffffff",
// add more paint properties here to customize the appearance of the markers
},
});
const handleClick = (
e: maplibregl.MapMouseEvent & {
features?: maplibregl.MapGeoJSONFeature[];
}
) => {
if (!e.features?.length) return;
const feature = e.features[0];
const coords = (feature.geometry as GeoJSON.Point).coordinates as [
number,
number
];
setSelectedPoint({
id: feature.properties?.id,
name: feature.properties?.name,
category: feature.properties?.category,
coordinates: coords,
});
};
const handleMouseEnter = () => {
map.getCanvas().style.cursor = "pointer";
};
const handleMouseLeave = () => {
map.getCanvas().style.cursor = "";
};
map.on("click", layerId, handleClick);
map.on("mouseenter", layerId, handleMouseEnter);
map.on("mouseleave", layerId, handleMouseLeave);
return () => {
map.off("click", layerId, handleClick);
map.off("mouseenter", layerId, handleMouseEnter);
map.off("mouseleave", layerId, handleMouseLeave);
try {
if (map.getLayer(layerId)) map.removeLayer(layerId);
if (map.getSource(sourceId)) map.removeSource(sourceId);
} catch {
// ignore cleanup errors
}
};
}, [map, isLoaded, sourceId, layerId]);
return (
<>
{selectedPoint && (
<MapPopup
longitude={selectedPoint.coordinates[0]}
latitude={selectedPoint.coordinates[1]}
onClose={() => setSelectedPoint(null)}
closeOnClick={false}
focusAfterOpen={false}
offset={10}
closeButton
>
<div className="min-w-[140px]">
<p className="font-medium">{selectedPoint.name}</p>
<p className="text-sm text-muted-foreground">
{selectedPoint.category}
</p>
</div>
</MapPopup>
)}
</>
);
}
export function LayerMarkersExample() {
return (
<div className="h-[400px] w-full">
<Map center={[-73.98, 40.75]} zoom={11}>
<MarkersLayer />
</Map>
</div>
);
}

View File

@@ -8,6 +8,7 @@ import {
import { ComponentPreview } from "../_components/component-preview";
import { AdvancedUsageExample } from "../_components/examples/advanced-usage-example";
import { CustomLayerExample } from "../_components/examples/custom-layer-example";
import { LayerMarkersExample } from "../_components/examples/layer-markers-example";
import { CodeBlock } from "../_components/code-block";
import { getExampleSource } from "@/lib/get-example-source";
import { Metadata } from "next";
@@ -16,12 +17,11 @@ export const metadata: Metadata = {
title: "Advanced Usage",
};
const refCode = `import { Map } from "@/components/ui/map";
const refCode = `import { Map, type MapRef } from "@/components/ui/map";
import { useRef } from "react";
import type MapLibreGL from "maplibre-gl";
function MyMapComponent() {
const mapRef = useRef<MapLibreGL.Map>(null);
const mapRef = useRef<MapRef>(null);
const handleFlyTo = () => {
// Access the MapLibre GL map instance via ref
@@ -65,6 +65,7 @@ function MapEventListener() {
export default function AdvancedPage() {
const advancedSource = getExampleSource("advanced-usage-example.tsx");
const customLayerSource = getExampleSource("custom-layer-example.tsx");
const layerMarkersSource = getExampleSource("layer-markers-example.tsx");
return (
<DocsLayout
@@ -132,6 +133,19 @@ export default function AdvancedPage() {
<CustomLayerExample />
</ComponentPreview>
<DocsSection title="Example: Markers via Layers">
<p>
When displaying hundreds or thousands of markers, use GeoJSON layers
instead of DOM-based <DocsCode>MapMarker</DocsCode> components. This
approach renders markers on the WebGL canvas, providing significantly
better performance.
</p>
</DocsSection>
<ComponentPreview code={layerMarkersSource}>
<LayerMarkersExample />
</ComponentPreview>
<DocsSection title="Extend to Build">
<p>You can extend this to build custom features like:</p>
<ul>

View File

@@ -1,4 +1,10 @@
import { DocsLayout, DocsSection, DocsCode } from "../_components/docs";
import {
DocsLayout,
DocsSection,
DocsCode,
DocsNote,
DocsLink,
} from "../_components/docs";
import { ComponentPreview } from "../_components/component-preview";
import { MarkersExample } from "../_components/examples/markers-example";
import { PopupExample } from "../_components/examples/popup-example";
@@ -13,7 +19,9 @@ export const metadata: Metadata = {
export default function MarkersPage() {
const markersSource = getExampleSource("markers-example.tsx");
const popupSource = getExampleSource("popup-example.tsx");
const draggableMarkerSource = getExampleSource("draggable-marker-example.tsx");
const draggableMarkerSource = getExampleSource(
"draggable-marker-example.tsx"
);
return (
<DocsLayout
@@ -30,6 +38,16 @@ export default function MarkersPage() {
</p>
</DocsSection>
<DocsNote>
<strong>Performance tip:</strong> <DocsCode>MapMarker</DocsCode> is
DOM-based and works best for a few dozen of markers. For larger
datasets, see the{" "}
<DocsLink href="/docs/advanced-usage#example-markers-via-layers">
GeoJSON layers example
</DocsLink>{" "}
instead. Rendering many DOM markers can make the browser sluggish.
</DocsNote>
<DocsSection title="Basic Example">
<p>
Simple markers with tooltips and popups showing location information.

View File

@@ -857,7 +857,7 @@ type MapRouteProps = {
};
function MapRoute({
id,
id: propId,
coordinates,
color = "#4285F4",
width = 3,
@@ -870,8 +870,9 @@ function MapRoute({
}: MapRouteProps) {
const { map, isLoaded } = useMap();
const autoId = useId();
const sourceId = id ?? `route-source-${autoId}`;
const layerId = id ?? `route-layer-${autoId}`;
const id = propId ?? autoId;
const sourceId = `route-source-${id}`;
const layerId = `route-layer-${id}`;
// Add source and layer on mount
useEffect(() => {
@@ -1283,3 +1284,5 @@ export {
MapRoute,
MapClusterLayer,
};
export type { MapRef };