Skip to content

Commit

Permalink
add(widgets) fullscreen widget (#8024)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisgervang authored and Pessimistress committed Oct 5, 2023
1 parent a2b5690 commit d03dc5a
Show file tree
Hide file tree
Showing 18 changed files with 739 additions and 135 deletions.
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ module.exports = getESLintConfig({
'import/named': 0,
'no-new': 0
}
},
{
files: ['modules/widgets/**/*.tsx'],
rules: {
// For widgets module. Disable React-style JSX linting since they conflict with Preact JSX.
'react/react-in-jsx-scope': 0
}
}
],

Expand Down
8 changes: 8 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ module.exports = getBabelConfig({
patterns: ['**/*.glsl.js', '**/*.glsl.ts']
}
]
],
overrides: [
{
include: './modules/widgets/**/*.{ts,tsx}',
// Parse preact-style JSX in @deck.gl/widgets.
presets: [['@babel/typescript', {jsxPragma: 'h'}]],
plugins: [['@babel/plugin-transform-react-jsx', {pragma: 'h'}]]
}
]
}
});
45 changes: 45 additions & 0 deletions docs/api-reference/widgets/fullscreen-widget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# FullscreenWidget

## Props

### `id` (String)

Default: `'fullscreen'`

Unique identifier of the widget.

### `placement` (String, optional)

Default: `'top-left'`

Widget position within the view relatitive to the map container. Valid options are `top-left`, `top-right`, `bottom-left`, `bottom-right`, or `fill`.

### `container` (HTMLElement, optional)

Default: `undefined`

