// deck.gl-community // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors import {Component, render} from 'preact'; // import {FlyToInterpolator} from '@deck.gl/core'; import {Widget, type Deck, type Viewport, type WidgetPlacement} from '@deck.gl/core'; import {LongPressButton} from './long-press-button'; export const ViewControlWrapper = ({children}) => (
{' '} {children}{' '}
); export const NavigationButtonContainer = ({children}) => (
{' '} {children}{' '}
); export type NavigationButtonProps = { left: any; top: any; rotate?: number; children?: any; onClick?: () => void; }; export const NavigationButton = (props: NavigationButtonProps) => (
{' '} {props.children}{' '}
); export const ZoomControlWrapper = ({children}) => (
{' '} {children}{' '}
); export const VerticalSlider = ({children}) => (
input[type='range'][orient='vertical'] { // -webkit-appearance: slider-vertical; // height: 100px; // padding: 0; // margin: 0; // width: 10px; // } }} > {' '} {children}{' '}
); export const ZoomControlButton = ({children}) => (
{' '} {children}{' '}
); export type ViewControlProps = { id?: string; viewId?: string; placement?: WidgetPlacement; fitBounds: () => void; panBy?: (dx: number, dy: number) => void; zoomBy?: (delta: number) => void; zoomLevel: number; minZoom: number; maxZoom: number; deltaPan: number; deltaZoom: number; /** CSS inline style overrides. */ style?: Partial; /** Additional CSS class. */ className?: string; }; export class ViewControl extends Component { static displayName = 'ViewControl'; static defaultProps: Required = { id: undefined, viewId: undefined, placement: 'top-left', fitBounds: () => {}, panBy: () => {}, zoomBy: () => {}, zoomLevel: 1, deltaPan: 10, deltaZoom: 0.1, minZoom: 0.1, maxZoom: 1, style: {}, className: '' }; // pan actions panUp = () => this.props.panBy(0, this.props.deltaPan); panDown = () => this.props.panBy(0, -1 * this.props.deltaPan); panLeft = () => this.props.panBy(this.props.deltaPan, 0); panRight = () => this.props.panBy(-1 * this.props.deltaPan, 0); // zoom actions zoomIn = () => this.props.zoomBy(this.props.deltaZoom); zoomOut = () => this.props.zoomBy(-1 * this.props.deltaZoom); onChangeZoomLevel = (evt) => { const delta = evt.target.value - this.props.zoomLevel; this.props.zoomBy(delta); }; render() { const buttons = [ {top: -2, left: 14, rotate: 0, onClick: this.panUp, content: '▲', key: 'up'}, {top: 12, left: 0, rotate: -90, onClick: this.panLeft, content: '◀', key: 'left'}, {top: 12, left: 28, rotate: 90, onClick: this.panRight, content: '▶', key: 'right'}, {top: 25, left: 14, rotate: 180, onClick: this.panDown, content: '▼', key: 'down'} ]; return ( {buttons.map((b: any) => ( {b.content} ))} { // console.log('on click fit bounds') || this.props.fitBounds; }} > {'¤'} {'+'} {'-'} ); } } export class ViewControlWidget extends Widget { id = 'zoom'; placement: WidgetPlacement = 'top-left'; orientation: 'vertical' | 'horizontal' = 'vertical'; viewId?: string | null = null; viewports: {[id: string]: Viewport} = {}; element?: HTMLDivElement; className = 'deck-widget-view-control'; constructor(props: ViewControlProps) { super(props); this.props = {...ViewControl.defaultProps, ...props}; this.id = props.id || 'zoom'; this.viewId = props.viewId || null; this.placement = props.placement || 'top-left'; // this.orientation = props.orientation || 'vertical'; // props.transitionDuration = props.transitionDuration || 200; // props.zoomInLabel = props.zoomInLabel || 'Zoom In'; // props.zoomOutLabel = props.zoomOutLabel || 'Zoom Out'; props.style = props.style || {}; } onAdd({deck}: {deck: Deck}): HTMLDivElement { this.deck = deck; this.element = document.createElement('div'); const {style, className} = this.props; this.element.classList.add('deck-widget', 'deck-widget-zoom'); if (className) { this.element.classList.add(className); } if (style) { Object.entries(style).map(([key, value]) => this.element.style.setProperty(key, value as string) ); } return this.element; } onRemove() { this.deck = undefined; this.element = undefined; } onRenderHTML(rootElement: HTMLElement): void { const ui = ( ); render(ui, rootElement); } setProps(props: Partial) { Object.assign(this.props, props); } onViewportChange(viewport: Viewport) { this.viewports[viewport.id] = viewport; } handleDeltaZoom(deltaZoom: number) { // console.log('Handle delta zoom'); for (const view of this.deck.getViewports()) { this.handleZoomView(view, view.zoom + deltaZoom); } } handlePanBy(deltaX: number, deltaY: number) { // console.log('Handle panby', deltaX, deltaY); for (const viewport of this.deck.getViewports()) { this.handlePanViewport(viewport, deltaX, deltaY); } } handleZoomView(viewport: Viewport, nextZoom: number) { const viewId = this.viewId || viewport?.id || 'default-view'; // @ts-expect-error TODO we lack a proper API for getting viewStates const viewState = this.deck.viewManager.viewState || viewport; const nextViewState = { ...viewState, zoom: nextZoom // transitionDuration: this.props.transitionDuration, // transitionInterpolator: new FlyToInterpolator() }; // @ts-ignore Using private method temporary until there's a public one this.deck._onViewStateChange({viewId, viewState: nextViewState, interactionState: {}}); } handlePanViewport(viewport: Viewport, deltaX: number, deltaY: number) { const viewId = this.viewId || viewport?.id || 'default-view'; // @ts-expect-error TODO we lack a proper API for getting viewStates const viewState = this.deck.viewManager.viewState || viewport; // console.log('Handle pan viewport', deltaX, deltaY, viewState); const nextViewState = { ...viewState, position: [viewport.position[0] + deltaX, viewport.position[1] + deltaY] }; // @ts-ignore Using private method temporary until there's a public one this.deck._onViewStateChange({viewId, viewState: nextViewState, interactionState: {}}); } }