From 4f856f083839ffd25c2005e2eaaf0fc06623901b Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Thu, 23 Jan 2025 13:33:05 -0800 Subject: [PATCH] [v8] Align type names across modules (#2468) --- .github/workflows/release.yml | 4 +- .../components/attribution-control.ts | 22 +- .../components/fullscreen-control.ts | 17 +- .../components/geolocate-control.ts | 53 ++- .../src/mapbox-legacy/components/layer.ts | 34 +- .../main/src/mapbox-legacy/components/map.tsx | 48 +-- .../src/mapbox-legacy/components/marker.ts | 198 +++++------ .../components/navigation-control.ts | 15 +- .../src/mapbox-legacy/components/popup.ts | 156 ++++----- .../mapbox-legacy/components/scale-control.ts | 24 +- .../src/mapbox-legacy/components/source.ts | 40 +-- .../mapbox-legacy/components/use-control.ts | 12 +- .../src/mapbox-legacy/components/use-map.tsx | 19 +- .../main/src/mapbox-legacy/exports-mapbox.ts | 159 --------- modules/main/src/mapbox-legacy/index.ts | 35 +- .../src/mapbox-legacy/mapbox/create-ref.ts | 28 +- .../main/src/mapbox-legacy/mapbox/mapbox.ts | 101 +++--- .../main/src/mapbox-legacy/types/common.ts | 82 +---- .../src/mapbox-legacy/types/events-mapbox.ts | 76 ---- .../main/src/mapbox-legacy/types/events.ts | 152 +++++--- modules/main/src/mapbox-legacy/types/index.ts | 93 ----- .../main/src/mapbox-legacy/types/internal.ts | 50 +++ modules/main/src/mapbox-legacy/types/lib.ts | 328 +++--------------- .../main/src/mapbox-legacy/types/public.ts | 9 - .../{style-spec-mapbox.ts => style-spec.ts} | 41 +-- .../src/mapbox-legacy/utils/deep-equal.ts | 2 +- .../src/mapbox-legacy/utils/style-utils.ts | 3 +- .../main/src/mapbox-legacy/utils/transform.ts | 3 +- modules/main/src/mapbox.ts | 2 + modules/main/src/maplibre.ts | 2 + modules/react-mapbox/src/components/source.ts | 8 +- modules/react-mapbox/src/types/style-spec.ts | 30 +- .../react-maplibre/src/types/style-spec.ts | 30 +- 33 files changed, 648 insertions(+), 1228 deletions(-) delete mode 100644 modules/main/src/mapbox-legacy/exports-mapbox.ts delete mode 100644 modules/main/src/mapbox-legacy/types/events-mapbox.ts delete mode 100644 modules/main/src/mapbox-legacy/types/index.ts create mode 100644 modules/main/src/mapbox-legacy/types/internal.ts delete mode 100644 modules/main/src/mapbox-legacy/types/public.ts rename modules/main/src/mapbox-legacy/types/{style-spec-mapbox.ts => style-spec.ts} (60%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a212d98f..c851e09e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: check_branch: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: should_build: ${{ steps.permitted.outputs.result }} @@ -30,7 +30,7 @@ jobs: echo "result=${result}" >> "$GITHUB_OUTPUT" release: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: check_branch permissions: contents: write diff --git a/modules/main/src/mapbox-legacy/components/attribution-control.ts b/modules/main/src/mapbox-legacy/components/attribution-control.ts index 9ba419bb..937d3a3d 100644 --- a/modules/main/src/mapbox-legacy/components/attribution-control.ts +++ b/modules/main/src/mapbox-legacy/components/attribution-control.ts @@ -1,32 +1,28 @@ import * as React from 'react'; import {useEffect, memo} from 'react'; import {applyReactStyle} from '../utils/apply-react-style'; -import useControl from './use-control'; +import {useControl} from './use-control'; -import type {ControlPosition, AttributionControlInstance} from '../types'; +import type {ControlPosition, AttributionControlOptions} from '../types/lib'; -export type AttributionControlProps = OptionsT & { +export type AttributionControlProps = AttributionControlOptions & { /** Placement of the control relative to the map. */ position?: ControlPosition; /** CSS style override, applied to the control's container */ style?: React.CSSProperties; }; -function AttributionControl( - props: AttributionControlProps -): null { - const ctrl = useControl( - ({mapLib}) => new mapLib.AttributionControl(props) as ControlT, - { - position: props.position - } - ); +function _AttributionControl(props: AttributionControlProps) { + const ctrl = useControl(({mapLib}) => new mapLib.AttributionControl(props), { + position: props.position + }); useEffect(() => { + // @ts-expect-error accessing private member applyReactStyle(ctrl._container, props.style); }, [props.style]); return null; } -export default memo(AttributionControl); +export const AttributionControl = memo(_AttributionControl); diff --git a/modules/main/src/mapbox-legacy/components/fullscreen-control.ts b/modules/main/src/mapbox-legacy/components/fullscreen-control.ts index e79fb64e..dbeb6735 100644 --- a/modules/main/src/mapbox-legacy/components/fullscreen-control.ts +++ b/modules/main/src/mapbox-legacy/components/fullscreen-control.ts @@ -2,11 +2,11 @@ import * as React from 'react'; import {useEffect, memo} from 'react'; import {applyReactStyle} from '../utils/apply-react-style'; -import useControl from './use-control'; +import {useControl} from './use-control'; -import type {ControlPosition, FullscreenControlInstance} from '../types'; +import type {ControlPosition, FullscreenControlOptions} from '../types/lib'; -export type FullscreenControlProps = Omit & { +export type FullscreenControlProps = Omit & { /** Id of the DOM element which should be made full screen. By default, the map container * element will be made full screen. */ containerId?: string; @@ -16,22 +16,21 @@ export type FullscreenControlProps = Omit & { style?: React.CSSProperties; }; -function FullscreenControl( - props: FullscreenControlProps -): null { - const ctrl = useControl( +function _FullscreenControl(props: FullscreenControlProps) { + const ctrl = useControl( ({mapLib}) => new mapLib.FullscreenControl({ container: props.containerId && document.getElementById(props.containerId) - }) as ControlT, + }), {position: props.position} ); useEffect(() => { + // @ts-expect-error accessing private member applyReactStyle(ctrl._controlContainer, props.style); }, [props.style]); return null; } -export default memo(FullscreenControl); +export const FullscreenControl = memo(_FullscreenControl); diff --git a/modules/main/src/mapbox-legacy/components/geolocate-control.ts b/modules/main/src/mapbox-legacy/components/geolocate-control.ts index 4b74d1a0..576bb50d 100644 --- a/modules/main/src/mapbox-legacy/components/geolocate-control.ts +++ b/modules/main/src/mapbox-legacy/components/geolocate-control.ts @@ -1,74 +1,68 @@ import * as React from 'react'; import {useImperativeHandle, useRef, useEffect, forwardRef, memo} from 'react'; import {applyReactStyle} from '../utils/apply-react-style'; -import useControl from './use-control'; +import {useControl} from './use-control'; import type { ControlPosition, GeolocateControlInstance, - GeolocateEvent, - GeolocateResultEvent, - GeolocateErrorEvent -} from '../types'; + GeolocateControlOptions +} from '../types/lib'; +import type {GeolocateEvent, GeolocateResultEvent, GeolocateErrorEvent} from '../types/events'; -export type GeolocateControlProps< - OptionsT, - ControlT extends GeolocateControlInstance -> = OptionsT & { +export type GeolocateControlProps = GeolocateControlOptions & { /** Placement of the control relative to the map. */ position?: ControlPosition; /** CSS style override, applied to the control's container */ style?: React.CSSProperties; /** Called on each Geolocation API position update that returned as success. */ - onGeolocate?: (e: GeolocateResultEvent) => void; + onGeolocate?: (e: GeolocateResultEvent) => void; /** Called on each Geolocation API position update that returned as an error. */ - onError?: (e: GeolocateErrorEvent) => void; + onError?: (e: GeolocateErrorEvent) => void; /** Called on each Geolocation API position update that returned as success but user position * is out of map `maxBounds`. */ - onOutOfMaxBounds?: (e: GeolocateResultEvent) => void; + onOutOfMaxBounds?: (e: GeolocateResultEvent) => void; /** Called when the GeolocateControl changes to the active lock state. */ - onTrackUserLocationStart?: (e: GeolocateEvent) => void; + onTrackUserLocationStart?: (e: GeolocateEvent) => void; /** Called when the GeolocateControl changes to the background state. */ - onTrackUserLocationEnd?: (e: GeolocateEvent) => void; + onTrackUserLocationEnd?: (e: GeolocateEvent) => void; }; -function GeolocateControl( - props: GeolocateControlProps, - ref: React.Ref -) { +function _GeolocateControl(props: GeolocateControlProps, ref: React.Ref) { const thisRef = useRef({props}); - const ctrl = useControl( + const ctrl = useControl( ({mapLib}) => { - const gc = new mapLib.GeolocateControl(props) as ControlT; + const gc = new mapLib.GeolocateControl(props); // Hack: fix GeolocateControl reuse // When using React strict mode, the component is mounted twice. // GeolocateControl's UI creation is asynchronous. Removing and adding it back causes the UI to be initialized twice. - // @ts-expect-error private method - const setupUI = gc._setupUI; - // @ts-expect-error private method + // @ts-expect-error accessing private method + const setupUI = gc._setupUI.bind(gc); + // @ts-expect-error overriding private method gc._setupUI = args => { + // @ts-expect-error accessing private member if (!gc._container.hasChildNodes()) { setupUI(args); } }; gc.on('geolocate', e => { - thisRef.current.props.onGeolocate?.(e as GeolocateResultEvent); + thisRef.current.props.onGeolocate?.(e as GeolocateResultEvent); }); gc.on('error', e => { - thisRef.current.props.onError?.(e as GeolocateErrorEvent); + thisRef.current.props.onError?.(e as GeolocateErrorEvent); }); gc.on('outofmaxbounds', e => { - thisRef.current.props.onOutOfMaxBounds?.(e as GeolocateResultEvent); + thisRef.current.props.onOutOfMaxBounds?.(e as GeolocateResultEvent); }); gc.on('trackuserlocationstart', e => { - thisRef.current.props.onTrackUserLocationStart?.(e as GeolocateEvent); + thisRef.current.props.onTrackUserLocationStart?.(e as GeolocateEvent); }); gc.on('trackuserlocationend', e => { - thisRef.current.props.onTrackUserLocationEnd?.(e as GeolocateEvent); + thisRef.current.props.onTrackUserLocationEnd?.(e as GeolocateEvent); }); return gc; @@ -81,10 +75,11 @@ function GeolocateControl ctrl, []); useEffect(() => { + // @ts-expect-error accessing private member applyReactStyle(ctrl._container, props.style); }, [props.style]); return null; } -export default memo(forwardRef(GeolocateControl)); +export const GeolocateControl = memo(forwardRef(_GeolocateControl)); diff --git a/modules/main/src/mapbox-legacy/components/layer.ts b/modules/main/src/mapbox-legacy/components/layer.ts index 748891ed..35e63cc5 100644 --- a/modules/main/src/mapbox-legacy/components/layer.ts +++ b/modules/main/src/mapbox-legacy/components/layer.ts @@ -3,25 +3,21 @@ import {MapContext} from './map'; import assert from '../utils/assert'; import {deepEqual} from '../utils/deep-equal'; -import type {MapInstance, CustomLayerInterface, ILayer} from '../types'; +import type {MapInstance, CustomLayerInterface} from '../types/lib'; +import type {AnyLayer} from '../types/style-spec'; // Omiting property from a union type, see // https://github.com/microsoft/TypeScript/issues/39556#issuecomment-656925230 type OptionalId = T extends {id: string} ? Omit & {id?: string} : T; type OptionalSource = T extends {source: string} ? Omit & {source?: string} : T; -export type LayerProps = OptionalSource> & { +export type LayerProps = (OptionalSource> | CustomLayerInterface) & { /** If set, the layer will be inserted before the specified layer */ beforeId?: string; }; /* eslint-disable complexity, max-statements */ -function updateLayer( - map: MapInstance, - id: string, - props: LayerProps, - prevProps: LayerProps -) { +function updateLayer(map: MapInstance, id: string, props: LayerProps, prevProps: LayerProps) { assert(props.id === prevProps.id, 'layer id changed'); assert(props.type === prevProps.type, 'layer type changed'); @@ -29,6 +25,7 @@ function updateLayer( return; } + // @ts-ignore filter does not exist in some Layer types const {layout = {}, paint = {}, filter, minzoom, maxzoom, beforeId} = props; if (beforeId !== prevProps.beforeId) { @@ -38,12 +35,12 @@ function updateLayer( const prevLayout = prevProps.layout || {}; for (const key in layout) { if (!deepEqual(layout[key], prevLayout[key])) { - map.setLayoutProperty(id, key, layout[key]); + map.setLayoutProperty(id, key as any, layout[key]); } } for (const key in prevLayout) { if (!layout.hasOwnProperty(key)) { - map.setLayoutProperty(id, key, undefined); + map.setLayoutProperty(id, key as any, undefined); } } } @@ -51,16 +48,17 @@ function updateLayer( const prevPaint = prevProps.paint || {}; for (const key in paint) { if (!deepEqual(paint[key], prevPaint[key])) { - map.setPaintProperty(id, key, paint[key]); + map.setPaintProperty(id, key as any, paint[key]); } } for (const key in prevPaint) { if (!paint.hasOwnProperty(key)) { - map.setPaintProperty(id, key, undefined); + map.setPaintProperty(id, key as any, undefined); } } } + // @ts-ignore filter does not exist in some Layer types if (!deepEqual(filter, prevProps.filter)) { map.setFilter(id, filter); } @@ -69,14 +67,10 @@ function updateLayer( } } -function createLayer( - map: MapInstance, - id: string, - props: LayerProps -) { +function createLayer(map: MapInstance, id: string, props: LayerProps) { // @ts-ignore if (map.style && map.style._loaded && (!('source' in props) || map.getSource(props.source))) { - const options: LayerProps = {...props, id}; + const options: LayerProps = {...props, id}; delete options.beforeId; // @ts-ignore @@ -88,7 +82,7 @@ function createLayer( let layerCounter = 0; -function Layer(props: LayerProps) { +export function Layer(props: LayerProps) { const map = useContext(MapContext).map.getMap(); const propsRef = useRef(props); const [, setStyleLoaded] = useState(0); @@ -129,5 +123,3 @@ function Layer(props: LayerProps = { - mapLib: MapLib; - map: MapRef; +export type MapContextValue = { + mapLib: MapLib; + map: MapRef; }; export const MapContext = React.createContext(null); -type MapInitOptions = Omit< +type MapInitOptions = Omit< MapOptions, 'style' | 'container' | 'bounds' | 'fitBoundsOptions' | 'center' >; -export type MapProps< - MapOptions, - StyleT extends MapStyle, - CallbacksT extends Callbacks, - MapT extends MapInstance -> = MapInitOptions & - MapboxProps & +export type MapProps = MapInitOptions & + MapboxProps & GlobalSettings & { - mapLib?: MapLib | Promise>; + mapLib?: MapLib | Promise; reuseMaps?: boolean; /** Map container id */ id?: string; @@ -39,29 +34,20 @@ export type MapProps< children?: any; }; -export default function Map< - MapOptions, - StyleT extends MapStyle, - CallbacksT extends Callbacks, - MapT extends MapInstance ->( - props: MapProps, - ref: React.Ref>, - defaultLib: MapLib | Promise> -) { +function _Map(props: MapProps, ref: React.Ref) { const mountedMapsContext = useContext(MountedMapsContext); - const [mapInstance, setMapInstance] = useState>(null); + const [mapInstance, setMapInstance] = useState(null); const containerRef = useRef(); - const {current: contextValue} = useRef>({mapLib: null, map: null}); + const {current: contextValue} = useRef({mapLib: null, map: null}); useEffect(() => { const mapLib = props.mapLib; let isMounted = true; - let mapbox: Mapbox; + let mapbox: Mapbox; - Promise.resolve(mapLib || defaultLib) - .then((module: MapLib | {default: MapLib}) => { + Promise.resolve(mapLib || import('mapbox-gl')) + .then((module: MapLib | {default: MapLib}) => { if (!isMounted) { return; } @@ -98,8 +84,8 @@ export default function Map< onError({ type: 'error', target: null, - originalEvent: null, - error + error, + originalEvent: error }); } else { console.error(error); // eslint-disable-line @@ -153,3 +139,5 @@ export default function Map< ); } + +export const Map = React.forwardRef(_Map); diff --git a/modules/main/src/mapbox-legacy/components/marker.ts b/modules/main/src/mapbox-legacy/components/marker.ts index 08f9a9ed..45406b41 100644 --- a/modules/main/src/mapbox-legacy/components/marker.ts +++ b/modules/main/src/mapbox-legacy/components/marker.ts @@ -4,134 +4,126 @@ import {createPortal} from 'react-dom'; import {useImperativeHandle, useEffect, useMemo, useRef, useContext, forwardRef, memo} from 'react'; import {applyReactStyle} from '../utils/apply-react-style'; -import type {MarkerEvent, MarkerDragEvent, PointLike, MarkerInstance} from '../types'; +import type {PopupInstance, MarkerInstance, MarkerOptions} from '../types/lib'; +import type {MarkerEvent, MarkerDragEvent} from '../types/events'; import {MapContext} from './map'; import {arePointsEqual} from '../utils/deep-equal'; -export type MarkerProps = OptionsT & { +export type MarkerProps = MarkerOptions & { /** Longitude of the anchor location */ longitude: number; /** Latitude of the anchor location */ latitude: number; - // These types will be further constraint by OptionsT - draggable?: boolean; - offset?: PointLike; - pitchAlignment?: string; - rotation?: number; - rotationAlignment?: string; - popup?: any; + popup?: PopupInstance; /** CSS style override, applied to the control's container */ style?: React.CSSProperties; - onClick?: (e: MarkerEvent) => void; - onDragStart?: (e: MarkerDragEvent) => void; - onDrag?: (e: MarkerDragEvent) => void; - onDragEnd?: (e: MarkerDragEvent) => void; + onClick?: (e: MarkerEvent) => void; + onDragStart?: (e: MarkerDragEvent) => void; + onDrag?: (e: MarkerDragEvent) => void; + onDragEnd?: (e: MarkerDragEvent) => void; children?: React.ReactNode; }; /* eslint-disable complexity,max-statements */ -function Marker( - props: MarkerProps, - ref: React.Ref -) { - const {map, mapLib} = useContext(MapContext); - const thisRef = useRef({props}); - thisRef.current.props = props; +export const Marker = memo( + forwardRef((props: MarkerProps, ref: React.Ref) => { + const {map, mapLib} = useContext(MapContext); + const thisRef = useRef({props}); + thisRef.current.props = props; - const marker: MarkerT = useMemo(() => { - let hasChildren = false; - React.Children.forEach(props.children, el => { - if (el) { - hasChildren = true; - } - }); - const options = { - ...props, - element: hasChildren ? document.createElement('div') : null - }; - - const mk = new mapLib.Marker(options) as MarkerT; - mk.setLngLat([props.longitude, props.latitude]); - - mk.getElement().addEventListener('click', (e: MouseEvent) => { - thisRef.current.props.onClick?.({ - type: 'click', - target: mk, - originalEvent: e + const marker: MarkerInstance = useMemo(() => { + let hasChildren = false; + React.Children.forEach(props.children, el => { + if (el) { + hasChildren = true; + } }); - }); + const options = { + ...props, + element: hasChildren ? document.createElement('div') : null + }; - mk.on('dragstart', e => { - const evt = e as MarkerDragEvent; - evt.lngLat = marker.getLngLat(); - thisRef.current.props.onDragStart?.(evt); - }); - mk.on('drag', e => { - const evt = e as MarkerDragEvent; - evt.lngLat = marker.getLngLat(); - thisRef.current.props.onDrag?.(evt); - }); - mk.on('dragend', e => { - const evt = e as MarkerDragEvent; - evt.lngLat = marker.getLngLat(); - thisRef.current.props.onDragEnd?.(evt); - }); + const mk = new mapLib.Marker(options); + mk.setLngLat([props.longitude, props.latitude]); - return mk; - }, []); + mk.getElement().addEventListener('click', (e: MouseEvent) => { + thisRef.current.props.onClick?.({ + type: 'click', + target: mk, + originalEvent: e + }); + }); - useEffect(() => { - marker.addTo(map.getMap()); + mk.on('dragstart', e => { + const evt = e as MarkerDragEvent; + evt.lngLat = marker.getLngLat(); + thisRef.current.props.onDragStart?.(evt); + }); + mk.on('drag', e => { + const evt = e as MarkerDragEvent; + evt.lngLat = marker.getLngLat(); + thisRef.current.props.onDrag?.(evt); + }); + mk.on('dragend', e => { + const evt = e as MarkerDragEvent; + evt.lngLat = marker.getLngLat(); + thisRef.current.props.onDragEnd?.(evt); + }); - return () => { - marker.remove(); - }; - }, []); + return mk; + }, []); - const { - longitude, - latitude, - offset, - style, - draggable = false, - popup = null, - rotation = 0, - rotationAlignment = 'auto', - pitchAlignment = 'auto' - } = props; + useEffect(() => { + marker.addTo(map.getMap()); - useEffect(() => { - applyReactStyle(marker.getElement(), style); - }, [style]); + return () => { + marker.remove(); + }; + }, []); - useImperativeHandle(ref, () => marker, []); + const { + longitude, + latitude, + offset, + style, + draggable = false, + popup = null, + rotation = 0, + rotationAlignment = 'auto', + pitchAlignment = 'auto' + } = props; - if (marker.getLngLat().lng !== longitude || marker.getLngLat().lat !== latitude) { - marker.setLngLat([longitude, latitude]); - } - if (offset && !arePointsEqual(marker.getOffset(), offset)) { - marker.setOffset(offset); - } - if (marker.isDraggable() !== draggable) { - marker.setDraggable(draggable); - } - if (marker.getRotation() !== rotation) { - marker.setRotation(rotation); - } - if (marker.getRotationAlignment() !== rotationAlignment) { - marker.setRotationAlignment(rotationAlignment); - } - if (marker.getPitchAlignment() !== pitchAlignment) { - marker.setPitchAlignment(pitchAlignment); - } - if (marker.getPopup() !== popup) { - marker.setPopup(popup); - } + useEffect(() => { + applyReactStyle(marker.getElement(), style); + }, [style]); - return createPortal(props.children, marker.getElement()); -} + useImperativeHandle(ref, () => marker, []); -export default memo(forwardRef(Marker)); + if (marker.getLngLat().lng !== longitude || marker.getLngLat().lat !== latitude) { + marker.setLngLat([longitude, latitude]); + } + if (offset && !arePointsEqual(marker.getOffset(), offset)) { + marker.setOffset(offset); + } + if (marker.isDraggable() !== draggable) { + marker.setDraggable(draggable); + } + if (marker.getRotation() !== rotation) { + marker.setRotation(rotation); + } + if (marker.getRotationAlignment() !== rotationAlignment) { + marker.setRotationAlignment(rotationAlignment); + } + if (marker.getPitchAlignment() !== pitchAlignment) { + marker.setPitchAlignment(pitchAlignment); + } + if (marker.getPopup() !== popup) { + marker.setPopup(popup); + } + + return createPortal(props.children, marker.getElement()); + }) +); diff --git a/modules/main/src/mapbox-legacy/components/navigation-control.ts b/modules/main/src/mapbox-legacy/components/navigation-control.ts index 3f25f7f2..84b672d5 100644 --- a/modules/main/src/mapbox-legacy/components/navigation-control.ts +++ b/modules/main/src/mapbox-legacy/components/navigation-control.ts @@ -1,29 +1,28 @@ import * as React from 'react'; import {useEffect, memo} from 'react'; import {applyReactStyle} from '../utils/apply-react-style'; -import useControl from './use-control'; +import {useControl} from './use-control'; -import type {ControlPosition, NavigationControlInstance} from '../types'; +import type {ControlPosition, NavigationControlOptions} from '../types/lib'; -export type NavigationControlProps = OptionsT & { +export type NavigationControlProps = NavigationControlOptions & { /** Placement of the control relative to the map. */ position?: ControlPosition; /** CSS style override, applied to the control's container */ style?: React.CSSProperties; }; -function NavigationControl( - props: NavigationControlProps -): null { - const ctrl = useControl(({mapLib}) => new mapLib.NavigationControl(props) as ControlT, { +function _NavigationControl(props: NavigationControlProps) { + const ctrl = useControl(({mapLib}) => new mapLib.NavigationControl(props), { position: props.position }); useEffect(() => { + // @ts-expect-error accessing private member applyReactStyle(ctrl._container, props.style); }, [props.style]); return null; } -export default memo(NavigationControl); +export const NavigationControl = memo(_NavigationControl); diff --git a/modules/main/src/mapbox-legacy/components/popup.ts b/modules/main/src/mapbox-legacy/components/popup.ts index a2e38e95..58db1815 100644 --- a/modules/main/src/mapbox-legacy/components/popup.ts +++ b/modules/main/src/mapbox-legacy/components/popup.ts @@ -4,28 +4,23 @@ import {createPortal} from 'react-dom'; import {useImperativeHandle, useEffect, useMemo, useRef, useContext, forwardRef, memo} from 'react'; import {applyReactStyle} from '../utils/apply-react-style'; -import type {PopupEvent, PopupInstance} from '../types'; +import type {PopupInstance as _PopupInstance, PopupOptions} from '../types/lib'; +import type {PopupEvent} from '../types/events'; import {MapContext} from './map'; import {deepEqual} from '../utils/deep-equal'; -export type PopupProps = OptionsT & { +export type PopupProps = PopupOptions & { /** Longitude of the anchor location */ longitude: number; /** Latitude of the anchor location */ latitude: number; - // These types will be further constraint by OptionsT - anchor?: string; - offset?: any; - className?: string; - maxWidth?: string; - /** CSS style override, applied to the control's container */ style?: React.CSSProperties; - onOpen?: (e: PopupEvent) => void; - onClose?: (e: PopupEvent) => void; + onOpen?: (e: PopupEvent) => void; + onClose?: (e: PopupEvent) => void; children?: React.ReactNode; }; @@ -34,83 +29,84 @@ function getClassList(className: string) { return new Set(className ? className.trim().split(/\s+/) : []); } +type PopupInstance = _PopupInstance & { + options: PopupOptions; +}; + /* eslint-disable complexity,max-statements */ -function Popup( - props: PopupProps, - ref: React.Ref -) { - const {map, mapLib} = useContext(MapContext); - const container = useMemo(() => { - return document.createElement('div'); - }, []); - const thisRef = useRef({props}); - thisRef.current.props = props; +export const Popup = memo( + forwardRef((props: PopupProps, ref: React.Ref) => { + const {map, mapLib} = useContext(MapContext); + const container = useMemo(() => { + return document.createElement('div'); + }, []); + const thisRef = useRef({props}); + thisRef.current.props = props; - const popup: PopupT = useMemo(() => { - const options = {...props}; - const pp = new mapLib.Popup(options) as PopupT; - pp.setLngLat([props.longitude, props.latitude]); - pp.once('open', e => { - thisRef.current.props.onOpen?.(e as PopupEvent); - }); - return pp; - }, []); + const popup = useMemo(() => { + const options = {...props}; + const pp = new mapLib.Popup(options); + pp.setLngLat([props.longitude, props.latitude]); + pp.once('open', e => { + thisRef.current.props.onOpen?.(e as PopupEvent); + }); + return pp as PopupInstance; + }, []); - useEffect(() => { - const onClose = e => { - thisRef.current.props.onClose?.(e as PopupEvent); - }; - popup.on('close', onClose); - popup.setDOMContent(container).addTo(map.getMap()); + useEffect(() => { + const onClose = e => { + thisRef.current.props.onClose?.(e as PopupEvent); + }; + popup.on('close', onClose); + popup.setDOMContent(container).addTo(map.getMap()); - return () => { - // https://github.com/visgl/react-map-gl/issues/1825 - // onClose should not be fired if the popup is removed by unmounting - // When using React strict mode, the component is mounted twice. - // Firing the onClose callback here would be a false signal to remove the component. - popup.off('close', onClose); - if (popup.isOpen()) { - popup.remove(); - } - }; - }, []); - - useEffect(() => { - applyReactStyle(popup.getElement(), props.style); - }, [props.style]); - - useImperativeHandle(ref, () => popup, []); - - if (popup.isOpen()) { - if (popup.getLngLat().lng !== props.longitude || popup.getLngLat().lat !== props.latitude) { - popup.setLngLat([props.longitude, props.latitude]); - } - if (props.offset && !deepEqual(popup.options.offset, props.offset)) { - popup.setOffset(props.offset); - } - if (popup.options.anchor !== props.anchor || popup.options.maxWidth !== props.maxWidth) { - popup.options.anchor = props.anchor; - popup.setMaxWidth(props.maxWidth); - } - if (popup.options.className !== props.className) { - const prevClassList = getClassList(popup.options.className); - const nextClassList = getClassList(props.className); - - for (const c of prevClassList) { - if (!nextClassList.has(c)) { - popup.removeClassName(c); + return () => { + // https://github.com/visgl/react-map-gl/issues/1825 + // onClose should not be fired if the popup is removed by unmounting + // When using React strict mode, the component is mounted twice. + // Firing the onClose callback here would be a false signal to remove the component. + popup.off('close', onClose); + if (popup.isOpen()) { + popup.remove(); } + }; + }, []); + + useEffect(() => { + applyReactStyle(popup.getElement(), props.style); + }, [props.style]); + + useImperativeHandle(ref, () => popup, []); + + if (popup.isOpen()) { + if (popup.getLngLat().lng !== props.longitude || popup.getLngLat().lat !== props.latitude) { + popup.setLngLat([props.longitude, props.latitude]); } - for (const c of nextClassList) { - if (!prevClassList.has(c)) { - popup.addClassName(c); + if (props.offset && !deepEqual(popup.options.offset, props.offset)) { + popup.setOffset(props.offset); + } + if (popup.options.anchor !== props.anchor || popup.options.maxWidth !== props.maxWidth) { + popup.options.anchor = props.anchor; + popup.setMaxWidth(props.maxWidth); + } + if (popup.options.className !== props.className) { + const prevClassList = getClassList(popup.options.className); + const nextClassList = getClassList(props.className); + + for (const c of prevClassList) { + if (!nextClassList.has(c)) { + popup.removeClassName(c); + } } + for (const c of nextClassList) { + if (!prevClassList.has(c)) { + popup.addClassName(c); + } + } + popup.options.className = props.className; } - popup.options.className = props.className; } - } - return createPortal(props.children, container); -} - -export default memo(forwardRef(Popup)); + return createPortal(props.children, container); + }) +); diff --git a/modules/main/src/mapbox-legacy/components/scale-control.ts b/modules/main/src/mapbox-legacy/components/scale-control.ts index b5039cce..304f8f30 100644 --- a/modules/main/src/mapbox-legacy/components/scale-control.ts +++ b/modules/main/src/mapbox-legacy/components/scale-control.ts @@ -1,28 +1,22 @@ import * as React from 'react'; import {useEffect, useRef, memo} from 'react'; import {applyReactStyle} from '../utils/apply-react-style'; -import useControl from './use-control'; +import {useControl} from './use-control'; -import type {ControlPosition, ScaleControlInstance} from '../types'; - -export type ScaleControlProps = OptionsT & { - // These props will be further constraint by OptionsT - unit?: string; - maxWidth?: number; +import type {ControlPosition, ScaleControlOptions} from '../types/lib'; +export type ScaleControlProps = ScaleControlOptions & { /** Placement of the control relative to the map. */ position?: ControlPosition; /** CSS style override, applied to the control's container */ style?: React.CSSProperties; }; -function ScaleControl( - props: ScaleControlProps -): null { - const ctrl = useControl(({mapLib}) => new mapLib.ScaleControl(props) as ControlT, { +function _ScaleControl(props: ScaleControlProps) { + const ctrl = useControl(({mapLib}) => new mapLib.ScaleControl(props), { position: props.position }); - const propsRef = useRef>(props); + const propsRef = useRef(props); const prevProps = propsRef.current; propsRef.current = props; @@ -30,17 +24,19 @@ function ScaleControl { + // @ts-expect-error accessing private member applyReactStyle(ctrl._container, style); }, [style]); return null; } -export default memo(ScaleControl); +export const ScaleControl = memo(_ScaleControl); diff --git a/modules/main/src/mapbox-legacy/components/source.ts b/modules/main/src/mapbox-legacy/components/source.ts index 16d07716..27e74552 100644 --- a/modules/main/src/mapbox-legacy/components/source.ts +++ b/modules/main/src/mapbox-legacy/components/source.ts @@ -5,27 +5,21 @@ import assert from '../utils/assert'; import {deepEqual} from '../utils/deep-equal'; import type { - MapInstance, - ISource, - CustomSource, GeoJSONSourceImplementation, ImageSourceImplemtation, AnySourceImplementation -} from '../types'; -import type {GeoJSONSourceRaw, ImageSourceRaw, VectorSourceRaw} from '../types/style-spec-mapbox'; +} from '../types/internal'; +import type {AnySource, ImageSource, VectorSource} from '../types/style-spec'; +import type {MapInstance} from '../types/lib'; -export type SourceProps = (SourceT | CustomSource) & { +export type SourceProps = AnySource & { id?: string; children?: any; }; let sourceCounter = 0; -function createSource( - map: MapInstance, - id: string, - props: SourceProps -) { +function createSource(map: MapInstance, id: string, props: SourceProps) { // @ts-ignore if (map.style && map.style._loaded) { const options = {...props}; @@ -39,11 +33,7 @@ function createSource( } /* eslint-disable complexity */ -function updateSource( - source: AnySourceImplementation, - props: SourceProps, - prevProps: SourceProps -) { +function updateSource(source: AnySourceImplementation, props: SourceProps, prevProps: SourceProps) { assert(props.id === prevProps.id, 'source id changed'); assert(props.type === prevProps.type, 'source type changed'); @@ -64,20 +54,18 @@ function updateSource( const type = props.type; if (type === 'geojson') { - (source as GeoJSONSourceImplementation).setData( - (props as unknown as GeoJSONSourceRaw).data as any - ); + (source as GeoJSONSourceImplementation).setData(props.data as any); } else if (type === 'image') { (source as ImageSourceImplemtation).updateImage({ - url: (props as unknown as ImageSourceRaw).url, - coordinates: (props as unknown as ImageSourceRaw).coordinates + url: props.url, + coordinates: props.coordinates }); } else if ('setCoordinates' in source && changedKeyCount === 1 && changedKey === 'coordinates') { - source.setCoordinates((props as ImageSourceRaw).coordinates); + source.setCoordinates((props as unknown as ImageSource).coordinates); } else if ('setUrl' in source && changedKey === 'url') { - source.setUrl((props as VectorSourceRaw).url); + source.setUrl((props as VectorSource).url); } else if ('setTiles' in source && changedKey === 'tiles') { - source.setTiles((props as VectorSourceRaw).tiles); + source.setTiles((props as VectorSource).tiles); } else { // eslint-disable-next-line console.warn(`Unable to update prop: ${changedKey}`); @@ -85,7 +73,7 @@ function updateSource( } /* eslint-enable complexity */ -function Source(props: SourceProps) { +export function Source(props: SourceProps) { const map = useContext(MapContext).map.getMap(); const propsRef = useRef(props); const [, setStyleLoaded] = useState(0); @@ -144,5 +132,3 @@ function Source(props: SourceProps) { null ); } - -export default Source; diff --git a/modules/main/src/mapbox-legacy/components/use-control.ts b/modules/main/src/mapbox-legacy/components/use-control.ts index c5e3013f..aa06fff4 100644 --- a/modules/main/src/mapbox-legacy/components/use-control.ts +++ b/modules/main/src/mapbox-legacy/components/use-control.ts @@ -1,5 +1,5 @@ import {useContext, useMemo, useEffect} from 'react'; -import type {IControl, ControlPosition} from '../types'; +import type {IControl, ControlPosition} from '../types/lib'; import {MapContext} from './map'; import type {MapContextValue} from './map'; @@ -7,25 +7,25 @@ type ControlOptions = { position?: ControlPosition; }; -function useControl( +export function useControl( onCreate: (context: MapContextValue) => T, opts?: ControlOptions ): T; -function useControl( +export function useControl( onCreate: (context: MapContextValue) => T, onRemove: (context: MapContextValue) => void, opts?: ControlOptions ): T; -function useControl( +export function useControl( onCreate: (context: MapContextValue) => T, onAdd: (context: MapContextValue) => void, onRemove: (context: MapContextValue) => void, opts?: ControlOptions ): T; -function useControl( +export function useControl( onCreate: (context: MapContextValue) => T, arg1?: ((context: MapContextValue) => void) | ControlOptions, arg2?: ((context: MapContextValue) => void) | ControlOptions, @@ -60,5 +60,3 @@ function useControl( return ctrl; } - -export default useControl; diff --git a/modules/main/src/mapbox-legacy/components/use-map.tsx b/modules/main/src/mapbox-legacy/components/use-map.tsx index e7f8b64b..65353fef 100644 --- a/modules/main/src/mapbox-legacy/components/use-map.tsx +++ b/modules/main/src/mapbox-legacy/components/use-map.tsx @@ -3,20 +3,19 @@ import {useState, useCallback, useMemo, useContext} from 'react'; import {MapRef} from '../mapbox/create-ref'; import {MapContext} from './map'; -import {MapInstance} from '../types'; type MountedMapsContextValue = { - maps: {[id: string]: MapRef}; - onMapMount: (map: MapRef, id: string) => void; + maps: {[id: string]: MapRef}; + onMapMount: (map: MapRef, id: string) => void; onMapUnmount: (id: string) => void; }; export const MountedMapsContext = React.createContext(null); export const MapProvider: React.FC<{children?: React.ReactNode}> = props => { - const [maps, setMaps] = useState<{[id: string]: MapRef}>({}); + const [maps, setMaps] = useState<{[id: string]: MapRef}>({}); - const onMapMount = useCallback((map: MapRef, id: string = 'default') => { + const onMapMount = useCallback((map: MapRef, id: string = 'default') => { setMaps(currMaps => { if (id === 'current') { throw new Error("'current' cannot be used as map id"); @@ -52,12 +51,12 @@ export const MapProvider: React.FC<{children?: React.ReactNode}> = props => { ); }; -export type MapCollection = { - [id: string]: MapRef | undefined; - current?: MapRef; +export type MapCollection = { + [id: string]: MapRef | undefined; + current?: MapRef; }; -export function useMap(): MapCollection { +export function useMap(): MapCollection { const maps = useContext(MountedMapsContext)?.maps; const currentMap = useContext(MapContext); @@ -65,5 +64,5 @@ export function useMap(): MapCollection { return {...maps, current: currentMap?.map}; }, [maps, currentMap]); - return mapsWithCurrent as MapCollection; + return mapsWithCurrent as MapCollection; } diff --git a/modules/main/src/mapbox-legacy/exports-mapbox.ts b/modules/main/src/mapbox-legacy/exports-mapbox.ts deleted file mode 100644 index 64d5d737..00000000 --- a/modules/main/src/mapbox-legacy/exports-mapbox.ts +++ /dev/null @@ -1,159 +0,0 @@ -import * as React from 'react'; -import type { - Map as MapboxMap, - MapboxOptions, - Marker as MapboxMarker, - MarkerOptions, - Popup as MapboxPopup, - PopupOptions, - AttributionControl as MapboxAttributionControl, - FullscreenControl as MapboxFullscreenControl, - GeolocateControl as MapboxGeolocateControl, - NavigationControl as MapboxNavigationControl, - ScaleControl as MapboxScaleControl -} from 'mapbox-gl'; -import {MapStyle, AnyLayer, AnySource} from './types/style-spec-mapbox'; - -import {default as _Map, MapProps as _MapProps} from './components/map'; -import {default as _Marker, MarkerProps as _MarkerProps} from './components/marker'; -import {default as _Popup, PopupProps as _PopupProps} from './components/popup'; -import { - default as _AttributionControl, - AttributionControlProps as _AttributionControlProps -} from './components/attribution-control'; -import { - default as _FullscreenControl, - FullscreenControlProps as _FullscreenControlProps -} from './components/fullscreen-control'; -import { - default as _GeolocateControl, - GeolocateControlProps as _GeolocateControlProps -} from './components/geolocate-control'; -import { - default as _NavigationControl, - NavigationControlProps as _NavigationControlProps -} from './components/navigation-control'; -import { - default as _ScaleControl, - ScaleControlProps as _ScaleControlProps -} from './components/scale-control'; -import {default as _Layer, LayerProps as _LayerProps} from './components/layer'; -import {default as _Source, SourceProps as _SourceProps} from './components/source'; -import {useMap as _useMap} from './components/use-map'; -import type {MapRef as _MapRef} from './mapbox/create-ref'; -import type * as events from './types/events'; -import type {MapCallbacks} from './types/events-mapbox'; - -export function useMap() { - return _useMap(); -} - -export type MapProps = _MapProps; -export type MapRef = _MapRef; -const mapLib = import('mapbox-gl'); -export const Map = (() => { - return React.forwardRef(function Map(props: MapProps, ref: React.Ref) { - return _Map(props, ref, mapLib); - }); -})(); - -export type MarkerProps = _MarkerProps; -export const Marker = _Marker as ( - props: MarkerProps & React.RefAttributes -) => React.ReactElement | null; - -export type PopupProps = _PopupProps; -export const Popup = _Popup as ( - props: PopupProps & React.RefAttributes -) => React.ReactElement | null; - -type AttributionControlOptions = ConstructorParameters[0]; -export type AttributionControlProps = _AttributionControlProps; -export const AttributionControl = _AttributionControl as ( - props: AttributionControlProps -) => React.ReactElement | null; - -type FullscreenControlOptions = ConstructorParameters[0]; -export type FullscreenControlProps = _FullscreenControlProps; -export const FullscreenControl = _FullscreenControl as ( - props: FullscreenControlProps -) => React.ReactElement | null; - -type NavigationControlOptions = ConstructorParameters[0]; -export type NavigationControlProps = _NavigationControlProps; -export const NavigationControl = _NavigationControl as ( - props: NavigationControlProps -) => React.ReactElement | null; - -type GeolocateControlOptions = ConstructorParameters[0]; -export type GeolocateControlProps = _GeolocateControlProps< - GeolocateControlOptions, - MapboxGeolocateControl ->; -export const GeolocateControl = _GeolocateControl as ( - props: GeolocateControlProps & React.RefAttributes -) => React.ReactElement | null; - -type ScaleControlOptions = ConstructorParameters[0]; -export type ScaleControlProps = _ScaleControlProps; -export const ScaleControl = _ScaleControl as ( - props: ScaleControlProps -) => React.ReactElement | null; - -export type LayerProps = _LayerProps; -export const Layer = _Layer as (props: LayerProps) => React.ReactElement | null; - -export type SourceProps = _SourceProps; -export const Source = _Source as (props: SourceProps) => React.ReactElement | null; - -export {default as useControl} from './components/use-control'; -export {MapProvider} from './components/use-map'; - -export default Map; - -// Types -export * from './types/public'; -export type { - Point, - PointLike, - LngLat, - LngLatLike, - LngLatBounds, - LngLatBoundsLike, - PaddingOptions, - MapboxGeoJSONFeature as MapGeoJSONFeature, - GeoJSONSource, - VideoSource, - ImageSource, - CanvasSource, - VectorSourceImpl as VectorTileSource -} from 'mapbox-gl'; -export * from './types/style-spec-mapbox'; - -// Events -export type { - MapEvent, - MapMouseEvent, - MapLayerMouseEvent, - MapTouchEvent, - MapLayerTouchEvent, - MapStyleDataEvent, - MapSourceDataEvent, - MapWheelEvent, - MapBoxZoomEvent, - ErrorEvent, - ViewStateChangeEvent -} from './types/events-mapbox'; -export type PopupEvent = events.PopupEvent; -export type MarkerEvent = events.MarkerEvent; -export type MarkerDragEvent = events.MarkerDragEvent; -export type GeolocateEvent = events.GeolocateEvent; -export type GeolocateResultEvent = events.GeolocateResultEvent; -export type GeolocateErrorEvent = events.GeolocateErrorEvent; - -// v7.0 backward compatibility - -/** @deprecated use `MapStyle` */ -export type MapboxStyle = MapStyle; - -export type {Map as MapboxMap, MapboxEvent, MapboxGeoJSONFeature} from 'mapbox-gl'; diff --git a/modules/main/src/mapbox-legacy/index.ts b/modules/main/src/mapbox-legacy/index.ts index 0a9a29f5..e25d0182 100644 --- a/modules/main/src/mapbox-legacy/index.ts +++ b/modules/main/src/mapbox-legacy/index.ts @@ -1,2 +1,33 @@ -export * from './exports-mapbox'; -export {default as default} from './exports-mapbox'; +import {Map} from './components/map'; +export {Map}; +export default Map; + +export {Marker} from './components/marker'; +export {Popup} from './components/popup'; +export {AttributionControl} from './components/attribution-control'; +export {FullscreenControl} from './components/fullscreen-control'; +export {GeolocateControl} from './components/geolocate-control'; +export {NavigationControl} from './components/navigation-control'; +export {ScaleControl} from './components/scale-control'; +export {Source} from './components/source'; +export {Layer} from './components/layer'; +export {useControl} from './components/use-control'; +export {MapProvider, useMap} from './components/use-map'; + +export type {MapProps} from './components/map'; +export type {MapRef} from './mapbox/create-ref'; +export type {MarkerProps} from './components/marker'; +export type {PopupProps} from './components/popup'; +export type {AttributionControlProps} from './components/attribution-control'; +export type {FullscreenControlProps} from './components/fullscreen-control'; +export type {GeolocateControlProps} from './components/geolocate-control'; +export type {NavigationControlProps} from './components/navigation-control'; +export type {ScaleControlProps} from './components/scale-control'; +export type {SourceProps} from './components/source'; +export type {LayerProps} from './components/layer'; + +// Types +export * from './types/common'; +export * from './types/events'; +export * from './types/lib'; +export * from './types/style-spec'; diff --git a/modules/main/src/mapbox-legacy/mapbox/create-ref.ts b/modules/main/src/mapbox-legacy/mapbox/create-ref.ts index 21d5b6c8..f405cc81 100644 --- a/modules/main/src/mapbox-legacy/mapbox/create-ref.ts +++ b/modules/main/src/mapbox-legacy/mapbox/create-ref.ts @@ -1,11 +1,7 @@ -import type { - MapInstance, - MapInstanceInternal, - MapStyle, - Callbacks, - LngLatLike, - PointLike -} from '../types'; +import type {MapInstance} from '../types/lib'; +import type {MapInstanceInternal} from '../types/internal'; +import {LngLatLike, PointLike} from '../types/common'; + import type Mapbox from './mapbox'; /** These methods may break the react binding if called directly */ @@ -32,22 +28,18 @@ const skipMethods = [ 'remove' ] as const; -export type MapRef = { - getMap(): MapT; -} & Omit; +export type MapRef = { + getMap(): MapInstance; +} & Omit; -export default function createRef< - StyleT extends MapStyle, - CallbacksT extends Callbacks, - MapT extends MapInstance ->(mapInstance: Mapbox): MapRef | null { +export default function createRef(mapInstance: Mapbox): MapRef | null { if (!mapInstance) { return null; } - const map = mapInstance.map as MapInstanceInternal; + const map = mapInstance.map as MapInstanceInternal; const ref: any = { - getMap: () => map, + getMap: () => mapInstance.map, // Overwrite getters to use our shadow transform getCenter: () => mapInstance.transform.center, diff --git a/modules/main/src/mapbox-legacy/mapbox/mapbox.ts b/modules/main/src/mapbox-legacy/mapbox/mapbox.ts index 11035341..8e590f0c 100644 --- a/modules/main/src/mapbox-legacy/mapbox/mapbox.ts +++ b/modules/main/src/mapbox-legacy/mapbox/mapbox.ts @@ -8,29 +8,27 @@ import {normalizeStyle} from '../utils/style-utils'; import {deepEqual} from '../utils/deep-equal'; import type { - Transform, ViewState, - ViewStateChangeEvent, Point, PointLike, PaddingOptions, - MapStyle, ImmutableLike, LngLatBoundsLike, - Callbacks, + MapGeoJSONFeature +} from '../types/common'; +import type {MapStyle, Light, Terrain, Fog, Projection} from '../types/style-spec'; +import type {MapInstance} from '../types/lib'; +import type {Transform, MapInstanceInternal} from '../types/internal'; +import type { + MapCallbacks, + ViewStateChangeEvent, MapEvent, ErrorEvent, - MapMouseEvent, - MapGeoJSONFeature, - MapInstance, - MapInstanceInternal -} from '../types'; + MapMouseEvent +} from '../types/events'; -export type MapboxProps< - StyleT extends MapStyle = MapStyle, - CallbacksT extends Callbacks = {} -> = Partial & - CallbacksT & { +export type MapboxProps = Partial & + MapCallbacks & { // Init options mapboxAccessToken?: string; @@ -59,19 +57,23 @@ export type MapboxProps< // Styling /** Mapbox style */ - mapStyle?: string | StyleT | ImmutableLike; + mapStyle?: string | MapStyle | ImmutableLike; /** Enable diffing when the map style changes * @default true */ styleDiffing?: boolean; + /** The projection property of the style. Must conform to the Projection Style Specification. + * @default 'mercator' + */ + projection?: Projection; /** The fog property of the style. Must conform to the Fog Style Specification . * If `undefined` is provided, removes the fog from the map. */ - fog?: StyleT['fog']; + fog?: Fog; /** Light properties of the map. */ - light?: StyleT['light']; + light?: Light; /** Terrain property of the style. Must conform to the Terrain Style Specification . * If `undefined` is provided, removes terrain from the map. */ - terrain?: StyleT['terrain']; + terrain?: Terrain; /** Default layers to query on pointer events */ interactiveLayerIds?: string[]; @@ -152,16 +154,12 @@ const handlerNames = [ /** * A wrapper for mapbox-gl's Map class */ -export default class Mapbox< - StyleT extends MapStyle = MapStyle, - CallbacksT extends Callbacks = {}, - MapT extends MapInstance = MapInstance -> { +export default class Mapbox { private _MapClass: {new (options: any): MapInstance}; // mapboxgl.Map instance - private _map: MapInstanceInternal = null; + private _map: MapInstanceInternal = null; // User-supplied props - props: MapboxProps; + props: MapboxProps; // Mapbox map is stateful. // During method calls/user interactions, map.transform is mutated and @@ -191,7 +189,7 @@ export default class Mapbox< constructor( MapClass: {new (options: any): MapInstance}, - props: MapboxProps, + props: MapboxProps, container: HTMLDivElement ) { this._MapClass = MapClass; @@ -199,7 +197,7 @@ export default class Mapbox< this._initialize(container); } - get map(): MapT { + get map(): MapInstance { return this._map; } @@ -207,7 +205,7 @@ export default class Mapbox< return this._renderTransform; } - setProps(props: MapboxProps) { + setProps(props: MapboxProps) { const oldProps = this.props; this.props = props; @@ -229,11 +227,8 @@ export default class Mapbox< } } - static reuse( - props: MapboxProps, - container: HTMLDivElement - ): Mapbox { - const that = Mapbox.savedMaps.pop() as Mapbox; + static reuse(props: MapboxProps, container: HTMLDivElement): Mapbox { + const that = Mapbox.savedMaps.pop(); if (!that) { return null; } @@ -251,16 +246,6 @@ export default class Mapbox< // @ts-ignore map._container = container; - // With maplibre-gl as mapLib, map uses ResizeObserver to observe when its container resizes. - // When reusing the saved map, we need to disconnect the observer and observe the new container. - // Step 3: telling the ResizeObserver to disconnect and observe the new container - // @ts-ignore - const resizeObserver = map._resizeObserver; - if (resizeObserver) { - resizeObserver.disconnect(); - resizeObserver.observe(container); - } - // Step 4: apply new props that.setProps({...props, styleDiffing: false}); map.resize(); @@ -319,7 +304,7 @@ export default class Mapbox< }; } - const map = new this._MapClass(mapOptions) as MapInstanceInternal; + const map = new this._MapClass(mapOptions) as MapInstanceInternal; // Props that are not part of constructor options if (viewState.padding) { map.setPadding(viewState.padding); @@ -331,12 +316,14 @@ export default class Mapbox< // Hack // Insert code into map's render cycle + // eslint-disable-next-line @typescript-eslint/unbound-method const renderMap = map._render; map._render = (arg: number) => { this._inRender = true; renderMap.call(map, arg); this._inRender = false; }; + // eslint-disable-next-line @typescript-eslint/unbound-method const runRenderTaskQueue = map._renderTaskQueue.run; map._renderTaskQueue.run = (arg: number) => { runRenderTaskQueue.call(map._renderTaskQueue, arg); @@ -414,7 +401,7 @@ export default class Mapbox< @param {object} nextProps @returns {bool} true if size has changed */ - _updateSize(nextProps: MapboxProps): boolean { + _updateSize(nextProps: MapboxProps): boolean { // Check if size is controlled const {viewState} = nextProps; if (viewState) { @@ -433,7 +420,7 @@ export default class Mapbox< @param {bool} triggerEvents - should fire camera events @returns {bool} true if anything is changed */ - _updateViewState(nextProps: MapboxProps, triggerEvents: boolean): boolean { + _updateViewState(nextProps: MapboxProps, triggerEvents: boolean): boolean { if (this._internalUpdate) { return false; } @@ -480,7 +467,7 @@ export default class Mapbox< @param {object} currProps @returns {bool} true if anything is changed */ - _updateSettings(nextProps: MapboxProps, currProps: MapboxProps): boolean { + _updateSettings(nextProps: MapboxProps, currProps: MapboxProps): boolean { const map = this._map; let changed = false; for (const propName of settingNames) { @@ -498,7 +485,7 @@ export default class Mapbox< @param {object} currProps @returns {bool} true if style is changed */ - _updateStyle(nextProps: MapboxProps, currProps: MapboxProps): boolean { + _updateStyle(nextProps: MapboxProps, currProps: MapboxProps): boolean { if (nextProps.cursor !== currProps.cursor) { this._map.getCanvas().style.cursor = nextProps.cursor || ''; } @@ -522,7 +509,7 @@ export default class Mapbox< @param {object} currProps @returns {bool} true if anything is changed */ - _updateStyleComponents(nextProps: MapboxProps, currProps: MapboxProps): boolean { + _updateStyleComponents(nextProps: MapboxProps, currProps: MapboxProps): boolean { const map = this._map; let changed = false; if (map.isStyleLoaded()) { @@ -553,7 +540,7 @@ export default class Mapbox< @param {object} currProps @returns {bool} true if anything is changed */ - _updateHandlers(nextProps: MapboxProps, currProps: MapboxProps): boolean { + _updateHandlers(nextProps: MapboxProps, currProps: MapboxProps): boolean { const map = this._map; let changed = false; for (const propName of handlerNames) { @@ -571,13 +558,13 @@ export default class Mapbox< return changed; } - _onEvent = (e: MapEvent) => { + _onEvent = (e: MapEvent) => { // @ts-ignore const cb = this.props[otherEvents[e.type]]; if (cb) { cb(e); } else if (e.type === 'error') { - console.error((e as ErrorEvent).error); // eslint-disable-line + console.error((e as ErrorEvent).error); // eslint-disable-line } }; @@ -598,7 +585,7 @@ export default class Mapbox< } } - _updateHover(e: MapMouseEvent) { + _updateHover(e: MapMouseEvent) { const {props} = this; const shouldTrackHoveredFeatures = props.interactiveLayerIds && (props.onMouseMove || props.onMouseEnter || props.onMouseLeave); @@ -624,7 +611,7 @@ export default class Mapbox< } } - _onPointerEvent = (e: MapMouseEvent) => { + _onPointerEvent = (e: MapMouseEvent) => { if (e.type === 'mousemove' || e.type === 'mouseout') { this._updateHover(e); } @@ -640,7 +627,7 @@ export default class Mapbox< } }; - _onCameraEvent = (e: ViewStateChangeEvent) => { + _onCameraEvent = (e: ViewStateChangeEvent) => { if (!this._internalUpdate) { // @ts-ignore const cb = this.props[cameraEvents[e.type]]; @@ -653,7 +640,7 @@ export default class Mapbox< } }; - _fireEvent(baseFire: Function, event: string | MapEvent, properties?: object) { + _fireEvent(baseFire: Function, event: string | MapEvent, properties?: object) { const map = this._map; const tr = map.transform; @@ -663,7 +650,7 @@ export default class Mapbox< } if (eventType in cameraEvents) { if (typeof event === 'object') { - (event as unknown as ViewStateChangeEvent).viewState = transformToViewState(tr); + (event as unknown as ViewStateChangeEvent).viewState = transformToViewState(tr); } if (this._map.isMoving()) { // Replace map.transform with ours during the callbacks diff --git a/modules/main/src/mapbox-legacy/types/common.ts b/modules/main/src/mapbox-legacy/types/common.ts index a44414c1..44c42216 100644 --- a/modules/main/src/mapbox-legacy/types/common.ts +++ b/modules/main/src/mapbox-legacy/types/common.ts @@ -1,73 +1,15 @@ -import type GeoJSON from 'geojson'; +import type {PaddingOptions} from 'mapbox-gl'; -/* Data types that are compatible with both mapbox and maplibre. Not exposed to the end user. */ - -/** @mapbox/point-geometry */ -export interface Point { - x: number; - y: number; -} -export type PointLike = Point | [number, number]; - -export interface LngLat { - lng: number; - lat: number; - - wrap(): LngLat; - /** Return a LngLat as an array */ - toArray(): number[]; - /** Return a LngLat as a string */ - toString(): string; - /** Returns the approximate distance between a pair of coordinates in meters - * Uses the Haversine Formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159) */ - distanceTo(lngLat: LngLat): number; -} -export type LngLatLike = - | [number, number] - | LngLat - | {lng: number; lat: number} - | {lon: number; lat: number}; - -export interface LngLatBounds { - contains(lnglat: LngLatLike): boolean; - setNorthEast(ne: LngLatLike): this; - setSouthWest(sw: LngLatLike): this; - extend(obj: LngLatLike | LngLatBoundsLike): this; - - getCenter(): LngLat; - getSouthWest(): LngLat; - getNorthEast(): LngLat; - getNorthWest(): LngLat; - getSouthEast(): LngLat; - - getWest(): number; - getSouth(): number; - getEast(): number; - getNorth(): number; - - toArray(): number[][]; - toString(): string; - isEmpty(): boolean; -} -export type LngLatBoundsLike = - | LngLatBounds - | [LngLatLike, LngLatLike] - | [number, number, number, number] - | LngLatLike; - -export type MapGeoJSONFeature = GeoJSON.Feature & { - layer: any; - source: string; - sourceLayer: string; - state: {[key: string]: any}; -}; - -export type PaddingOptions = { - top: number; - bottom: number; - left: number; - right: number; -}; +export type { + Point, + PointLike, + LngLat, + LngLatLike, + LngLatBounds, + LngLatBoundsLike, + PaddingOptions, + MapboxGeoJSONFeature as MapGeoJSONFeature +} from 'mapbox-gl'; /* Public */ @@ -87,8 +29,6 @@ export type ViewState = { padding: PaddingOptions; }; -export type ControlPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'; - export interface ImmutableLike { toJS: () => T; } diff --git a/modules/main/src/mapbox-legacy/types/events-mapbox.ts b/modules/main/src/mapbox-legacy/types/events-mapbox.ts deleted file mode 100644 index 44b3b90a..00000000 --- a/modules/main/src/mapbox-legacy/types/events-mapbox.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { - Map, - MapboxEvent as MapEvent, - MapMouseEvent, - MapLayerMouseEvent, - MapTouchEvent, - MapLayerTouchEvent, - MapStyleDataEvent, - MapSourceDataEvent, - MapWheelEvent, - MapBoxZoomEvent -} from 'mapbox-gl'; -import {ErrorEvent as _ErrorEvent, ViewStateChangeEvent as _ViewStateChangeEvent} from './events'; - -export type { - MapEvent, - MapMouseEvent, - MapLayerMouseEvent, - MapTouchEvent, - MapLayerTouchEvent, - MapStyleDataEvent, - MapSourceDataEvent, - MapWheelEvent, - MapBoxZoomEvent -}; - -export type ErrorEvent = _ErrorEvent; -export type ViewStateChangeEvent = _ViewStateChangeEvent; - -export type MapCallbacks = { - onMouseDown?: (e: MapLayerMouseEvent) => void; - onMouseUp?: (e: MapLayerMouseEvent) => void; - onMouseOver?: (e: MapLayerMouseEvent) => void; - onMouseMove?: (e: MapLayerMouseEvent) => void; - onClick?: (e: MapLayerMouseEvent) => void; - onDblClick?: (e: MapLayerMouseEvent) => void; - onMouseEnter?: (e: MapLayerMouseEvent) => void; - onMouseLeave?: (e: MapLayerMouseEvent) => void; - onMouseOut?: (e: MapLayerMouseEvent) => void; - onContextMenu?: (e: MapLayerMouseEvent) => void; - onTouchStart?: (e: MapLayerTouchEvent) => void; - onTouchEnd?: (e: MapLayerTouchEvent) => void; - onTouchMove?: (e: MapLayerTouchEvent) => void; - onTouchCancel?: (e: MapLayerTouchEvent) => void; - - onMoveStart?: (e: ViewStateChangeEvent) => void; - onMove?: (e: ViewStateChangeEvent) => void; - onMoveEnd?: (e: ViewStateChangeEvent) => void; - onDragStart?: (e: ViewStateChangeEvent) => void; - onDrag?: (e: ViewStateChangeEvent) => void; - onDragEnd?: (e: ViewStateChangeEvent) => void; - onZoomStart?: (e: ViewStateChangeEvent) => void; - onZoom?: (e: ViewStateChangeEvent) => void; - onZoomEnd?: (e: ViewStateChangeEvent) => void; - onRotateStart?: (e: ViewStateChangeEvent) => void; - onRotate?: (e: ViewStateChangeEvent) => void; - onRotateEnd?: (e: ViewStateChangeEvent) => void; - onPitchStart?: (e: ViewStateChangeEvent) => void; - onPitch?: (e: ViewStateChangeEvent) => void; - onPitchEnd?: (e: ViewStateChangeEvent) => void; - - onWheel?: (e: MapWheelEvent) => void; - onBoxZoomStart?: (e: MapBoxZoomEvent) => void; - onBoxZoomEnd?: (e: MapBoxZoomEvent) => void; - onBoxZoomCancel?: (e: MapBoxZoomEvent) => void; - - onResize?: (e: MapEvent) => void; - onLoad?: (e: MapEvent) => void; - onRender?: (e: MapEvent) => void; - onIdle?: (e: MapEvent) => void; - onError?: (e: ErrorEvent) => void; - onRemove?: (e: MapEvent) => void; - onData?: (e: MapStyleDataEvent | MapSourceDataEvent) => void; - onStyleData?: (e: MapStyleDataEvent) => void; - onSourceData?: (e: MapSourceDataEvent) => void; -}; diff --git a/modules/main/src/mapbox-legacy/types/events.ts b/modules/main/src/mapbox-legacy/types/events.ts index 784ae678..6c7d0641 100644 --- a/modules/main/src/mapbox-legacy/types/events.ts +++ b/modules/main/src/mapbox-legacy/types/events.ts @@ -1,73 +1,117 @@ -import type {ViewState, Point, LngLat, MapGeoJSONFeature} from './common'; -import type { - MapInstance, - Evented, - MarkerInstance, - PopupInstance, - GeolocateControlInstance -} from './lib'; +import type {ViewState, LngLat} from './common'; +import { + MapboxEvent as MapEvent, + MapLayerMouseEvent, + MapLayerTouchEvent, + MapStyleDataEvent, + MapSourceDataEvent, + MapWheelEvent, + MapBoxZoomEvent, + ErrorEvent, + Marker, + Popup, + GeolocateControl +} from 'mapbox-gl'; -export interface Callbacks { - [key: `on${string}`]: Function; -} +export type MapCallbacks = { + onMouseDown?: (e: MapLayerMouseEvent) => void; + onMouseUp?: (e: MapLayerMouseEvent) => void; + onMouseOver?: (e: MapLayerMouseEvent) => void; + onMouseMove?: (e: MapLayerMouseEvent) => void; + onClick?: (e: MapLayerMouseEvent) => void; + onDblClick?: (e: MapLayerMouseEvent) => void; + onMouseEnter?: (e: MapLayerMouseEvent) => void; + onMouseLeave?: (e: MapLayerMouseEvent) => void; + onMouseOut?: (e: MapLayerMouseEvent) => void; + onContextMenu?: (e: MapLayerMouseEvent) => void; + onTouchStart?: (e: MapLayerTouchEvent) => void; + onTouchEnd?: (e: MapLayerTouchEvent) => void; + onTouchMove?: (e: MapLayerTouchEvent) => void; + onTouchCancel?: (e: MapLayerTouchEvent) => void; -export interface MapEvent { + onMoveStart?: (e: ViewStateChangeEvent) => void; + onMove?: (e: ViewStateChangeEvent) => void; + onMoveEnd?: (e: ViewStateChangeEvent) => void; + onDragStart?: (e: ViewStateChangeEvent) => void; + onDrag?: (e: ViewStateChangeEvent) => void; + onDragEnd?: (e: ViewStateChangeEvent) => void; + onZoomStart?: (e: ViewStateChangeEvent) => void; + onZoom?: (e: ViewStateChangeEvent) => void; + onZoomEnd?: (e: ViewStateChangeEvent) => void; + onRotateStart?: (e: ViewStateChangeEvent) => void; + onRotate?: (e: ViewStateChangeEvent) => void; + onRotateEnd?: (e: ViewStateChangeEvent) => void; + onPitchStart?: (e: ViewStateChangeEvent) => void; + onPitch?: (e: ViewStateChangeEvent) => void; + onPitchEnd?: (e: ViewStateChangeEvent) => void; + + onWheel?: (e: MapWheelEvent) => void; + onBoxZoomStart?: (e: MapBoxZoomEvent) => void; + onBoxZoomEnd?: (e: MapBoxZoomEvent) => void; + onBoxZoomCancel?: (e: MapBoxZoomEvent) => void; + + onResize?: (e: MapEvent) => void; + onLoad?: (e: MapEvent) => void; + onRender?: (e: MapEvent) => void; + onIdle?: (e: MapEvent) => void; + onError?: (e: ErrorEvent) => void; + onRemove?: (e: MapEvent) => void; + onData?: (e: MapStyleDataEvent | MapSourceDataEvent) => void; + onStyleData?: (e: MapStyleDataEvent) => void; + onSourceData?: (e: MapSourceDataEvent) => void; +}; + +export type { + MapEvent, + ErrorEvent, + MapLayerMouseEvent as MapMouseEvent, + MapLayerTouchEvent as MapTouchEvent, + MapStyleDataEvent, + MapSourceDataEvent, + MapWheelEvent, + MapBoxZoomEvent +}; + +interface IMapEvent { type: string; target: SourceT; originalEvent: OriginalEventT; } -export type ErrorEvent = MapEvent & { - type: 'error'; - error: Error; +export type ViewStateChangeEvent = MapEvent & { + type: + | 'movestart' + | 'move' + | 'moveend' + | 'zoomstart' + | 'zoom' + | 'zoomend' + | 'rotatestart' + | 'rotate' + | 'rotateend' + | 'dragstart' + | 'drag' + | 'dragend' + | 'pitchstart' + | 'pitch' + | 'pitchend'; + viewState: ViewState; }; -export type MapMouseEvent = MapEvent & { - point: Point; - lngLat: LngLat; - features?: MapGeoJSONFeature[]; -}; - -export type ViewStateChangeEvent = - | (MapEvent & { - type: 'movestart' | 'move' | 'moveend' | 'zoomstart' | 'zoom' | 'zoomend'; - viewState: ViewState; - }) - | (MapEvent & { - type: - | 'rotatestart' - | 'rotate' - | 'rotateend' - | 'dragstart' - | 'drag' - | 'dragend' - | 'pitchstart' - | 'pitch' - | 'pitchend'; - viewState: ViewState; - }); - -export type PopupEvent = { +export type PopupEvent = { type: 'open' | 'close'; - target: PopupT; + target: Popup; }; -export type MarkerEvent = MapEvent< - MarkerT, - OriginalEventT ->; +export type MarkerEvent = IMapEvent; -export type MarkerDragEvent = MarkerEvent & { +export type MarkerDragEvent = MarkerEvent & { type: 'dragstart' | 'drag' | 'dragend'; - target: MarkerT; lngLat: LngLat; }; -export type GeolocateEvent = - MapEvent; +export type GeolocateEvent = IMapEvent; -export type GeolocateResultEvent = - GeolocateEvent & GeolocationPosition; +export type GeolocateResultEvent = GeolocateEvent & GeolocationPosition; -export type GeolocateErrorEvent = - GeolocateEvent & GeolocationPositionError; +export type GeolocateErrorEvent = GeolocateEvent & GeolocationPositionError; diff --git a/modules/main/src/mapbox-legacy/types/index.ts b/modules/main/src/mapbox-legacy/types/index.ts deleted file mode 100644 index 618d31a0..00000000 --- a/modules/main/src/mapbox-legacy/types/index.ts +++ /dev/null @@ -1,93 +0,0 @@ -export * from './common'; -export * from './lib'; -export * from './events'; - -import type GeoJSON from 'geojson'; -import type {CustomSourceImplementation} from './lib'; - -// Internal: source implementations - -export interface ISource { - type: string; -} - -export interface ILayer { - id: string; - type: string; - - metadata?: any; - source?: unknown; - - minzoom?: number; - maxzoom?: number; - - filter?: any; - layout?: { - [property: string]: any; - }; - paint?: { - [property: string]: any; - }; -} - -export interface MapStyle { - name?: string; - metadata?: unknown; - version: number; - layers: ILayer[]; - sources: { - [sourceName: string]: object; - }; - - fog?: any; - terrain?: any; - light?: any; -} - -export interface GeoJSONSourceImplementation { - type: 'geojson'; - setData( - data: GeoJSON.Feature | GeoJSON.FeatureCollection | String - ): this; -} - -export interface ImageSourceImplemtation { - type: 'image'; - updateImage(options: {url?: string; coordinates?: number[][]}): this; - setCoordinates(coordinates: number[][]): this; -} - -export interface CanvasSourceImplemtation { - type: 'canvas'; - play(): void; - pause(): void; - getCanvas(): HTMLCanvasElement; - setCoordinates(coordinates: number[][]): this; -} - -export interface VectorSourceImplementation { - type: 'vector'; - setTiles(tiles: ReadonlyArray): this; - setUrl(url: string): this; -} - -export interface RasterSourceImplementation { - type: 'raster' | 'raster-dem'; - setTiles(tiles: ReadonlyArray): this; - setUrl(url: string): this; -} - -export interface VideoSourceImplementation { - type: 'video'; - getVideo(): HTMLVideoElement; - setCoordinates(coordinates: number[][]): this; -} - -export type AnySourceImplementation = - | GeoJSONSourceImplementation - | VideoSourceImplementation - | ImageSourceImplemtation - | CanvasSourceImplemtation - | VectorSourceImplementation - | RasterSourceImplementation - | CustomSourceImplementation; diff --git a/modules/main/src/mapbox-legacy/types/internal.ts b/modules/main/src/mapbox-legacy/types/internal.ts new file mode 100644 index 00000000..0c2c520d --- /dev/null +++ b/modules/main/src/mapbox-legacy/types/internal.ts @@ -0,0 +1,50 @@ +// Internal types +import type {Map, LngLat, PaddingOptions, Point} from 'mapbox-gl'; + +export type { + AnySourceImpl as AnySourceImplementation, + GeoJSONSource as GeoJSONSourceImplementation, + ImageSource as ImageSourceImplemtation, + CanvasSource as CanvasSourceImplemtation, + VectorSourceImpl as VectorSourceImplementation, + RasterSourceImpl as RasterSourceImplementation, + VideoSource as VideoSourceImplementation +} from 'mapbox-gl'; + +/** + * Stub for mapbox's Transform class + * https://github.com/mapbox/mapbox-gl-js/blob/main/src/geo/transform.js + */ +export type Transform = { + width: number; + height: number; + center: LngLat; + zoom: number; + bearing: number; + pitch: number; + padding: PaddingOptions; + elevation: any; + pixelsToGLUnits: [number, number]; + cameraElevationReference: 'ground' | 'sea'; + + clone: () => Transform; + resize: (width: number, height: number) => void; + isPaddingEqual: (value: PaddingOptions) => boolean; + getBounds: () => any; + locationPoint: (lngLat: LngLat) => Point; + pointLocation: (p: Point) => LngLat; + + // Mapbox only + getProjection?: () => any; + setProjection?: (projection: any) => void; +}; + +export type MapInstanceInternal = Map & { + transform: Transform; + + _render: Function; + + _renderTaskQueue: { + run: Function; + }; +}; diff --git a/modules/main/src/mapbox-legacy/types/lib.ts b/modules/main/src/mapbox-legacy/types/lib.ts index 5ebdfbcb..bf7f5838 100644 --- a/modules/main/src/mapbox-legacy/types/lib.ts +++ b/modules/main/src/mapbox-legacy/types/lib.ts @@ -1,293 +1,67 @@ -import type {PaddingOptions, LngLat, Point, LngLatLike, PointLike, ControlPosition} from './common'; - -export interface IControl { - onAdd(map: MapT): HTMLElement; - - onRemove(map: MapT): void; - - getDefaultPosition?: (() => string) | undefined; -} - -type Listener = (event?: any) => any; - -export interface Evented { - on(type: string, listener: Listener); - - off(type?: string | any, listener?: Listener); - - once(type: string, listener: Listener); -} - -/** - * A user-facing type that represents the minimal intersection between Mapbox.Map and Maplibre.Map - * User provided `mapLib.Map` is supposed to implement this interface - * Only losely typed for compatibility - */ -export interface MapInstance extends Evented { - // https://github.com/mapbox/mapbox-gl-js/issues/6522 - fire(type: string, properties?: {[key: string]: any}); - - addControl(control: IControl, position?: ControlPosition); - - removeControl(control: IControl); - - hasControl(control: IControl): boolean; - - resize(): this; - - queryRenderedFeatures(geometry?: any, options?: any): any[]; - - setStyle(style: any, options?: any); - - isMoving(): boolean; - - getStyle(): any; - - isStyleLoaded(): boolean | void; - - addSource(id: string, source: any); - - removeSource(id: string): this; - - getSource(id: string): any; - - addLayer(layer: any, before?: string); - - moveLayer(id: string, beforeId?: string); - - removeLayer(id: string); - - getLayer(id: string): any; - - setFilter(layer: string, filter?: any[] | boolean | null); - - setLayerZoomRange(layerId: string, minzoom: number, maxzoom: number); - - setPaintProperty(layer: string, name: string, value: any); - - setLayoutProperty(layer: string, name: string, value: any); - - project(lnglat: any): Point; - - unproject(point: any): LngLat; - - queryTerrainElevation?(lngLat: any, options?: any): number | null; - - getContainer(): HTMLElement; - - getCanvas(): HTMLCanvasElement; - - getCanvasContainer(): HTMLElement; - - remove(): void; - - triggerRepaint(): void; - - getCenter(): LngLat; - - getZoom(): number; - - getBearing(): number; - - getPitch(): number; - - getPadding(): PaddingOptions; - - getRenderWorldCopies(): boolean; - - setPadding(padding: PaddingOptions); - - fitBounds(bounds: any, options?: any); - - setFog?(fog: any); - - setLight?(options: any, lightOptions?: any); - - setTerrain?(terrain?: any); -} - -export interface MarkerInstance extends Evented { - addTo(map: MapInstance): this; - remove(): this; - - getLngLat(): LngLat; - setLngLat(lngLat: LngLatLike): this; - - getElement(): HTMLElement; - - setPopup(popup?: any): this; - getPopup(): any; - - getOffset(): PointLike; - setOffset(offset: PointLike): this; - - setDraggable(value: boolean): this; - isDraggable(): boolean; - - getRotation(): number; - setRotation(rotation: number): this; - - getRotationAlignment(): any; - setRotationAlignment(alignment: any): this; - - getPitchAlignment(): any; - setPitchAlignment(alignment: any): this; -} - -export interface PopupInstance extends Evented { - options?: any; - - addTo(map: MapInstance): this; - remove(): this; - - isOpen(): boolean; - - getLngLat(): LngLat; - setLngLat(lnglat: LngLatLike): this; - - getElement(): HTMLElement; - - setDOMContent(htmlNode: any): this; - - getMaxWidth(): any; - setMaxWidth(maxWidth: any): this; - - addClassName(className: string): void; - removeClassName(className: string): void; - - setOffset(offset?: any): this; -} - -export interface AttributionControlInstance extends IControl { - _container?: HTMLElement; -} - -export interface FullscreenControlInstance extends IControl { - _controlContainer?: HTMLElement; -} - -export interface GeolocateControlInstance extends IControl, Evented { - _container?: HTMLElement; - trigger(); -} - -export interface NavigationControlInstance extends IControl { - _container?: HTMLElement; -} - -export interface ScaleControlInstance extends IControl { - _container?: HTMLElement; - options?: any; - setUnit(unit: any): void; -} +import type { + Map as MapInstance, + MapboxOptions as MapOptions, + Marker as MarkerInstance, + MarkerOptions, + Popup as PopupInstance, + PopupOptions, + AttributionControl as AttributionControlInstance, + FullscreenControl as FullscreenControlInstance, + FullscreenControlOptions, + GeolocateControl as GeolocateControlInstance, + NavigationControl as NavigationControlInstance, + ScaleControl as ScaleControlInstance +} from 'mapbox-gl'; + +type AttributionControlOptions = ConstructorParameters[0]; +type GeolocateControlOptions = ConstructorParameters[0]; +type NavigationControlOptions = ConstructorParameters[0]; +type ScaleControlOptions = ConstructorParameters[0]; + +export type {IControl, CustomLayerInterface} from 'mapbox-gl'; + +export type { + MapInstance, + MapOptions, + MarkerInstance, + MarkerOptions, + PopupInstance, + PopupOptions, + AttributionControlInstance, + AttributionControlOptions, + FullscreenControlInstance, + FullscreenControlOptions, + GeolocateControlInstance, + GeolocateControlOptions, + NavigationControlInstance, + NavigationControlOptions, + ScaleControlInstance, + ScaleControlOptions +}; + +export type ControlPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'; /** * A user-facing type that represents the minimal intersection between Mapbox and Maplibre * User provided `mapLib` is supposed to implement this interface * Only loosely typed for compatibility */ -export interface MapLib { +export interface MapLib { supported?: (options: any) => boolean; - Map: {new (options: any): MapT}; + Map: {new (options: MapOptions): MapInstance}; - Marker: {new (...options: any[]): MarkerInstance}; + Marker: {new (options: MarkerOptions): MarkerInstance}; - Popup: {new (options: any): PopupInstance}; + Popup: {new (options: PopupOptions): PopupInstance}; - AttributionControl: {new (options: any): AttributionControlInstance}; + AttributionControl: {new (options: AttributionControlOptions): AttributionControlInstance}; - FullscreenControl: {new (options: any): FullscreenControlInstance}; + FullscreenControl: {new (options: FullscreenControlOptions): FullscreenControlInstance}; - GeolocateControl: {new (options: any): GeolocateControlInstance}; + GeolocateControl: {new (options: GeolocateControlOptions): GeolocateControlInstance}; - NavigationControl: {new (options: any): NavigationControlInstance}; + NavigationControl: {new (options: NavigationControlOptions): NavigationControlInstance}; - ScaleControl: {new (options: any): ScaleControlInstance}; -} - -/** - * Stub for mapbox's Transform class - * https://github.com/mapbox/mapbox-gl-js/blob/main/src/geo/transform.js - */ -export type Transform = { - width: number; - height: number; - center: LngLat; - zoom: number; - bearing: number; - pitch: number; - padding: PaddingOptions; - elevation: any; - pixelsToGLUnits: [number, number]; - cameraElevationReference: 'ground' | 'sea'; - - clone: () => Transform; - resize: (width: number, height: number) => void; - isPaddingEqual: (value: PaddingOptions) => boolean; - getBounds: () => any; - locationPoint: (lngLat: LngLat) => Point; - pointLocation: (p: Point) => LngLat; - - // Mapbox only - getProjection?: () => any; - setProjection?: (projection: any) => void; -}; - -export type MapInstanceInternal = MapT & { - transform: Transform; - - _render: Function; - - _renderTaskQueue: { - run: Function; - }; -}; - -// Custom layer -export interface CustomLayerInterface { - id: string; - type: 'custom'; - renderingMode?: '2d' | '3d'; - - onRemove?(map: MapInstance, gl: WebGLRenderingContext): void; - onAdd?(map: MapInstance, gl: WebGLRenderingContext): void; - - prerender?(gl: WebGLRenderingContext, matrix: number[]): void; - render(gl: WebGLRenderingContext, matrix: number[]): void; -} - -// Custom source - -export interface CustomSourceImplementation { - id: string; - type: 'custom'; - dataType: 'raster'; - minzoom?: number; - maxzoom?: number; - scheme?: string; - tileSize?: number; - attribution?: string; - bounds?: [number, number, number, number]; - hasTile?: (tileID: {z: number; x: number; y: number}) => boolean; - loadTile: ( - tileID: {z: number; x: number; y: number}, - options: {signal: AbortSignal} - ) => Promise; - prepareTile?: (tileID: {z: number; x: number; y: number}) => TileDataT | undefined; - unloadTile?: (tileID: {z: number; x: number; y: number}) => void; - onAdd?: (map: MapInstance) => void; - onRemove?: (map: MapInstance) => void; -} - -export interface CustomSource { - id: string; - type: 'custom'; - scheme: string; - minzoom: number; - maxzoom: number; - tileSize: number; - attribution: string; - - _implementation: CustomSourceImplementation; + ScaleControl: {new (options: ScaleControlOptions): ScaleControlInstance}; } diff --git a/modules/main/src/mapbox-legacy/types/public.ts b/modules/main/src/mapbox-legacy/types/public.ts deleted file mode 100644 index a3e02dba..00000000 --- a/modules/main/src/mapbox-legacy/types/public.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type {ViewState, ControlPosition} from './common'; -export type { - IControl, - MapInstance, - MapLib, - CustomLayerInterface, - CustomSource, - CustomSourceImplementation -} from './lib'; diff --git a/modules/main/src/mapbox-legacy/types/style-spec-mapbox.ts b/modules/main/src/mapbox-legacy/types/style-spec.ts similarity index 60% rename from modules/main/src/mapbox-legacy/types/style-spec-mapbox.ts rename to modules/main/src/mapbox-legacy/types/style-spec.ts index d8b40737..114b7d01 100644 --- a/modules/main/src/mapbox-legacy/types/style-spec-mapbox.ts +++ b/modules/main/src/mapbox-legacy/types/style-spec.ts @@ -42,39 +42,36 @@ export type { // Sources import type { - GeoJSONSourceRaw, - VideoSourceRaw, - ImageSourceRaw, - VectorSource as VectorSourceRaw, + Projection as ProjectionSpecification, + GeoJSONSourceRaw as GeoJSONSource, + VideoSourceRaw as VideoSource, + ImageSourceRaw as ImageSource, + VectorSource, RasterSource, - CanvasSourceRaw, + CanvasSourceRaw as CanvasSource, RasterDemSource } from 'mapbox-gl'; export type AnySource = - | GeoJSONSourceRaw - | VideoSourceRaw - | ImageSourceRaw - | CanvasSourceRaw - | VectorSourceRaw + | GeoJSONSource + | VideoSource + | ImageSource + | CanvasSource + | VectorSource | RasterSource | RasterDemSource; export type { - GeoJSONSourceRaw, - VideoSourceRaw, - ImageSourceRaw, - CanvasSourceRaw, - VectorSourceRaw, + GeoJSONSource, + VideoSource, + ImageSource, + CanvasSource, + VectorSource, RasterSource, RasterDemSource }; // Other -export type { - Style as MapStyle, - Light, - Fog, - TerrainSpecification as Terrain, - Projection -} from 'mapbox-gl'; +export type {Style as MapStyle, Light, Fog, TerrainSpecification as Terrain} from 'mapbox-gl'; + +export type Projection = ProjectionSpecification | ProjectionSpecification['name']; diff --git a/modules/main/src/mapbox-legacy/utils/deep-equal.ts b/modules/main/src/mapbox-legacy/utils/deep-equal.ts index abb2499f..879d98e8 100644 --- a/modules/main/src/mapbox-legacy/utils/deep-equal.ts +++ b/modules/main/src/mapbox-legacy/utils/deep-equal.ts @@ -1,4 +1,4 @@ -import type {PointLike} from '../types'; +import type {PointLike} from '../types/common'; /** * Compare two points diff --git a/modules/main/src/mapbox-legacy/utils/style-utils.ts b/modules/main/src/mapbox-legacy/utils/style-utils.ts index 51656a74..bb61d366 100644 --- a/modules/main/src/mapbox-legacy/utils/style-utils.ts +++ b/modules/main/src/mapbox-legacy/utils/style-utils.ts @@ -1,4 +1,5 @@ -import {ImmutableLike, MapStyle} from '../types'; +import {ImmutableLike} from '../types/common'; +import {MapStyle} from '../types/style-spec'; const refProps = ['type', 'source', 'source-layer', 'minzoom', 'maxzoom', 'filter', 'layout']; diff --git a/modules/main/src/mapbox-legacy/utils/transform.ts b/modules/main/src/mapbox-legacy/utils/transform.ts index 63e724aa..3a0dd482 100644 --- a/modules/main/src/mapbox-legacy/utils/transform.ts +++ b/modules/main/src/mapbox-legacy/utils/transform.ts @@ -1,5 +1,6 @@ import type {MapboxProps} from '../mapbox/mapbox'; -import type {Transform, ViewState} from '../types'; +import type {Transform} from '../types/internal'; +import type {ViewState} from '../types/common'; import {deepEqual} from './deep-equal'; /** diff --git a/modules/main/src/mapbox.ts b/modules/main/src/mapbox.ts index 51b2499e..005ce4b2 100644 --- a/modules/main/src/mapbox.ts +++ b/modules/main/src/mapbox.ts @@ -1 +1,3 @@ export * from '@vis.gl/react-mapbox'; + +export {Map as default} from '@vis.gl/react-mapbox'; diff --git a/modules/main/src/maplibre.ts b/modules/main/src/maplibre.ts index 8c5e52fa..4bf52880 100644 --- a/modules/main/src/maplibre.ts +++ b/modules/main/src/maplibre.ts @@ -1 +1,3 @@ export * from '@vis.gl/react-maplibre'; + +export {Map as default} from '@vis.gl/react-maplibre'; diff --git a/modules/react-mapbox/src/components/source.ts b/modules/react-mapbox/src/components/source.ts index f6316cc2..2b7f9e88 100644 --- a/modules/react-mapbox/src/components/source.ts +++ b/modules/react-mapbox/src/components/source.ts @@ -9,7 +9,7 @@ import type { ImageSourceImplemtation, AnySourceImplementation } from '../types/internal'; -import type {AnySource, ImageSourceRaw, VectorSourceRaw} from '../types/style-spec'; +import type {AnySource, ImageSource, VectorSource} from '../types/style-spec'; import type {MapInstance} from '../types/lib'; export type SourceProps = AnySource & { @@ -61,11 +61,11 @@ function updateSource(source: AnySourceImplementation, props: SourceProps, prevP coordinates: props.coordinates }); } else if ('setCoordinates' in source && changedKeyCount === 1 && changedKey === 'coordinates') { - source.setCoordinates((props as unknown as ImageSourceRaw).coordinates); + source.setCoordinates((props as unknown as ImageSource).coordinates); } else if ('setUrl' in source && changedKey === 'url') { - source.setUrl((props as VectorSourceRaw).url); + source.setUrl((props as VectorSource).url); } else if ('setTiles' in source && changedKey === 'tiles') { - source.setTiles((props as VectorSourceRaw).tiles); + source.setTiles((props as VectorSource).tiles); } else { // eslint-disable-next-line console.warn(`Unable to update prop: ${changedKey}`); diff --git a/modules/react-mapbox/src/types/style-spec.ts b/modules/react-mapbox/src/types/style-spec.ts index 4f28444a..62a3c45e 100644 --- a/modules/react-mapbox/src/types/style-spec.ts +++ b/modules/react-mapbox/src/types/style-spec.ts @@ -13,16 +13,16 @@ import type { LineLayerSpecification as LineLayer, RasterLayerSpecification as RasterLayer, SymbolLayerSpecification as SymbolLayer, - GeoJSONSourceSpecification as GeoJSONSourceRaw, - VideoSourceSpecification as VideoSourceRaw, - ImageSourceSpecification as ImageSourceRaw, - VectorSourceSpecification as VectorSourceRaw, + GeoJSONSourceSpecification as GeoJSONSource, + VideoSourceSpecification as VideoSource, + ImageSourceSpecification as ImageSource, + VectorSourceSpecification as VectorSource, RasterSourceSpecification as RasterSource, RasterDEMSourceSpecification as RasterDemSource, ProjectionSpecification } from 'mapbox-gl'; -type CanvasSourceRaw = { +type CanvasSource = { type: 'canvas'; coordinates: [[number, number], [number, number], [number, number], [number, number]]; animate?: boolean; @@ -55,20 +55,20 @@ export type { }; export type AnySource = - | GeoJSONSourceRaw - | VideoSourceRaw - | ImageSourceRaw - | CanvasSourceRaw - | VectorSourceRaw + | GeoJSONSource + | VideoSource + | ImageSource + | CanvasSource + | VectorSource | RasterSource | RasterDemSource; export type { - GeoJSONSourceRaw, - VideoSourceRaw, - ImageSourceRaw, - CanvasSourceRaw, - VectorSourceRaw, + GeoJSONSource, + VideoSource, + ImageSource, + CanvasSource, + VectorSource, RasterSource, RasterDemSource }; diff --git a/modules/react-maplibre/src/types/style-spec.ts b/modules/react-maplibre/src/types/style-spec.ts index 14bb0f4e..c1a6e06c 100644 --- a/modules/react-maplibre/src/types/style-spec.ts +++ b/modules/react-maplibre/src/types/style-spec.ts @@ -12,13 +12,13 @@ import type { LineLayerSpecification as LineLayer, RasterLayerSpecification as RasterLayer, SymbolLayerSpecification as SymbolLayer, - GeoJSONSourceSpecification as GeoJSONSourceRaw, - VideoSourceSpecification as VideoSourceRaw, - ImageSourceSpecification as ImageSourceRaw, - VectorSourceSpecification as VectorSourceRaw, + GeoJSONSourceSpecification as GeoJSONSource, + VideoSourceSpecification as VideoSource, + ImageSourceSpecification as ImageSource, + VectorSourceSpecification as VectorSource, RasterSourceSpecification as RasterSource, RasterDEMSourceSpecification as RasterDemSource, - CanvasSourceSpecification as CanvasSourceRaw, + CanvasSourceSpecification as CanvasSource, ProjectionSpecification } from 'maplibre-gl'; @@ -48,21 +48,21 @@ export type AnyLayer = // Sources export type { - GeoJSONSourceRaw, - VideoSourceRaw, - ImageSourceRaw, - CanvasSourceRaw, - VectorSourceRaw, + GeoJSONSource, + VideoSource, + ImageSource, + CanvasSource, + VectorSource, RasterSource, RasterDemSource }; export type AnySource = - | GeoJSONSourceRaw - | VideoSourceRaw - | ImageSourceRaw - | CanvasSourceRaw - | VectorSourceRaw + | GeoJSONSource + | VideoSource + | ImageSource + | CanvasSource + | VectorSource | RasterSource | RasterDemSource;