A [compatible DOM element](https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen#Compatible_elements) which should be made full screen. By default, the map container element will be made full screen.

### `enterLabel` (String, optional)

Toolip message displayed while hovering a mouse over the widget when out of fullscreen.

Default: `'Enter Fullscreen'`

### `exitLabel` (String, optional)

Toolip message displayed while hovering a mouse over the widget when fullscreen.

Default: `'Exit Fullscreen'`

### `style` (Object, optional)

Default: `{}`

Additional CSS styles for the canvas.

### `className` (String, optional)

Default: `undefined`

Class name to attach to the widget element. The element has the default class name of `deck-widget deck-fullscreen-widget`.
103 changes: 102 additions & 1 deletion docs/api-reference/widgets/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,119 @@ npm install @deck.gl/core @deck.gl/widgets

```js
import {FullscreenWidget} from '@deck.gl/widgets';
import '@deck.gl/widgets/stylesheet.css';

new FullscreenWidget({});
```

### Include the Standalone Bundle

```html
<script src="https://unpkg.com/deck.gl@^8.10.0/dist.min.js"></script>
<link src="https://unpkg.com/deck.gl@^8.10.0/widgets/stylesheet.css" rel='stylesheet' />
<!-- or -->
<script src="https://unpkg.com/@deck.gl/core@^8.10.0/dist.min.js"></script>
<script src="https://unpkg.com/@deck.gl/widgets@^8.10.0/dist.min.js"></script>
<link src="https://unpkg.com/deck.gl@^8.10.0/widgets/stylesheet.css" rel='stylesheet' />
```

```js
new deck.FullscreenWidget({});
```
```

## CSS Theming

Customizing the appearance of widgets can be achieved using CSS variables. This section provides guidance on how to theme widgets at different levels of specificity.

### Global Theming

Apply to all wigdets with the `.deck-widget` selector.

```css
.deck-widget {
--button-size: 48px;
}
```

> Note: While variables can be globally applied using the `:root` selector, ensuring their availability throughout the entire document, this method is not recommended. Applying variables globally can lead to naming conflicts, especially in larger projects or when integrating with other libraries.
### Type-specific Theming

Theme a specific type of widget using the `.deck-widget-[type]` selector.

```css
.deck-widget-fullscreen {
--button-size: 48px;
}
```

### Instance-specific Theming

Apply styles to a single instance of a widget using inline styles.

```js
new FullscreenWidget({ style: {'--button-size': '48px'}})
```

### Custom Class Theming

Define a custom class with your desired styles and apply it to a widget.

```css
.my-class {
--button-size: 48px;
}
```
```js
new FullscreenWidget({ className: 'my-class'})
```

## Customizable CSS Variables

We've provided a set of CSS variables to make styling UI Widgets more convenient. These variables allow for customization of widget sizes, colors, and other properties. Below is a comprehensive list of these variables, their expected types, and default values:

### Size

| Name | Type | Default |
| ---- | ---- | ------- |
| `--button-size` | [Dimension](https://developer.mozilla.org/en-US/docs/Web/CSS/dimension) | `28px` |
| `--button-border-radius` | [Dimension](https://developer.mozilla.org/en-US/docs/Web/CSS/dimension) | `12px` |
| `--widget-margin` | [Dimension](https://developer.mozilla.org/en-US/docs/Web/CSS/dimension) | `12px` |

### Color

| Name | Type | Default |
| ---- | ---- | ------- |
| `--button-background` | [Color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) | `#fff` |
| `--button-stroke` | [Color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) | `rgba(255, 255, 255, 0.3)` |
| `--button-inner-stroke` | [Border](https://developer.mozilla.org/en-US/docs/Web/CSS/border) | `unset` |
| `--button-shadow` | [Box Shadow](https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow) | `0px 0px 8px 0px rgba(0, 0, 0, 0.25)` |
| `--button-backdrop-filter` | [Backdrop Filter](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter) | `unset` |
| `--button-icon-idle` | [Color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) | `rgba(97, 97, 102, 1)`
| `--button-icon-hover` | [Color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) | `rgba(24, 24, 26, 1)`

### Icon
| Name | Type | Default |
| ---- | ---- | ------- |
| `--icon-fullscreen-enter` | [SVG Data Url](https://developer.mozilla.org/en-US/docs/Web/CSS/url#using_a_data_url) | [Material Symbol Fullscreen](https://fonts.google.com/icons?selected=Material+Symbols+Rounded:fullscreen:FILL@0;wght@400;GRAD@0;opsz@40) |
| `--icon-fullscreen-enter` | [SVG Data Url](https://developer.mozilla.org/en-US/docs/Web/CSS/url#using_a_data_url) | [Material Symbol Fullscreen Exit](https://fonts.google.com/icons?selected=Material+Symbols+Rounded:fullscreen_exit:FILL@0;wght@400;GRAD@0;opsz@40) |

#### Replacing Icons

Users can to customize icons to better align with their design preferences or branding. This section provides a step-by-step guide on how to replace and customize these icons.

1. Prepare Your Icons:
- Ensure your icons are available as [SVG Data Url](https://developer.mozilla.org/en-US/docs/Web/CSS/url#using_a_data_url). These will be used for a CSS [mask-image](https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image).
2. Icon Replacement:
- Use CSS variables, such as `--icon-fullscreen-enter`, to replace the default icons with your customized ones.
3. Color Customization:
- The original color embedded in your SVG will be disregarded. However, it's crucial that the SVG isn't transparent.
- Customize the color of your icon using the appropriate CSS variable, such as `--button-icon-idle`.

Example:
```css
.deck-widget {
--icon-fullscreen-enter: url('path_to_your_svg_icon.svg');
--button-icon-idle: blue;
}
```
17 changes: 17 additions & 0 deletions examples/get-started/pure-js/widgets/README.md
Original file line number Diff line number Diff line change
@@ -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.
75 changes: 75 additions & 0 deletions examples/get-started/pure-js/widgets/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {Deck} from '@deck.gl/core';
import {GeoJsonLayer, ArcLayer} from '@deck.gl/layers';
import {FullscreenWidget, DarkGlassTheme, LightGlassTheme} from '@deck.gl/widgets';
import '@deck.gl/widgets/stylesheet.css';
import {luma} from '@luma.gl/core';
import {WebGLDevice} from '@luma.gl/webgl';

luma.registerDevices([WebGLDevice]);

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
};

new Deck({
initialViewState: INITIAL_VIEW_STATE,
controller: true,
layers: [
new GeoJsonLayer({
id: 'base-map',
data: COUNTRIES,
// Styles
stroked: true,
filled: true,
lineWidthMinPixels: 2,
opacity: 0.4,
getLineColor: [60, 60, 60],
getFillColor: [200, 200, 200]
}),
new GeoJsonLayer({
id: 'airports',
data: AIR_PORTS,
// Styles
filled: true,
pointRadiusMinPixels: 2,
pointRadiusScale: 2000,
getPointRadius: f => 11 - f.properties.scalerank,
getFillColor: [200, 0, 80, 180],
// Interactive props
pickable: true,
autoHighlight: true,
onClick: info =>
// eslint-disable-next-line
info.object && alert(`${info.object.properties.name} (${info.object.properties.abbrev})`)
}),
new ArcLayer({
id: 'arcs',
data: AIR_PORTS,
dataTransform: d => d.features.filter(f => f.properties.scalerank < 4),
// Styles
getSourcePosition: f => [-0.4531566, 51.4709959], // London
getTargetPosition: f => f.geometry.coordinates,
getSourceColor: [0, 128, 200],
getTargetColor: [200, 0, 80],
getWidth: 1
})
],
widgets: [
new FullscreenWidget({}),
new FullscreenWidget({id: 'themed', style: widgetTheme}),
new FullscreenWidget({id: 'purple', className: 'purple'})
]
});
27 changes: 27 additions & 0 deletions examples/get-started/pure-js/widgets/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>deck.gl Example</title>
<style>
body {margin: 0; width: 100vw; height: 100vh; overflow: hidden;}

@media screen and (max-width: 600px) {
.deck-widget {
/* mobile */
--button-size: 48px;
--button-corner-radius: 12px;
}
}

.purple {
--button-background: hsl(240, 40%, 40%);
--button-stroke: hsla(240, 40%, 40%, 0.3);
--button-icon-hover: rgb(236, 236, 236);
--button-icon-idle: rgb(174, 174, 202);
}
</style>
</head>
<body></body>
<script type="module" src="app.js"></script>
</html>
21 changes: 21 additions & 0 deletions examples/get-started/pure-js/widgets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "deckgl-example-pure-js-basic",
"version": "0.0.0",
"private": true,
"license": "MIT",
"scripts": {
"start": "vite --open",
"start-local": "vite --config ../../../vite.config.local.mjs",
"build": "vite build"
},
"dependencies": {
"@deck.gl/core": "^8.8.0",
"@deck.gl/layers": "^8.8.0",
"@deck.gl/widgets": "8.10.0-alpha.2",
"@luma.gl/core": "^8.6.0-alpha.1",
"@luma.gl/webgl": "^8.5.20"
},
"devDependencies": {
"vite": "^4.0.0"
}
}
4 changes: 1 addition & 3 deletions examples/website/map-tile/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ function getTooltip({tile}) {
export default function App({showBorder = false, onTilesLoad = null}) {
const tileLayer = new TileLayer({
// https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Tile_servers
data: [
'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
],
data: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],

// Since these OSM tiles support HTTP/2, we can make many concurrent requests
// and we aren't limited by the browser to a certain number per domain.
Expand Down
5 changes: 4 additions & 1 deletion modules/mapbox/src/mapbox-overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ export default class MapboxOverlay implements IControl {

/** If interleaved: true, returns base map's canvas, otherwise forwards the Deck.getCanvas method. */
getCanvas(): HTMLCanvasElement | null {
return this._interleaved ? this._map.getCanvas() : this._deck.getCanvas();
if (!this._map) {
return null;
}
return this._interleaved ? this._map.getCanvas() : this._deck!.getCanvas();
}

private _handleStyleChange = () => {
Expand Down
3 changes: 2 additions & 1 deletion modules/widgets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"prepublishOnly": "npm run build-bundle && npm run build-bundle -- --env=dev"
},
"dependencies": {
"@babel/runtime": "^7.0.0"
"@babel/runtime": "^7.0.0",
"preact": "^10.17.0"
},
"peerDependencies": {
"@deck.gl/core": "^8.0.0"
Expand Down
17 changes: 17 additions & 0 deletions modules/widgets/src/components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {h, render} from 'preact';

export const IconButton = props => {
const {className, label, onClick} = props;
return (
<div className="deck-widget-button-border">
<button
className={`deck-widget-button ${className}`}
type="button"
onClick={onClick}
title={label}
>
<div className="deck-widget-icon" />
</button>
</div>
);
};
Loading

0 comments on commit d03dc5a

Please sign in to comment.