@deck.gl-community/widgets 9.2.8 → 9.3.0-beta.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 (295) hide show
  1. package/README.md +9 -1
  2. package/dist/graph-widgets/_deprecate/long-press-button.d.ts.map +1 -0
  3. package/dist/graph-widgets/_deprecate/long-press-button.js.map +1 -0
  4. package/dist/graph-widgets/_deprecate/view-control-widget.d.ts.map +1 -0
  5. package/dist/graph-widgets/_deprecate/view-control-widget.js.map +1 -0
  6. package/dist/{widgets → graph-widgets}/long-press-button.d.ts +1 -0
  7. package/dist/graph-widgets/long-press-button.d.ts.map +1 -0
  8. package/dist/{widgets → graph-widgets}/long-press-button.js +1 -0
  9. package/dist/graph-widgets/long-press-button.js.map +1 -0
  10. package/dist/graph-widgets/long-press-controller.d.ts.map +1 -0
  11. package/dist/graph-widgets/long-press-controller.js.map +1 -0
  12. package/dist/{widgets → graph-widgets}/pan-widget.d.ts +3 -2
  13. package/dist/graph-widgets/pan-widget.d.ts.map +1 -0
  14. package/dist/{widgets → graph-widgets}/pan-widget.js +19 -15
  15. package/dist/graph-widgets/pan-widget.js.map +1 -0
  16. package/dist/{widgets → graph-widgets}/zoom-range-widget.d.ts +3 -3
  17. package/dist/graph-widgets/zoom-range-widget.d.ts.map +1 -0
  18. package/dist/{widgets → graph-widgets}/zoom-range-widget.js +24 -23
  19. package/dist/graph-widgets/zoom-range-widget.js.map +1 -0
  20. package/dist/html-overlay-widgets/html-cluster-widget.d.ts.map +1 -0
  21. package/dist/html-overlay-widgets/html-cluster-widget.js.map +1 -0
  22. package/dist/{widgets → html-overlay-widgets}/html-overlay-item.d.ts +1 -0
  23. package/dist/html-overlay-widgets/html-overlay-item.d.ts.map +1 -0
  24. package/dist/html-overlay-widgets/html-overlay-item.js.map +1 -0
  25. package/dist/{widgets → html-overlay-widgets}/html-overlay-widget.d.ts +1 -1
  26. package/dist/html-overlay-widgets/html-overlay-widget.d.ts.map +1 -0
  27. package/dist/{widgets → html-overlay-widgets}/html-overlay-widget.js +2 -2
  28. package/dist/html-overlay-widgets/html-overlay-widget.js.map +1 -0
  29. package/dist/{widgets → html-overlay-widgets}/html-tooltip-widget.d.ts +1 -0
  30. package/dist/html-overlay-widgets/html-tooltip-widget.d.ts.map +1 -0
  31. package/dist/html-overlay-widgets/html-tooltip-widget.js.map +1 -0
  32. package/dist/index.cjs +5102 -82
  33. package/dist/index.cjs.map +4 -4
  34. package/dist/index.d.ts +33 -12
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +27 -6
  37. package/dist/index.js.map +1 -1
  38. package/dist/keyboard-shortcuts/keyboard-shortcuts-manager.d.ts +18 -0
  39. package/dist/keyboard-shortcuts/keyboard-shortcuts-manager.d.ts.map +1 -0
  40. package/dist/keyboard-shortcuts/keyboard-shortcuts-manager.js +47 -0
  41. package/dist/keyboard-shortcuts/keyboard-shortcuts-manager.js.map +1 -0
  42. package/dist/keyboard-shortcuts/keyboard-shortcuts.d.ts +22 -0
  43. package/dist/keyboard-shortcuts/keyboard-shortcuts.d.ts.map +1 -0
  44. package/dist/keyboard-shortcuts/keyboard-shortcuts.js +83 -0
  45. package/dist/keyboard-shortcuts/keyboard-shortcuts.js.map +1 -0
  46. package/dist/lib/settings/settings.d.ts +17 -0
  47. package/dist/lib/settings/settings.d.ts.map +1 -0
  48. package/dist/lib/settings/settings.js +140 -0
  49. package/dist/lib/settings/settings.js.map +1 -0
  50. package/dist/ready-to-upstream-widgets/reset-view-widget.d.ts +21 -0
  51. package/dist/ready-to-upstream-widgets/reset-view-widget.d.ts.map +1 -0
  52. package/dist/ready-to-upstream-widgets/reset-view-widget.js +29 -0
  53. package/dist/ready-to-upstream-widgets/reset-view-widget.js.map +1 -0
  54. package/dist/widget-components/icon-button.d.ts +12 -0
  55. package/dist/widget-components/icon-button.d.ts.map +1 -0
  56. package/dist/widget-components/icon-button.js +12 -0
  57. package/dist/widget-components/icon-button.js.map +1 -0
  58. package/dist/widget-components/select-widget-component.d.ts +15 -0
  59. package/dist/widget-components/select-widget-component.d.ts.map +1 -0
  60. package/dist/widget-components/select-widget-component.js +229 -0
  61. package/dist/widget-components/select-widget-component.js.map +1 -0
  62. package/dist/widget-panels/box-widget.d.ts +43 -0
  63. package/dist/widget-panels/box-widget.d.ts.map +1 -0
  64. package/dist/widget-panels/box-widget.js +191 -0
  65. package/dist/widget-panels/box-widget.js.map +1 -0
  66. package/dist/widget-panels/box-widget.test.d.ts +2 -0
  67. package/dist/widget-panels/box-widget.test.d.ts.map +1 -0
  68. package/dist/widget-panels/box-widget.test.js +41 -0
  69. package/dist/widget-panels/box-widget.test.js.map +1 -0
  70. package/dist/widget-panels/full-screen-panel-widget.d.ts +33 -0
  71. package/dist/widget-panels/full-screen-panel-widget.d.ts.map +1 -0
  72. package/dist/widget-panels/full-screen-panel-widget.js +153 -0
  73. package/dist/widget-panels/full-screen-panel-widget.js.map +1 -0
  74. package/dist/widget-panels/full-screen-panel-widget.test.d.ts +2 -0
  75. package/dist/widget-panels/full-screen-panel-widget.test.d.ts.map +1 -0
  76. package/dist/widget-panels/full-screen-panel-widget.test.js +40 -0
  77. package/dist/widget-panels/full-screen-panel-widget.test.js.map +1 -0
  78. package/dist/widget-panels/heap-memory-widget.d.ts +26 -0
  79. package/dist/widget-panels/heap-memory-widget.d.ts.map +1 -0
  80. package/dist/widget-panels/heap-memory-widget.js +156 -0
  81. package/dist/widget-panels/heap-memory-widget.js.map +1 -0
  82. package/dist/widget-panels/keyboard-shortcuts-widget.d.ts +46 -0
  83. package/dist/widget-panels/keyboard-shortcuts-widget.d.ts.map +1 -0
  84. package/dist/widget-panels/keyboard-shortcuts-widget.js +301 -0
  85. package/dist/widget-panels/keyboard-shortcuts-widget.js.map +1 -0
  86. package/dist/widget-panels/modal-widget.d.ts +62 -0
  87. package/dist/widget-panels/modal-widget.d.ts.map +1 -0
  88. package/dist/widget-panels/modal-widget.js +309 -0
  89. package/dist/widget-panels/modal-widget.js.map +1 -0
  90. package/dist/widget-panels/modal-widget.test.d.ts +2 -0
  91. package/dist/widget-panels/modal-widget.test.d.ts.map +1 -0
  92. package/dist/widget-panels/modal-widget.test.js +103 -0
  93. package/dist/widget-panels/modal-widget.test.js.map +1 -0
  94. package/dist/widget-panels/omni-box-widget.d.ts +59 -0
  95. package/dist/widget-panels/omni-box-widget.d.ts.map +1 -0
  96. package/dist/widget-panels/omni-box-widget.js +562 -0
  97. package/dist/widget-panels/omni-box-widget.js.map +1 -0
  98. package/dist/widget-panels/omni-box-widget.test.d.ts +2 -0
  99. package/dist/widget-panels/omni-box-widget.test.d.ts.map +1 -0
  100. package/dist/widget-panels/omni-box-widget.test.js +49 -0
  101. package/dist/widget-panels/omni-box-widget.test.js.map +1 -0
  102. package/dist/widget-panels/reset-view-widget.d.ts +20 -0
  103. package/dist/widget-panels/reset-view-widget.d.ts.map +1 -0
  104. package/dist/widget-panels/reset-view-widget.js +28 -0
  105. package/dist/widget-panels/reset-view-widget.js.map +1 -0
  106. package/dist/widget-panels/settings-panel.d.ts +49 -0
  107. package/dist/widget-panels/settings-panel.d.ts.map +1 -0
  108. package/dist/widget-panels/settings-panel.js +263 -0
  109. package/dist/widget-panels/settings-panel.js.map +1 -0
  110. package/dist/widget-panels/settings-panel.test.d.ts +2 -0
  111. package/dist/widget-panels/settings-panel.test.d.ts.map +1 -0
  112. package/dist/widget-panels/settings-panel.test.js +217 -0
  113. package/dist/widget-panels/settings-panel.test.js.map +1 -0
  114. package/dist/widget-panels/sidebar-widget.d.ts +65 -0
  115. package/dist/widget-panels/sidebar-widget.d.ts.map +1 -0
  116. package/dist/widget-panels/sidebar-widget.js +339 -0
  117. package/dist/widget-panels/sidebar-widget.js.map +1 -0
  118. package/dist/widget-panels/sidebar-widget.test.d.ts +2 -0
  119. package/dist/widget-panels/sidebar-widget.test.d.ts.map +1 -0
  120. package/dist/widget-panels/sidebar-widget.test.js +175 -0
  121. package/dist/widget-panels/sidebar-widget.test.js.map +1 -0
  122. package/dist/widget-panels/stats-panel.d.ts +34 -0
  123. package/dist/widget-panels/stats-panel.d.ts.map +1 -0
  124. package/dist/widget-panels/stats-panel.js +61 -0
  125. package/dist/widget-panels/stats-panel.js.map +1 -0
  126. package/dist/widget-panels/stats-panel.test.d.ts +2 -0
  127. package/dist/widget-panels/stats-panel.test.d.ts.map +1 -0
  128. package/dist/widget-panels/stats-panel.test.js +36 -0
  129. package/dist/widget-panels/stats-panel.test.js.map +1 -0
  130. package/dist/widget-panels/text-editor-panel-monaco-runtime.d.ts +17 -0
  131. package/dist/widget-panels/text-editor-panel-monaco-runtime.d.ts.map +1 -0
  132. package/dist/widget-panels/text-editor-panel-monaco-runtime.js +69 -0
  133. package/dist/widget-panels/text-editor-panel-monaco-runtime.js.map +1 -0
  134. package/dist/widget-panels/text-editor-panel.d.ts +42 -0
  135. package/dist/widget-panels/text-editor-panel.d.ts.map +1 -0
  136. package/dist/widget-panels/text-editor-panel.js +249 -0
  137. package/dist/widget-panels/text-editor-panel.js.map +1 -0
  138. package/dist/widget-panels/text-editor-panel.test.d.ts +2 -0
  139. package/dist/widget-panels/text-editor-panel.test.d.ts.map +1 -0
  140. package/dist/widget-panels/text-editor-panel.test.js +393 -0
  141. package/dist/widget-panels/text-editor-panel.test.js.map +1 -0
  142. package/dist/widget-panels/time-measure-widget.d.ts +49 -0
  143. package/dist/widget-panels/time-measure-widget.d.ts.map +1 -0
  144. package/dist/widget-panels/time-measure-widget.js +351 -0
  145. package/dist/widget-panels/time-measure-widget.js.map +1 -0
  146. package/dist/widget-panels/toast-manager.d.ts +24 -0
  147. package/dist/widget-panels/toast-manager.d.ts.map +1 -0
  148. package/dist/widget-panels/toast-manager.js +96 -0
  149. package/dist/widget-panels/toast-manager.js.map +1 -0
  150. package/dist/widget-panels/toast-manager.test.d.ts +2 -0
  151. package/dist/widget-panels/toast-manager.test.d.ts.map +1 -0
  152. package/dist/widget-panels/toast-manager.test.js +75 -0
  153. package/dist/widget-panels/toast-manager.test.js.map +1 -0
  154. package/dist/widget-panels/toast-widget.d.ts +20 -0
  155. package/dist/widget-panels/toast-widget.d.ts.map +1 -0
  156. package/dist/widget-panels/toast-widget.js +207 -0
  157. package/dist/widget-panels/toast-widget.js.map +1 -0
  158. package/dist/widget-panels/toast-widget.test.d.ts +2 -0
  159. package/dist/widget-panels/toast-widget.test.d.ts.map +1 -0
  160. package/dist/widget-panels/toast-widget.test.js +81 -0
  161. package/dist/widget-panels/toast-widget.test.js.map +1 -0
  162. package/dist/widget-panels/toggle-widget.d.ts +34 -0
  163. package/dist/widget-panels/toggle-widget.d.ts.map +1 -0
  164. package/dist/widget-panels/toggle-widget.js +46 -0
  165. package/dist/widget-panels/toggle-widget.js.map +1 -0
  166. package/dist/widget-panels/toolbar-widget.d.ts +53 -0
  167. package/dist/widget-panels/toolbar-widget.d.ts.map +1 -0
  168. package/dist/widget-panels/toolbar-widget.js +160 -0
  169. package/dist/widget-panels/toolbar-widget.js.map +1 -0
  170. package/dist/widget-panels/toolbar-widget.test.d.ts +2 -0
  171. package/dist/widget-panels/toolbar-widget.test.d.ts.map +1 -0
  172. package/dist/widget-panels/toolbar-widget.test.js +105 -0
  173. package/dist/widget-panels/toolbar-widget.test.js.map +1 -0
  174. package/dist/widget-panels/widget-containers.d.ts +275 -0
  175. package/dist/widget-panels/widget-containers.d.ts.map +1 -0
  176. package/dist/widget-panels/widget-containers.js +761 -0
  177. package/dist/widget-panels/widget-containers.js.map +1 -0
  178. package/dist/widget-panels/widget-containers.test.d.ts +2 -0
  179. package/dist/widget-panels/widget-containers.test.d.ts.map +1 -0
  180. package/dist/widget-panels/widget-containers.test.js +337 -0
  181. package/dist/widget-panels/widget-containers.test.js.map +1 -0
  182. package/dist/widget-panels/y-zoom-widget.d.ts +66 -0
  183. package/dist/widget-panels/y-zoom-widget.d.ts.map +1 -0
  184. package/dist/widget-panels/y-zoom-widget.js +264 -0
  185. package/dist/widget-panels/y-zoom-widget.js.map +1 -0
  186. package/dist/widget-panels/y-zoom-widget.test.d.ts +2 -0
  187. package/dist/widget-panels/y-zoom-widget.test.d.ts.map +1 -0
  188. package/dist/widget-panels/y-zoom-widget.test.js +71 -0
  189. package/dist/widget-panels/y-zoom-widget.test.js.map +1 -0
  190. package/dist/widgets/heap-memory-widget.d.ts +26 -0
  191. package/dist/widgets/heap-memory-widget.d.ts.map +1 -0
  192. package/dist/widgets/heap-memory-widget.js +158 -0
  193. package/dist/widgets/heap-memory-widget.js.map +1 -0
  194. package/dist/widgets/keyboard-shortcuts-widget.d.ts +28 -0
  195. package/dist/widgets/keyboard-shortcuts-widget.d.ts.map +1 -0
  196. package/dist/widgets/keyboard-shortcuts-widget.js +125 -0
  197. package/dist/widgets/keyboard-shortcuts-widget.js.map +1 -0
  198. package/dist/widgets/omni-box-widget.d.ts +59 -0
  199. package/dist/widgets/omni-box-widget.d.ts.map +1 -0
  200. package/dist/widgets/omni-box-widget.js +493 -0
  201. package/dist/widgets/omni-box-widget.js.map +1 -0
  202. package/dist/widgets/settings-widget.d.ts +64 -0
  203. package/dist/widgets/settings-widget.d.ts.map +1 -0
  204. package/dist/widgets/settings-widget.js +148 -0
  205. package/dist/widgets/settings-widget.js.map +1 -0
  206. package/dist/widgets/view-manager-utils.d.ts +1 -1
  207. package/dist/widgets/view-manager-utils.d.ts.map +1 -1
  208. package/dist/widgets/view-manager-utils.js.map +1 -1
  209. package/package.json +4 -3
  210. package/src/{widgets → graph-widgets}/long-press-button.tsx +1 -0
  211. package/src/{widgets → graph-widgets}/pan-widget.tsx +30 -23
  212. package/src/{widgets → graph-widgets}/zoom-range-widget.tsx +36 -34
  213. package/src/{widgets → html-overlay-widgets}/html-overlay-item.tsx +1 -0
  214. package/src/{widgets → html-overlay-widgets}/html-overlay-widget.tsx +2 -2
  215. package/src/{widgets → html-overlay-widgets}/html-tooltip-widget.tsx +1 -0
  216. package/src/index.ts +109 -12
  217. package/src/keyboard-shortcuts/keyboard-shortcuts-manager.ts +58 -0
  218. package/src/keyboard-shortcuts/keyboard-shortcuts.ts +113 -0
  219. package/src/keyboard-shortcuts/keyboard-shortcuts.ts.disabled +107 -0
  220. package/src/lib/settings/settings.ts +203 -0
  221. package/src/ready-to-upstream-widgets/reset-view-widget.tsx +57 -0
  222. package/src/widget-components/icon-button.tsx +38 -0
  223. package/src/widget-components/select-widget-component.tsx +354 -0
  224. package/src/widget-panels/box-widget.test.tsx +50 -0
  225. package/src/widget-panels/box-widget.tsx +284 -0
  226. package/src/widget-panels/full-screen-panel-widget.test.tsx +49 -0
  227. package/src/widget-panels/full-screen-panel-widget.tsx +223 -0
  228. package/src/widget-panels/heap-memory-widget.tsx +221 -0
  229. package/src/widget-panels/keyboard-shortcuts-widget.tsx +511 -0
  230. package/src/widget-panels/modal-widget.test.tsx +124 -0
  231. package/src/widget-panels/modal-widget.tsx +464 -0
  232. package/src/widget-panels/omni-box-widget.test.tsx +59 -0
  233. package/src/widget-panels/omni-box-widget.tsx +849 -0
  234. package/src/widget-panels/reset-view-widget.tsx +56 -0
  235. package/src/widget-panels/settings-panel.test.tsx +286 -0
  236. package/src/widget-panels/settings-panel.tsx +619 -0
  237. package/src/widget-panels/sidebar-widget.test.tsx +215 -0
  238. package/src/widget-panels/sidebar-widget.tsx +525 -0
  239. package/src/widget-panels/stats-panel.test.tsx +41 -0
  240. package/src/widget-panels/stats-panel.tsx +108 -0
  241. package/src/widget-panels/text-editor-panel-monaco-runtime.ts +97 -0
  242. package/src/widget-panels/text-editor-panel.test.tsx +618 -0
  243. package/src/widget-panels/text-editor-panel.tsx +375 -0
  244. package/src/widget-panels/time-measure-widget.tsx +445 -0
  245. package/src/widget-panels/toast-manager.test.ts +98 -0
  246. package/src/widget-panels/toast-manager.ts +134 -0
  247. package/src/widget-panels/toast-widget.test.tsx +105 -0
  248. package/src/widget-panels/toast-widget.tsx +293 -0
  249. package/src/widget-panels/toggle-widget.tsx +93 -0
  250. package/src/widget-panels/toolbar-widget.test.ts +129 -0
  251. package/src/widget-panels/toolbar-widget.tsx +293 -0
  252. package/src/widget-panels/widget-containers.test.tsx +453 -0
  253. package/src/widget-panels/widget-containers.tsx +1330 -0
  254. package/src/widget-panels/worker-modules.d.ts +7 -0
  255. package/src/widget-panels/y-zoom-widget.test.tsx +101 -0
  256. package/src/widget-panels/y-zoom-widget.tsx +376 -0
  257. package/src/widgets/heap-memory-widget.tsx +223 -0
  258. package/src/widgets/keyboard-shortcuts-widget.tsx +245 -0
  259. package/src/widgets/omni-box-widget.tsx +768 -0
  260. package/src/widgets/settings-widget.tsx +277 -0
  261. package/src/widgets/view-manager-utils.ts +1 -1
  262. package/dist/_deprecate/long-press-button.d.ts.map +0 -1
  263. package/dist/_deprecate/long-press-button.js.map +0 -1
  264. package/dist/_deprecate/view-control-widget.d.ts.map +0 -1
  265. package/dist/_deprecate/view-control-widget.js.map +0 -1
  266. package/dist/widgets/html-cluster-widget.d.ts.map +0 -1
  267. package/dist/widgets/html-cluster-widget.js.map +0 -1
  268. package/dist/widgets/html-overlay-item.d.ts.map +0 -1
  269. package/dist/widgets/html-overlay-item.js.map +0 -1
  270. package/dist/widgets/html-overlay-widget.d.ts.map +0 -1
  271. package/dist/widgets/html-overlay-widget.js.map +0 -1
  272. package/dist/widgets/html-tooltip-widget.d.ts.map +0 -1
  273. package/dist/widgets/html-tooltip-widget.js.map +0 -1
  274. package/dist/widgets/long-press-button.d.ts.map +0 -1
  275. package/dist/widgets/long-press-button.js.map +0 -1
  276. package/dist/widgets/long-press-controller.d.ts.map +0 -1
  277. package/dist/widgets/long-press-controller.js.map +0 -1
  278. package/dist/widgets/pan-widget.d.ts.map +0 -1
  279. package/dist/widgets/pan-widget.js.map +0 -1
  280. package/dist/widgets/zoom-range-widget.d.ts.map +0 -1
  281. package/dist/widgets/zoom-range-widget.js.map +0 -1
  282. /package/dist/{_deprecate → graph-widgets/_deprecate}/long-press-button.d.ts +0 -0
  283. /package/dist/{_deprecate → graph-widgets/_deprecate}/long-press-button.js +0 -0
  284. /package/dist/{_deprecate → graph-widgets/_deprecate}/view-control-widget.d.ts +0 -0
  285. /package/dist/{_deprecate → graph-widgets/_deprecate}/view-control-widget.js +0 -0
  286. /package/dist/{widgets → graph-widgets}/long-press-controller.d.ts +0 -0
  287. /package/dist/{widgets → graph-widgets}/long-press-controller.js +0 -0
  288. /package/dist/{widgets → html-overlay-widgets}/html-cluster-widget.d.ts +0 -0
  289. /package/dist/{widgets → html-overlay-widgets}/html-cluster-widget.js +0 -0
  290. /package/dist/{widgets → html-overlay-widgets}/html-overlay-item.js +0 -0
  291. /package/dist/{widgets → html-overlay-widgets}/html-tooltip-widget.js +0 -0
  292. /package/src/{_deprecate → graph-widgets/_deprecate}/long-press-button.tsx +0 -0
  293. /package/src/{_deprecate → graph-widgets/_deprecate}/view-control-widget.tsx +0 -0
  294. /package/src/{widgets → graph-widgets}/long-press-controller.ts +0 -0
  295. /package/src/{widgets → html-overlay-widgets}/html-cluster-widget.ts +0 -0
