UNPKG

3.53 kBTypeScriptView Raw
1import * as React from 'react';
2import { Map } from 'mapbox-gl';
3import { AnchorLimits } from './util/types';
4import { withMap } from './context';
5
6const 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
15const 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
22const 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
34const buttonStyleHovered = {
35 backgroundColor: '#fff',
36 opacity: 1
37};
38
39const 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
47const 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
54const compassSpan = {
55 width: 20,
56 height: 20,
57 display: 'inline-block'
58};
59
60const [COMPASS] = [0];
61const POSITIONS = Object.keys(positions);
62
63export interface Props {
64 position?: AnchorLimits;
65 style?: React.CSSProperties;
66 className?: string;
67 tabIndex?: number;
68 map: Map;
69}
70
71export interface State {
72 hover?: number;
73}
74
75export 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 // tslint:disable-next-line:no-any
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
155export default withMap(RotationControl);