@deck.gl/widgets 9.3.0-alpha.1 → 9.3.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/context-menu-widget.d.ts +7 -5
  2. package/dist/context-menu-widget.d.ts.map +1 -1
  3. package/dist/context-menu-widget.js +2 -1
  4. package/dist/context-menu-widget.js.map +1 -1
  5. package/dist/dist.dev.js +687 -555
  6. package/dist/icon-widget.d.ts +28 -0
  7. package/dist/icon-widget.d.ts.map +1 -0
  8. package/dist/icon-widget.js +35 -0
  9. package/dist/icon-widget.js.map +1 -0
  10. package/dist/index.cjs +468 -309
  11. package/dist/index.cjs.map +4 -4
  12. package/dist/index.d.ts +11 -9
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +7 -7
  15. package/dist/index.js.map +1 -1
  16. package/dist/info-widget.d.ts +1 -0
  17. package/dist/info-widget.d.ts.map +1 -1
  18. package/dist/info-widget.js +6 -0
  19. package/dist/info-widget.js.map +1 -1
  20. package/dist/lib/components/dropdown-menu.d.ts +3 -2
  21. package/dist/lib/components/dropdown-menu.d.ts.map +1 -1
  22. package/dist/lib/components/dropdown-menu.js +12 -8
  23. package/dist/lib/components/dropdown-menu.js.map +1 -1
  24. package/dist/lib/components/icon-button.d.ts +3 -0
  25. package/dist/lib/components/icon-button.d.ts.map +1 -1
  26. package/dist/lib/components/icon-button.js +10 -2
  27. package/dist/lib/components/icon-button.js.map +1 -1
  28. package/dist/lib/components/popover.js +1 -1
  29. package/dist/lib/components/popover.js.map +1 -1
  30. package/dist/lib/data-url.d.ts +5 -0
  31. package/dist/lib/data-url.d.ts.map +1 -0
  32. package/dist/lib/data-url.js +7 -0
  33. package/dist/lib/data-url.js.map +1 -0
  34. package/dist/popup-widget.d.ts +1 -0
  35. package/dist/popup-widget.d.ts.map +1 -1
  36. package/dist/popup-widget.js +6 -0
  37. package/dist/popup-widget.js.map +1 -1
  38. package/dist/selector-widget.d.ts +44 -0
  39. package/dist/selector-widget.d.ts.map +1 -0
  40. package/dist/selector-widget.js +64 -0
  41. package/dist/selector-widget.js.map +1 -0
  42. package/dist/splitter-widget.d.ts +52 -11
  43. package/dist/splitter-widget.d.ts.map +1 -1
  44. package/dist/splitter-widget.js +177 -76
  45. package/dist/splitter-widget.js.map +1 -1
  46. package/dist/stats-widget.d.ts +2 -1
  47. package/dist/stats-widget.d.ts.map +1 -1
  48. package/dist/stats-widget.js +37 -8
  49. package/dist/stats-widget.js.map +1 -1
  50. package/dist/stylesheet.css +50 -13
  51. package/dist/timeline-widget.d.ts.map +1 -1
  52. package/dist/timeline-widget.js +6 -1
  53. package/dist/timeline-widget.js.map +1 -1
  54. package/dist/toggle-widget.d.ts +38 -0
  55. package/dist/toggle-widget.d.ts.map +1 -0
  56. package/dist/toggle-widget.js +47 -0
  57. package/dist/toggle-widget.js.map +1 -0
  58. package/dist.min.js +2 -2
  59. package/package.json +3 -3
  60. package/src/context-menu-widget.tsx +9 -9
  61. package/src/icon-widget.tsx +69 -0
  62. package/src/index.ts +11 -9
  63. package/src/info-widget.tsx +7 -0
  64. package/src/lib/components/dropdown-menu.tsx +22 -15
  65. package/src/lib/components/icon-button.tsx +15 -3
  66. package/src/lib/components/popover.tsx +1 -1
  67. package/src/lib/data-url.ts +5 -0
  68. package/src/popup-widget.tsx +7 -0
  69. package/src/selector-widget.tsx +111 -0
  70. package/src/splitter-widget.tsx +281 -96
  71. package/src/stats-widget.tsx +50 -9
  72. package/src/stylesheet.css +50 -13
  73. package/src/timeline-widget.tsx +8 -1
  74. package/src/toggle-widget.tsx +101 -0
  75. package/dist/fps-widget.d.ts +0 -28
  76. package/dist/fps-widget.d.ts.map +0 -1
  77. package/dist/fps-widget.js +0 -51
  78. package/dist/fps-widget.js.map +0 -1
  79. package/dist/lib/components/icon-menu.d.ts +0 -16
  80. package/dist/lib/components/icon-menu.d.ts.map +0 -1
  81. package/dist/lib/components/icon-menu.js +0 -33
  82. package/dist/lib/components/icon-menu.js.map +0 -1
  83. package/dist/view-selector-widget.d.ts +0 -35
  84. package/dist/view-selector-widget.d.ts.map +0 -1
  85. package/dist/view-selector-widget.js +0 -63
  86. package/dist/view-selector-widget.js.map +0 -1
  87. package/src/fps-widget.tsx +0 -77
  88. package/src/lib/components/icon-menu.tsx +0 -70
  89. package/src/view-selector-widget.tsx +0 -151
