@deck.gl-community/experimental 9.2.0-beta.2 → 9.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,125 +0,0 @@
1
- // deck.gl-community
2
- // SPDX-License-Identifier: MIT
3
- // Copyright (c) vis.gl contributors
4
-
5
- import {Component, type ComponentChildren} from 'preact';
6
-
7
- const REPEAT_DELAY_MS = 300;
8
- const REPEAT_INTERVAL_MS = 100;
9
-
10
- export type LongPressButtonProps = {
11
- onClick: () => void;
12
- children: ComponentChildren;
13
- };
14
-
15
- export class LongPressButton extends Component<LongPressButtonProps> {
16
- buttonPressTimer: ReturnType<typeof setTimeout> | null = null;
17
- usingPointerEvents = false;
18
-
19
- private stopEvent(event: Event) {
20
- event.stopPropagation();
21
- if (typeof (event as any).stopImmediatePropagation === 'function') {
22
- (event as any).stopImmediatePropagation();
23
- }
24
- if (typeof (event as any).preventDefault === 'function') {
25
- (event as any).preventDefault();
26
- }
27
- }
28
-
29
- private repeat = () => {
30
- if (this.buttonPressTimer) {
31
- this.props.onClick();
32
- this.buttonPressTimer = setTimeout(this.repeat, REPEAT_INTERVAL_MS);
33
- }
34
- };
35
-
36
- private startPress(event: Event) {
37
- this.stopEvent(event);
38
- this.props.onClick();
39
- this.buttonPressTimer = setTimeout(this.repeat, REPEAT_DELAY_MS);
40
- }
41
-
42
- private endPress(event?: Event) {
43
- if (event) {
44
- this.stopEvent(event);
45
- }
46
- if (this.buttonPressTimer) {
47
- clearTimeout(this.buttonPressTimer);
48
- }
49
- this.buttonPressTimer = null;
50
- }
51
-
52
- private handlePointerDown = (event: PointerEvent) => {
53
- this.usingPointerEvents = true;
54
- (event.currentTarget as HTMLElement | null)?.setPointerCapture?.(event.pointerId);
55
- this.startPress(event);
56
- };
57
-
58
- private handlePointerUp = (event: PointerEvent) => {
59
- (event.currentTarget as HTMLElement | null)?.releasePointerCapture?.(event.pointerId);
60
- this.endPress(event);
61
- };
62
-
63
- private handlePointerCancel = (event: PointerEvent) => {
64
- (event.currentTarget as HTMLElement | null)?.releasePointerCapture?.(event.pointerId);
65
- this.endPress(event);
66
- };
67
-
68
- private handleMouseDown = (event: MouseEvent) => {
69
- if (this.usingPointerEvents) {
70
- return;
71
- }
72
- this.startPress(event);
73
- document.addEventListener('mouseup', this.handleMouseUp, {once: true});
74
- };
75
-
76
- private handleMouseUp = (event: MouseEvent) => {
77
- if (this.usingPointerEvents) {
78
- return;
79
- }
80
- this.endPress(event);
81
- };
82
-
83
- private handleTouchStart = (event: TouchEvent) => {
84
- if (this.usingPointerEvents) {
85
- return;
86
- }
87
- this.startPress(event);
88
- document.addEventListener('touchend', this.handleTouchEnd, {once: true});
89
- document.addEventListener('touchcancel', this.handleTouchEnd, {once: true});
90
- };
91
-
92
- private handleTouchEnd = (event: TouchEvent) => {
93
- if (this.usingPointerEvents) {
94
- return;
95
- }
96
- this.endPress(event);
97
- };
98
-
99
- render() {
100
- return (
101
- <div className="deck-widget-button">
102
- <div
103
- style={{pointerEvents: 'auto'}}
104
- onPointerDown={this.handlePointerDown}
105
- onPointerUp={this.handlePointerUp}
106
- onPointerCancel={this.handlePointerCancel}
107
- onPointerMove={(event) => this.stopEvent(event)}
108
- onPointerLeave={this.handlePointerCancel}
109
- onPointerOut={this.handlePointerCancel}
110
- onMouseDown={this.handleMouseDown}
111
- onMouseUp={this.handleMouseUp}
112
- onMouseMove={(event) => this.stopEvent(event)}
113
- onTouchStart={this.handleTouchStart}
114
- onTouchEnd={this.handleTouchEnd}
115
- onTouchMove={(event) => this.stopEvent(event)}
116
- onContextMenu={(event) => event.preventDefault()}
117
- onWheel={(event) => this.stopEvent(event)}
118
- onClick={(event) => this.stopEvent(event)}
119
- >
120
- {this.props.children}
121
- </div>
122
- </div>
123
- );
124
- }
125
- }
@@ -1,178 +0,0 @@
1
- // deck.gl-community
2
- // SPDX-License-Identifier: MIT
3
- // Copyright (c) vis.gl contributors
4
-
5
- import {render} from 'preact';
6
- import type {JSX} from 'preact';
7
- import {LongPressButton} from './long-press-button';
8
- import {
9
- Widget,
10
- type Deck,
11
- type Viewport,
12
- type WidgetPlacement,
13
- type WidgetProps
14
- } from '@deck.gl/core';
15
-
16
- export type PanWidgetProps = WidgetProps & {
17
- viewId?: string | null;
18
- placement?: WidgetPlacement;
19
- /** Amount in screen pixels to pan by when a button is pressed. */
20
- step?: number;
21
- };
22
-
23
- const WRAPPER_STYLE: Partial<CSSStyleDeclaration> = {
24
- position: 'absolute',
25
- display: 'flex',
26
- flexDirection: 'column',
27
- alignItems: 'center',
28
- zIndex: '99',
29
- userSelect: 'none'
30
- };
31
-
32
- const NAVIGATION_CONTAINER_STYLE: JSX.CSSProperties = {
33
- position: 'relative',
34
- background: '#f7f7f7',
35
- borderRadius: '23px',
36
- border: '0.5px solid #eaeaea',
37
- boxShadow: 'inset 11px 11px 5px -7px rgba(230, 230, 230, 0.49)',
38
- height: '46px',
39
- width: '46px'
40
- };
41
-
42
- const NAVIGATION_BUTTON_STYLE: JSX.CSSProperties = {
43
- color: '#848484',
44
- cursor: 'pointer',
45
- position: 'absolute',
46
- pointerEvents: 'auto'
47
- };
48
-
49
- export class PanWidget extends Widget<PanWidgetProps> {
50
- static override defaultProps = {
51
- id: 'pan',
52
- viewId: null,
53
- placement: 'top-left',
54
- step: 48,
55
- style: {},
56
- className: ''
57
- } satisfies Required<WidgetProps> & Required<Pick<PanWidgetProps, 'step'>> & PanWidgetProps;
58
-
59
- placement: WidgetPlacement = 'top-left';
60
- className = 'deck-widget-pan';
61
- deck?: Deck | null = null;
62
- step: number;
63
-
64
- constructor(props: PanWidgetProps = {}) {
65
- super({...PanWidget.defaultProps, ...props});
66
- this.viewId = props.viewId ?? null;
67
- this.placement = props.placement ?? 'top-left';
68
- this.step = props.step ?? PanWidget.defaultProps.step;
69
- }
70
-
71
- override setProps(props: Partial<PanWidgetProps>): void {
72
- if (props.viewId !== undefined) {
73
- this.viewId = props.viewId;
74
- }
75
- if (props.placement !== undefined) {
76
- this.placement = props.placement;
77
- }
78
- if (props.step !== undefined) {
79
- this.step = props.step;
80
- }
81
- super.setProps(props);
82
- }
83
-
84
- override onAdd({deck, viewId}: {deck: Deck; viewId: string | null}): void {
85
- this.deck = deck;
86
- if (this.viewId === undefined) {
87
- this.viewId = viewId;
88
- }
89
- }
90
-
91
- override onRemove(): void {
92
- this.deck = null;
93
- }
94
-
95
- override onRenderHTML(rootElement: HTMLElement): void {
96
- const style = {...WRAPPER_STYLE, ...this.props.style};
97
- Object.assign(rootElement.style, style);
98
-
99
- const buttons = [
100
- {top: -2, left: 14, onClick: () => this.handlePan(0, this.step), label: '▲', key: 'up'},
101
- {top: 12, left: 0, onClick: () => this.handlePan(this.step, 0), label: '◀', key: 'left'},
102
- {top: 12, left: 28, onClick: () => this.handlePan(-this.step, 0), label: '▶', key: 'right'},
103
- {top: 25, left: 14, onClick: () => this.handlePan(0, -this.step), label: '▼', key: 'down'}
104
- ] as const;
105
-
106
- const ui = (
107
- <div style={NAVIGATION_CONTAINER_STYLE}>
108
- {buttons.map((button) => (
109
- <div
110
- key={button.key}
111
- style={{
112
- ...NAVIGATION_BUTTON_STYLE,
113
- top: `${button.top}px`,
114
- left: `${button.left}px`
115
- } as JSX.CSSProperties}
116
- >
117
- <LongPressButton onClick={button.onClick}>{button.label}</LongPressButton>
118
- </div>
119
- ))}
120
- </div>
121
- );
122
-
123
- render(ui, rootElement);
124
- }
125
-
126
- private getTargetViewports(): Viewport[] {
127
- const deck = this.deck as (Deck & {viewManager?: any}) | null;
128
- if (!deck) {
129
- return [];
130
- }
131
-
132
- if (this.viewId) {
133
- const viewport = deck.viewManager?.getViewport(this.viewId);
134
- return viewport ? [viewport] : [];
135
- }
136
- return deck.getViewports();
137
- }
138
-
139
- private getViewState(viewport: Viewport): any {
140
- const viewManager = (this.deck as (Deck & {viewManager?: any}) | null)?.viewManager;
141
- const viewId = this.viewId || viewport.id;
142
- if (viewManager) {
143
- try {
144
- return {...viewManager.getViewState(viewId)};
145
- } catch (err) {
146
- return {...(viewManager.viewState as any)};
147
- }
148
- }
149
- return {...(viewport as any)};
150
- }
151
-
152
- private handlePan(deltaX: number, deltaY: number) {
153
- if (!this.deck) {
154
- return;
155
- }
156
-
157
- const viewports = this.getTargetViewports();
158
- for (const viewport of viewports) {
159
- const center = viewport.unproject([viewport.width / 2, viewport.height / 2]);
160
- if (!center) {
161
- continue;
162
- }
163
-
164
- const nextPixel: [number, number] = [
165
- viewport.width / 2 + deltaX,
166
- viewport.height / 2 + deltaY
167
- ];
168
-
169
- const viewState = this.getViewState(viewport);
170
- const panUpdate = viewport.panByPosition(center, nextPixel);
171
- const nextViewState = {...viewState, ...panUpdate};
172
- const viewId = this.viewId || viewport.id || 'default-view';
173
-
174
- // @ts-ignore Using private method until a public alternative is available
175
- this.deck._onViewStateChange({viewId, viewState: nextViewState, interactionState: {}});
176
- }
177
- }
178
- }
@@ -1,279 +0,0 @@
1
- // deck.gl-community
2
- // SPDX-License-Identifier: MIT
3
- // Copyright (c) vis.gl contributors
4
-
5
- import {render} from 'preact';
6
- import type {JSX} from 'preact';
7
- import {LongPressButton} from './long-press-button';
8
- import {
9
- Widget,
10
- type Deck,
11
- type Viewport,
12
- type WidgetPlacement,
13
- type WidgetProps
14
- } from '@deck.gl/core';
15
-
16
- export type ZoomRangeWidgetProps = WidgetProps & {
17
- viewId?: string | null;
18
- placement?: WidgetPlacement;
19
- minZoom?: number;
20
- maxZoom?: number;
21
- step?: number;
22
- };
23
-
24
- const WRAPPER_STYLE: Partial<CSSStyleDeclaration> = {
25
- position: 'absolute',
26
- display: 'flex',
27
- flexDirection: 'column',
28
- alignItems: 'center',
29
- background: '#f7f7f7',
30
- border: '0.5px solid #eaeaea',
31
- marginTop: '6px',
32
- padding: '2px 0',
33
- width: '18px',
34
- userSelect: 'none',
35
- pointerEvents: 'auto'
36
- };
37
-
38
- const ZOOM_BUTTON_STYLE: JSX.CSSProperties = {
39
- cursor: 'pointer',
40
- fontSize: '14px',
41
- fontWeight: '500',
42
- margin: '-4px'
43
- };
44
-
45
- const SLIDER_CONTAINER_STYLE: JSX.CSSProperties = {
46
- display: 'inline-block',
47
- height: '100px',
48
- padding: '0',
49
- width: '10px'
50
- };
51
-
52
- export class ZoomRangeWidget extends Widget<ZoomRangeWidgetProps> {
53
- static override defaultProps = {
54
- id: 'zoom-range',
55
- viewId: null,
56
- placement: 'top-left',
57
- minZoom: undefined,
58
- maxZoom: undefined,
59
- step: 0.1,
60
- style: {},
61
- className: ''
62
- } satisfies Required<WidgetProps> & Required<Pick<ZoomRangeWidgetProps, 'step'>> & ZoomRangeWidgetProps;
63
-
64
- placement: WidgetPlacement = 'top-left';
65
- className = 'deck-widget-zoom-range';
66
- deck?: Deck | null = null;
67
- step: number;
68
- currentZoom = 0;
69
- inferredMinZoom: number | null = null;
70
- inferredMaxZoom: number | null = null;
71
-
72
- constructor(props: ZoomRangeWidgetProps = {}) {
73
- super({...ZoomRangeWidget.defaultProps, ...props});
74
- this.viewId = props.viewId ?? null;
75
- this.placement = props.placement ?? 'top-left';
76
- this.step = props.step ?? ZoomRangeWidget.defaultProps.step;
77
- }
78
-
79
- override setProps(props: Partial<ZoomRangeWidgetProps>): void {
80
- if (props.viewId !== undefined) {
81
- this.viewId = props.viewId;
82
- }
83
- if (props.placement !== undefined) {
84
- this.placement = props.placement;
85
- }
86
- if (props.step !== undefined) {
87
- this.step = props.step;
88
- }
89
- super.setProps(props);
90
- }
91
-
92
- override onAdd({deck, viewId}: {deck: Deck; viewId: string | null}): void {
93
- this.deck = deck;
94
- if (this.viewId === undefined) {
95
- this.viewId = viewId;
96
- }
97
- }
98
-
99
- override onRemove(): void {
100
- this.deck = null;
101
- }
102
-
103
- override onRenderHTML(rootElement: HTMLElement): void {
104
- const style = {...WRAPPER_STYLE, ...this.props.style};
105
- Object.assign(rootElement.style, style);
106
-
107
- const {minZoom, maxZoom} = this.getZoomBounds();
108
- const clampedZoom = Math.max(minZoom, Math.min(maxZoom, this.currentZoom));
109
-
110
- const stopEventPropagation = (event: Event) => {
111
- event.stopPropagation();
112
- if (typeof (event as any).stopImmediatePropagation === 'function') {
113
- (event as any).stopImmediatePropagation();
114
- }
115
- };
116
-
117
- const ui = (
118
- <>
119
- <div style={ZOOM_BUTTON_STYLE}>
120
- <LongPressButton onClick={() => this.handleZoomDelta(this.step)}>{'+'}</LongPressButton>
121
- </div>
122
- <div
123
- style={SLIDER_CONTAINER_STYLE}
124
- onPointerDown={stopEventPropagation}
125
- onPointerMove={stopEventPropagation}
126
- onPointerUp={stopEventPropagation}
127
- onMouseDown={stopEventPropagation}
128
- onMouseMove={stopEventPropagation}
129
- onMouseUp={stopEventPropagation}
130
- onClick={stopEventPropagation}
131
- onWheel={stopEventPropagation}
132
- onTouchStart={stopEventPropagation}
133
- onTouchMove={stopEventPropagation}
134
- onTouchEnd={stopEventPropagation}
135
- >
136
- <input
137
- type="range"
138
- value={clampedZoom}
139
- min={minZoom}
140
- max={maxZoom}
141
- step={this.step}
142
- onInput={(event) =>
143
- this.handleZoomTo(Number((event.target as HTMLInputElement).value))
144
- }
145
- onChange={(event) =>
146
- this.handleZoomTo(Number((event.target as HTMLInputElement).value))
147
- }
148
- onPointerDown={stopEventPropagation}
149
- onPointerMove={stopEventPropagation}
150
- onPointerUp={stopEventPropagation}
151
- onMouseDown={stopEventPropagation}
152
- onMouseMove={stopEventPropagation}
153
- onMouseUp={stopEventPropagation}
154
- onClick={stopEventPropagation}
155
- onWheel={stopEventPropagation}
156
- onTouchStart={stopEventPropagation}
157
- onTouchMove={stopEventPropagation}
158
- onTouchEnd={stopEventPropagation}
159
- /* @ts-expect-error - non-standard attribute for vertical sliders */
160
- orient="vertical"
161
- style={
162
- {
163
- writingMode: 'vertical-lr',
164
- height: '100px',
165
- padding: '0',
166
- margin: '0',
167
- width: '10px'
168
- } as JSX.CSSProperties
169
- }
170
- />
171
- </div>
172
- <div style={ZOOM_BUTTON_STYLE}>
173
- <LongPressButton onClick={() => this.handleZoomDelta(-this.step)}>{'-'}</LongPressButton>
174
- </div>
175
- </>
176
- );
177
-
178
- render(ui, rootElement);
179
- }
180
-
181
- override onViewportChange(viewport: Viewport): void {
182
- const viewState = this.getViewState(viewport);
183
- const zoom = Number(viewState?.zoom);
184
- if (Number.isFinite(zoom)) {
185
- this.currentZoom = zoom;
186
- }
187
-
188
- if (this.props.minZoom === undefined) {
189
- const minZoom = Number(viewState?.minZoom);
190
- if (Number.isFinite(minZoom)) {
191
- this.inferredMinZoom = minZoom;
192
- }
193
- }
194
-
195
- if (this.props.maxZoom === undefined) {
196
- const maxZoom = Number(viewState?.maxZoom);
197
- if (Number.isFinite(maxZoom)) {
198
- this.inferredMaxZoom = maxZoom;
199
- }
200
- }
201
-
202
- this.updateHTML();
203
- }
204
-
205
- private getZoomBounds(): {minZoom: number; maxZoom: number} {
206
- const minZoom =
207
- this.props.minZoom ?? this.inferredMinZoom ?? Number.NEGATIVE_INFINITY;
208
- const maxZoom =
209
- this.props.maxZoom ?? this.inferredMaxZoom ?? Number.POSITIVE_INFINITY;
210
-
211
- if (minZoom > maxZoom) {
212
- return {minZoom: maxZoom, maxZoom: minZoom};
213
- }
214
- return {
215
- minZoom: Number.isFinite(minZoom) ? minZoom : -20,
216
- maxZoom: Number.isFinite(maxZoom) ? maxZoom : 20
217
- };
218
- }
219
-
220
- private getTargetViewports(): Viewport[] {
221
- const deck = this.deck as (Deck & {viewManager?: any}) | null;
222
- if (!deck) {
223
- return [];
224
- }
225
- if (this.viewId) {
226
- const viewport = deck.viewManager?.getViewport(this.viewId);
227
- return viewport ? [viewport] : [];
228
- }
229
- return deck.getViewports();
230
- }
231
-
232
- private getViewState(viewport: Viewport): any {
233
- const viewManager = (this.deck as (Deck & {viewManager?: any}) | null)?.viewManager;
234
- const viewId = this.viewId || viewport.id;
235
- if (viewManager) {
236
- try {
237
- return {...viewManager.getViewState(viewId)};
238
- } catch (err) {
239
- return {...(viewManager.viewState as any)};
240
- }
241
- }
242
- return {...(viewport as any)};
243
- }
244
-
245
- private handleZoomDelta(delta: number) {
246
- const {minZoom, maxZoom} = this.getZoomBounds();
247
-
248
- for (const viewport of this.getTargetViewports()) {
249
- const viewState = this.getViewState(viewport);
250
- const baseZoom = Number(viewState.zoom);
251
- const current = Number.isFinite(baseZoom) ? baseZoom : this.currentZoom;
252
- const nextZoom = Math.max(minZoom, Math.min(maxZoom, current + delta));
253
- this.updateViewState(viewport, {...viewState, zoom: nextZoom});
254
- }
255
- }
256
-
257
- private handleZoomTo(zoom: number) {
258
- const {minZoom, maxZoom} = this.getZoomBounds();
259
- const nextZoom = Math.max(minZoom, Math.min(maxZoom, zoom));
260
-
261
- for (const viewport of this.getTargetViewports()) {
262
- const viewState = this.getViewState(viewport);
263
- this.updateViewState(viewport, {...viewState, zoom: nextZoom});
264
- }
265
- }
266
-
267
- private updateViewState(viewport: Viewport, viewState: any) {
268
- if (!this.deck) {
269
- return;
270
- }
271
-
272
- const viewId = this.viewId || viewport.id || 'default-view';
273
- this.currentZoom = Number(viewState.zoom) || this.currentZoom;
274
- this.updateHTML();
275
-
276
- // @ts-ignore Using private method until a public alternative is available
277
- this.deck._onViewStateChange({viewId, viewState, interactionState: {}});
278
- }
279
- }