-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathcompass-widget.tsx
More file actions
140 lines (126 loc) · 4.52 KB
/
Copy pathcompass-widget.tsx
File metadata and controls
140 lines (126 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import {Widget, FlyToInterpolator, WebMercatorViewport, _GlobeViewport} from '@deck.gl/core';
import type {Viewport, WidgetPlacement, WidgetProps} from '@deck.gl/core';
import {render} from 'preact';
import {Tooltip} from './lib/components/tooltip';
export type CompassWidgetProps = WidgetProps & {
/** Widget positioning within the view. Default 'top-left'. */
placement?: WidgetPlacement;
/** View to attach to and interact with. Required when using multiple views. */
viewId?: string | null;
/** Tooltip message. */
label?: string;
/** Custom tooltip content. Overrides label for tooltip display. */
tooltip?: string | HTMLElement | false;
/** Bearing and pitch reset transition duration in ms. */
transitionDuration?: number;
/**
* Callback when the compass reset button is clicked.
* Called for each viewport that will be reset.
*/
onReset?: (params: {
/** The view being reset */
viewId: string;
/** The new bearing value (0) */
bearing: number;
/** The new pitch value (0 if bearing was already 0) */
pitch: number;
}) => void;
};
export class CompassWidget extends Widget<CompassWidgetProps> {
static defaultProps: Required<CompassWidgetProps> = {
...Widget.defaultProps,
id: 'compass',
placement: 'top-left',
viewId: null,
label: 'Reset Compass',
tooltip: undefined!,
transitionDuration: 200,
onReset: () => {}
};
className = 'deck-widget-compass';
placement: WidgetPlacement = 'top-left';
viewports: {[id: string]: Viewport} = {};
constructor(props: CompassWidgetProps = {}) {
super(props);
this.setProps(this.props);
}
setProps(props: Partial<CompassWidgetProps>) {
this.placement = props.placement ?? this.placement;
this.viewId = props.viewId ?? this.viewId;
super.setProps(props);
}
onRenderHTML(rootElement: HTMLElement): void {
const viewId = this.viewId || Object.values(this.viewports)[0]?.id;
const widgetViewport = this.viewports[viewId];
const [rz, rx] = this.getRotation(widgetViewport);
const tooltipContent =
this.props.tooltip === false ? undefined : (this.props.tooltip ?? this.props.label);
const ui = (
<div className="deck-widget-button" style={{perspective: 100}}>
<Tooltip content={tooltipContent}>
<button
type="button"
onClick={() => {
for (const viewport of Object.values(this.viewports)) {
this.handleCompassReset(viewport);
}
}}
aria-label={this.props.label}
style={{transform: `rotateX(${rx}deg)`}}
>
<svg fill="none" width="100%" height="100%" viewBox="0 0 26 26">
<g transform={`rotate(${rz},13,13)`}>
<path
d="M10 13.0001L12.9999 5L15.9997 13.0001H10Z"
fill="var(--icon-compass-north-color, rgb(240, 92, 68))"
/>
<path
d="M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z"
fill="var(--icon-compass-south-color, rgb(204, 204, 204))"
/>
</g>
</svg>
</button>
</Tooltip>
</div>
);
render(ui, rootElement);
}
onViewportChange(viewport: Viewport) {
// no need to update if viewport is the same
if (!viewport.equals(this.viewports[viewport.id])) {
this.viewports[viewport.id] = viewport;
this.updateHTML();
}
}
getRotation(viewport?: Viewport) {
if (viewport instanceof WebMercatorViewport) {
return [-viewport.bearing, viewport.pitch];
} else if (viewport instanceof _GlobeViewport) {
return [0, Math.max(-80, Math.min(80, viewport.latitude))];
}
return [0, 0];
}
handleCompassReset(viewport: Viewport) {
const viewId = this.viewId || viewport.id;
if (viewport instanceof WebMercatorViewport) {
const viewState = this.getViewState(viewId);
const resetPitch = this.getRotation(viewport)[0] === 0;
const nextBearing = 0;
const nextPitch = resetPitch ? 0 : viewport.pitch;
// Call callback
this.props.onReset?.({viewId, bearing: nextBearing, pitch: nextPitch});
const nextViewState = {
...viewState,
bearing: nextBearing,
...(resetPitch ? {pitch: nextPitch} : {}),
transitionDuration: this.props.transitionDuration,
transitionInterpolator: new FlyToInterpolator()
};
this.setViewState(viewId, nextViewState);
}
}
}