@deck.gl/widgets 9.4.0-alpha.2 → 9.4.0-beta.3

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 (118) hide show
  1. package/dist/compass-widget.d.ts +2 -0
  2. package/dist/compass-widget.d.ts.map +1 -1
  3. package/dist/compass-widget.js +8 -5
  4. package/dist/compass-widget.js.map +1 -1
  5. package/dist/context-menu-widget.d.ts.map +1 -1
  6. package/dist/dist.dev.js +3319 -3264
  7. package/dist/fullscreen-widget.d.ts +4 -0
  8. package/dist/fullscreen-widget.d.ts.map +1 -1
  9. package/dist/fullscreen-widget.js +3 -1
  10. package/dist/fullscreen-widget.js.map +1 -1
  11. package/dist/geocoder-widget.d.ts.map +1 -1
  12. package/dist/gimbal-widget.d.ts +2 -0
  13. package/dist/gimbal-widget.d.ts.map +1 -1
  14. package/dist/gimbal-widget.js +18 -15
  15. package/dist/gimbal-widget.js.map +1 -1
  16. package/dist/icon-widget.d.ts +2 -0
  17. package/dist/icon-widget.d.ts.map +1 -1
  18. package/dist/icon-widget.js +3 -2
  19. package/dist/icon-widget.js.map +1 -1
  20. package/dist/index.cjs +3444 -219
  21. package/dist/index.cjs.map +3 -4
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/info-widget.d.ts.map +1 -1
  27. package/dist/lib/components/button-group.d.ts.map +1 -1
  28. package/dist/lib/components/dropdown-menu.d.ts.map +1 -1
  29. package/dist/lib/components/icon-button.d.ts +2 -0
  30. package/dist/lib/components/icon-button.d.ts.map +1 -1
  31. package/dist/lib/components/icon-button.js +5 -2
  32. package/dist/lib/components/icon-button.js.map +1 -1
  33. package/dist/lib/components/popover.d.ts.map +1 -1
  34. package/dist/lib/components/tooltip.d.ts +14 -0
  35. package/dist/lib/components/tooltip.d.ts.map +1 -0
  36. package/dist/lib/components/tooltip.js +64 -0
  37. package/dist/lib/components/tooltip.js.map +1 -0
  38. package/dist/lib/components/user-content.d.ts.map +1 -1
  39. package/dist/loading-widget.d.ts +2 -0
  40. package/dist/loading-widget.d.ts.map +1 -1
  41. package/dist/loading-widget.js +2 -1
  42. package/dist/loading-widget.js.map +1 -1
  43. package/dist/popup-widget.d.ts.map +1 -1
  44. package/dist/reset-view-widget.d.ts +2 -0
  45. package/dist/reset-view-widget.d.ts.map +1 -1
  46. package/dist/reset-view-widget.js +2 -1
  47. package/dist/reset-view-widget.js.map +1 -1
  48. package/dist/screenshot-widget.d.ts +2 -0
  49. package/dist/screenshot-widget.d.ts.map +1 -1
  50. package/dist/screenshot-widget.js +2 -1
  51. package/dist/screenshot-widget.js.map +1 -1
  52. package/dist/scrollbar-widget.d.ts +6 -1
  53. package/dist/scrollbar-widget.d.ts.map +1 -1
  54. package/dist/scrollbar-widget.js +12 -3
  55. package/dist/scrollbar-widget.js.map +1 -1
  56. package/dist/selector-widget.d.ts +2 -0
  57. package/dist/selector-widget.d.ts.map +1 -1
  58. package/dist/selector-widget.js +2 -1
  59. package/dist/selector-widget.js.map +1 -1
  60. package/dist/splitter-widget.d.ts.map +1 -1
  61. package/dist/stylesheet.css +102 -116
  62. package/dist/theme-widget.d.ts +4 -0
  63. package/dist/theme-widget.d.ts.map +1 -1
  64. package/dist/theme-widget.js +4 -2
  65. package/dist/theme-widget.js.map +1 -1
  66. package/dist/themes.d.ts +5 -0
  67. package/dist/themes.d.ts.map +1 -1
  68. package/dist/themes.js +4 -0
  69. package/dist/themes.js.map +1 -1
  70. package/dist/timeline-widget.d.ts +4 -0
  71. package/dist/timeline-widget.d.ts.map +1 -1
  72. package/dist/timeline-widget.js +5 -3
  73. package/dist/timeline-widget.js.map +1 -1
  74. package/dist/toggle-widget.d.ts +4 -0
  75. package/dist/toggle-widget.d.ts.map +1 -1
  76. package/dist/toggle-widget.js +4 -2
  77. package/dist/toggle-widget.js.map +1 -1
  78. package/dist/zoom-widget.d.ts +4 -0
  79. package/dist/zoom-widget.d.ts.map +1 -1
  80. package/dist/zoom-widget.js +3 -1
  81. package/dist/zoom-widget.js.map +1 -1
  82. package/dist.min.js +2 -2
  83. package/package.json +4 -4
  84. package/src/compass-widget.tsx +31 -23
  85. package/src/fullscreen-widget.tsx +7 -0
  86. package/src/gimbal-widget.tsx +62 -54
  87. package/src/icon-widget.tsx +5 -1
  88. package/src/icons/add.svg +1 -0
  89. package/src/icons/chevron-down.svg +1 -0
  90. package/src/icons/chevron-left.svg +1 -0
  91. package/src/icons/chevron-right.svg +1 -0
  92. package/src/icons/chevron-up.svg +1 -0
  93. package/src/icons/close.svg +1 -0
  94. package/src/icons/dark-mode.svg +1 -0
  95. package/src/icons/dropdown.svg +1 -0
  96. package/src/icons/fullscreen-exit.svg +1 -0
  97. package/src/icons/fullscreen.svg +1 -0
  98. package/src/icons/light-mode.svg +1 -0
  99. package/src/icons/loading.svg +1 -0
  100. package/src/icons/pause.svg +1 -0
  101. package/src/icons/photo-camera.svg +1 -0
  102. package/src/icons/play-arrow.svg +1 -0
  103. package/src/icons/remove.svg +1 -0
  104. package/src/icons/reset-focus.svg +1 -0
  105. package/src/index.ts +1 -0
  106. package/src/lib/components/icon-button.tsx +17 -9
  107. package/src/lib/components/tooltip.tsx +116 -0
  108. package/src/loading-widget.tsx +4 -0
  109. package/src/reset-view-widget.tsx +4 -0
  110. package/src/screenshot-widget.tsx +4 -0
  111. package/src/scrollbar-widget.tsx +30 -4
  112. package/src/selector-widget.tsx +4 -0
  113. package/src/stylesheet.css +67 -114
  114. package/src/theme-widget.tsx +8 -1
  115. package/src/themes.ts +5 -0
  116. package/src/timeline-widget.tsx +10 -2
  117. package/src/toggle-widget.tsx +9 -0
  118. package/src/zoom-widget.tsx +8 -0