@@ -6,18 +6,17 @@
6
6
  import {Widget} from '@deck.gl/core';
7
7
  import type {Deck, PickingInfo, WidgetProps} from '@deck.gl/core';
8
8
  import {render, type JSX} from 'preact';
9
- import {SimpleMenu, SimpleMenuProps} from './lib/components/dropdown-menu';
9
+ import {SimpleMenu, type MenuItem} from './lib/components/dropdown-menu';
10
10
  import {Popover, type PopoverProps} from './lib/components/popover';
11
11
 
12
12
  export type ContextMenuWidgetProps = WidgetProps & {
13
13
  /** View to attach to and interact with. Required when using multiple views. */
14
14
  viewId?: string | null;
15
- /** Provide menu items for the menu given the picked object */
16
- getMenuItems: (
17
- info: PickingInfo,
18
- widget: ContextMenuWidget
19
- ) => SimpleMenuProps['menuItems'] | null;
20
- /** Callback with the selected item */
15
+ /** Menu items for the menu. */
16
+ menuItems?: MenuItem[];
17
+ /** Callback to provide menu items for the menu given the picked object. Overrides `menuItems` */
18
+ getMenuItems?: (info: PickingInfo, widget: ContextMenuWidget) => MenuItem[] | null;
19
+ /** Callback when a menu item is selected */
21
20
  onMenuItemSelected?: (value: string, pickInfo: PickingInfo | null) => void;
22
21
  /** Position menu relative to the anchor.
23
22
  * @default 'bottom-start'
@@ -39,6 +38,7 @@ export class ContextMenuWidget extends Widget<ContextMenuWidgetProps> {
39
38
  ...Widget.defaultProps,
40
39
  id: 'context',
41
40
  viewId: null,
41
+ menuItems: [],
42
42
  getMenuItems: undefined!,
43
43
  onMenuItemSelected: () => {},
44
44
  placement: 'bottom-start',
@@ -50,7 +50,7 @@ export class ContextMenuWidget extends Widget<ContextMenuWidgetProps> {
50
50
  placement = 'fill' as const;
51
51
 
52
52
  menu: {
53
- items: SimpleMenuProps['menuItems'];
53
+ items: MenuItem[];
54
54
  pickInfo: PickingInfo;
55
55
  } | null = null;
56
56
 
@@ -77,7 +77,7 @@ export class ContextMenuWidget extends Widget<ContextMenuWidgetProps> {
77
77
  index: -1,
78
78
  pixelRatio: 1
79
79
  };
80
- const menuItems = this.props.getMenuItems(pickInfo, this) || [];
80
+ const menuItems = this.props.getMenuItems?.(pickInfo, this) || this.props.menuItems;
81
81
  this.menu =
82
82
  menuItems.length > 0
83
83
  ? {
@@ -0,0 +1,69 @@
1
+ // deck.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {WidgetPlacement, WidgetProps} from '@deck.gl/core';
6
+ import {render, type JSX} from 'preact';
7
+ import {Widget} from '@deck.gl/core';
8
+ import {IconButton} from './lib/components/icon-button';
9
+
10
+ export type IconWidgetProps = WidgetProps & {
11
+ /** Widget positioning within the view. Default 'bottom-left'. */
12
+ placement?: WidgetPlacement;
13
+ /** View to attach to and interact with. Required when using multiple views. */
14
+ viewId?: string | null;
15
+ /** Data url to display as icon */
16
+ icon: string;
17
+ /** Tooltip label */
18
+ label?: string;
19
+ /** Icon color, a CSS Color string */
20
+ color?: string;
21
+ /** Callback when the widget is clicked */
22
+ onClick?: () => void;
23
+ };
24
+
25
+ /**
26
+ * A generic widget that displays a button with icon or text content.
27
+ */
28
+ export class IconWidget extends Widget<IconWidgetProps> {
29
+ static defaultProps: Required<IconWidgetProps> = {
30
+ ...Widget.defaultProps,
31
+ id: 'icon',
32
+ placement: 'top-left',
33
+ viewId: null,
34
+ icon: '',
35
+ label: '',
36
+ color: '',
37
+ onClick: undefined!
38
+ };
39
+
40
+ className = '';
41
+ placement: WidgetPlacement = 'top-left';
42
+
43
+ constructor(props: IconWidgetProps) {
44
+ super(props);
45
+ this.setProps(this.props);
46
+ }
47
+
48
+ setProps(props: Partial<IconWidgetProps>) {
49
+ this.placement = props.placement ?? this.placement;
50
+ this.viewId = props.viewId;
51
+ super.setProps(props);
52
+ }
53
+
54
+ onRenderHTML(rootElement: HTMLElement): void {
55
+ const {className, style, icon, color, label, onClick} = this.props;
56
+
57
+ render(
58
+ <IconButton
59
+ className={className}
60
+ style={style as JSX.CSSProperties}
61
+ color={color}
62
+ icon={icon}
63
+ label={label}
64
+ onClick={onClick}
65
+ />,
66
+ rootElement
67
+ );
68
+ }
69
+ }
package/src/index.ts CHANGED
@@ -15,22 +15,23 @@ export {GeocoderWidget as _GeocoderWidget} from './geocoder-widget';
15
15
  // View widgets
