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

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 (111) hide show
  1. package/dist/compass-widget.js +5 -5
  2. package/dist/compass-widget.js.map +1 -1
  3. package/dist/context-menu-widget.d.ts +23 -18
  4. package/dist/context-menu-widget.d.ts.map +1 -1
  5. package/dist/context-menu-widget.js +44 -48
  6. package/dist/context-menu-widget.js.map +1 -1
  7. package/dist/dist.dev.js +2533 -420
  8. package/dist/fullscreen-widget.js +1 -1
  9. package/dist/fullscreen-widget.js.map +1 -1
  10. package/dist/geocoder-widget.d.ts +2 -4
  11. package/dist/geocoder-widget.d.ts.map +1 -1
  12. package/dist/geocoder-widget.js +16 -20
  13. package/dist/geocoder-widget.js.map +1 -1
  14. package/dist/gimbal-widget.d.ts +0 -3
  15. package/dist/gimbal-widget.d.ts.map +1 -1
  16. package/dist/gimbal-widget.js +4 -22
  17. package/dist/gimbal-widget.js.map +1 -1
  18. package/dist/index.cjs +921 -370
  19. package/dist/index.cjs.map +4 -4
  20. package/dist/index.d.ts +5 -3
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +4 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/info-widget.d.ts +38 -17
  25. package/dist/info-widget.d.ts.map +1 -1
  26. package/dist/info-widget.js +40 -130
  27. package/dist/info-widget.js.map +1 -1
  28. package/dist/lib/components/dropdown-menu.d.ts +10 -4
  29. package/dist/lib/components/dropdown-menu.d.ts.map +1 -1
  30. package/dist/lib/components/dropdown-menu.js +12 -7
  31. package/dist/lib/components/dropdown-menu.js.map +1 -1
  32. package/dist/lib/components/icon-menu.d.ts.map +1 -1
  33. package/dist/lib/components/icon-menu.js +2 -3
  34. package/dist/lib/components/icon-menu.js.map +1 -1
  35. package/dist/lib/components/popover.d.ts +30 -0
  36. package/dist/lib/components/popover.d.ts.map +1 -0
  37. package/dist/lib/components/popover.js +93 -0
  38. package/dist/lib/components/popover.js.map +1 -0
  39. package/dist/lib/components/range-input.d.ts +26 -0
  40. package/dist/lib/components/range-input.d.ts.map +1 -0
  41. package/dist/lib/components/range-input.js +261 -0
  42. package/dist/lib/components/range-input.js.map +1 -0
  43. package/dist/lib/components/user-content.d.ts +8 -0
  44. package/dist/lib/components/user-content.d.ts.map +1 -0
  45. package/dist/lib/components/user-content.js +15 -0
  46. package/dist/lib/components/user-content.js.map +1 -0
  47. package/dist/popup-widget.d.ts +63 -0
  48. package/dist/popup-widget.d.ts.map +1 -0
  49. package/dist/popup-widget.js +70 -0
  50. package/dist/popup-widget.js.map +1 -0
  51. package/dist/reset-view-widget.d.ts +1 -1
  52. package/dist/reset-view-widget.d.ts.map +1 -1
  53. package/dist/reset-view-widget.js +12 -11
  54. package/dist/reset-view-widget.js.map +1 -1
  55. package/dist/scrollbar-widget.d.ts +68 -0
  56. package/dist/scrollbar-widget.d.ts.map +1 -0
  57. package/dist/scrollbar-widget.js +181 -0
  58. package/dist/scrollbar-widget.js.map +1 -0
  59. package/dist/stats-widget.d.ts +4 -0
  60. package/dist/stats-widget.d.ts.map +1 -1
  61. package/dist/stats-widget.js +3 -3
  62. package/dist/stats-widget.js.map +1 -1
  63. package/dist/stylesheet.css +244 -29
  64. package/dist/theme-widget.d.ts.map +1 -1
  65. package/dist/theme-widget.js +7 -5
  66. package/dist/theme-widget.js.map +1 -1
  67. package/dist/themes.d.ts +30 -0
  68. package/dist/themes.d.ts.map +1 -1
  69. package/dist/themes.js +28 -4
  70. package/dist/themes.js.map +1 -1
  71. package/dist/timeline-widget.d.ts +25 -6
  72. package/dist/timeline-widget.d.ts.map +1 -1
  73. package/dist/timeline-widget.js +46 -18
  74. package/dist/timeline-widget.js.map +1 -1
  75. package/dist/zoom-widget.d.ts +2 -8
  76. package/dist/zoom-widget.d.ts.map +1 -1
  77. package/dist/zoom-widget.js +23 -18
  78. package/dist/zoom-widget.js.map +1 -1
  79. package/dist.min.js +2 -2
  80. package/package.json +4 -3
  81. package/src/compass-widget.tsx +5 -5
  82. package/src/context-menu-widget.tsx +86 -76
  83. package/src/fullscreen-widget.tsx +1 -1
  84. package/src/geocoder-widget.tsx +17 -24
  85. package/src/gimbal-widget.tsx +4 -26
  86. package/src/index.ts +11 -6
  87. package/src/info-widget.tsx +93 -157
  88. package/src/lib/components/dropdown-menu.tsx +42 -20
  89. package/src/lib/components/icon-menu.tsx +3 -4
  90. package/src/lib/components/popover.tsx +167 -0
  91. package/src/lib/components/range-input.tsx +432 -0
  92. package/src/lib/components/user-content.tsx +30 -0
  93. package/src/popup-widget.tsx +163 -0
  94. package/src/reset-view-widget.tsx +12 -11
  95. package/src/scrollbar-widget.tsx +288 -0
  96. package/src/stats-widget.tsx +10 -4
  97. package/src/stylesheet.css +244 -29
  98. package/src/theme-widget.tsx +7 -5
  99. package/src/themes.ts +35 -4
  100. package/src/timeline-widget.tsx +96 -33
  101. package/src/zoom-widget.tsx +25 -21
  102. package/dist/lib/components/grouped-icon-button.d.ts +0 -10
  103. package/dist/lib/components/grouped-icon-button.d.ts.map +0 -1
  104. package/dist/lib/components/grouped-icon-button.js +0 -7
  105. package/dist/lib/components/grouped-icon-button.js.map +0 -1
  106. package/dist/lib/components/simple-menu.d.ts +0 -16
  107. package/dist/lib/components/simple-menu.d.ts.map +0 -1
  108. package/dist/lib/components/simple-menu.js +0 -30
  109. package/dist/lib/components/simple-menu.js.map +0 -1
  110. package/src/lib/components/grouped-icon-button.tsx +0 -27
  111. package/src/lib/components/simple-menu.tsx +0 -52
