1 | import * as React from 'react';
|
2 | import { Map, Point } from 'mapbox-gl';
|
3 | import { OverlayParams, overlayState, overlayTransform } from './util/overlays';
|
4 | import { Anchor } from './util/types';
|
5 | import { withMap } from './context';
|
6 |
|
7 | const defaultStyle = {
|
8 | zIndex: 3
|
9 | };
|
10 |
|
11 | export 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 |
|
29 | export 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 |
|
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 |
|
58 |
|
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 |
|
126 | export default withMap(ProjectedLayer);
|