16
16
  export {FullscreenWidget} from './fullscreen-widget';
17
17
  export {SplitterWidget as _SplitterWidget} from './splitter-widget';
18
- export {ViewSelectorWidget as _ViewSelectorWidget} from './view-selector-widget';
19
18
 
20
19
  // Information widgets
21
- export {InfoWidget as _InfoWidget} from './info-widget';
20
+ export {InfoWidget} from './info-widget';
22
21
  export {PopupWidget} from './popup-widget';
23
- export {ContextMenuWidget as _ContextMenuWidget} from './context-menu-widget';
22
+ export {ContextMenuWidget} from './context-menu-widget';
24
23
  export {ScrollbarWidget} from './scrollbar-widget';
25
24
 
26
25
  // Control widgets
26
+ export {IconWidget} from './icon-widget';
27
+ export {ToggleWidget} from './toggle-widget';
28
+ export {SelectorWidget} from './selector-widget';
27
29
  export {TimelineWidget as _TimelineWidget} from './timeline-widget';
28
30
 
29
31
  // Utility widgets
30
32
  export {ScreenshotWidget} from './screenshot-widget';
31
- export {ThemeWidget as _ThemeWidget} from './theme-widget';
32
- export {LoadingWidget as _LoadingWidget} from './loading-widget';
33
- export {FpsWidget as _FpsWidget} from './fps-widget';
33
+ export {ThemeWidget} from './theme-widget';
34
+ export {LoadingWidget} from './loading-widget';
34
35
  export {StatsWidget as _StatsWidget} from './stats-widget';
35
36
 
36
37
  export type {FullscreenWidgetProps} from './fullscreen-widget';
