import * as React from 'react'; import { Map, Point } from 'mapbox-gl'; import { OverlayParams, overlayState, overlayTransform } from './util/overlays'; import { Anchor } from './util/types'; import { withMap } from './context'; const defaultStyle = { zIndex: 3 }; export interface Props { type: 'marker' | 'popup'; coordinates: [number, number]; anchor?: Anchor; offset?: number | [number, number] | Point; children?: JSX.Element | JSX.Element[]; onClick?: React.MouseEventHandler; onDoubleClick?: React.MouseEventHandler; onMouseEnter?: React.MouseEventHandler; onMouseLeave?: React.MouseEventHandler; onScroll?: React.UIEventHandler; onWheel?: React.MouseEventHandler; style?: React.CSSProperties; className: string; tabIndex?: number; map: Map; } export class ProjectedLayer extends React.Component { private container: HTMLElement | undefined = undefined; private prevent: boolean = false; public static defaultProps = { offset: 0, // tslint:disable-next-line:no-any onClick: (...args: any[]) => args }; public state: OverlayParams = {}; private setContainer = (el: HTMLElement | null) => { if (el) { this.container = el; } }; private handleMapMove = () => { if (!this.prevent) { this.setState(overlayState(this.props, this.props.map, this.container!)); } }; public componentDidMount() { const { map } = this.props; map.on('move', this.handleMapMove); // Now this.container is rendered and the size of container is known. // Recalculate the anchor/position this.handleMapMove(); } private havePropsChanged(props: Props, prevProps: Props) { return ( props.coordinates[0] !== prevProps.coordinates[0] || props.coordinates[1] !== prevProps.coordinates[1] || props.offset !== prevProps.offset || props.anchor !== prevProps.anchor ); } public componentDidUpdate(prevProps: Props) { if (this.havePropsChanged(this.props, prevProps)) { this.setState(overlayState(this.props, this.props.map, this.container!)); } } public componentWillUnmount() { const { map } = this.props; this.prevent = true; map.off('move', this.handleMapMove); } public render() { const { style, children, className, onClick, onDoubleClick, onMouseEnter, onMouseLeave, onScroll, onWheel, type, tabIndex } = this.props; const { anchor } = this.state; const finalStyle = { ...defaultStyle, ...style, transform: overlayTransform(this.state).join(' ') }; const anchorClassname = anchor && type === 'popup' ? `mapboxgl-popup-anchor-${anchor}` : ''; return (
{children}
); } } export default withMap(ProjectedLayer);