@@ -0,0 +1,288 @@
1
+ import {Widget} from '@deck.gl/core';
2
+ import type {Position, Viewport, WidgetPlacement, WidgetProps} from '@deck.gl/core';
3
+ import {render} from 'preact';
4
+ import {RangeInput, type RangeInputDecoration} from './lib/components/range-input';
5
+
6
+ export type ScrollbarOrientation = 'vertical' | 'horizontal';
7
+
8
+ export type ContentBounds = [min: Position, max: Position];
9
+
10
+ export type ScrollbarDecoration = {
11
+ contentBounds: ContentBounds;
12
+ color: string;
13
+ title?: string;
14
+ /** Callback when the decoration is clicked. Return `true` to mark the event as handled, and prevent the default behavior. */
15
+ onClick?: (evt: MouseEvent) => boolean;
16
+ };
17
+
18
+ const clamp = (value: number, min: number, max: number) => {
19
+ if (value < min) {
20
+ return min;
21
+ }
22
+ if (value > max) {
23
+ return max;
24
+ }
25
+ return value;
26
+ };
27
+
28
+ export type ScrollbarWidgetProps = WidgetProps & {
29
+ /** The full extent of the scrollable content, in world coordinates.
30
+ * The widget relies on this value to calculate the position and size of the slider button and track.
31
+ * If not supplied, the scrollbar will always be hidden.
32
+ */
33
+ contentBounds?: ContentBounds | null;
34
+ placement?: WidgetPlacement;
35
+ viewId?: string | null;
36
+ /** Direction of the scrollbar. `'horizontal'` scrolls the camera along the X axis, and `'vertical'` scrolls the camera along the Y axis.
37
+ * @default 'vertical'
38
+ */
39
+ orientation?: ScrollbarOrientation;
40
+ /* Pixels scrolled when clicked on the step buttons.
41
+ * @default 1/10 of the viewport size
42
+ */
43
+ stepSize?: number | null;
44
+ /* Pixels scrolled when clicked on the track.
45
+ * @default 100% of the viewport size
46
+ */
47
+ pageSize?: number | null;
48
+ /** Label of the step button at the start.
49
+ * @default 'Scroll left' | 'Scroll up'
50
+ */
51
+ startButtonAriaLabel?: string;
52
+ /** Label of the end button at the start.
53
+ * @default 'Scroll right' | 'Scroll down'
54
+ */
55
+ endButtonAriaLabel?: string;
56
+ /** If `true`, mouse wheel events over the canvas will be intercepted by this scrollbar.
57
+ * Useful when simulating the native scrollbar's behavior.
58
+ * @default false
59
+ */
60
+ captureWheel?: boolean;
61
+ /** Custom markers to overlay on the track. */
62
+ decorations?: ScrollbarDecoration[];
63
+ };
64
+
65
+ type ScrollbarWidgetRequiredProps = Required<ScrollbarWidgetProps>;
66
+
67
+ /** A scrollbar widget to be used with OrthographicView */
68
+ export class ScrollbarWidget extends Widget<ScrollbarWidgetProps> {
69
+ static override defaultProps: ScrollbarWidgetRequiredProps = {
70
+ ...Widget.defaultProps,
71
+ contentBounds: null,
72
+ placement: 'top-right',
73
+ viewId: null,
74
+ orientation: 'vertical',
75
+ stepSize: null,
76
+ pageSize: null,
77
+ startButtonAriaLabel: '',
78
+ endButtonAriaLabel: '',
79
+ captureWheel: false,
80
+ decorations: []
81
+ };
82
+
83
+ override className = 'deck-widget-scrollbar';
84
+
85
+ placement: WidgetPlacement = 'fill';
86
+
87
+ private viewport?: Viewport;
88
+ private contentSize = 0;
89
+ private viewportSize = 0;
90
+ private scrollOffset = 0;
91
+
92
+ constructor(props: ScrollbarWidgetProps) {
93
+ const resolved = {
94
+ ...ScrollbarWidget.defaultProps,
95
+ ...props
96
+ } as ScrollbarWidgetRequiredProps;
97
+ super(resolved);
98
+ this.viewId = resolved.viewId ?? null;
99
+ }
100
+
101
+ override onViewportChange(viewport: Viewport) {
102
+ this.viewport = viewport;
103
+ this.onRenderHTML();
104
+ }
105
+
106
+ override onRenderHTML(): void {
107
+ const element = this.rootElement;
108
+ if (!element) {
109
+ return;
110
+ }
111
+
112
+ element.dataset.placement = this.props.placement;
113
+ const viewport = this.viewport;
114
+ this.updateViewport(viewport);
115
+
116
+ const clampedOffset = this.getClampedOffset();
117
+ const wheelTarget = this.getWheelEventTarget(this.props.captureWheel ? 'global' : 'local');
118
+ const decorations = this.getDecorations(viewport);
119
+ const isVertical = this.isVertical();
120
+ const startLabel =
121
+ this.props.startButtonAriaLabel ?? (isVertical ? 'Scroll up' : 'Scroll left');
122
+ const endLabel = this.props.endButtonAriaLabel ?? (isVertical ? 'Scroll down' : 'Scroll right');
123
+
124
+ const ui = (
125
+ <RangeInput
126
+ min={0}
127
+ max={Math.max(0, this.contentSize)}
128
+ step={this.getEffectiveStep()}
129
+ pageSize={this.getEffectivePage()}
130
+ value={[clampedOffset, clampedOffset + this.viewportSize]}
131
+ orientation={this.props.orientation}
132
+ stepButtons
133
+ startButtonAriaLabel={startLabel}
134
+ endButtonAriaLabel={endLabel}
135
+ eventTarget={wheelTarget}
136
+ decorations={decorations}
137
+ onChange={this.handleRangeChange}
138
+ />
139
+ );
140
+
141
+ render(ui, element);
142
+ }
143
+
144
+ override onRemove(): void {
145
+ if (this.rootElement) {
146
+ render(null, this.rootElement);
147
+ }
148
+ super.onRemove();
149
+ }
150
+
151
+ private updateViewport(viewport?: Viewport): void {
152
+ if (!viewport) {
153
+ this.contentSize = 0;
154
+ this.scrollOffset = 0;
155
+ this.viewportSize = 0;
156
+ return;
157
+ }
158
+
159
+ const {contentBounds} = this.props;
160
+ const isVertical = this.isVertical();
161
+ const projectedBounds = contentBounds
162
+ ? projectBounds(contentBounds, viewport, isVertical)
163
+ : ([0, 0] as [number, number]);
164
+
165
+ this.contentSize = projectedBounds[1] - projectedBounds[0];
166
+ this.scrollOffset = -projectedBounds[0];
167
+ this.viewportSize = isVertical ? viewport.height : viewport.width;
168
+ }
169
+
170
+ private getDecorations(viewport?: Viewport): RangeInputDecoration[] {
171
+ const {decorations = [], contentBounds} = this.props;
172
+ if (!viewport || !contentBounds || decorations.length === 0) {
173
+ return [];
174
+ }
175
+
176
+ const isVertical = this.isVertical();
177
+ const [contentStart] = projectBounds(contentBounds, viewport, isVertical);
178
+
179
+ return decorations.map(decoration => {
180
+ const [start, end] = projectBounds(decoration.contentBounds, viewport, isVertical);
181
+ const onClick = decoration.onClick
182
+ ? (e: MouseEvent) => {
183
+ const handled = decoration.onClick?.(e);
184
+ if (handled) {
185
+ e.stopPropagation();
186
+ e.preventDefault();
187
+ }
188
+ }
189
+ : undefined;
190
+ return {
191
+ position: [start - contentStart, end - contentStart],
192
+ element: (
193
+ <div
194
+ style={{
195
+ pointerEvents: onClick ? 'all' : 'none',
196
+ width: '100%',
197
+ height: '100%',
198
+ backgroundColor: decoration.color
199
+ }}
200
+ title={decoration.title}
201
+ onClick={onClick}
202
+ />
203
+ )
204
+ };
205
+ });
206
+ }
207
+
208
+ private getWheelEventTarget(mode: 'global' | 'local' | null) {
209
+ if (mode === null) return null;
210
+ if (mode === 'local') return this.rootElement;
211
+
212
+ return this.deck?.props.parent || this.deck?.getCanvas()?.parentElement || this.rootElement;
213
+ }
214
+
215
+ private getMaxScroll(): number {
216
+ return Math.max(0, this.contentSize - this.viewportSize);
217
+ }
218
+
219
+ private getClampedOffset(): number {
220
+ const maxScroll = this.getMaxScroll();
221
+ return clamp(this.scrollOffset, 0, maxScroll);
222
+ }
223
+
224
+ private isVertical(): boolean {
225
+ return this.props.orientation !== 'horizontal';
226
+ }
227
+
228
+ private getEffectiveStep(): number {
229
+ if (typeof this.props.stepSize === 'number' && !Number.isNaN(this.props.stepSize)) {
230
+ return this.props.stepSize;
231
+ }
232
+ return Math.max(1, this.viewportSize / 10 || 1);
233
+ }
234
+
235
+ private getEffectivePage(): number {
236
+ if (typeof this.props.pageSize === 'number' && !Number.isNaN(this.props.pageSize)) {
237
+ return this.props.pageSize;
238
+ }
239
+ return this.viewportSize;
240
+ }
241
+
242
+ private emitScroll(next: number): void {
243
+ const maxScroll = this.getMaxScroll();
244
+ const target = clamp(Math.round(next), 0, maxScroll);
245
+ const viewport = this.viewport;
246
+ if (viewport && target !== this.getClampedOffset()) {
247
+ const pixel = viewport.project(viewport.position) as [number, number];
248
+ if (this.isVertical()) {
249
+ pixel[1] -= target - this.scrollOffset;
250
+ } else {
251
+ pixel[0] -= target - this.scrollOffset;
252
+ }
253
+ const {target: newTarget} = viewport.panByPosition(viewport.position, pixel);
254
+
255
+ // @ts-expect-error Using private method temporary until there's a public one
256
+ this.deck._onViewStateChange({
257
+ viewId: viewport.id,
258
+ viewState: {
259
+ ...this.getViewState(viewport.id),
260
+ target: newTarget
261
+ },
262
+ interactionState: {}
263
+ });
264
+ }
265
+ }
266
+
267
+ private handleRangeChange = (nextValue: [number, number]) => {
268
+ this.emitScroll(nextValue[0]);
269
+ };
270
+ }
271
+
272
+ function projectBounds(
273
+ bounds: ContentBounds,
274
+ viewport: Viewport,
275
+ isVertical: boolean
276
+ ): [min: number, max: number] {
277
+ return bounds
278
+ .map(([x, y]) => viewport.project([x, y, 0]))
279
+ .reduce(
280
+ (range: [number, number], [x, y]) => {
281
+ const value = isVertical ? y : x;
282
+ range[0] = Math.min(range[0], value);
283
+ range[1] = Math.max(range[1], value);
284
+ return range;
285
+ },
286
+ [Infinity, -Infinity]
287
+ );
288
+ }
@@ -7,9 +7,6 @@ import {luma} from '@luma.gl/core';
7
7
  import {render} from 'preact';