@@ -0,0 +1,511 @@
1
+ /** @jsxImportSource preact */
2
+ import {Widget} from '@deck.gl/core';
3
+ import {render} from 'preact';
4
+
5
+ import {DEFAULT_SHORTCUTS, formatKey} from '../keyboard-shortcuts/keyboard-shortcuts';
6
+ import {KeyboardShortcutsManager} from '../keyboard-shortcuts/keyboard-shortcuts-manager';
7
+
8
+ import type {KeyboardShortcut} from '../keyboard-shortcuts/keyboard-shortcuts';
9
+ import type {WidgetPanel, WidgetPanelTheme} from './widget-containers';
10
+ import type {WidgetPlacement, WidgetProps} from '@deck.gl/core';
11
+ import type {JSX} from 'preact';
12
+
13
+ export type KeyboardShortcutsWidgetProps = WidgetProps & {
14
+ placement?: WidgetPlacement;
15
+ keyboardShortcuts: KeyboardShortcut[];
16
+ installShortcuts?: boolean;
17
+ };
18
+
19
+ export type KeyboardShortcutsPanelProps = {
20
+ /** Optional list of keyboard shortcuts to render in the panel. */
21
+ keyboardShortcuts?: KeyboardShortcut[];
22
+ /** Optional theme override applied to this panel subtree. */
23
+ theme?: WidgetPanelTheme;
24
+ };
25
+
26
+ /**
27
+ * A panel definition representing keyboard shortcut details for a modal/tab container.
28
+ */
29
+ export class KeyboardShortcutsPanel implements WidgetPanel {
30
+ id = 'keyboard-shortcuts';
31
+ title = 'Keyboard Shortcuts';
32
+ theme?: WidgetPanelTheme;
33
+ content: JSX.Element;
34
+
35
+ constructor({keyboardShortcuts = [], theme = 'inherit'}: KeyboardShortcutsPanelProps = {}) {
36
+ this.theme = theme;
37
+ this.content = <KeyboardSettingsPanelContent keyboardShortcuts={keyboardShortcuts} />;
38
+ }
39
+ }
40
+
41
+ export class KeyboardShortcutsWidget extends Widget<KeyboardShortcutsWidgetProps> {
42
+ static override defaultProps = {
43
+ ...Widget.defaultProps,
44
+ id: 'keyboard-bindings',
45
+ placement: 'top-left',
46
+ keyboardShortcuts: []
47
+ } satisfies Required<WidgetProps> &
48
+ Required<Pick<KeyboardShortcutsWidgetProps, 'placement' | 'keyboardShortcuts'>> &
49
+ KeyboardShortcutsWidgetProps;
50
+
51
+ className = 'deck-widget-keyboard-bindings';
52
+ placement: WidgetPlacement = KeyboardShortcutsWidget.defaultProps.placement;
53
+
54
+ #isOpen = false;
55
+ #rootElement: HTMLElement | null = null;
56
+ #keyboardShortcuts: KeyboardShortcut[] = KeyboardShortcutsWidget.defaultProps.keyboardShortcuts;
57
+ #keyboardShortcutsManager: KeyboardShortcutsManager | null = null;
58
+
59
+ constructor(props: KeyboardShortcutsWidgetProps) {
60
+ super({...KeyboardShortcutsWidget.defaultProps, ...props});
61
+ this.#keyboardShortcuts = props.keyboardShortcuts;
62
+ if (props.placement !== undefined) {
63
+ this.placement = props.placement;
64
+ }
65
+ }
66
+
67
+ override setProps(props: Partial<KeyboardShortcutsWidgetProps>): void {
68
+ if (props.keyboardShortcuts !== undefined) {
69
+ this.#keyboardShortcuts = props.keyboardShortcuts;
70
+ this.#restartKeyboardShortcutsManager();
71
+ this.#renderRootElement();
72
+ }
73
+ if (props.installShortcuts !== undefined) {
74
+ this.#restartKeyboardShortcutsManager();
75
+ }
76
+ if (props.placement !== undefined) {
77
+ this.placement = props.placement;
78
+ }
79
+ super.setProps(props);
80
+ }
81
+
82
+ override onAdd(): void {
83
+ this.#restartKeyboardShortcutsManager();
84
+ }
85
+
86
+ override onRenderHTML(rootElement: HTMLElement): void {
87
+ this.#rootElement = rootElement;
88
+
89
+ const className = ['deck-widget', this.className, this.props.className]
90
+ .filter(Boolean)
91
+ .join(' ');
92
+ rootElement.className = className;
93
+
94
+ this.#renderRootElement();
95
+ }
96
+
97
+ override onRemove(): void {
98
+ if (this.#rootElement) {
99
+ render(null, this.#rootElement);
100
+ }
101
+ if (this.#keyboardShortcutsManager) {
102
+ this.#keyboardShortcutsManager.stop();
103
+ this.#keyboardShortcutsManager = null;
104
+ }
105
+ }
106
+
107
+ #restartKeyboardShortcutsManager(): void {
108
+ if (this.#keyboardShortcutsManager) {
109
+ this.#keyboardShortcutsManager.stop();
110
+ this.#keyboardShortcutsManager = null;
111
+ }
112
+ // @ts-expect-error Accessing protected member 'eventManager'.
113
+ const eventManager = this.deck?.eventManager;
114
+ if (eventManager && this.props.installShortcuts) {
115
+ this.#keyboardShortcutsManager = new KeyboardShortcutsManager(
116
+ eventManager,
117
+ this.#getEffectiveKeyboardShortcuts()
118
+ );
119
+ this.#keyboardShortcutsManager.start();
120
+ }
121
+ }
122
+
123
+ #renderRootElement(): void {
124
+ if (!this.#rootElement) {
125
+ return;
126
+ }
127
+
128
+ render(
129
+ <KeyboardShortcutsWidgetView
130
+ isOpen={this.#isOpen}
131
+ keyboardShortcuts={this.#getEffectiveKeyboardShortcuts()}
132
+ onClose={this.#handleClose}
133
+ onOpen={this.#handleOpen}
134
+ />,
135
+ this.#rootElement
136
+ );
137
+ }
138
+
139
+ #getEffectiveKeyboardShortcuts(): KeyboardShortcut[] {
140
+ return [
141
+ ...DEFAULT_SHORTCUTS.map((shortcut) => ({
142
+ ...shortcut,
143
+ onKeyPress: this.#handleOpen
144
+ })),
145
+ ...this.#keyboardShortcuts
146
+ ];
147
+ }
148
+
149
+ #handleOpen = (): void => {
150
+ if (this.#isOpen) {
151
+ return;
152
+ }
153
+
154
+ this.#isOpen = true;
155
+ this.#renderRootElement();
156
+ };
157
+
158
+ #handleClose = (): void => {
159
+ if (!this.#isOpen) {
160
+ return;
161
+ }
162
+
163
+ this.#isOpen = false;
164
+ this.#renderRootElement();
165
+ };
166
+ }
167
+
168
+ const MODAL_BACKDROP_STYLE: JSX.CSSProperties = {
169
+ position: 'fixed',
170
+ inset: 0,
171
+ display: 'flex',
172
+ alignItems: 'center',
173
+ justifyContent: 'center',
174
+ backgroundColor: 'rgba(15, 23, 42, 0.28)',
175
+ pointerEvents: 'auto',
176
+ zIndex: 1000
177
+ };
178
+
179
+ const MODAL_STYLE: JSX.CSSProperties = {
180
+ width: 'min(640px, calc(100vw - 24px))',
181
+ maxHeight: 'min(520px, calc(100vh - 24px))',
182
+ borderRadius: '14px',
183
+ border: '1px solid rgba(148, 163, 184, 0.75)',
184
+ backgroundColor: 'rgba(255, 255, 255, 0.98)',
185
+ boxShadow: '0 14px 40px rgba(15, 23, 42, 0.28)',
186
+ display: 'flex',
187
+ flexDirection: 'column',
188
+ overflow: 'hidden'
189
+ };
190
+
191
+ const KEY_STYLE: JSX.CSSProperties = {
192
+ borderRadius: '6px',
193
+ border: '1px solid rgba(148, 163, 184, 0.85)',
194
+ backgroundColor: 'rgba(248, 250, 252, 1)',
195
+ padding: '2px 8px',
196
+ fontSize: '11px',
197
+ color: 'var(--button-text, #0f172a)',
198
+ whiteSpace: 'nowrap'
199
+ };
200
+ const ICON_COLOR = 'var(--button-icon-idle, var(--button-text, currentColor))';
201
+ const SHORTCUT_BADGE_STYLE: JSX.CSSProperties = {
202
+ display: 'inline-flex',
203
+ alignItems: 'center',
204
+ borderRadius: '999px',
205
+ border: '1px solid rgba(148, 163, 184, 0.75)',
206
+ backgroundColor: 'rgba(248, 250, 252, 1)',
207
+ padding: '1px 6px',
208
+ fontSize: '10px',
209
+ lineHeight: '12px',
210
+ color: 'rgb(71, 85, 105)',
211
+ whiteSpace: 'nowrap'
212
+ };
213
+ const SHORTCUT_ROW_STYLE: JSX.CSSProperties = {
214
+ display: 'grid',
215
+ gridTemplateColumns: 'minmax(104px, 148px) minmax(0, 1fr) auto',
216
+ gap: '10px 14px',
217
+ alignItems: 'start',
218
+ padding: '10px 0',
219
+ borderBottom: '1px solid rgba(226, 232, 240, 0.8)'
220
+ };
221
+ const SHORTCUT_KEYS_STYLE: JSX.CSSProperties = {
222
+ display: 'flex',
223
+ alignItems: 'flex-start',
224
+ flexWrap: 'wrap',
225
+ gap: '10px',
226
+ minWidth: 0
227
+ };
228
+ const SHORTCUT_DESCRIPTION_STYLE: JSX.CSSProperties = {
229
+ fontSize: '13px',
230
+ lineHeight: '18px',
231
+ fontWeight: 500,
232
+ color: 'rgb(71, 85, 105)',
233
+ textAlign: 'left',
234
+ minWidth: 0,
235
+ width: '100%'
236
+ };
237
+ const SHORTCUT_BADGES_STYLE: JSX.CSSProperties = {
238
+ display: 'flex',
239
+ alignItems: 'flex-start',
240
+ gap: '6px',
241
+ flexWrap: 'wrap',
242
+ justifySelf: 'end'
243
+ };
244
+ const SHORTCUT_CHORD_STYLE: JSX.CSSProperties = {
245
+ display: 'inline-flex',
246
+ alignItems: 'center',
247
+ gap: '2px',
248
+ color: 'rgb(51, 65, 85)',
249
+ fontSize: '11px',
250
+ whiteSpace: 'nowrap'
251
+ };
252
+
253
+ function KeyboardShortcutsWidgetView({
254
+ isOpen,
255
+ keyboardShortcuts,
256
+ onClose,
257
+ onOpen
258
+ }: {
259
+ isOpen: boolean;
260
+ keyboardShortcuts: KeyboardShortcut[];
261
+ onClose: () => void;
262
+ onOpen: () => void;
263
+ }) {
264
+ return (
265
+ <>
266
+ <div className="deck-widget-button">
267
+ <button
268
+ className="deck-widget-icon-button"
269
+ style={{color: 'var(--button-text, currentColor)'}}
270
+ type="button"
271
+ title="Keyboard shortcuts"
272
+ aria-label="Keyboard shortcuts"
273
+ onClick={onOpen}
274
+ >
275
+ <span
276
+ style={{
277
+ fontSize: '12px',
278
+ fontWeight: 700,
279
+ color: ICON_COLOR
280
+ }}
281
+ >
282
+ <kbd
283
+ style={{
284
+ color: ICON_COLOR
285
+ }}
286
+ >
287
+ ?
288
+ </kbd>
289
+ </span>
290
+ </button>
291
+ </div>
292
+
293
+ {isOpen && (
294
+ <div style={MODAL_BACKDROP_STYLE} onClick={onClose}>
295
+ <div
296
+ style={MODAL_STYLE}
297
+ role="dialog"
298
+ aria-label="Keyboard Shortcuts"
299
+ onClick={(event) => event.stopPropagation()}
300
+ >
301
+ <div
302
+ style={{
303
+ display: 'flex',
304
+ alignItems: 'center',
305
+ justifyContent: 'space-between',
306
+ borderBottom: '1px solid rgba(226, 232, 240, 1)',
307
+ padding: '10px 12px'
308
+ }}
309
+ >
310
+ <div style={{fontSize: '14px', fontWeight: 700, color: 'rgb(30, 41, 59)'}}>
311
+ Keyboard Shortcuts
312
+ </div>
313
+ <button
314
+ type="button"
315
+ onClick={onClose}
316
+ style={{
317
+ border: 0,
318
+ background: 'transparent',
319
+ color: 'rgb(71, 85, 105)',
320
+ cursor: 'pointer',
321
+ fontSize: '18px',
322
+ lineHeight: '18px'
323
+ }}
324
+ aria-label="Close keyboard shortcuts"
325
+ title="Close"
326
+ >
327
+ ×
328
+ </button>
329
+ </div>
330
+ <KeyboardSettingsPanelContent keyboardShortcuts={keyboardShortcuts} />
331
+ </div>
332
+ </div>
333
+ )}
334
+ </>
335
+ );
336
+ }
337
+
338
+ function KeyboardSettingsPanelContent({
339
+ keyboardShortcuts
340
+ }: {
341
+ keyboardShortcuts: KeyboardShortcut[];
342
+ }) {
343
+ const shortcutRows = buildKeyboardShortcutRows(keyboardShortcuts);
344
+
345
+ return (
346
+ <div style={{overflowY: 'auto', padding: '10px 12px', flex: 1, minHeight: 0}}>
347
+ <div style={{display: 'flex', flexDirection: 'column'}}>
348
+ {shortcutRows.map((row) => (
349
+ <div key={row.key} style={SHORTCUT_ROW_STYLE}>
350
+ <div
351
+ data-shortcut-row-kind={row.shortcuts.length === 2 ? 'pair' : 'single'}
352
+ style={SHORTCUT_KEYS_STYLE}
353
+ >
354
+ {row.shortcuts.map((shortcut, index) => (
355
+ <div
356
+ key={`${row.key}-${index}`}
357
+ style={{display: 'inline-flex', alignItems: 'center'}}
358
+ >
359
+ <div data-shortcut-key-group="true" key={`${row.key}-${index}`}>
360
+ <ShortcutKey shortcut={shortcut} />
361
+ </div>
362
+ </div>
363
+ ))}
364
+ </div>
365
+ <span data-shortcut-description="true" style={SHORTCUT_DESCRIPTION_STYLE}>
366
+ {row.description}
367
+ </span>
368
+ {row.badges.length > 0 ? (
369
+ <span data-shortcut-badges="true" style={SHORTCUT_BADGES_STYLE}>
370
+ {row.badges.map((badge) => (
371
+ <span key={`${row.key}-${badge}`} style={SHORTCUT_BADGE_STYLE}>
372
+ {badge}
373
+ </span>
374
+ ))}
375
+ </span>
376
+ ) : null}
377
+ </div>
378
+ ))}
379
+ </div>
380
+ </div>
381
+ );
382
+ }
383
+
384
+ function ShortcutKey({shortcut}: {shortcut: KeyboardShortcut}) {
385
+ const parts: JSX.Element[] = [];
386
+
387
+ if (shortcut.commandKey) {
388
+ parts.push(
389
+ <kbd key="command" style={KEY_STYLE}>
390
+
391
+ </kbd>
392
+ );
393
+ }
394
+ if (shortcut.ctrlKey) {
395
+ parts.push(
396
+ <kbd key="ctrl" style={KEY_STYLE}>
397
+ ^
398
+ </kbd>
399
+ );
400
+ }
401
+ if (shortcut.shiftKey) {
402
+ parts.push(
403
+ <kbd key="shift" style={KEY_STYLE}>
404
+ Shift
405
+ </kbd>
406
+ );
407
+ }
408
+ if (shortcut.key) {
409
+ parts.push(
410
+ <kbd key="key" style={KEY_STYLE}>
411
+ {formatKey(shortcut.key)}
412
+ </kbd>
413
+ );
414
+ }
415
+ if (shortcut.dragMouse) {
416
+ parts.push(
417
+ <span key="drag" style={{whiteSpace: 'nowrap'}}>
418
+ drag mouse
419
+ </span>
420
+ );
421
+ }
422
+
423
+ return (
424
+ <div style={SHORTCUT_CHORD_STYLE}>
425
+ {parts.map((part, index) => (
426
+ <span key={`shortcut-part-${index}`} style={{display: 'inline-flex', alignItems: 'center'}}>
427
+ {part}
428
+ </span>
429
+ ))}
430
+ </div>
431
+ );
432
+ }
433
+
434
+ type KeyboardShortcutRow = {
435
+ badges: string[];
436
+ description: string;
437
+ key: string;
438
+ shortcuts: KeyboardShortcut[];
439
+ };
440
+
441
+ function buildKeyboardShortcutRows(shortcuts: KeyboardShortcut[]): KeyboardShortcutRow[] {
442
+ const rows: KeyboardShortcutRow[] = [];
443
+
444
+ for (let index = 0; index < shortcuts.length; index += 1) {
445
+ const shortcut = shortcuts[index];
446
+ const nextShortcut = shortcuts[index + 1];
447
+ const shortcutDisplayPair = shortcut?.displayPair;
448
+
449
+ if (
450
+ shortcut &&
451
+ shortcutDisplayPair &&
452
+ nextShortcut &&
453
+ canPairShortcuts(shortcut, nextShortcut)
454
+ ) {
455
+ rows.push({
456
+ badges: mergeShortcutBadges(shortcut, nextShortcut),
457
+ description: shortcutDisplayPair.description,
458
+ key: `${shortcutDisplayPair.id}-${index}`,
459
+ shortcuts: [shortcut, nextShortcut]
460
+ });
461
+ index += 1;
462
+ } else if (shortcut) {
463
+ rows.push({
464
+ badges: [...(shortcut.badges ?? [])],
465
+ description: shortcut.description,
466
+ key: `${shortcut.name}-${shortcut.key}-${index}`,
467
+ shortcuts: [shortcut]
468
+ });
469
+ }
470
+ }
471
+
472
+ return rows;
473
+ }
474
+
475
+ function canPairShortcuts(
476
+ shortcut: KeyboardShortcut,
477
+ nextShortcut: KeyboardShortcut | undefined
478
+ ): nextShortcut is KeyboardShortcut {
479
+ if (!nextShortcut) {
480
+ return false;
481
+ }
482
+
483
+ const shortcutPair = shortcut.displayPair;
484
+ const nextShortcutPair = nextShortcut.displayPair;
485
+ if (!shortcutPair || !nextShortcutPair) {
486
+ return false;
487
+ }
488
+
489
+ return (
490
+ shortcutPair.position === 'primary' &&
491
+ nextShortcutPair.position === 'secondary' &&
492
+ shortcutPair.id === nextShortcutPair.id &&
493
+ shortcutPair.description === nextShortcutPair.description
494
+ );
495
+ }
496
+
497
+ function mergeShortcutBadges(...shortcuts: KeyboardShortcut[]): string[] {
498
+ const seenBadges = new Set<string>();
499
+ const mergedBadges: string[] = [];
500
+
501
+ for (const shortcut of shortcuts) {
502
+ for (const badge of shortcut.badges ?? []) {
503
+ if (!seenBadges.has(badge)) {
504
+ seenBadges.add(badge);
505
+ mergedBadges.push(badge);
506
+ }
507
+ }
508
+ }
509
+
510
+ return mergedBadges;
511
+ }
@@ -0,0 +1,124 @@
1
+ /** @jsxImportSource preact */
2
+ import {afterEach, describe, expect, it} from 'vitest';
3
+
4
+ import {ModalWidget} from './modal-widget';
5
+ import {TabbedPanel} from './widget-containers';
6
+
7
+ import type {WidgetPanel} from './widget-containers';
8
+
9
+ const panel: WidgetPanel = {
10
+ id: 'help',
11
+ title: 'Help',
12
+ content: <div>help content</div>
13
+ };
14
+
15
+ afterEach(() => {
16
+ document.body.innerHTML = '';
17
+ });
18
+
19
+ describe('ModalWidget', () => {
20
+ it('shows a built-in trigger when button is enabled', () => {
21
+ const root = document.createElement('div');
22
+ document.body.appendChild(root);
23
+ const widget = new ModalWidget({
24
+ id: 'help',
25
+ panel,
26
+ button: true,
27
+ icon: 'icon'
28
+ });
29
+
30
+ widget.onRenderHTML(root);
31
+
32
+ const triggerButton = root.querySelector<HTMLButtonElement>('.deck-widget-icon-button');
33
+ expect(triggerButton).toBeTruthy();
34
+ const triggerWrapper = triggerButton?.parentElement;
35
+ expect(triggerWrapper?.className).toContain('deck-widget-button');
36
+ });
37
+
38
+ it('hides the built-in trigger when button is disabled', () => {
39
+ const root = document.createElement('div');
40
+ document.body.appendChild(root);
41
+ const widget = new ModalWidget({
42
+ id: 'help-hidden-button',
43
+ panel,
44
+ button: false
45
+ });
46
+
47
+ widget.onRenderHTML(root);
48
+
49
+ expect(root.querySelector('button')).toBeNull();
50
+ });
51
+
52
+ it('closes the modal when Escape is pressed', () => {
53
+ const root = document.createElement('div');
54
+ document.body.appendChild(root);
55
+ const widget = new ModalWidget({
56
+ id: 'help-escape',
57
+ panel,
58
+ defaultOpen: true,
59
+ button: true
60
+ });
61
+
62
+ widget.onRenderHTML(root);
63
+
64
+ expect(root.textContent).toContain('help content');
65
+
66
+ document.dispatchEvent(new KeyboardEvent('keydown', {key: 'Escape', bubbles: true}));
67
+
68
+ expect(root.textContent).not.toContain('help content');
69
+ expect(root.querySelector('[aria-label="Close"]')).toBeNull();
70
+ });
71
+
72
+ it('renders a visible tab row when given a tabbed widget panel', () => {
73
+ const root = document.createElement('div');
74
+ document.body.appendChild(root);
75
+ const widget = new ModalWidget({
76
+ id: 'help-tabs',
77
+ defaultOpen: true,
78
+ panel: new TabbedPanel({
79
+ id: 'help-tabs-panel',
80
+ panels: {
81
+ first: {
82
+ id: 'first',
83
+ title: 'First',
84
+ content: <div>first content</div>
85
+ },
86
+ second: {
87
+ id: 'second',
88
+ title: 'Second',
89
+ content: <div>second content</div>
90
+ }
91
+ }
92
+ }),
93
+ title: 'Help'
94
+ });
95
+
96
+ widget.onRenderHTML(root);
97
+
98
+ const tabRow = root.querySelector<HTMLDivElement>('[data-widget-tabs]');
99
+ expect(tabRow).toBeTruthy();
100
+ expect(tabRow?.textContent).toContain('First');
101
+ expect(tabRow?.textContent).toContain('Second');
102
+ });
103
+
104
+ it('raises its placement container above sibling widgets while open', () => {
105
+ const placementContainer = document.createElement('div');
106
+ placementContainer.style.zIndex = '2';
107
+ const root = document.createElement('div');
108
+ placementContainer.appendChild(root);
109
+ document.body.appendChild(placementContainer);
110
+
111
+ const widget = new ModalWidget({
112
+ id: 'help-stacking',
113
+ panel,
114
+ defaultOpen: true,
115
+ button: true
116
+ });
117
+
118
+ widget.onRenderHTML(root);
119
+ expect(placementContainer.style.zIndex).toBe('40');
120
+
121
+ widget.setProps({open: false});
122
+ expect(placementContainer.style.zIndex).toBe('2');
123
+ });
124
+ });