Skip to content

Commit

Permalink
fix(widgets): widget style prop keys should accept camelCase css prop…
Browse files Browse the repository at this point in the history
…erties and dashed css variables (#8991)

* fix(widgets): handle css variable and camel cased keys passed to widget style prop

---------

Co-authored-by: Chris Gervang <[email protected]>
  • Loading branch information
TylerMatteo and chrisgervang authored Dec 17, 2024
1 parent e9eada6 commit 691baea
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 23 deletions.
2 changes: 1 addition & 1 deletion docs/api-reference/widgets/compass-widget.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Bearing and pitch reset transition duration in milliseconds.

Default: `{}`

Additional CSS styles for the canvas.
Additional CSS styles for the widget. camelCase CSS properties (e.g. `backgroundColor`) and kabab-case CSS variables are accepted (e.g. `--button-size`).

#### `className` (string, optional) {#classname}

Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/widgets/fullscreen-widget.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Default: `'Exit Fullscreen'`

Default: `{}`

Additional CSS styles for the canvas.
Additional CSS styles for the widget. camelCase CSS properties (e.g. `backgroundColor`) and kabab-case CSS variables are accepted (e.g. `--button-size`).

#### `className` (string, optional) {#classname}

Expand Down
6 changes: 6 additions & 0 deletions docs/api-reference/widgets/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ Apply styles to a single instance of a widget using inline styles.
new FullscreenWidget({ style: {'--button-size': '48px'}})
```

To style hyphenated CSS properties (e.g. `background-color`, `border-color`, etc.), use the camelCase equivalent.

```js
new FullscreenWidget({ style: {'backgroundColor': '#fff'}})
```

### Custom Class Theming

Define a custom class with your desired styles and apply it to a widget.
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/widgets/zoom-widget.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Zoom transition duration in milliseconds.

Default: `{}`

Additional CSS styles for the canvas.
Additional CSS styles for the widget. camelCase CSS properties (e.g. `backgroundColor`) and kabab-case CSS variables are accepted (e.g. `--button-size`).

#### `className` (string, optional) {#classname}

Expand Down
1 change: 1 addition & 0 deletions modules/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export {deepEqual as _deepEqual} from './utils/deep-equal';
export {default as _memoize} from './utils/memoize';
export {mergeShaders as _mergeShaders} from './utils/shader';
export {compareProps as _compareProps} from './lifecycle/props';
export {applyStyles as _applyStyles, removeStyles as _removeStyles} from './utils/apply-styles';

// Types
export type {CoordinateSystem} from './lib/constants';
Expand Down
27 changes: 27 additions & 0 deletions modules/core/src/utils/apply-styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export function applyStyles(element: HTMLElement, style?: Partial<CSSStyleDeclaration>): void {
if (style) {
Object.entries(style).map(([key, value]) => {
if (key.startsWith('--')) {
// Assume CSS variable
element.style.setProperty(key, value as string);
} else {
// Assume camelCase
element.style[key] = value;
}
});
}
}

export function removeStyles(element: HTMLElement, style?: Partial<CSSStyleDeclaration>): void {
if (style) {
Object.keys(style).map(key => {
if (key.startsWith('--')) {
// Assume CSS variable
element.style.removeProperty(key);
} else {
// Assume camelCase
element.style[key] = '';
}
});
}
}
27 changes: 23 additions & 4 deletions modules/widgets/src/compass-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
// Copyright (c) vis.gl contributors

/* global document */
import {FlyToInterpolator, WebMercatorViewport, _GlobeViewport} from '@deck.gl/core';
import {
FlyToInterpolator,
WebMercatorViewport,
_GlobeViewport,
_deepEqual as deepEqual,
_applyStyles as applyStyles,
_removeStyles as removeStyles
} from '@deck.gl/core';
import type {Deck, Viewport, Widget, WidgetPlacement} from '@deck.gl/core';
import {render} from 'preact';

Expand Down Expand Up @@ -52,6 +59,20 @@ export class CompassWidget implements Widget<CompassWidgetProps> {
}

setProps(props: Partial<CompassWidgetProps>) {
const oldProps = this.props;
const el = this.element;
if (el) {
if (oldProps.className !== props.className) {
if (oldProps.className) el.classList.remove(oldProps.className);
if (props.className) el.classList.add(props.className);
}

if (!deepEqual(oldProps.style, props.style, 1)) {
removeStyles(el, oldProps.style);
applyStyles(el, props.style);
}
}

Object.assign(this.props, props);
}

Expand All @@ -65,9 +86,7 @@ export class CompassWidget implements Widget<CompassWidgetProps> {
const element = document.createElement('div');
element.classList.add('deck-widget', 'deck-widget-compass');
if (className) element.classList.add(className);
if (style) {
Object.entries(style).map(([key, value]) => element.style.setProperty(key, value as string));
}
applyStyles(element, style);
this.deck = deck;
this.element = element;
this.update();
Expand Down
20 changes: 8 additions & 12 deletions modules/widgets/src/fullscreen-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
// Copyright (c) vis.gl contributors

/* global document */
import {_deepEqual as deepEqual} from '@deck.gl/core';
import {
_deepEqual as deepEqual,
_applyStyles as applyStyles,
_removeStyles as removeStyles
} from '@deck.gl/core';
import type {Deck, Widget, WidgetPlacement} from '@deck.gl/core';
import {render} from 'preact';
import {IconButton} from './components';
Expand Down Expand Up @@ -59,9 +63,7 @@ export class FullscreenWidget implements Widget<FullscreenWidgetProps> {
const el = document.createElement('div');
el.classList.add('deck-widget', 'deck-widget-fullscreen');
if (className) el.classList.add(className);
if (style) {
Object.entries(style).map(([key, value]) => el.style.setProperty(key, value as string));
}
applyStyles(el, style);
this.deck = deck;
this.element = el;
this.update();
Expand Down Expand Up @@ -102,14 +104,8 @@ export class FullscreenWidget implements Widget<FullscreenWidgetProps> {
}

if (!deepEqual(oldProps.style, props.style, 1)) {
if (oldProps.style) {
Object.entries(oldProps.style).map(([key]) => el.style.removeProperty(key));
}
if (props.style) {
Object.entries(props.style).map(([key, value]) =>
el.style.setProperty(key, value as string)
);
}
removeStyles(el, oldProps.style);
applyStyles(el, props.style);
}
}

Expand Down
25 changes: 21 additions & 4 deletions modules/widgets/src/zoom-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
// Copyright (c) vis.gl contributors

/* global document */
import {FlyToInterpolator} from '@deck.gl/core';
import {
FlyToInterpolator,
_deepEqual as deepEqual,
_applyStyles as applyStyles,
_removeStyles as removeStyles
} from '@deck.gl/core';
import type {Deck, Viewport, Widget, WidgetPlacement} from '@deck.gl/core';
import {render} from 'preact';
import {ButtonGroup, GroupedIconButton} from './components';
Expand Down Expand Up @@ -68,9 +73,7 @@ export class ZoomWidget implements Widget<ZoomWidgetProps> {
const element = document.createElement('div');
element.classList.add('deck-widget', 'deck-widget-zoom');
if (className) element.classList.add(className);
if (style) {
Object.entries(style).map(([key, value]) => element.style.setProperty(key, value as string));
}
applyStyles(element, style);
const ui = (
<ButtonGroup orientation={this.orientation}>
<GroupedIconButton
Expand Down Expand Up @@ -99,6 +102,20 @@ export class ZoomWidget implements Widget<ZoomWidgetProps> {
}

setProps(props: Partial<ZoomWidgetProps>) {
const oldProps = this.props;
const el = this.element;
if (el) {
if (oldProps.className !== props.className) {
if (oldProps.className) el.classList.remove(oldProps.className);
if (props.className) el.classList.add(props.className);
}

if (!deepEqual(oldProps.style, props.style, 1)) {
removeStyles(el, oldProps.style);
applyStyles(el, props.style);
}
}

Object.assign(this.props, props);
}

Expand Down
59 changes: 59 additions & 0 deletions test/modules/core/utils/apply-styles.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* global document */
import test from 'tape-promise/tape';
import {applyStyles, removeStyles} from '@deck.gl/core/utils/apply-styles';

const APPLY_TEST_CASES = [
{
title: 'CSS variable',
argument: {property: '--my-var', value: 'red'},
result: {property: '--my-var', value: 'red'}
},
{
title: 'camelCase property',
argument: {property: 'backgroundColor', value: 'red'},
result: {property: 'background-color', value: 'red'}
}
];

test('applyStyles', t => {
const container = document.body.appendChild(document.createElement('div'));
for (const tc of APPLY_TEST_CASES) {
const {argument, result} = tc;
applyStyles(container, {[argument.property]: argument.value});
t.deepEqual(
container.style.getPropertyValue(result.property),
result.value,
`applyStyles ${tc.title} returned expected result`
);
}
t.end();
});

const REMOVE_TEST_CASES = [
{
title: 'CSS variable',
argument: {property: '--my-var', value: 'red'},
result: {property: '--my-var', value: ''}
},
{
title: 'camelCase property',
argument: {property: 'backgroundColor', value: 'red'},
result: {property: 'background-color', value: ''}
}
];

test('removeStyles', t => {
const container = document.body.appendChild(document.createElement('div'));
for (const tc of REMOVE_TEST_CASES) {
const {argument, result} = tc;
// setProperty expects kabab-case
container.style.setProperty(result.property, argument.value);
removeStyles(container, {[argument.property]: argument.value});
t.deepEqual(
container.style.getPropertyValue(result.property),
result.value,
`removeStyles ${tc.title} returned expected result`
);
}
t.end();
});
1 change: 1 addition & 0 deletions test/modules/core/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ import './range.spec';
import './math-utils.spec';
import './shader.spec';
import './typed-array-manager.spec';
import './apply-styles.spec';

0 comments on commit 691baea

Please sign in to comment.