1 | import * as React from 'react';
|
2 | import { Map } from 'mapbox-gl';
|
3 | import { AnchorLimits } from './util/types';
|
4 | import { withMap } from './context';
|
5 |
|
6 | const containerStyle: React.CSSProperties = {
|
7 | position: 'absolute',
|
8 | zIndex: 10,
|
9 | display: 'flex',
|
10 | flexDirection: 'column',
|
11 | boxShadow: '0px 1px 4px rgba(0, 0, 0, .3)',
|
12 | border: '1px solid rgba(0, 0, 0, 0.1)'
|
13 | };
|
14 |
|
15 | const positions = {
|
16 | 'top-right': { top: 62, right: 10, bottom: 'auto', left: 'auto' },
|
17 | 'top-left': { top: 62, left: 10, bottom: 'auto', right: 'auto' },
|
18 | 'bottom-right': { bottom: 63, right: 10, top: 'auto', left: 'auto' },
|
19 | 'bottom-left': { bottom: 63, left: 10, top: 'auto', right: 'auto' }
|
20 | };
|
21 |
|
22 | const buttonStyle = {
|
23 | backgroundColor: '#f9f9f9',
|
24 | opacity: 0.95,
|
25 | transition: 'background-color 0.16s ease-out',
|
26 | cursor: 'pointer',
|
27 | border: 0,
|
28 | height: 26,
|
29 | width: 26,
|
30 | outline: 0,
|
31 | padding: 3
|
32 | };
|
33 |
|
34 | const buttonStyleHovered = {
|
35 | backgroundColor: '#fff',
|
36 | opacity: 1
|
37 | };
|
38 |
|
39 | const buttonStyleCompass = {
|
40 | borderBottom: '1px solid rgba(0, 0, 0, 0.1)',
|
41 | borderTopLeftRadius: 2,
|
42 | borderTopRightRadius: 2,
|
43 | borderBottomLeftRadius: 2,
|
44 | borderBottomRightRadius: 2
|
45 | };
|
46 |
|
47 | const Icon = () => (
|
48 | <svg viewBox="0 0 20 20">
|
49 | <polygon fill="#333333" points="6,9 10,1 14,9" />
|
50 | <polygon fill="#CCCCCC" points="6,11 10,19 14,11" />
|
51 | </svg>
|
52 | );
|
53 |
|
54 | const compassSpan = {
|
55 | width: 20,
|
56 | height: 20,
|
57 | display: 'inline-block'
|
58 | };
|
59 |
|
60 | const [COMPASS] = [0];
|
61 | const POSITIONS = Object.keys(positions);
|
62 |
|
63 | export interface Props {
|
64 | position?: AnchorLimits;
|
65 | style?: React.CSSProperties;
|
66 | className?: string;
|
67 | tabIndex?: number;
|
68 | map: Map;
|
69 | }
|
70 |
|
71 | export interface State {
|
72 | hover?: number;
|
73 | }
|
74 |
|
75 | export class RotationControl extends React.Component<Props, State> {
|
76 | public static defaultProps = {
|
77 | position: POSITIONS[0]
|
78 | };
|
79 |
|
80 | public state = {
|
81 | hover: undefined
|
82 | };
|
83 |
|
84 | public componentDidMount() {
|
85 | this.props.map.on('rotate', this.onMapRotate);
|
86 | }
|
87 |
|
88 | public componentWillUnmount() {
|
89 | this.props.map.off('rotate', this.onMapRotate);
|
90 | }
|
91 |
|
92 | public compassIcon: HTMLSpanElement | null = null;
|
93 |
|
94 | private onMouseOut = () => {
|
95 | if (!this.state.hover) {
|
96 | this.setState({ hover: undefined });
|
97 | }
|
98 | };
|
99 |
|
100 | private onMouseIn = () => {
|
101 | if (COMPASS !== this.state.hover) {
|
102 | this.setState({ hover: COMPASS });
|
103 | }
|
104 | };
|
105 |
|
106 | private onClickCompass = () => {
|
107 | this.props.map.resetNorth();
|
108 | };
|
109 |
|
110 | private onMapRotate = () => {
|
111 | const { map } = this.props;
|
112 |
|
113 | const rotate = `rotate(${(map as any).transform.angle *
|
114 | (180 / Math.PI)}deg)`;
|
115 |
|
116 | if (this.compassIcon) {
|
117 | this.compassIcon.style.transform = rotate;
|
118 | }
|
119 | };
|
120 |
|
121 | private assignRef = (icon: HTMLSpanElement | null) => {
|
122 | this.compassIcon = icon;
|
123 | };
|
124 |
|
125 | public render() {
|
126 | const { position, style, className, tabIndex } = this.props;
|
127 | const { hover } = this.state;
|
128 | const controlStyle = {
|
129 | ...buttonStyle,
|
130 | ...buttonStyleCompass,
|
131 | ...(hover === COMPASS ? buttonStyleHovered : {})
|
132 | };
|
133 |
|
134 | return (
|
135 | <div
|
136 | className={className}
|
137 | tabIndex={tabIndex}
|
138 | style={{ ...containerStyle, ...positions[position!], ...style }}
|
139 | >
|
140 | <button
|
141 | style={controlStyle}
|
142 | onMouseOver={this.onMouseIn}
|
143 | onMouseOut={this.onMouseOut}
|
144 | onClick={this.onClickCompass}
|
145 | >
|
146 | <span ref={this.assignRef} style={compassSpan}>
|
147 | <Icon />
|
148 | </span>
|
149 | </button>
|
150 | </div>
|
151 | );
|
152 | }
|
153 | }
|
154 |
|
155 | export default withMap(RotationControl);
|