UNPKG

3.29 kBTypeScriptView Raw
1import * as React from 'react';
2import { Map, Point } from 'mapbox-gl';
3import { OverlayParams, overlayState, overlayTransform } from './util/overlays';
4import { Anchor } from './util/types';
5import { withMap } from './context';
6
7const defaultStyle = {
8 zIndex: 3
9};
10
11export interface Props {
12 type: 'marker' | 'popup';
13 coordinates: [number, number];
14 anchor?: Anchor;
15 offset?: number | [number, number] | Point;
16 children?: JSX.Element | JSX.Element[];
17 onClick?: React.MouseEventHandler<HTMLDivElement>;
18 onDoubleClick?: React.MouseEventHandler<HTMLDivElement>;
19 onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
20 onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
21 onScroll?: React.UIEventHandler<HTMLDivElement>;
22 onWheel?: React.MouseEventHandler<HTMLDivElement>;
23 style?: React.CSSProperties;
24 className: string;
25 tabIndex?: number;
26 map: Map;
27}
28
29export class ProjectedLayer extends React.Component<Props, OverlayParams> {
30 private container: HTMLElement | undefined = undefined;
31 private prevent: boolean = false;
32
33 public static defaultProps = {
34 offset: 0,
35 // tslint:disable-next-line:no-any
36 onClick: (...args: any[]) => args
37 };
38
39 public state: OverlayParams = {};
40
41 private setContainer = (el: HTMLElement | null) => {
42 if (el) {
43 this.container = el;
44 }
45 };
46
47 private handleMapMove = () => {
48 if (!this.prevent) {
49 this.setState(overlayState(this.props, this.props.map, this.container!));
50 }
51 };
52
53 public componentDidMount() {
54 const { map } = this.props;
55
56 map.on('move', this.handleMapMove);
57 // Now this.container is rendered and the size of container is known.
58 // Recalculate the anchor/position
59 this.handleMapMove();
60 }
61
62 private havePropsChanged(props: Props, prevProps: Props) {
63 return (
64 props.coordinates[0] !== prevProps.coordinates[0] ||
65 props.coordinates[1] !== prevProps.coordinates[1] ||
66 props.offset !== prevProps.offset ||
67 props.anchor !== prevProps.anchor
68 );
69 }
70
71 public componentDidUpdate(prevProps: Props) {
72 if (this.havePropsChanged(this.props, prevProps)) {
73 this.setState(overlayState(this.props, this.props.map, this.container!));
74 }
75 }
76
77 public componentWillUnmount() {
78 const { map } = this.props;
79
80 this.prevent = true;
81
82 map.off('move', this.handleMapMove);
83 }
84
85 public render() {
86 const {
87 style,
88 children,
89 className,
90 onClick,
91 onDoubleClick,
92 onMouseEnter,
93 onMouseLeave,
94 onScroll,
95 onWheel,
96 type,
97 tabIndex
98 } = this.props;
99 const { anchor } = this.state;
100 const finalStyle = {
101 ...defaultStyle,
102 ...style,
103 transform: overlayTransform(this.state).join(' ')
104 };
105 const anchorClassname =
106 anchor && type === 'popup' ? `mapboxgl-popup-anchor-${anchor}` : '';
107 return (
108 <div
109 className={`${className} ${anchorClassname}`}
110 onClick={onClick}
111 onDoubleClick={onDoubleClick}
112 onMouseEnter={onMouseEnter}
113 onMouseLeave={onMouseLeave}
114 onScroll={onScroll}
115 onWheel={onWheel}
116 style={finalStyle}
117 ref={this.setContainer}
118 tabIndex={tabIndex}
119 >
120 {children}
121 </div>
122 );
123 }
124}
125
126export default withMap(ProjectedLayer);