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: 10, right: 10, bottom: 'auto', left: 'auto' },
|
17 | 'top-left': { top: 10, left: 10, bottom: 'auto', right: 'auto' },
|
18 | 'bottom-right': { bottom: 10, right: 10, top: 'auto', left: 'auto' },
|
19 | 'bottom-left': { bottom: 10, 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 | backgroundImage: `url('https://api.mapbox.com/mapbox.js/v2.4.0/images/icons-000000@2x.png')`,
|
31 | backgroundPosition: '0px 0px',
|
32 | backgroundSize: '26px 260px',
|
33 | outline: 0
|
34 | };
|
35 |
|
36 | const buttonStyleHovered = {
|
37 | backgroundColor: '#fff',
|
38 | opacity: 1
|
39 | };
|
40 |
|
41 | const buttonStylePlus = {
|
42 | borderBottom: '1px solid rgba(0, 0, 0, 0.1)',
|
43 | borderTopLeftRadius: 2,
|
44 | borderTopRightRadius: 2
|
45 | };
|
46 |
|
47 | const buttonStyleMinus = {
|
48 | backgroundPosition: '0px -26px',
|
49 | borderBottomLeftRadius: 2,
|
50 | borderBottomRightRadius: 2
|
51 | };
|
52 |
|
53 | const [PLUS, MINUS] = [0, 1];
|
54 | const POSITIONS = Object.keys(positions);
|
55 |
|
56 | export interface Props {
|
57 | zoomDiff?: number;
|
58 | onControlClick?: (map: Map, zoomDiff: number) => void;
|
59 | position?: AnchorLimits;
|
60 | style?: React.CSSProperties;
|
61 | className?: string;
|
62 | tabIndex?: number;
|
63 | map: Map;
|
64 | }
|
65 |
|
66 | export interface State {
|
67 | hover?: number;
|
68 | }
|
69 |
|
70 | export class ZoomControl extends React.Component<Props, State> {
|
71 | public static defaultProps = {
|
72 | position: POSITIONS[0],
|
73 | zoomDiff: 0.5,
|
74 | onControlClick: (map: Map, zoomDiff: number) => {
|
75 | map.zoomTo(map.getZoom() + zoomDiff);
|
76 | }
|
77 | };
|
78 |
|
79 | public state = {
|
80 | hover: undefined
|
81 | };
|
82 |
|
83 | private onMouseOut = () => {
|
84 | this.setState({ hover: undefined });
|
85 | };
|
86 |
|
87 | private plusOver = () => {
|
88 | if (PLUS !== this.state.hover) {
|
89 | this.setState({ hover: PLUS });
|
90 | }
|
91 | };
|
92 |
|
93 | private minusOver = () => {
|
94 | if (MINUS !== this.state.hover) {
|
95 | this.setState({ hover: MINUS });
|
96 | }
|
97 | };
|
98 |
|
99 | private onClickPlus = () => {
|
100 | this.props.onControlClick!(this.props.map, this.props.zoomDiff!);
|
101 | };
|
102 |
|
103 | private onClickMinus = () => {
|
104 | this.props.onControlClick!(this.props.map, -this.props.zoomDiff!);
|
105 | };
|
106 |
|
107 | public render() {
|
108 | const { position, style, className, tabIndex } = this.props;
|
109 | const { hover } = this.state;
|
110 | const plusStyle = {
|
111 | ...buttonStyle,
|
112 | ...buttonStylePlus,
|
113 | ...(hover === PLUS ? buttonStyleHovered : {})
|
114 | };
|
115 | const minusStyle = {
|
116 | ...buttonStyle,
|
117 | ...buttonStyleMinus,
|
118 | ...(hover === MINUS ? buttonStyleHovered : {})
|
119 | };
|
120 |
|
121 | return (
|
122 | <div
|
123 | className={className}
|
124 | tabIndex={tabIndex}
|
125 | style={{ ...containerStyle, ...positions[position!], ...style }}
|
126 | >
|
127 | <button
|
128 | id="zoomIn"
|
129 | type="button"
|
130 | style={plusStyle}
|
131 | aria-label="Zoom in"
|
132 | onMouseOver={this.plusOver}
|
133 | onMouseOut={this.onMouseOut}
|
134 | onClick={this.onClickPlus}
|
135 | />
|
136 | <button
|
137 | id="zoomOut"
|
138 | type="button"
|
139 | style={minusStyle}
|
140 | aria-label="Zoom out"
|
141 | onMouseOver={this.minusOver}
|
142 | onMouseOut={this.onMouseOut}
|
143 | onClick={this.onClickMinus}
|
144 | />
|
145 | </div>
|
146 | );
|
147 | }
|
148 | }
|
149 |
|
150 | export default withMap(ZoomControl);
|