@@ -40,15 +41,17 @@ export type {ScreenshotWidgetProps} from './screenshot-widget';
40
41
  export type {ResetViewWidgetProps} from './reset-view-widget';
41
42
  export type {GeocoderWidgetProps} from './geocoder-widget';
42
43
  export type {LoadingWidgetProps} from './loading-widget';
43
- export type {FpsWidgetProps} from './fps-widget';
44
+ export type {IconWidgetProps} from './icon-widget';
45
+ export type {ToggleWidgetProps} from './toggle-widget';
44
46
  export type {ScaleWidgetProps} from './scale-widget';
45
47
  export type {ThemeWidgetProps} from './theme-widget';
46
48
  export type {InfoWidgetProps} from './info-widget';
49
+ export type {PopupWidgetProps} from './popup-widget';
47
50
  export type {StatsWidgetProps} from './stats-widget';
48
51
  export type {ContextMenuWidgetProps} from './context-menu-widget';
49
52
  export type {SplitterWidgetProps} from './splitter-widget';
50
53
  export type {TimelineWidgetProps} from './timeline-widget';
51
- export type {ViewSelectorWidgetProps} from './view-selector-widget';
54
+ export type {SelectorWidgetProps} from './selector-widget';
52
55
  export type {GimbalWidgetProps} from './gimbal-widget';
53
56
  export type {ContentBounds, ScrollbarWidgetProps, ScrollbarDecoration} from './scrollbar-widget';
54
57
 
@@ -64,7 +67,6 @@ export {
64
67
  SimpleMenu as _SimpleMenu,
65
68
  type SimpleMenuProps
66
69
  } from './lib/components/dropdown-menu';