8
8
  import type {Stats, Stat} from '@probe.gl/stats';
9
9
 
10
- const RIGHT_ARROW = '\u25b6';
11
- const DOWN_ARROW = '\u2b07';
12
-
13
10
  const DEFAULT_COUNT_FORMATTER = (stat: Stat): string => `${stat.name}: ${stat.count}`;
14
11
 
15
12
  function formatTime(time: number): string {
@@ -36,6 +33,10 @@ export type StatsWidgetProps = WidgetProps & {
36
33
  viewId?: string | null;
37
34
  /** Type of stats to display. */
38
35
  type?: 'deck' | 'luma' | 'device' | 'custom';
36
+ /** Expand the stats UI by default.
37
+ * @default false
38
+ */
39
+ defaultIsExpanded?: boolean;
39
40
  /** Stats object to visualize. */
40
41
  stats?: Stats;
41
42
  /** Title shown in the header of the pop-up. Defaults to stats.id. */
@@ -55,6 +56,7 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
55
56
  type: 'deck',
56
57
  placement: 'top-left',
57
58
  viewId: null,
59
+ defaultIsExpanded: false,
58
60
  stats: undefined!,
59
61
  title: 'Stats',
60
62
  framesPerUpdate: 1,
@@ -75,6 +77,7 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
75
77
  super(props);
76
78
  this._formatters = {...DEFAULT_FORMATTERS};
77
79
  this._resetOnUpdate = {...this.props.resetOnUpdate};
80
+ this.collapsed = !props.defaultIsExpanded;
78
81
  this.setProps(props);
79
82
  }
80
83
 
@@ -127,7 +130,10 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
127
130
  style={{cursor: 'pointer', pointerEvents: 'auto'}}
128
131
  onClick={this._toggleCollapsed}
129
132
  >
130
- {collapsed ? RIGHT_ARROW : DOWN_ARROW} {title}
133
+ <b>{title}</b>
134
+ <button className="deck-widget-dropdown-button">
135
+ <span className={`deck-widget-dropdown-icon ${collapsed ? '' : 'open'}`} />
136
+ </button>
131
137
  </div>
132
138
  {!collapsed && <div className="deck-widget-stats-content">{items}</div>}
133
139
  </div>,
@@ -1,5 +1,6 @@
1
1
  .deck-widget {
2
2
  margin: var(--widget-margin, 12px);
3
+ box-sizing: border-box;
3
4
  }
4
5
 
5
6
  /* Common button container styles */
@@ -29,6 +30,9 @@
29
30
  padding: 1px;
30
31
  gap: 1px;
31
32
  }
