[v8] Align type names across modules (#2468)

This commit is contained in:
Xiaoji Chen 2025-01-23 13:33:05 -08:00
parent 2785a32263
commit 4f856f0838
33 changed files with 648 additions and 1228 deletions

View File

@ -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

View File

@ -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> = 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<AttributionControlOptions, ControlT extends AttributionControlInstance>(
props: AttributionControlProps<AttributionControlOptions>
): null {
const ctrl = useControl<ControlT>(
({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);

View File

@ -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<OptionsT> = Omit<OptionsT, 'container'> & {
export type FullscreenControlProps = Omit<FullscreenControlOptions, 'container'> & {
/** 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<OptionsT> = Omit<OptionsT, 'container'> & {
style?: React.CSSProperties;
};
function FullscreenControl<FullscreenControlOptions, ControlT extends FullscreenControlInstance>(
props: FullscreenControlProps<FullscreenControlOptions>
): null {
const ctrl = useControl<ControlT>(
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);

View File

@ -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<ControlT>) => void;
onGeolocate?: (e: GeolocateResultEvent) => void;
/** Called on each Geolocation API position update that returned as an error. */
onError?: (e: GeolocateErrorEvent<ControlT>) => 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<ControlT>) => void;
onOutOfMaxBounds?: (e: GeolocateResultEvent) => void;
/** Called when the GeolocateControl changes to the active lock state. */
onTrackUserLocationStart?: (e: GeolocateEvent<ControlT>) => void;
onTrackUserLocationStart?: (e: GeolocateEvent) => void;
/** Called when the GeolocateControl changes to the background state. */
onTrackUserLocationEnd?: (e: GeolocateEvent<ControlT>) => void;
onTrackUserLocationEnd?: (e: GeolocateEvent) => void;
};
function GeolocateControl<GeolocateControlOptions, ControlT extends GeolocateControlInstance>(
props: GeolocateControlProps<GeolocateControlOptions, ControlT>,
ref: React.Ref<ControlT>
) {
function _GeolocateControl(props: GeolocateControlProps, ref: React.Ref<GeolocateControlInstance>) {
const thisRef = useRef({props});
const ctrl = useControl<ControlT>(
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<ControlT>);
thisRef.current.props.onGeolocate?.(e as GeolocateResultEvent);
});
gc.on('error', e => {
thisRef.current.props.onError?.(e as GeolocateErrorEvent<ControlT>);
thisRef.current.props.onError?.(e as GeolocateErrorEvent);
});
gc.on('outofmaxbounds', e => {
thisRef.current.props.onOutOfMaxBounds?.(e as GeolocateResultEvent<ControlT>);
thisRef.current.props.onOutOfMaxBounds?.(e as GeolocateResultEvent);
});
gc.on('trackuserlocationstart', e => {
thisRef.current.props.onTrackUserLocationStart?.(e as GeolocateEvent<ControlT>);
thisRef.current.props.onTrackUserLocationStart?.(e as GeolocateEvent);
});
gc.on('trackuserlocationend', e => {
thisRef.current.props.onTrackUserLocationEnd?.(e as GeolocateEvent<ControlT>);
thisRef.current.props.onTrackUserLocationEnd?.(e as GeolocateEvent);
});
return gc;
@ -81,10 +75,11 @@ function GeolocateControl<GeolocateControlOptions, ControlT extends GeolocateCon
useImperativeHandle(ref, () => 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));

View File

@ -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> = T extends {id: string} ? Omit<T, 'id'> & {id?: string} : T;
type OptionalSource<T> = T extends {source: string} ? Omit<T, 'source'> & {source?: string} : T;
export type LayerProps<LayerT> = OptionalSource<OptionalId<LayerT>> & {
export type LayerProps = (OptionalSource<OptionalId<AnyLayer>> | CustomLayerInterface) & {
/** If set, the layer will be inserted before the specified layer */
beforeId?: string;
};
/* eslint-disable complexity, max-statements */
function updateLayer<LayerT extends ILayer>(
map: MapInstance,
id: string,
props: LayerProps<LayerT>,
prevProps: LayerProps<LayerT>
) {
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<LayerT extends ILayer>(
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<LayerT extends ILayer>(
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<LayerT extends ILayer>(
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<LayerT extends ILayer>(
}
}
function createLayer<LayerT extends ILayer>(
map: MapInstance,
id: string,
props: LayerProps<LayerT>
) {
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<LayerT> = {...props, id};
const options: LayerProps = {...props, id};
delete options.beforeId;
// @ts-ignore
@ -88,7 +82,7 @@ function createLayer<LayerT extends ILayer>(
let layerCounter = 0;
function Layer<LayerT extends ILayer>(props: LayerProps<LayerT | CustomLayerInterface>) {
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<LayerT extends ILayer>(props: LayerProps<LayerT | CustomLayerInte
return null;
}
export default Layer;

View File

@ -8,29 +8,24 @@ import createRef, {MapRef} from '../mapbox/create-ref';
import type {CSSProperties} from 'react';
import useIsomorphicLayoutEffect from '../utils/use-isomorphic-layout-effect';
import setGlobals, {GlobalSettings} from '../utils/set-globals';
import type {MapLib, MapInstance, MapStyle, Callbacks} from '../types';
import type {MapLib, MapOptions} from '../types/lib';
export type MapContextValue<MapT extends MapInstance = MapInstance> = {
mapLib: MapLib<MapT>;
map: MapRef<MapT>;
export type MapContextValue = {
mapLib: MapLib;
map: MapRef;
};
export const MapContext = React.createContext<MapContextValue>(null);
type MapInitOptions<MapOptions> = Omit<
type MapInitOptions = Omit<
MapOptions,
'style' | 'container' | 'bounds' | 'fitBoundsOptions' | 'center'
>;
export type MapProps<
MapOptions,
StyleT extends MapStyle,
CallbacksT extends Callbacks,
MapT extends MapInstance
> = MapInitOptions<MapOptions> &
MapboxProps<StyleT, CallbacksT> &
export type MapProps = MapInitOptions &
MapboxProps &
GlobalSettings & {
mapLib?: MapLib<MapT> | Promise<MapLib<MapT>>;
mapLib?: MapLib | Promise<MapLib>;
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<MapOptions, StyleT, CallbacksT, MapT>,
ref: React.Ref<MapRef<MapT>>,
defaultLib: MapLib<MapT> | Promise<MapLib<MapT>>
) {
function _Map(props: MapProps, ref: React.Ref<MapRef>) {
const mountedMapsContext = useContext(MountedMapsContext);
const [mapInstance, setMapInstance] = useState<Mapbox<StyleT, CallbacksT, MapT>>(null);
const [mapInstance, setMapInstance] = useState<Mapbox>(null);
const containerRef = useRef();
const {current: contextValue} = useRef<MapContextValue<MapT>>({mapLib: null, map: null});
const {current: contextValue} = useRef<MapContextValue>({mapLib: null, map: null});
useEffect(() => {
const mapLib = props.mapLib;
let isMounted = true;
let mapbox: Mapbox<StyleT, CallbacksT, MapT>;
let mapbox: Mapbox;
Promise.resolve(mapLib || defaultLib)
.then((module: MapLib<MapT> | {default: MapLib<MapT>}) => {
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<
</div>
);
}
export const Map = React.forwardRef(_Map);

View File

@ -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, MarkerT extends MarkerInstance> = 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<MarkerT, MouseEvent>) => void;
onDragStart?: (e: MarkerDragEvent<MarkerT>) => void;
onDrag?: (e: MarkerDragEvent<MarkerT>) => void;
onDragEnd?: (e: MarkerDragEvent<MarkerT>) => void;
onClick?: (e: MarkerEvent<MouseEvent>) => void;
onDragStart?: (e: MarkerDragEvent) => void;
onDrag?: (e: MarkerDragEvent) => void;
onDragEnd?: (e: MarkerDragEvent) => void;
children?: React.ReactNode;
};
/* eslint-disable complexity,max-statements */
function Marker<MarkerOptions, MarkerT extends MarkerInstance>(
props: MarkerProps<MarkerOptions, MarkerT>,
ref: React.Ref<MarkerT>
) {
const {map, mapLib} = useContext(MapContext);
const thisRef = useRef({props});
thisRef.current.props = props;
export const Marker = memo(
forwardRef((props: MarkerProps, ref: React.Ref<MarkerInstance>) => {
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<MarkerT>;
evt.lngLat = marker.getLngLat();
thisRef.current.props.onDragStart?.(evt);
});
mk.on('drag', e => {
const evt = e as MarkerDragEvent<MarkerT>;
evt.lngLat = marker.getLngLat();
thisRef.current.props.onDrag?.(evt);
});
mk.on('dragend', e => {
const evt = e as MarkerDragEvent<MarkerT>;
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());
})
);

View File

@ -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> = 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<NavigationControlOptions, ControlT extends NavigationControlInstance>(
props: NavigationControlProps<NavigationControlOptions>
): null {
const ctrl = useControl<ControlT>(({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);

View File

@ -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, PopupT extends PopupInstance> = 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<PopupT>) => void;
onClose?: (e: PopupEvent<PopupT>) => 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<PopupOptions, PopupT extends PopupInstance>(
props: PopupProps<PopupOptions, PopupT>,
ref: React.Ref<PopupT>
) {
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<PopupInstance>) => {
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<PopupT>);
});
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<PopupT>);
};
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);
})
);

View File

@ -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> = 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<ScaleControlOptions, ControlT extends ScaleControlInstance>(
props: ScaleControlProps<ScaleControlOptions>
): null {
const ctrl = useControl<ControlT>(({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<ScaleControlProps<ScaleControlOptions>>(props);
const propsRef = useRef<ScaleControlProps>(props);
const prevProps = propsRef.current;
propsRef.current = props;
@ -30,17 +24,19 @@ function ScaleControl<ScaleControlOptions, ControlT extends ScaleControlInstance
const {style} = props;
if (props.maxWidth !== undefined && props.maxWidth !== prevProps.maxWidth) {
// @ts-expect-error accessing private member
ctrl.options.maxWidth = props.maxWidth;
}
if (props.unit !== undefined && props.unit !== prevProps.unit) {
ctrl.setUnit(props.unit);
ctrl.setUnit(props.unit as any);
}
useEffect(() => {
// @ts-expect-error accessing private member
applyReactStyle(ctrl._container, style);
}, [style]);
return null;
}
export default memo(ScaleControl);
export const ScaleControl = memo(_ScaleControl);

View File

@ -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> = (SourceT | CustomSource) & {
export type SourceProps = AnySource & {
id?: string;
children?: any;
};
let sourceCounter = 0;
function createSource<SourceT extends ISource>(
map: MapInstance,
id: string,
props: SourceProps<SourceT>
) {
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<SourceT extends ISource>(
}
/* eslint-disable complexity */
function updateSource<SourceT extends ISource>(
source: AnySourceImplementation,
props: SourceProps<SourceT>,
prevProps: SourceProps<SourceT>
) {
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<SourceT extends ISource>(
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 <Source> prop: ${changedKey}`);
@ -85,7 +73,7 @@ function updateSource<SourceT extends ISource>(
}
/* eslint-enable complexity */
function Source<SourceT extends ISource>(props: SourceProps<SourceT>) {
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<SourceT extends ISource>(props: SourceProps<SourceT>) {
null
);
}
export default Source;

View File

@ -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<T extends IControl>(
export function useControl<T extends IControl>(
onCreate: (context: MapContextValue) => T,
opts?: ControlOptions
): T;
function useControl<T extends IControl>(
export function useControl<T extends IControl>(
onCreate: (context: MapContextValue) => T,
onRemove: (context: MapContextValue) => void,
opts?: ControlOptions
): T;
function useControl<T extends IControl>(
export function useControl<T extends IControl>(
onCreate: (context: MapContextValue) => T,
onAdd: (context: MapContextValue) => void,
onRemove: (context: MapContextValue) => void,
opts?: ControlOptions
): T;
function useControl<T extends IControl>(
export function useControl<T extends IControl>(
onCreate: (context: MapContextValue) => T,
arg1?: ((context: MapContextValue) => void) | ControlOptions,
arg2?: ((context: MapContextValue) => void) | ControlOptions,
@ -60,5 +60,3 @@ function useControl<T extends IControl>(
return ctrl;
}
export default useControl;

View File

@ -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<MapInstance>};
onMapMount: (map: MapRef<MapInstance>, id: string) => void;
maps: {[id: string]: MapRef};
onMapMount: (map: MapRef, id: string) => void;
onMapUnmount: (id: string) => void;
};
export const MountedMapsContext = React.createContext<MountedMapsContextValue>(null);
export const MapProvider: React.FC<{children?: React.ReactNode}> = props => {
const [maps, setMaps] = useState<{[id: string]: MapRef<MapInstance>}>({});
const [maps, setMaps] = useState<{[id: string]: MapRef}>({});
const onMapMount = useCallback((map: MapRef<MapInstance>, 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<MapT extends MapInstance> = {
[id: string]: MapRef<MapT> | undefined;
current?: MapRef<MapT>;
export type MapCollection = {
[id: string]: MapRef | undefined;
current?: MapRef;
};
export function useMap<MapT extends MapInstance>(): MapCollection<MapT> {
export function useMap(): MapCollection {
const maps = useContext(MountedMapsContext)?.maps;
const currentMap = useContext(MapContext);
@ -65,5 +64,5 @@ export function useMap<MapT extends MapInstance>(): MapCollection<MapT> {
return {...maps, current: currentMap?.map};
}, [maps, currentMap]);
return mapsWithCurrent as MapCollection<MapT>;
return mapsWithCurrent as MapCollection;
}

View File

@ -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<MapboxMap>();
}
export type MapProps = _MapProps<MapboxOptions, MapStyle, MapCallbacks, MapboxMap>;
export type MapRef = _MapRef<MapboxMap>;
const mapLib = import('mapbox-gl');
export const Map = (() => {
return React.forwardRef(function Map(props: MapProps, ref: React.Ref<MapRef>) {
return _Map<MapboxOptions, MapStyle, MapCallbacks, MapboxMap>(props, ref, mapLib);
});
})();
export type MarkerProps = _MarkerProps<MarkerOptions, MapboxMarker>;
export const Marker = _Marker as (
props: MarkerProps & React.RefAttributes<MapboxMarker>
) => React.ReactElement | null;
export type PopupProps = _PopupProps<PopupOptions, MapboxPopup>;
export const Popup = _Popup as (
props: PopupProps & React.RefAttributes<MapboxPopup>
) => React.ReactElement | null;
type AttributionControlOptions = ConstructorParameters<typeof MapboxAttributionControl>[0];
export type AttributionControlProps = _AttributionControlProps<AttributionControlOptions>;
export const AttributionControl = _AttributionControl as (
props: AttributionControlProps
) => React.ReactElement | null;
type FullscreenControlOptions = ConstructorParameters<typeof MapboxFullscreenControl>[0];
export type FullscreenControlProps = _FullscreenControlProps<FullscreenControlOptions>;
export const FullscreenControl = _FullscreenControl as (
props: FullscreenControlProps
) => React.ReactElement | null;
type NavigationControlOptions = ConstructorParameters<typeof MapboxNavigationControl>[0];
export type NavigationControlProps = _NavigationControlProps<NavigationControlOptions>;
export const NavigationControl = _NavigationControl as (
props: NavigationControlProps
) => React.ReactElement | null;
type GeolocateControlOptions = ConstructorParameters<typeof MapboxGeolocateControl>[0];
export type GeolocateControlProps = _GeolocateControlProps<
GeolocateControlOptions,
MapboxGeolocateControl
>;
export const GeolocateControl = _GeolocateControl as (
props: GeolocateControlProps & React.RefAttributes<MapboxGeolocateControl>
) => React.ReactElement | null;
type ScaleControlOptions = ConstructorParameters<typeof MapboxScaleControl>[0];
export type ScaleControlProps = _ScaleControlProps<ScaleControlOptions>;
export const ScaleControl = _ScaleControl as (
props: ScaleControlProps
) => React.ReactElement | null;
export type LayerProps = _LayerProps<AnyLayer>;
export const Layer = _Layer as (props: LayerProps) => React.ReactElement | null;
export type SourceProps = _SourceProps<AnySource>;
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<MapboxPopup>;
export type MarkerEvent = events.MarkerEvent<MapboxMarker>;
export type MarkerDragEvent = events.MarkerDragEvent<MapboxMarker>;
export type GeolocateEvent = events.GeolocateEvent<MapboxGeolocateControl>;
export type GeolocateResultEvent = events.GeolocateResultEvent<MapboxGeolocateControl>;
export type GeolocateErrorEvent = events.GeolocateErrorEvent<MapboxGeolocateControl>;
// v7.0 backward compatibility
/** @deprecated use `MapStyle` */
export type MapboxStyle = MapStyle;
export type {Map as MapboxMap, MapboxEvent, MapboxGeoJSONFeature} from 'mapbox-gl';

View File

@ -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';

View File

@ -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<MapT extends MapInstance> = {
getMap(): MapT;
} & Omit<MapT, (typeof skipMethods)[number]>;
export type MapRef = {
getMap(): MapInstance;
} & Omit<MapInstance, (typeof skipMethods)[number]>;
export default function createRef<
StyleT extends MapStyle,
CallbacksT extends Callbacks,
MapT extends MapInstance
>(mapInstance: Mapbox<StyleT, CallbacksT, MapT>): MapRef<MapT> | null {
export default function createRef(mapInstance: Mapbox): MapRef | null {
if (!mapInstance) {
return null;
}
const map = mapInstance.map as MapInstanceInternal<MapT>;
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,

View File

@ -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<ViewState> &
CallbacksT & {
export type MapboxProps = Partial<ViewState> &
MapCallbacks & {
// Init options
mapboxAccessToken?: string;
@ -59,19 +57,23 @@ export type MapboxProps<
// Styling
/** Mapbox style */
mapStyle?: string | StyleT | ImmutableLike<StyleT>;
mapStyle?: string | MapStyle | ImmutableLike<MapStyle>;
/** 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<MapT> = null;
private _map: MapInstanceInternal = null;
// User-supplied props
props: MapboxProps<StyleT, CallbacksT>;
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<StyleT, CallbacksT>,
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<StyleT, CallbacksT>) {
setProps(props: MapboxProps) {
const oldProps = this.props;
this.props = props;
@ -229,11 +227,8 @@ export default class Mapbox<
}
}
static reuse<StyleT extends MapStyle, CallbacksT extends Callbacks, MapT extends MapInstance>(
props: MapboxProps<StyleT, CallbacksT>,
container: HTMLDivElement
): Mapbox<StyleT, CallbacksT, MapT> {
const that = Mapbox.savedMaps.pop() as Mapbox<StyleT, CallbacksT, MapT>;
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<MapT>;
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<StyleT>): 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<StyleT>, 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<StyleT>, currProps: MapboxProps<StyleT>): 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<StyleT>, currProps: MapboxProps<StyleT>): 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<StyleT>, currProps: MapboxProps<StyleT>): 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<StyleT>, currProps: MapboxProps<StyleT>): 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<MapT>) => {
_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<MapT>).error); // eslint-disable-line
console.error((e as ErrorEvent).error); // eslint-disable-line
}
};
@ -598,7 +585,7 @@ export default class Mapbox<
}
}
_updateHover(e: MapMouseEvent<MapT>) {
_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<MapT>) => {
_onPointerEvent = (e: MapMouseEvent) => {
if (e.type === 'mousemove' || e.type === 'mouseout') {
this._updateHover(e);
}
@ -640,7 +627,7 @@ export default class Mapbox<
}
};
_onCameraEvent = (e: ViewStateChangeEvent<MapT>) => {
_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<MapT>, 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<MapT>).viewState = transformToViewState(tr);
(event as unknown as ViewStateChangeEvent).viewState = transformToViewState(tr);
}
if (this._map.isMoving()) {
// Replace map.transform with ours during the callbacks

View File

@ -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<GeoJSON.Geometry> & {
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<T> {
toJS: () => T;
}

View File

@ -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<Map>;
export type ViewStateChangeEvent = _ViewStateChangeEvent<Map>;
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;
};

View File

@ -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<SourceT extends Evented, OriginalEventT = undefined> {
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<SourceT, OriginalEventT = undefined> {
type: string;
target: SourceT;
originalEvent: OriginalEventT;
}
export type ErrorEvent<MapT extends MapInstance> = MapEvent<MapT> & {
type: 'error';
error: Error;
export type ViewStateChangeEvent = MapEvent<MouseEvent | TouchEvent | WheelEvent | undefined> & {
type:
| 'movestart'
| 'move'
| 'moveend'
| 'zoomstart'
| 'zoom'
| 'zoomend'
| 'rotatestart'
| 'rotate'
| 'rotateend'
| 'dragstart'
| 'drag'
| 'dragend'
| 'pitchstart'
| 'pitch'
| 'pitchend';
viewState: ViewState;
};
export type MapMouseEvent<MapT extends MapInstance> = MapEvent<MapT, MouseEvent> & {
point: Point;
lngLat: LngLat;
features?: MapGeoJSONFeature[];
};
export type ViewStateChangeEvent<MapT extends MapInstance> =
| (MapEvent<MapT, MouseEvent | TouchEvent | WheelEvent | undefined> & {
type: 'movestart' | 'move' | 'moveend' | 'zoomstart' | 'zoom' | 'zoomend';
viewState: ViewState;
})
| (MapEvent<MapT, MouseEvent | TouchEvent | undefined> & {
type:
| 'rotatestart'
| 'rotate'
| 'rotateend'
| 'dragstart'
| 'drag'
| 'dragend'
| 'pitchstart'
| 'pitch'
| 'pitchend';
viewState: ViewState;
});
export type PopupEvent<PopupT extends PopupInstance> = {
export type PopupEvent = {
type: 'open' | 'close';
target: PopupT;
target: Popup;
};
export type MarkerEvent<MarkerT extends MarkerInstance, OriginalEventT = undefined> = MapEvent<
MarkerT,
OriginalEventT
>;
export type MarkerEvent<OriginalEventT = undefined> = IMapEvent<Marker, OriginalEventT>;
export type MarkerDragEvent<MarkerT extends MarkerInstance> = MarkerEvent<MarkerT> & {
export type MarkerDragEvent = MarkerEvent & {
type: 'dragstart' | 'drag' | 'dragend';
target: MarkerT;
lngLat: LngLat;
};
export type GeolocateEvent<GeolocateControlT extends GeolocateControlInstance> =
MapEvent<GeolocateControlT>;
export type GeolocateEvent = IMapEvent<GeolocateControl>;
export type GeolocateResultEvent<GeolocateControlT extends GeolocateControlInstance> =
GeolocateEvent<GeolocateControlT> & GeolocationPosition;
export type GeolocateResultEvent = GeolocateEvent & GeolocationPosition;
export type GeolocateErrorEvent<GeolocateControlT extends GeolocateControlInstance> =
GeolocateEvent<GeolocateControlT> & GeolocationPositionError;
export type GeolocateErrorEvent = GeolocateEvent & GeolocationPositionError;

View File

@ -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.Geometry> | GeoJSON.FeatureCollection<GeoJSON.Geometry> | 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<string>): this;
setUrl(url: string): this;
}
export interface RasterSourceImplementation {
type: 'raster' | 'raster-dem';
setTiles(tiles: ReadonlyArray<string>): 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<HTMLImageElement | ImageData | ImageBitmap>;

View File

@ -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;
};
};

View File

@ -1,293 +1,67 @@
import type {PaddingOptions, LngLat, Point, LngLatLike, PointLike, ControlPosition} from './common';
export interface IControl<MapT extends MapInstance = MapInstance> {
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<this>, position?: ControlPosition);
removeControl(control: IControl<this>);
hasControl(control: IControl<this>): 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<typeof AttributionControlInstance>[0];
type GeolocateControlOptions = ConstructorParameters<typeof GeolocateControlInstance>[0];
type NavigationControlOptions = ConstructorParameters<typeof NavigationControlInstance>[0];
type ScaleControlOptions = ConstructorParameters<typeof ScaleControlInstance>[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<MapT extends MapInstance> {
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 extends MapInstance> = 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<TileDataT> {
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<TileDataT>;
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<TileDataT = unknown> {
id: string;
type: 'custom';
scheme: string;
minzoom: number;
maxzoom: number;
tileSize: number;
attribution: string;
_implementation: CustomSourceImplementation<TileDataT>;
ScaleControl: {new (options: ScaleControlOptions): ScaleControlInstance};
}

View File

@ -1,9 +0,0 @@
export type {ViewState, ControlPosition} from './common';
export type {
IControl,
MapInstance,
MapLib,
CustomLayerInterface,
CustomSource,
CustomSourceImplementation
} from './lib';

View File

@ -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'];

View File

@ -1,4 +1,4 @@
import type {PointLike} from '../types';
import type {PointLike} from '../types/common';
/**
* Compare two points

View File

@ -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'];

View File

@ -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';
/**

View File

@ -1 +1,3 @@
export * from '@vis.gl/react-mapbox';
export {Map as default} from '@vis.gl/react-mapbox';

View File

@ -1 +1,3 @@
export * from '@vis.gl/react-maplibre';
export {Map as default} from '@vis.gl/react-maplibre';

View File

@ -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 <Source> prop: ${changedKey}`);

View File

@ -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
};

View File

@ -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;