Flight
flightcn/flightFreeComponent
Composable flight map components for routes, live tracking, weighted networks, geodesic ranges, aircraft trails, traffic flow, and a built-in 515-airport database.
Live preview
live · rendered by the registry
Rendered by flightcn on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://flightcn.yencheng.dev/r/flight.jsonSource
1"use client";23import MapLibreGL from "maplibre-gl";4import {5 useCallback,6 useEffect,7 useId,8 useMemo,9 useRef,10 useState,11 useSyncExternalStore,12 type ReactNode,13} from "react";14import { createPortal } from "react-dom";15import greatCircle from "@turf/great-circle";16import bearing from "@turf/bearing";1718import {19 useMap,20 MapMarker,21 MarkerContent,22 MapPopup,23 MarkerLabel,24} from "@/components/ui/map";25import { cn } from "@/lib/utils";2627import { airports, type AirportInfo, type AirportRef } from "./flight-airports";28import { getAirportInfo, resolveAirport } from "./flight-airports-utils";29export type { AirportInfo, AirportRef } from "./flight-airports";3031type FlightMapTheme = "light" | "dark";3233function getDocumentTheme(): FlightMapTheme | null {34 if (typeof document === "undefined") return null;35 if (document.documentElement.classList.contains("dark")) return "dark";36 if (document.documentElement.classList.contains("light")) return "light";37 return null;38}3940function getSystemTheme(): FlightMapTheme {41 if (typeof window === "undefined") return "light";42 return window.matchMedia("(prefers-color-scheme: dark)").matches43 ? "dark"44 : "light";45}4647/**48 * Resolves light/dark the same way as Map’s basemap: `html` class (e.g. next-themes)49 * or `prefers-color-scheme` when unset.50 */51function useFlightMapTheme(): FlightMapTheme {52 const [theme, setTheme] = useState<FlightMapTheme>(53 () => getDocumentTheme() ?? getSystemTheme(),54 );5556 useEffect(() => {57 const observer = new MutationObserver(() => {58 const docTheme = getDocumentTheme();59 if (docTheme) setTheme(docTheme);60 });61 observer.observe(document.documentElement, {62 attributes: true,63 attributeFilter: ["class"],64 });6566 const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");67 const onSystem = (e: MediaQueryListEvent) => {68 if (!getDocumentTheme()) setTheme(e.matches ? "dark" : "light");69 };70 mediaQuery.addEventListener("change", onSystem);7172 return () => {73 observer.disconnect();74 mediaQuery.removeEventListener("change", onSystem);75 };76 }, []);7778 return theme;79}8081/** Default arc stroke on light basemap when `color` is omitted */82const FLIGHT_ROUTE_COLOR_LIGHT = "#0a0a0a";83/** Default arc stroke on dark basemap when `color` is omitted */84const FLIGHT_ROUTE_COLOR_DARK = "#e8e8e8";8586function normalizeAirportRefKey(ref: AirportRef): string {87 if (typeof ref === "string") {88// … truncated
What it pulls in
npm packages
Other registry items