33
+ .deck-widget-button-group > .deck-widget-button {
34
+ display: contents;
35
+ }
32
36
 
33
37
  .deck-widget-button-group.vertical {
34
38
  display: inline-flex;
@@ -201,24 +205,56 @@
201
205
 
202
206
  /* Timeline widget styles */
203
207
  .deck-widget.deck-widget-timeline {
204
- display: flex;
205
- align-items: center;
206
- pointer-events: 'auto';
208
+ position: absolute;
209
+ margin: var(--widget-margin, 12px);
210
+ left: 0;
211
+ right: 0;
212
+ pointer-events: all;
207
213
  }
208
-
209
- .deck-widget.deck-widget-timeline .timeline-slider {
210
- margin: 0 8px;
211
- flex: 1;
212
- pointer-events: 'auto';
213
- cursor: pointer;
214
+ .deck-widget.deck-widget-timeline[data-placement^=top-] {
215
+ top: 0;
214
216
  }
215
-
216
- .deck-widget.deck-widget-timeline .text {
217
- color: var(--button-icon-idle, rgb(97, 97, 102));
217
+ .deck-widget.deck-widget-timeline[data-placement^=bottom-] {
218
+ bottom: 0;
218
219
  }
219
-
220
- .deck-widget.deck-widget-timeline .text:hover {
221
- color: var(--button-icon-hover, rgb(24, 24, 26));
220
+ .deck-widget.deck-widget-timeline .deck-widget-button-group {
221
+ box-sizing: border-box;
222
+ gap: var(--widget-margin, 12px);
223
+ padding: var(--widget-margin, 12px);
224
+ }
225
+ .deck-widget-timeline button.deck-widget-timeline-pause .deck-widget-icon {
226
+ mask-image: var(
227
+ --icon-pause,
228
+ url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M560-200v-560h160v560H560Zm-320 0v-560h160v560H240Z"/></svg>')
229
+ );
230
+ -webkit-mask-image: var(
231
+ --icon-pause,
232
+ url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M560-200v-560h160v560H560Zm-320 0v-560h160v560H240Z"/></svg>')
233
+ );
234
+ }
235
+ .deck-widget-timeline button.deck-widget-timeline-play .deck-widget-icon {
236
+ mask-image: var(
237
+ --icon-play,
238
+ url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M320-200v-560l440 280-440 280Z"/></svg>')
239
+ );
240
+ -webkit-mask-image: var(
241
+ --icon-play,
242
+ url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="black"><path d="M320-200v-560l440 280-440 280Z"/></svg>')
243
+ );
244
+ }
245
+ .deck-widget-timeline-label {
246
+ position: relative;
247
+ pointer-events: none;
248
+ width: fit-content;
249
+ font-size: 13px;
250
+ color: var(--menu-text, rgb(24, 24, 26));
251
+ white-space: nowrap;
252
+ }
253
+ .deck-widget-timeline-label--current {
254
+ transform: translate(-50%, -100%);
255
+ border-radius: var(--button-corner-radius, 8px);
256
+ background: var(--menu-background, #fff);
257
+ padding: 4px;
222
258
  }
223
259
 
224
260
  /* FPS widget styles */
@@ -370,7 +406,6 @@
370
406
  }
371
407
 
372
408
  .deck-widget .deck-widget-dropdown-menu {
373
- position: absolute;
374
409
  top: calc(100% + var(--menu-gap, 4px));
375
410
  left: 1px;
376
411
  right: 1px;
@@ -389,14 +424,16 @@
389
424
 
390
425
  .deck-widget .deck-widget-dropdown-item {
391
426
  padding: 8px 12px;
392
- cursor: pointer;
393
427
  white-space: nowrap;
394
428
  overflow: hidden;
429
+ cursor: default;
430
+ user-select: none;
395
431
  text-overflow: ellipsis;
396
432
  color: var(--menu-text, rgb(24, 24, 26));
397
433
  }
398
434
 
399
- .deck-widget .deck-widget-dropdown-item:hover {
435
+ .deck-widget .deck-widget-dropdown-item:not(.disabled):hover {
436
+ cursor: pointer;
400
437
  background: var(--menu-item-hover, rgba(128, 128, 128, 0.15));
401
438
  }
402
439
 
@@ -415,20 +452,198 @@
415
452
 
416
453
  /* Stats styles */
417
454
  .deck-widget-stats-container {
418
- border-radius: var(--button-corner-radius, 4px);
455
+ border-radius: var(--button-corner-radius, 8px);
419
456
  display: inline-flex;
420
457
  flex-direction: column;
421
- padding: 4px;
422
458
  font-size: 0.7rem;
423
459
  /* Approximately 14px */
424
460
  line-height: 1.6;
425
- font-family:
426
- 'SF Mono',
427
- /* macOS */ 'Menlo',
428
- /* macOS */ 'Consolas',
429
- /* Windows */ 'DejaVu Sans Mono',
430
- /* Linux */ 'Liberation Mono',
431
- /* Linux */ monospace;
432
- background-color: var(--button-background, #fff);
433
- color: var(--button-text, rgb(24, 24, 26));
461
+ font-family: monospace;
462
+ background-color: var(--menu-background, #fff);
463
+ color: var(--menu-text, rgb(24, 24, 26));
464
+ box-shadow: var(--menu-shadow, 0px 0px 8px 0px rgba(0, 0, 0, 0.25));
465
+ }
466
+ .deck-widget-stats-header {
467
+ color: var(--button-icon-idle, rgba(97, 97, 102, 1));
468
+ padding-left: 8px;
469
+ display: flex;
470
+ align-items: center;
471
+ gap: 4px;
472
+ }
473
+ .deck-widget-stats-header:hover {
474
+ color: var(--button-icon-hover, rgb(24, 24, 26));
475
+ }
476
+ .deck-widget-stats-header .deck-widget-dropdown-button {
477
+ background: none;
478
+ border: none;
479
+ }
480
+ .deck-widget-stats-content {
481
+ padding: 4px 8px 8px;
482
+ }
483
+
484
+ /* Popup styles */
485
+ .deck-widget.deck-widget-popover {
486
+ margin: 0 !important;
487
+ }
488
+ .deck-widget-popup-content {
489
+ border-radius: var(--button-corner-radius, 8px);
490
+ background: var(--menu-background, #fff);
491
+ color: var(--menu-text, rgb(24, 24, 26));
492
+ box-shadow: var(--menu-shadow, 0px 0px 8px 0px rgba(0, 0, 0, 0.25));
493
+ padding: 10px;
494
+ }
495
+ .deck-widget-popup-content .deck-widget-popup-controls {
496
+ --button-size: 20px;
497
+ }
498
+ .deck-widget-popup-content .deck-widget-popup-controls .deck-widget-button {
499
+ box-shadow: none;
500
+ }
501
+ .deck-widget-popup-content button.deck-widget-popup-close-button .deck-widget-icon {
502
+ mask-image: var(
503
+ --icon-close,
504
+ url('data:image/svg+xml,<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>')
505
+ );
506
+ -webkit-mask-image: var(
507
+ --icon-close,
508
+ url('data:image/svg+xml,<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>')
509
+ );
510
+ }
511
+
512
+ /** Range input */
513
+ .deck-widget-range {
514
+ position: relative;
515
+ pointer-events: all;
516
+ display: flex;
517
+ align-items: center;
518
+ user-select: none;
519
+ touch-action: none;
520
+ box-sizing: border-box;
521
+ margin: 0;
522
+ }
523
+ .deck-widget-range__track {
524
+ position: relative;
525
+ cursor: pointer;
526
+ flex: 1;
527
+ background: var(--range-track-color, #888);
528
+ border-radius: calc(var(--range-track-size, 16px) / 2);
529
+ }
530
+ .deck-widget-range__decorations {
531
+ position: absolute;
532
+ }
533
+ .deck-widget-range__decoration {
534
+ position: absolute;
535
+ }
536
+ .deck-widget-range__decoration:active {
537
+ background-color: var(--range-decoration-active-color, gold) !important;
538
+ }
539
+ .deck-widget-range__thumb {
540
+ position: relative;
541
+ box-sizing: content-box;
542
+ padding: calc(var(--range-thumb-size, 10px) / 2);
543
+ border-radius: calc(var(--range-thumb-size, 10px) / 2);
544
+ margin: calc(var(--range-thumb-size, 10px) * -0.5);
545
+ background: var(--range-thumb-color, #444);
546
+ }
547
+ .deck-widget-range__button {
548
+ cursor: pointer;
549
+ opacity: 0.4;
550
+ background-color: transparent;
551
+ width: var(--range-step-button-size, 24px);
552
+ height: var(--range-step-button-size, 24px);
553
+ background-size: cover;
554
+ border: none;
555
+ padding: 0;
556
+ }
557
+ .deck-widget-range__button:hover {
558
+ opacity: 1;
559
+ }
560
+ .deck-widget-range:not(.deck-widget-range--disabled) .deck-widget-range__button:disabled {
561
+ pointer-events: none;
562
+ opacity: 0.1;
563
+ }
564
+ .deck-widget-range--disabled {
565
+ display: none;
566
+ }
567
+ .deck-widget-range--vertical {
568
+ flex-direction: column;
569
+ height: 100%;
570
+ top: 0;
571
+ }
572
+ .deck-widget-range--horizontal {
573
+ flex-direction: row;
574
+ width: 100%;
575
+ left: 0;
576
+ }
577
+ .deck-widget-range--vertical .deck-widget-range__track {
578
+ width: var(--range-track-size, 16px);
579
+ padding-top: calc(var(--range-track-size, 16px) / 2);
580
+ padding-bottom: calc(var(--range-track-size, 16px) / 2);
581
+ height: 100%;
582
+ min-height: calc(var(--range-track-size, 16px) * 3);
583
+ }
584
+ .deck-widget-range--vertical .deck-widget-range__thumb {
585
+ width: 0;
586
+ left: 50%;
587
+ cursor: ns-resize;
588
+ }
589
+ .deck-widget-range--vertical .deck-widget-range__decorations {
590
+ top: calc(var(--range-track-size, 16px) / 2);
591
+ bottom: calc(var(--range-track-size, 16px) / 2);
592
+ left: 0;
593
+ right: 0;
594
+ }
595
+ .deck-widget-range--vertical .deck-widget-range__button--start .deck-widget-icon {
596
+ mask-image: url('data:image/svg+xml,<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>');
597
+ }
598
+ .deck-widget-range--vertical .deck-widget-range__button--end .deck-widget-icon {
599
+ mask-image: url('data:image/svg+xml,<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>');
600
+ }
601
+ .deck-widget-range--horizontal .deck-widget-range__track {
602
+ height: var(--range-track-size, 16px);
603
+ padding-left: calc(var(--range-track-size, 16px) / 2);
604
+ padding-right: calc(var(--range-track-size, 16px) / 2);
605
+ width: 100%;
606
+ min-width: calc(var(--range-track-size, 16px) * 3);
607
+ }
608
+ .deck-widget-range--horizontal .deck-widget-range__thumb {
609
+ height: 0;
610
+ top: 50%;
611
+ cursor: ew-resize;
612
+ }
613
+ .deck-widget-range--horizontal .deck-widget-range__decorations {
614
+ left: calc(var(--range-track-size, 16px) / 2);
615
+ right: calc(var(--range-track-size, 16px) / 2);
616
+ top: 0;
617
+ bottom: 0;
618
+ }
619
+ .deck-widget-range--horizontal .deck-widget-range__button--start .deck-widget-icon {
620
+ mask-image: url('data:image/svg+xml,<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>');
621
+ }
622
+ .deck-widget-range--horizontal .deck-widget-range__button--end .deck-widget-icon {
623
+ mask-image: url('data:image/svg+xml,<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>');
624
+ }
625
+
626
+ /** Scrollbar */
627
+ .deck-widget-scrollbar .deck-widget-range {
628
+ position: absolute;
629
+ }
630
+ .deck-widget-scrollbar .deck-widget-range--vertical {
631
+ top: var(--range-step-button-size, 24px);
632
+ height: calc(100% - var(--range-step-button-size, 24px) * 2);
633
+ }
634
+ .deck-widget-scrollbar[data-placement$=-left] .deck-widget-range--vertical {
635
+ left: 0;
636
+ }
637
+ .deck-widget-scrollbar[data-placement$=-right] .deck-widget-range--vertical {
638
+ right: 0;
639
+ }
640
+ .deck-widget-scrollbar .deck-widget-range--horizontal {
641
+ left: var(--range-step-button-size, 24px);
642
+ width: calc(100% - var(--range-step-button-size, 24px) * 2);
643
+ }
644
+ .deck-widget-scrollbar[data-placement^=top-] .deck-widget-range--horizontal {
645
+ top: 0;
646
+ }
647
+ .deck-widget-scrollbar[data-placement^=bottom-] .deck-widget-range--horizontal {
648
+ bottom: 0;
434
649
  }
@@ -116,10 +116,12 @@ export class ThemeWidget extends Widget<ThemeWidgetProps> {
116
116
  /** Read browser preference */
117
117
  _getInitialThemeMode(): 'light' | 'dark' {
118
118
  const {initialThemeMode} = this.props;
119
- return initialThemeMode === 'auto'
120
- ? window.matchMedia('(prefers-color-scheme: dark)').matches
121
- ? 'dark'
122
- : 'light'
123
- : initialThemeMode;
119
+ if (initialThemeMode !== 'auto') {
120
+ return initialThemeMode;
121
+ }
122
+ if (typeof window === 'undefined') {
123
+ return 'light';
124
+ }
125
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
124
126
  }
125
127
  }