diff --git a/examples/get-started/pure-js/widgets/app.js b/examples/get-started/pure-js/widgets/app.js index 27f01104bf2..0cb331639b8 100644 --- a/examples/get-started/pure-js/widgets/app.js +++ b/examples/get-started/pure-js/widgets/app.js @@ -2,6 +2,7 @@ import {Deck} from '@deck.gl/core'; import {GeoJsonLayer, ArcLayer} from '@deck.gl/layers'; import { CompassWidget, + PopupWidget, ZoomWidget, FullscreenWidget, DarkGlassTheme, @@ -27,9 +28,29 @@ const INITIAL_VIEW_STATE = { pitch: 30 }; -new Deck({ +const UI_WIDGETS = [ + new ZoomWidget({style: widgetTheme}), + new CompassWidget({style: widgetTheme}), + new FullscreenWidget({style: widgetTheme}) +]; + +function updatePopup(object) { + const widgets = [...UI_WIDGETS]; + if (object) { + const position = object.geometry.coordinates; + const text = `${object.properties.name} (${object.properties.abbrev})`; + const style = {width: 200, boxShadow: 'rgba(0, 0, 0, 0.5) 2px 2px 5px'}; + widgets.push(new PopupWidget({position, text, style})); + } + + deck.setProps({widgets}); + return true; +} + +const deck = new Deck({ initialViewState: INITIAL_VIEW_STATE, controller: true, + onClick: () => updatePopup(), layers: [ new GeoJsonLayer({ id: 'base-map', @@ -54,9 +75,7 @@ new Deck({ // Interactive props pickable: true, autoHighlight: true, - onClick: info => - // eslint-disable-next-line - info.object && alert(`${info.object.properties.name} (${info.object.properties.abbrev})`) + onClick: info => updatePopup(info.object) }), new ArcLayer({ id: 'arcs', @@ -70,9 +89,5 @@ new Deck({ getWidth: 1 }) ], - widgets: [ - new ZoomWidget({style: widgetTheme}), - new CompassWidget({style: widgetTheme}), - new FullscreenWidget({style: widgetTheme}) - ] + widgets: UI_WIDGETS }); diff --git a/modules/widgets/src/index.ts b/modules/widgets/src/index.ts index 73d986ad5c3..9bd47625bf0 100644 --- a/modules/widgets/src/index.ts +++ b/modules/widgets/src/index.ts @@ -1,5 +1,6 @@ export {FullscreenWidget} from './fullscreen-widget'; export {CompassWidget} from './compass-widget'; +export {PopupWidget} from './popup-widget'; export {ZoomWidget} from './zoom-widget'; export * from './themes'; diff --git a/modules/widgets/src/popup-widget.tsx b/modules/widgets/src/popup-widget.tsx new file mode 100644 index 00000000000..c5afed10412 --- /dev/null +++ b/modules/widgets/src/popup-widget.tsx @@ -0,0 +1,97 @@ +/* global document */ +import {FlyToInterpolator, WebMercatorViewport, _GlobeViewport} from '@deck.gl/core'; +import type {Deck, Viewport, Widget, WidgetPlacement} from '@deck.gl/core'; +import {render} from 'preact'; + +interface PopupWidgetProps { + id: string; + /** + * View to attach to and interact with. Required when using multiple views. + */ + viewId?: string | null; + /** + * CSS inline style overrides. + */ + style?: Partial; + /** + * Additional CSS class. + */ + className?: string; + /** + * Position at which to place popup + */ + position: [number, number]; + /** + * Text of popup + */ + text: string; +} + +export class PopupWidget implements Widget { + id = 'popup'; + props: PopupWidgetProps; + viewId?: string | null = null; + viewport?: Viewport; + deck?: Deck; + element?: HTMLDivElement; + + constructor(props: PopupWidgetProps) { + this.id = props.id || 'popup'; + this.viewId = props.viewId || null; + props.style = props.style || {}; + props.position = props.position || [0, 0]; + props.text = props.text || ''; + this.props = props; + } + + setProps(props: Partial) { + Object.assign(this.props, props); + this.update(); + } + + onViewportChange(viewport) { + this.viewport = viewport; + this.update(); + } + + onAdd({deck, viewId}: {deck: Deck, viewId: string | null}): HTMLDivElement { + const {className} = this.props; + const element = document.createElement('div'); + element.classList.add('deck-widget', 'deck-widget-popup'); + if (className) element.classList.add(className); + const style = {margin: '0px', top: '0px', left: '0px', position: 'absolute'}; + Object.entries(style).map(([key, value]) => element.style.setProperty(key, value as string)); + this.deck = deck; + if (!viewId) { + this.viewport = deck.getViewports()[0]; + } else { + this.viewport = deck.getViewports().find(viewport => viewport.id === viewId); + } + this.element = element; + this.update(); + return element; + } + + update() { + const [longitude, latitude] = this.props.position; + const [x, y] = this.viewport!.project([longitude, latitude]); + const element = this.element; + if (!element) { + return; + } + const style = { + background: 'rgba(255, 255, 255, 0.9)', + padding: 10, + ...this.props.style as any, + perspective: 100, + transform: `translate(${x}px, ${y}px)` + } + const ui = (
{this.props.text}
); + render(ui, element); + } + + onRemove() { + this.deck = undefined; + this.element = undefined; + } +}