67
- export {IconMenu as _IconMenu, type IconMenuProps} from './lib/components/icon-menu';
68
70
  export {
69
71
  RangeInput as _RangeInput,
70
72
  type RangeInputProps,
@@ -81,6 +81,13 @@ export class InfoWidget extends Widget<InfoWidgetProps> {
81
81
  this.deck = deck;
82
82
  }
83
83
 
84
+ onRemove() {
85
+ // Invoke clean up of preact hooks
86
+ if (this.rootElement) {
87
+ render(null, this.rootElement);
88
+ }
89
+ }
90
+
84
91
  onViewportChange(viewport) {
85
92
  this.viewport = viewport;
86
93
  this.updateHTML();
@@ -4,12 +4,20 @@
4
4
 
5
5
  import {type JSX, type ComponentChild} from 'preact';
6
6
  import {useState, useRef, useEffect} from 'preact/hooks';
7
+ import {getCSSMask} from '../data-url';
7
8
 
8
- export type MenuItem = string | {label: string; value?: string; icon?: string};
9
+ export type MenuItem =
10
+ | string
11
+ | {
12
+ value?: string;
13
+ label: string;
14
+ icon?: string;
15
+ onSelect?: () => void;
16
+ };
9
17
 
10
18
  export type DropdownMenuProps = {
11
19
  menuItems: MenuItem[];
12
- onSelect: (value: string) => void;
20
+ onSelect?: (value: string) => void;
13
21
  style?: Partial<CSSStyleDeclaration>;
14
22
  };
15
23
 
@@ -52,22 +60,24 @@ export type SimpleMenuProps = DropdownMenuProps & {
52
60
  export const SimpleMenu = (props: SimpleMenuProps) => {
53
61
  const dropdownRef = useRef<HTMLDivElement>(null);
54
62
 
55
- const handleClickOutside = (event: MouseEvent) => {
56
- if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
57
- props.onClose();
58
- }
59
- };
60
-
61
63
  useEffect(() => {
64
+ const handleClickOutside = (event: MouseEvent) => {
65
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
66
+ props.onClose();
67
+ }
68
+ };
62
69
  document.addEventListener('mousedown', handleClickOutside);
63
70
  return () => {
64
71
  document.removeEventListener('mousedown', handleClickOutside);
65
72
  };
66
73
  }, []);
67
74
 
68
- const handleSelect = (value?: string) => {
75
+ const handleSelect = (value: string | undefined, item: MenuItem) => {
69
76
  if (value) {
70
- props.onSelect(value);
77
+ if (typeof item === 'object') {
78
+ item.onSelect?.();
79
+ }
80
+ props.onSelect?.(value);
71
81
  props.onClose();
72
82
  }
73
83
  };
@@ -89,13 +99,10 @@ export const SimpleMenu = (props: SimpleMenuProps) => {
89
99
  <li
90
100
  className={`deck-widget-dropdown-item ${value ? '' : 'disabled'}`}
91
101
  key={i}
92
- onClick={() => handleSelect(value)}
102
+ onClick={() => handleSelect(value, item)}
93
103
  >
94
104
  {icon && (
95
- <span
96
- className="deck-widget-dropdown-item-icon"
97
- style={{maskImage: `url("${icon}")`, WebkitMaskImage: `url("${icon}")`}}
98
- />
105
+ <span className="deck-widget-dropdown-item-icon" style={getCSSMask(icon)} />
99
106
  )}
100
107
  {getMenuItemLabel(item)}
101
108
  </li>
@@ -3,26 +3,38 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {ComponentChildren, JSX} from 'preact';
6
+ import {useMemo} from 'preact/hooks';
7
+ import {getCSSMask} from '../data-url';
6
8
 
7
9
  export type IconButtonProps = {
8
10
  className?: string;
11
+ icon?: string;
9
12
  label?: string;
13
+ color?: string;
14
+ style?: JSX.CSSProperties;
10
15
  onClick?: JSX.MouseEventHandler<HTMLButtonElement>;
11
16
  children?: ComponentChildren;
12
17
  };
13
18
 
14
19
  /** Renders a button component with widget CSS */
15
20
  export const IconButton = (props: IconButtonProps) => {
16
- const {className = '', label, onClick, children} = props;
21
+ const {className = '', style, color, icon, label, onClick, children} = props;
22
+
23
+ const iconStyle = useMemo(() => {
24
+ const css: JSX.CSSProperties | undefined = getCSSMask(icon);
25
+ if (!color) return css;
26
+ return {...css, backgroundColor: color};
27
+ }, [color, icon]);
28
+
17
29
  return (
18
- <div className="deck-widget-button">
30
+ <div className="deck-widget-button" style={style}>
19
31
  <button
20
32
  className={`deck-widget-icon-button ${className}`}
21
33
  type="button"
22
34
  onClick={onClick}
23
35
  title={label}
24
36
  >
25
- {children ? children : <div className="deck-widget-icon" />}
37
+ {children ? children : <div className="deck-widget-icon" style={iconStyle} />}
26
38
  </button>
27
39
  </div>
28
40
  );
@@ -89,7 +89,7 @@ export const Popover = ({
89
89
 
90
90
  useMemo(() => {
91
91
  updaterRef.current?.();
92
- }, [placement, arrowSize, pixelOffset]);
92
+ }, [x, y, placement, arrowSize, pixelOffset]);
93
93
 
94
94
  useEffect(() => {
95
95
  // initial mount
@@ -0,0 +1,5 @@
1
+ export function getCSSMask(imageUrl: string | null | undefined) {
2
+ if (!imageUrl) return undefined;
3
+ const cssUrl = `url("${imageUrl.replace(/"/g, `'`)}")`;
4
+ return {maskImage: cssUrl, WebkitMaskImage: cssUrl};
5
+ }
@@ -90,6 +90,13 @@ export class PopupWidget extends Widget<PopupWidgetProps> {
90
90
  this.deck = deck;
91
91
  }
92
92
 
93
+ onRemove() {
94
+ // Invoke clean up of preact hooks
95
+ if (this.rootElement) {
96
+ render(null, this.rootElement);
97
+ }
98
+ }
99
+
93
100
  onViewportChange(viewport) {
94
101
  this.viewport = viewport;
95
102
  this.updateHTML();
@@ -0,0 +1,111 @@
1
+ // deck.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {render} from 'preact';
6
+ import {Widget, type WidgetProps, type WidgetPlacement} from '@deck.gl/core';
7
+ import {SimpleMenu, type MenuItem} from './lib/components/dropdown-menu';
8
+ import {Popover, type PopoverProps} from './lib/components/popover';
9
+ import {IconButton} from './lib/components/icon-button';
10
+
11
+ export type SelectorWidgetOption<ValueT = string> = {
12
+ value: ValueT;
13
+ icon: string;
14
+ label?: string;
15
+ };
16
+
17
+ /** Properties for the ViewSelectorWidget */
18
+ export type SelectorWidgetProps<ValueT = string> = WidgetProps & {
19
+ /** Widget positioning within the view. Default 'top-left'. */
20
+ placement?: WidgetPlacement;
21
+ /** View to attach to and interact with. Required when using multiple views. */
22
+ viewId?: string | null;
23
+ options: SelectorWidgetOption<ValueT>[];
24
+ /** The initial value. Default to the first option. */
25
+ initialValue?: ValueT;
26
+ /** Callback invoked when the value changes */
27
+ onChange?: (value: ValueT) => void;
28
+ };
29
+
30
+ /**
31
+ * A widget that renders a popup menu for selecting a view mode.
32
+ * It displays a button with the current view mode icon. Clicking the button
33
+ * toggles a popup that shows three icons for:
34
+ * - Single view
35
+ * - Two views, split horizontally
36
+ * - Two views, split vertically
37
+ */
38
+ export class SelectorWidget<ValueT = string> extends Widget<SelectorWidgetProps<ValueT>> {
39
+ static defaultProps: Required<SelectorWidgetProps> = {
40
+ ...Widget.defaultProps,
41
+ id: 'view-selector',
42
+ placement: 'top-left',
43
+ viewId: null,
44
+ initialValue: '',
45
+ options: [],
46
+ onChange: () => {}
47
+ };
48
+
49
+ className = 'deck-widget-selector';
50
+ placement: WidgetPlacement = 'top-left';
51
+ value: ValueT;
52
+ isOpen: {x: number; y: number; placement: PopoverProps['placement']} | false = false;
53
+
54
+ constructor(props: SelectorWidgetProps<ValueT>) {
55
+ super(props);
56
+ this.value = this.props.initialValue;
57
+ this.setProps(this.props);
58
+ }
59
+
60
+ setProps(props: Partial<SelectorWidgetProps<ValueT>>) {
61
+ this.placement = props.placement ?? this.placement;
62
+ this.viewId = props.viewId ?? this.viewId;
63
+ super.setProps(props);
64
+ }
65
+
66
+ onRenderHTML(rootElement: HTMLElement) {
67
+ const selectedOption =
68
+ this.props.options.find(opt => opt.value === this.value) ?? this.props.options[0];
69
+
70
+ render(
71
+ <div>
72
+ <IconButton
73
+ icon={selectedOption.icon}
74
+ label={selectedOption.label}
75
+ onClick={this._toggleMenu}
76
+ />
77
+ {this.isOpen && (
78
+ <Popover {...this.isOpen}>
79
+ <SimpleMenu
80
+ isOpen
81
+ style={{pointerEvents: 'auto', position: 'static'}}
82
+ menuItems={this.props.options as MenuItem[]}
83
+ onSelect={this._handleSelectMode}
84
+ onClose={this._toggleMenu}
85
+ />
86
+ </Popover>
87
+ )}
88
+ </div>,
89
+ rootElement
90
+ );
91
+ }
92
+
93
+ private _toggleMenu = () => {
94
+ if (this.isOpen) {
95
+ this.isOpen = false;
96
+ } else if (this.rootElement) {
97
+ this.isOpen = {
98
+ x: this.rootElement.offsetLeft,
99
+ y: this.rootElement.offsetTop,
100
+ placement: this.props.placement.includes('right') ? 'left-start' : 'right-start'
101
+ };
102
+ }
103
+ this.updateHTML();
104
+ };
105
+
106
+ private _handleSelectMode = (value: string) => {
107
+ this.value = value as ValueT;
108
+ this.props.onChange(value as ValueT);
109
+ this.updateHTML();
110
+ };
111
+ }