@@ -5,6 +5,7 @@
5
5
  import {Widget, FlyToInterpolator, WebMercatorViewport, _GlobeViewport} from '@deck.gl/core';
6
6
  import type {Viewport, WidgetPlacement, WidgetProps} from '@deck.gl/core';
7
7
  import {render} from 'preact';
8
+ import {Tooltip} from './lib/components/tooltip';
8
9
 
9
10
  export type CompassWidgetProps = WidgetProps & {
10
11
  /** Widget positioning within the view. Default 'top-left'. */
@@ -13,6 +14,8 @@ export type CompassWidgetProps = WidgetProps & {
13
14
  viewId?: string | null;
14
15
  /** Tooltip message. */
15
16
  label?: string;
17
+ /** Custom tooltip content. Overrides label for tooltip display. */
18
+ tooltip?: string | HTMLElement | false;
16
19
  /** Bearing and pitch reset transition duration in ms. */
17
20
  transitionDuration?: number;
18
21
  /**
@@ -36,6 +39,7 @@ export class CompassWidget extends Widget<CompassWidgetProps> {
36
39
  placement: 'top-left',
37
40
  viewId: null,
38
41
  label: 'Reset Compass',
42
+ tooltip: undefined!,
39
43
  transitionDuration: 200,
40
44
  onReset: () => {}
41
45
  };
@@ -60,31 +64,35 @@ export class CompassWidget extends Widget<CompassWidgetProps> {
60
64
  const widgetViewport = this.viewports[viewId];
61
65
  const [rz, rx] = this.getRotation(widgetViewport);
62
66
 
67
+ const tooltipContent =
68
+ this.props.tooltip === false ? undefined : (this.props.tooltip ?? this.props.label);
63
69
  const ui = (
64
70
  <div className="deck-widget-button" style={{perspective: 100}}>
65
- <button
66
- type="button"
67
- onClick={() => {
68
- for (const viewport of Object.values(this.viewports)) {
69
- this.handleCompassReset(viewport);
70
- }
71
- }}
72
- title={this.props.label}
73
- style={{transform: `rotateX(${rx}deg)`}}
74
- >
75
- <svg fill="none" width="100%" height="100%" viewBox="0 0 26 26">
76
- <g transform={`rotate(${rz},13,13)`}>
77
- <path
78
- d="M10 13.0001L12.9999 5L15.9997 13.0001H10Z"
79
- fill="var(--icon-compass-north-color, rgb(240, 92, 68))"
80
- />
81
- <path
82
- d="M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z"
83
- fill="var(--icon-compass-south-color, rgb(204, 204, 204))"
84
- />
85
- </g>
86
- </svg>
87
- </button>
71
+ <Tooltip content={tooltipContent}>
72
+ <button
73
+ type="button"
74
+ onClick={() => {
75
+ for (const viewport of Object.values(this.viewports)) {
76
+ this.handleCompassReset(viewport);
77
+ }
78
+ }}
79
+ aria-label={this.props.label}
80
+ style={{transform: `rotateX(${rx}deg)`}}
81
+ >
82
+ <svg fill="none" width="100%" height="100%" viewBox="0 0 26 26">
83
+ <g transform={`rotate(${rz},13,13)`}>
84
+ <path
85
+ d="M10 13.0001L12.9999 5L15.9997 13.0001H10Z"
86
+ fill="var(--icon-compass-north-color, rgb(240, 92, 68))"
87
+ />
88
+ <path
89
+ d="M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z"
90
+ fill="var(--icon-compass-south-color, rgb(204, 204, 204))"
91
+ />
92
+ </g>
93
+ </svg>
94
+ </button>
95
+ </Tooltip>
88
96
  </div>
89
97
  );
90
98
 
@@ -19,6 +19,10 @@ export type FullscreenWidgetProps = WidgetProps & {
19
19
  enterLabel?: string;
20
20
  /** Tooltip message when fullscreen. */
21
21
  exitLabel?: string;
22
+ /** Custom tooltip content when out of fullscreen. Overrides enterLabel for tooltip display. */
23
+ enterTooltip?: string | HTMLElement | false;
24
+ /** Custom tooltip content when fullscreen. Overrides exitLabel for tooltip display. */
25
+ exitTooltip?: string | HTMLElement | false;
22
26
  /**
23
27
  * A compatible DOM element which should be made full screen. By default, the map container element will be made full screen.
24
28
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen#Compatible_elements
@@ -38,6 +42,8 @@ export class FullscreenWidget extends Widget<FullscreenWidgetProps> {
38
42
  viewId: null,
39
43
  enterLabel: 'Enter Fullscreen',
40
44
  exitLabel: 'Exit Fullscreen',
45
+ enterTooltip: undefined!,
46
+ exitTooltip: undefined!,
41
47
  container: undefined!,
42
48
  onFullscreenChange: () => {}
43
49
  };
@@ -67,6 +73,7 @@ export class FullscreenWidget extends Widget<FullscreenWidgetProps> {
67
73
  this.handleClick().catch(err => log.error(err)());
68
74
  }}
69
75
  label={isFullscreen ? this.props.exitLabel : this.props.enterLabel}
76
+ tooltip={isFullscreen ? this.props.exitTooltip : this.props.enterTooltip}
70
77
  className={isFullscreen ? 'deck-widget-fullscreen-exit' : 'deck-widget-fullscreen-enter'}
71
78
  />,
72
79
  rootElement
@@ -5,6 +5,7 @@
5
5
  import {Widget, LinearInterpolator} from '@deck.gl/core';
6
6
  import type {Viewport, WidgetPlacement, WidgetProps} from '@deck.gl/core';
7
7
  import {render} from 'preact';
8
+ import {Tooltip} from './lib/components/tooltip';
8
9
 
9
10
  export type GimbalWidgetProps = WidgetProps & {
10
11
  placement?: WidgetPlacement;
@@ -12,6 +13,8 @@ export type GimbalWidgetProps = WidgetProps & {
12
13
  viewId?: string | null;
13
14
  /** Tooltip message. */
14
15
  label?: string;
16
+ /** Custom tooltip content. Overrides label for tooltip display. */
17
+ tooltip?: string | HTMLElement | false;
15
18
  /** Width of gimbal lines. */
16
19
  strokeWidth?: number;
17
20
  /** Transition duration in ms when resetting rotation. */
@@ -37,6 +40,7 @@ export class GimbalWidget extends Widget<GimbalWidgetProps> {
37
40
  placement: 'top-left',
38
41
  viewId: null,
39
42
  label: 'Gimbal',
43
+ tooltip: undefined!,
40
44
  strokeWidth: 1.5,
41
45
  transitionDuration: 200,
42
46
  onReset: () => {}
@@ -62,64 +66,68 @@ export class GimbalWidget extends Widget<GimbalWidgetProps> {
62
66
  const widgetViewport = this.viewports[viewId];
63
67
  const {rotationOrbit, rotationX} = this.getNormalizedRotation(widgetViewport);
64
68
  // Note - we use CSS 3D transforms instead of SVG 2D transforms
69
+ const tooltipContent =
70
+ this.props.tooltip === false ? undefined : (this.props.tooltip ?? this.props.label);
65
71
  const ui = (
66
72
  <div className="deck-widget-button" style={{perspective: 100, pointerEvents: 'auto'}}>
67
- <button
68
- type="button"
69
- onClick={() => {
70
- for (const viewport of Object.values(this.viewports)) {
71
- this.resetOrbitView(viewport);
72
- }
73
- }}
74
- title={this.props.label}
75
- style={{position: 'relative', width: 26, height: 26}}
76
- >
77
- {/* Outer ring */}
78
- <svg
79
- className="gimbal-outer-ring"
80
- width="100%"
81
- height="100%"
82
- viewBox="0 0 26 26"
83
- style={{
84
- position: 'absolute',
85
- top: 0,
86
- left: 0,
87
- transform: `rotateY(${rotationOrbit}deg)`
73
+ <Tooltip content={tooltipContent}>
74
+ <button
75
+ type="button"
76
+ onClick={() => {
77
+ for (const viewport of Object.values(this.viewports)) {
78
+ this.resetOrbitView(viewport);
79
+ }
88
80
  }}
81
+ aria-label={this.props.label}
82
+ style={{position: 'relative', width: 26, height: 26}}
89
83
  >
90
- <circle
91
- cx="13"
92
- cy="13"
93
- r="10"
94
- stroke="var(--icon-gimbal-outer-color, rgb(68, 92, 204))"
95
- strokeWidth={this.props.strokeWidth}
96
- fill="none"
97
- />
98
- </svg>
99
-
100
- {/* Inner ring */}
101
- <svg
102
- className="gimbal-inner-ring"
103
- width="100%"
104
- height="100%"
105
- viewBox="0 0 26 26"
106
- style={{
107
- position: 'absolute',
108
- top: 0,
109
- left: 0,
110
- transform: `rotateX(${rotationX}deg)`
111
- }}
112
- >
113
- <circle
114
- cx="13"
115
- cy="13"
116
- r="7"
117
- stroke="var(--icon-gimbal-inner-color, rgb(240, 92, 68))"
118
- strokeWidth={this.props.strokeWidth}
119
- fill="none"
120
- />
121
- </svg>
122
- </button>
84
+ {/* Outer ring */}
85
+ <svg
86
+ className="gimbal-outer-ring"
87
+ width="100%"
88
+ height="100%"
89
+ viewBox="0 0 26 26"
90
+ style={{
91
+ position: 'absolute',
92
+ top: 0,
93
+ left: 0,
94
+ transform: `rotateY(${rotationOrbit}deg)`
95
+ }}
96
+ >
97
+ <circle
98
+ cx="13"
99
+ cy="13"
100
+ r="10"
101
+ stroke="var(--icon-gimbal-outer-color, rgb(68, 92, 204))"
102
+ strokeWidth={this.props.strokeWidth}
103
+ fill="none"
104
+ />
105
+ </svg>
106
+
107
+ {/* Inner ring */}
108
+ <svg
109
+ className="gimbal-inner-ring"
110
+ width="100%"
111
+ height="100%"
112
+ viewBox="0 0 26 26"
113
+ style={{
114
+ position: 'absolute',
115
+ top: 0,
116
+ left: 0,
117
+ transform: `rotateX(${rotationX}deg)`
118
+ }}
119
+ >
120
+ <circle
121
+ cx="13"
122
+ cy="13"
123
+ r="7"
124
+ stroke="var(--icon-gimbal-inner-color, rgb(240, 92, 68))"
125
+ strokeWidth={this.props.strokeWidth}
126
+ fill="none"
127
+ />
128
+ </svg>
129
+ </button>
130
+ </Tooltip>
123
131
  </div>
124
132
  );
125
133
 
@@ -16,6 +16,8 @@ export type IconWidgetProps = WidgetProps & {
16
16
  icon: string;
17
17
  /** Tooltip label */
18
18
  label?: string;
19
+ /** Custom tooltip content. Overrides label for tooltip display. */
20
+ tooltip?: string | HTMLElement | false;
19
21
  /** Icon color, a CSS Color string */
20
22
  color?: string;
21
23
  /** Callback when the widget is clicked */
@@ -33,6 +35,7 @@ export class IconWidget extends Widget<IconWidgetProps> {
33
35
  viewId: null,
34
36
  icon: '',
35
37
  label: '',
38
+ tooltip: undefined!,
36
39
  color: '',
37
40
  onClick: undefined!
38
41
  };
@@ -52,7 +55,7 @@ export class IconWidget extends Widget<IconWidgetProps> {
52
55
  }
53
56
 
54
57
  onRenderHTML(rootElement: HTMLElement): void {
55
- const {className, style, icon, color, label, onClick} = this.props;
58
+ const {className, style, icon, color, label, tooltip, onClick} = this.props;
56
59
 
57
60
  render(
58
61
  <IconButton
@@ -61,6 +64,7 @@ export class IconWidget extends Widget<IconWidgetProps> {
61
64
  color={color}
62
65
  icon={icon}
63
66
  label={label}
67
+ tooltip={tooltip}
64
68
  onClick={onClick}
65
69
  />,
66
70
  rootElement
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M440-440H240q-17 0-28.5-11.5T200-480q0-17 11.5-28.5T240-520h200v-200q0-17 11.5-28.5T480-760q17 0 28.5 11.5T520-720v200h200q17 0 28.5 11.5T760-480q0 17-11.5 28.5T720-440H520v200q0 17-11.5 28.5T480-200q-17 0-28.5-11.5T440-240v-200Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 -960 960 960" fill="black"><path d="M480-344 240-584l56-56 184 184 184-184 56 56-240 240Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 -960 960 960" fill="black"><path d="M560-240 320-480l240-240 56 56-184 184 184 184-56 56Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 -960 960 960" fill="black"><path d="M504-480 320-664l56-56 240 240-240 240-56-56 184-184Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 -960 960 960" fill="black"><path d="M480-528 296-344l-56-56 240-240 240 240-56 56-184-184Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 -120 120 120" fill="black"><path d="m32-25-7-7 28-28-28-28 7-7 28 28 28-28 7 7-28 28 28 28-7 7-28-28-28 28Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M480-120q-151 0-255.5-104.5T120-480q0-138 90-239.5T440-838q13-2 23 3.5t16 14.5q6 9 6.5 21t-7.5 23q-17 26-25.5 55t-8.5 61q0 90 63 153t153 63q31 0 61.5-9t54.5-25q11-7 22.5-6.5T819-479q10 5 15.5 15t3.5 24q-14 138-117.5 229T480-120Zm0-80q88 0 158-48.5T740-375q-20 5-40 8t-40 3q-123 0-209.5-86.5T364-660q0-20 3-40t8-40q-78 32-126.5 102T200-480q0 116 82 198t198 82Zm-10-270Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M240-240h-80q-17 0-28.5-11.5T120-280q0-17 11.5-28.5T160-320h120q17 0 28.5 11.5T320-280v120q0 17-11.5 28.5T280-120q-17 0-28.5-11.5T240-160v-80Zm480 0v80q0 17-11.5 28.5T680-120q-17 0-28.5-11.5T640-160v-120q0-17 11.5-28.5T680-320h120q17 0 28.5 11.5T840-280q0 17-11.5 28.5T800-240h-80ZM240-720v-80q0-17 11.5-28.5T280-840q17 0 28.5 11.5T320-800v120q0 17-11.5 28.5T280-640H160q-17 0-28.5-11.5T120-680q0-17 11.5-28.5T160-720h80Zm480 0h80q17 0 28.5 11.5T840-680q0 17-11.5 28.5T800-640H680q-17 0-28.5-11.5T640-680v-120q0-17 11.5-28.5T680-840q17 0 28.5 11.5T720-800v80Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M200-200h80q17 0 28.5 11.5T320-160q0 17-11.5 28.5T280-120H160q-17 0-28.5-11.5T120-160v-120q0-17 11.5-28.5T160-320q17 0 28.5 11.5T200-280v80Zm560 0v-80q0-17 11.5-28.5T800-320q17 0 28.5 11.5T840-280v120q0 17-11.5 28.5T800-120H680q-17 0-28.5-11.5T640-160q0-17 11.5-28.5T680-200h80ZM200-760v80q0 17-11.5 28.5T160-640q-17 0-28.5-11.5T120-680v-120q0-17 11.5-28.5T160-840h120q17 0 28.5 11.5T320-800q0 17-11.5 28.5T280-760h-80Zm560 0h-80q-17 0-28.5-11.5T640-800q0-17 11.5-28.5T680-840h120q17 0 28.5 11.5T840-800v120q0 17-11.5 28.5T800-640q-17 0-28.5-11.5T760-680v-80Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M565-395q35-35 35-85t-35-85q-35-35-85-35t-85 35q-35 35-35 85t35 85q35 35 85 35t85-35Zm-226.5 56.5Q280-397 280-480t58.5-141.5Q397-680 480-680t141.5 58.5Q680-563 680-480t-58.5 141.5Q563-280 480-280t-141.5-58.5ZM80-440q-17 0-28.5-11.5T40-480q0-17 11.5-28.5T80-520h80q17 0 28.5 11.5T200-480q0 17-11.5 28.5T160-440H80Zm720 0q-17 0-28.5-11.5T760-480q0-17 11.5-28.5T800-520h80q17 0 28.5 11.5T920-480q0 17-11.5 28.5T880-440h-80ZM451.5-771.5Q440-783 440-800v-80q0-17 11.5-28.5T480-920q17 0 28.5 11.5T520-880v80q0 17-11.5 28.5T480-760q-17 0-28.5-11.5Zm0 720Q440-63 440-80v-80q0-17 11.5-28.5T480-200q17 0 28.5 11.5T520-160v80q0 17-11.5 28.5T480-40q-17 0-28.5-11.5ZM226-678l-43-42q-12-11-11.5-28t11.5-29q12-12 29-12t28 12l42 43q11 12 11 28t-11 28q-11 12-27.5 11.5T226-678Zm494 495-42-43q-11-12-11-28.5t11-27.5q11-12 27.5-11.5T734-282l43 42q12 11 11.5 28T777-183q-12 12-29 12t-28-12Zm-42-495q-12-11-11.5-27.5T678-734l42-43q11-12 28-11.5t29 11.5q12 12 12 29t-12 28l-43 42q-12 11-28 11t-28-11ZM183-183q-12-12-12-29t12-28l43-42q12-11 28.5-11t27.5 11q12 11 11.5 27.5T282-226l-42 43q-11 12-28 11.5T183-183Zm297-297Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M560-200v-560h160v560H560Zm-320 0v-560h160v560H240Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M480-260q75 0 127.5-52.5T660-440q0-75-52.5-127.5T480-620q-75 0-127.5 52.5T300-440q0 75 52.5 127.5T480-260Zm0-80q-42 0-71-29t-29-71q0-42 29-71t71-29q42 0 71 29t29 71q0 42-29 71t-71 29ZM160-120q-33 0-56.5-23.5T80-200v-480q0-33 23.5-56.5T160-760h126l50-54q11-12 26.5-19t32.5-7h170q17 0 32.5 7t26.5 19l50 54h126q33 0 56.5 23.5T880-680v480q0 33-23.5 56.5T800-120H160Zm0-80h640v-480H638l-73-80H395l-73 80H160v480Zm320-240Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M320-200v-560l440 280-440 280Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M240-440q-17 0-28.5-11.5T200-480q0-17 11.5-28.5T240-520h480q17 0 28.5 11.5T760-480q0 17-11.5 28.5T720-440H240Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M540-420v70q0 13-8.5 21.5T510-320q-13 0-21.5-8.5T480-350v-90q0-17 11.5-28.5T520-480h90q13 0 21.5 8.5T640-450q0 13-8.5 21.5T610-420h-70Zm0 280h70q13 0 21.5 8.5T640-110q0 13-8.5 21.5T610-80h-90q-17 0-28.5-11.5T480-120v-90q0-13 8.5-21.5T510-240q13 0 21.5 8.5T540-210v70Zm280-280h-70q-13 0-21.5-8.5T720-450q0-13 8.5-21.5T750-480h90q17 0 28.5 11.5T880-440v90q0 13-8.5 21.5T850-320q-13 0-21.5-8.5T820-350v-70Zm0 280v-70q0-13 8.5-21.5T850-240q13 0 21.5 8.5T880-210v90q0 17-11.5 28.5T840-80h-90q-13 0-21.5-8.5T720-110q0-13 8.5-21.5T750-140h70ZM480-760q-117 0-198.5 81.5T200-480q0 72 32.5 132t87.5 98v-70q0-17 11.5-28.5T360-360q17 0 28.5 11.5T400-320v160q0 17-11.5 28.5T360-120H200q-17 0-28.5-11.5T160-160q0-17 11.5-28.5T200-200h54q-62-50-98-122.5T120-480q0-75 28.5-140.5t77-114q48.5-48.5 114-77T480-840q114 0 204 62.5T814-615q6 16 0 31t-22 21q-16 6-31.5 0T739-585q-31-78-100.5-126.5T480-760Z"/></svg>
package/src/index.ts CHANGED
@@ -66,6 +66,7 @@ export type {DeckWidgetTheme} from './themes';
66
66
  // Experimental preact components
67
67
  export {ButtonGroup as _ButtonGroup, type ButtonGroupProps} from './lib/components/button-group';
68
68
  export {IconButton as _IconButton, type IconButtonProps} from './lib/components/icon-button';
69
+ export {Tooltip as _Tooltip, type TooltipProps} from './lib/components/tooltip';
69
70
  export {
70
71
  DropdownMenu as _DropdownMenu,
71
72
  type DropdownMenuProps,
@@ -5,11 +5,14 @@
5
5
  import type {ComponentChildren, JSX} from 'preact';
6
6
  import {useMemo} from 'preact/hooks';
7
7
  import {getCSSMask} from '../data-url';
8
+ import {Tooltip} from './tooltip';
8
9
 
9
10
  export type IconButtonProps = {
10
11
  className?: string;
11
12
  icon?: string;
12
13
  label?: string;
14
+ /** Custom tooltip content. Overrides label for tooltip display. */
15
+ tooltip?: string | ComponentChildren | false;
13
16
  color?: string;
14
17
  style?: JSX.CSSProperties;
15
18
  onClick?: JSX.MouseEventHandler<HTMLButtonElement>;
@@ -18,7 +21,8 @@ export type IconButtonProps = {
18
21
 
19
22
  /** Renders a button component with widget CSS */
20
23
  export const IconButton = (props: IconButtonProps) => {
21
- const {className = '', style, color, icon, label, onClick, children} = props;
24
+ const {className = '', style, color, icon, label, tooltip, onClick, children} = props;
25
+ const tooltipContent = tooltip === false ? undefined : (tooltip ?? label);
22
26
 
23
27
  const iconStyle = useMemo(() => {
24
28
  const css: JSX.CSSProperties | undefined = getCSSMask(icon);
@@ -26,16 +30,20 @@ export const IconButton = (props: IconButtonProps) => {
26
30
  return {...css, backgroundColor: color};
27
31
  }, [color, icon]);
28
32
 
33
+ const button = (
34
+ <button
35
+ className={`deck-widget-icon-button ${className}`}
36
+ type="button"
37
+ onClick={onClick}
38
+ aria-label={label}
39
+ >
40
+ {children ? children : <div className="deck-widget-icon" style={iconStyle} />}
41
+ </button>
42
+ );
43
+
29
44
  return (
30
45
  <div className="deck-widget-button" style={style}>
31
- <button
32
- className={`deck-widget-icon-button ${className}`}
33
- type="button"
34
- onClick={onClick}
35
- title={label}
36
- >
37
- {children ? children : <div className="deck-widget-icon" style={iconStyle} />}
38
- </button>
46
+ {tooltipContent ? <Tooltip content={tooltipContent}>{button}</Tooltip> : button}
39
47
  </div>
40
48
  );
41
49
  };
@@ -0,0 +1,116 @@
1
+ // deck.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {ComponentChildren} from 'preact';
6
+ import {useRef, useState, useEffect, useCallback} from 'preact/hooks';
7
+ import {autoUpdate, computePosition, flip, shift, offset, type Placement} from '@floating-ui/dom';
8
+
9
+ export type TooltipProps = {
10
+ /** Tooltip content — string or Preact children. */
11
+ content: string | ComponentChildren;
12
+ /** Position relative to the trigger.
13
+ * @default 'right'
14
+ */
15
+ placement?: Placement;
16
+ /** The trigger element */
17
+ children: ComponentChildren;
18
+ };
19
+
20
+ export const Tooltip = ({content, placement = 'right', children}: TooltipProps) => {
21
+ const wrapperRef = useRef<HTMLDivElement>(null);
22
+ const tooltipRef = useRef<HTMLDivElement>(null);
23
+ const [visible, setVisible] = useState(false);
24
+ const delayRef = useRef<ReturnType<typeof setTimeout>>();
25
+
26
+ const show = useCallback(() => {
27
+ clearTimeout(delayRef.current);
28
+ setVisible(true);
29
+ }, []);
30
+
31
+ const hide = useCallback(() => {
32
+ clearTimeout(delayRef.current);
33
+ setVisible(false);
34
+ }, []);
35
+
36
+ const onKeyDown = useCallback(
37
+ (e: KeyboardEvent) => {
38
+ if (e.key === 'Escape') hide();
39
+ },
40
+ [hide]
41
+ );
42
+
43
+ useEffect(() => {
44
+ const tooltip = tooltipRef.current;
45
+ const trigger = wrapperRef.current?.firstElementChild as HTMLElement | null;
46
+ if (!visible || !tooltip || !trigger) return undefined;
47
+
48
+ const updatePosition = () => {
49
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
50
+ computePosition(trigger, tooltip, {
51
+ placement,
52
+ strategy: 'fixed',
53
+ middleware: [offset(8), flip(), shift({padding: 4})]
54
+ }).then(({x, y}) => {
55
+ if (tooltipRef.current === tooltip) {
56
+ Object.assign(tooltip.style, {left: `${x}px`, top: `${y}px`, opacity: '1'});
57
+ }
58
+ });
59
+ };
60
+
61
+ return autoUpdate(trigger, tooltip, updatePosition);
62
+ }, [visible, placement]);
63
+
64
+ useEffect(() => {
65
+ return () => clearTimeout(delayRef.current);
66
+ }, []);
67
+
68
+ const htmlRef = useCallback(
69
+ (el: HTMLDivElement | null) => {
70
+ if (el && content instanceof HTMLElement) {
71
+ el.replaceChildren(content.cloneNode(true));
72
+ }
73
+ },
74
+ [content]
75
+ );
76
+
77
+ const renderContent = () => {
78
+ if (content instanceof HTMLElement) {
79
+ return <div ref={htmlRef} />;
80
+ }
81
+ return content;
82
+ };
83
+
84
+ if (content === null || content === undefined || content === false || content === '') {
85
+ return <>{children}</>;
86
+ }
87
+
88
+ return (
89
+ <div
90
+ ref={wrapperRef}
91
+ className="deck-widget-tooltip-trigger"
92
+ onPointerEnter={show}
93
+ onPointerLeave={hide}
94
+ onFocusCapture={show}
95
+ onBlurCapture={hide}
96
+ onKeyDown={onKeyDown}
97
+ >
98
+ {children}
99
+ {visible && (
100
+ <div
101
+ ref={tooltipRef}
102
+ className="deck-widget-tooltip"
103
+ role="tooltip"
104
+ style={{
105
+ position: 'fixed',
106
+ opacity: 0,
107
+ fontSize: '12px',
108
+ padding: '4px 8px'
109
+ }}
110
+ >
111
+ {renderContent()}
112
+ </div>
113
+ )}
114
+ </div>
115
+ );
116
+ };
@@ -15,6 +15,8 @@ export type LoadingWidgetProps = WidgetProps & {
15
15
  viewId?: string | null;
16
16
  /** Tooltip message when loading */
17
17
  label?: string;
18
+ /** Custom tooltip content. Overrides label for tooltip display. */
19
+ tooltip?: string | HTMLElement | false;
18
20
  /**
19
21
  * Callback when the loading state changes.
20
22
  * Called when layers transition between loading and loaded states.
@@ -32,6 +34,7 @@ export class LoadingWidget extends Widget<LoadingWidgetProps> {
32
34
  placement: 'top-left',
33
35
  viewId: null,
34
36
  label: 'Loading layer data',
37
+ tooltip: undefined!,
35
38
  onLoadingChange: () => {}
36
39
  };
37
40
 
@@ -57,6 +60,7 @@ export class LoadingWidget extends Widget<LoadingWidgetProps> {
57
60
  <IconButton
58
61
  className="deck-widget-spinner"
59
62
  label={this.props.label}
63
+ tooltip={this.props.tooltip}
60
64
  onClick={this.handleClick.bind(this)}
61
65
  />
62
66
  ),
@@ -17,6 +17,8 @@ export type ResetViewWidgetProps<ViewsT extends ViewOrViews = null> = WidgetProp
17
17
  placement?: WidgetPlacement;
18
18
  /** Tooltip message */
19
19
  label?: string;
20
+ /** Custom tooltip content. Overrides label for tooltip display. */
21
+ tooltip?: string | HTMLElement | false;
20
22
  /** The initial view state to reset the view to. Defaults to deck.props.initialViewState */
21
23
  initialViewState?: ViewStateMap<ViewsT>;
22
24
  /** View to interact with. Required when using multiple views. */
@@ -44,6 +46,7 @@ export class ResetViewWidget<ViewsT extends ViewOrViews = null> extends Widget<
44
46
  id: 'reset-view',
45
47
  placement: 'top-left',
46
48
  label: 'Reset View',
49
+ tooltip: undefined!,
47
50
  initialViewState: undefined!,
48
51
  viewId: null,
49
52
  onReset: () => {}
@@ -68,6 +71,7 @@ export class ResetViewWidget<ViewsT extends ViewOrViews = null> extends Widget<
68
71
  <IconButton
69
72
  className="deck-widget-reset-focus"
70
73
  label={this.props.label}
74
+ tooltip={this.props.tooltip}
71
75
  onClick={this.handleClick.bind(this)}
72
76
  />,
73
77
  rootElement
@@ -16,6 +16,8 @@ export type ScreenshotWidgetProps = WidgetProps & {
16
16
  viewId?: string | null;
17
17
  /** Tooltip message */
18
18
  label?: string;
19
+ /** Custom tooltip content. Overrides label for tooltip display. */
20
+ tooltip?: string | HTMLElement | false;
19
21
  /** Filename to save to */
20
22
  filename?: string;
21
23
  /** Image format */
@@ -35,6 +37,7 @@ export class ScreenshotWidget extends Widget<ScreenshotWidgetProps> {
35
37
  placement: 'top-left',
36
38
  viewId: null,
37
39
  label: 'Screenshot',
40
+ tooltip: undefined!,
38
41
  filename: 'screenshot.png',
39
42
  imageFormat: 'image/png',
40
43
  onCapture: undefined!
@@ -59,6 +62,7 @@ export class ScreenshotWidget extends Widget<ScreenshotWidgetProps> {
59
62
  <IconButton
60
63
  className="deck-widget-camera"
61
64
  label={this.props.label}
65
+ tooltip={this.props.tooltip}
62
66
  onClick={this.handleClick.bind(this)}
63
67
  />,
64
68
  rootElement