diff --git a/docs/api-reference/core/widget.md b/docs/api-reference/core/widget.md index 6545c6fedc2..1b95db1d98a 100644 --- a/docs/api-reference/core/widget.md +++ b/docs/api-reference/core/widget.md @@ -12,7 +12,7 @@ You may find many ready-to-use widgets in the `@deck.gl/widgets` module. A widget is expected to implement the `Widget` interface. Here is a custom widget that shows a spinner while layers are loading: ```ts -import {Widget} from '@deck.gl/core'; +import {Deck, Widget} from '@deck.gl/core'; class LoadingIndicator implements Widget { element?: HTMLDivElement; @@ -43,7 +43,9 @@ class LoadingIndicator implements Widget { } } -deckgl.addWidget(new LoadingIndicator({size: 48})); +new Deck({ + widgets=[new LoadingIndicator({size: 48})] +}); ``` ## Widget Interface diff --git a/examples/get-started/pure-js/widgets/package.json b/examples/get-started/pure-js/widgets/package.json index f025ce2262b..3c762bf6815 100644 --- a/examples/get-started/pure-js/widgets/package.json +++ b/examples/get-started/pure-js/widgets/package.json @@ -1,5 +1,5 @@ { - "name": "deckgl-example-pure-js-basic", + "name": "deckgl-example-pure-js-widgets", "version": "0.0.0", "private": true, "license": "MIT", diff --git a/examples/get-started/react/widgets/README.md b/examples/get-started/react/widgets/README.md new file mode 100644 index 00000000000..277a76dc22d --- /dev/null +++ b/examples/get-started/react/widgets/README.md @@ -0,0 +1,17 @@ +## Example: Use deck.gl with widgets + +Uses [Vite](https://vitejs.dev/) to bundle and serve files. + +## Usage + +To install dependencies: + +```bash +npm install +# or +yarn +``` + +Commands: +* `npm start` is the development target, to serve the app and hot reload. +* `npm run build` is the production target, to create the final bundle and write to disk. diff --git a/examples/get-started/react/widgets/app.jsx b/examples/get-started/react/widgets/app.jsx new file mode 100644 index 00000000000..e276cdefa67 --- /dev/null +++ b/examples/get-started/react/widgets/app.jsx @@ -0,0 +1,158 @@ +import React, {useState, forwardRef, useImperativeHandle, useMemo, useRef} from 'react'; +import {createRoot} from 'react-dom/client'; +import DeckGL, {GeoJsonLayer, ArcLayer} from 'deck.gl'; +import { + CompassWidget, + ZoomWidget, + FullscreenWidget, + DarkGlassTheme, + LightGlassTheme +} from '@deck.gl/widgets'; +import '@deck.gl/widgets/stylesheet.css'; +import {createPortal} from 'react-dom'; + +/* global window */ +const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)'); +const widgetTheme = prefersDarkScheme.matches ? DarkGlassTheme : LightGlassTheme; + +// source: Natural Earth http://www.naturalearthdata.com/ via geojson.xyz +const COUNTRIES = + 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_scale_rank.geojson'; //eslint-disable-line +const AIR_PORTS = + 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson'; + +const INITIAL_VIEW_STATE = { + latitude: 51.47, + longitude: 0.45, + zoom: 4, + bearing: 0, + pitch: 30 +}; + +function useWidget(props = {}) { + const [container, setContainer] = useState(null); + + class ReactWidget { + constructor(props) { + this.id = props.id || 'react'; + this.placement = props.placement || 'top-left'; + this.viewId = props.viewId; + this.props = props; + } + + onAdd() { + const el = document.createElement('div'); + // Defer state update to avoid conflicts with rendering + requestAnimationFrame(() => setContainer(el)); + return el; + } + + onRemove() { + requestAnimationFrame(() => setContainer(null)); + } + + setProps(props) { + this.props = props; + this.placement = props.placement || this.placement; + this.viewId = props.viewId || this.viewId; + } + } + + const widget = useMemo(() => new ReactWidget(props), [props]); + + return { + widget, + container + }; +} + +function DeckWidgetWithRef(props, ref) { + const { widget, container } = useWidget(props); + + useImperativeHandle(ref, () => widget, [widget]); + + return container ? createPortal(props.children, container) : null; +} + +const DeckWidget = forwardRef(DeckWidgetWithRef); + +function Root() { + const [placement, setPlacement] = useState('top-left'); + const infoWidget = useWidget({id: 'hook'}); + const buttonWidget = useRef(null); + console.log(infoWidget, buttonWidget) + + const onClick = () => { + // eslint-disable-next-line + // alert('React widget!'); + setPlacement('top-right'); + }; + + const infoWidgetEl = ( +