@react-x11/components 0.8.0 → 0.9.0

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 (323) hide show
  1. package/README.md +95 -34
  2. package/dist/charts/node.d.ts +1 -1
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +6 -2
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/embed/client.d.ts +48 -3
  7. package/dist/embed/client.d.ts.map +1 -1
  8. package/dist/embed/client.js +79 -5
  9. package/dist/embed/client.js.map +1 -1
  10. package/dist/embed/index.d.ts +1 -1
  11. package/dist/embed/index.d.ts.map +1 -1
  12. package/dist/embed/index.js +1 -1
  13. package/dist/embed/index.js.map +1 -1
  14. package/dist/html/node.d.ts +3 -1
  15. package/dist/html/node.d.ts.map +1 -1
  16. package/dist/html/node.js +7 -5
  17. package/dist/html/node.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/{markdown → internal/markdown}/ast.d.ts +12 -0
  23. package/dist/internal/markdown/ast.d.ts.map +1 -0
  24. package/dist/internal/markdown/ast.js +22 -0
  25. package/dist/internal/markdown/ast.js.map +1 -0
  26. package/dist/internal/markdown/parse.d.ts.map +1 -0
  27. package/dist/{markdown → internal/markdown}/parse.js +112 -22
  28. package/dist/internal/markdown/parse.js.map +1 -0
  29. package/dist/internal/markdown/stringify.d.ts +4 -0
  30. package/dist/internal/markdown/stringify.d.ts.map +1 -0
  31. package/dist/internal/markdown/stringify.js +684 -0
  32. package/dist/internal/markdown/stringify.js.map +1 -0
  33. package/dist/internal/markdown/tags.d.ts.map +1 -0
  34. package/dist/internal/markdown/tags.js.map +1 -0
  35. package/dist/internal/scroll.d.ts +3 -2
  36. package/dist/internal/scroll.d.ts.map +1 -1
  37. package/dist/internal/scroll.js +1 -0
  38. package/dist/internal/scroll.js.map +1 -1
  39. package/dist/maps/anchors.d.ts +98 -0
  40. package/dist/maps/anchors.d.ts.map +1 -0
  41. package/dist/maps/anchors.js +531 -0
  42. package/dist/maps/anchors.js.map +1 -0
  43. package/dist/maps/controller.d.ts +283 -0
  44. package/dist/maps/controller.d.ts.map +1 -0
  45. package/dist/maps/controller.js +743 -0
  46. package/dist/maps/controller.js.map +1 -0
  47. package/dist/maps/gl/buckets.d.ts +95 -0
  48. package/dist/maps/gl/buckets.d.ts.map +1 -0
  49. package/dist/maps/gl/buckets.js +362 -0
  50. package/dist/maps/gl/buckets.js.map +1 -0
  51. package/dist/maps/gl/build-worker.d.ts +13 -0
  52. package/dist/maps/gl/build-worker.d.ts.map +1 -0
  53. package/dist/maps/gl/build-worker.js +44 -0
  54. package/dist/maps/gl/build-worker.js.map +1 -0
  55. package/dist/maps/gl/color.d.ts +8 -0
  56. package/dist/maps/gl/color.d.ts.map +1 -0
  57. package/dist/maps/gl/color.js +57 -0
  58. package/dist/maps/gl/color.js.map +1 -0
  59. package/dist/maps/gl/cover.d.ts +51 -0
  60. package/dist/maps/gl/cover.d.ts.map +1 -0
  61. package/dist/maps/gl/cover.js +124 -0
  62. package/dist/maps/gl/cover.js.map +1 -0
  63. package/dist/maps/gl/index.d.ts +3 -0
  64. package/dist/maps/gl/index.d.ts.map +1 -0
  65. package/dist/maps/gl/index.js +20 -0
  66. package/dist/maps/gl/index.js.map +1 -0
  67. package/dist/maps/gl/markers.d.ts +28 -0
  68. package/dist/maps/gl/markers.d.ts.map +1 -0
  69. package/dist/maps/gl/markers.js +60 -0
  70. package/dist/maps/gl/markers.js.map +1 -0
  71. package/dist/maps/gl/overlays.d.ts +68 -0
  72. package/dist/maps/gl/overlays.d.ts.map +1 -0
  73. package/dist/maps/gl/overlays.js +300 -0
  74. package/dist/maps/gl/overlays.js.map +1 -0
  75. package/dist/maps/gl/pane.d.ts +12 -0
  76. package/dist/maps/gl/pane.d.ts.map +1 -0
  77. package/dist/maps/gl/pane.js +37 -0
  78. package/dist/maps/gl/pane.js.map +1 -0
  79. package/dist/maps/gl/placement.d.ts +111 -0
  80. package/dist/maps/gl/placement.d.ts.map +1 -0
  81. package/dist/maps/gl/placement.js +567 -0
  82. package/dist/maps/gl/placement.js.map +1 -0
  83. package/dist/maps/gl/renderer.d.ts +352 -0
  84. package/dist/maps/gl/renderer.d.ts.map +1 -0
  85. package/dist/maps/gl/renderer.js +1127 -0
  86. package/dist/maps/gl/renderer.js.map +1 -0
  87. package/dist/maps/gl/shaders.d.ts +120 -0
  88. package/dist/maps/gl/shaders.d.ts.map +1 -0
  89. package/dist/maps/gl/shaders.js +451 -0
  90. package/dist/maps/gl/shaders.js.map +1 -0
  91. package/dist/maps/gl/store.d.ts +115 -0
  92. package/dist/maps/gl/store.d.ts.map +1 -0
  93. package/dist/maps/gl/store.js +469 -0
  94. package/dist/maps/gl/store.js.map +1 -0
  95. package/dist/maps/gl/text.d.ts +154 -0
  96. package/dist/maps/gl/text.d.ts.map +1 -0
  97. package/dist/maps/gl/text.js +382 -0
  98. package/dist/maps/gl/text.js.map +1 -0
  99. package/dist/maps/gl/view.d.ts +54 -0
  100. package/dist/maps/gl/view.d.ts.map +1 -0
  101. package/dist/maps/gl/view.js +974 -0
  102. package/dist/maps/gl/view.js.map +1 -0
  103. package/dist/maps/index.d.ts +1 -1
  104. package/dist/maps/index.d.ts.map +1 -1
  105. package/dist/maps/index.js +216 -52
  106. package/dist/maps/index.js.map +1 -1
  107. package/dist/maps/labels.d.ts +37 -16
  108. package/dist/maps/labels.d.ts.map +1 -1
  109. package/dist/maps/labels.js +188 -119
  110. package/dist/maps/labels.js.map +1 -1
  111. package/dist/maps/node.d.ts +162 -56
  112. package/dist/maps/node.d.ts.map +1 -1
  113. package/dist/maps/node.js +607 -515
  114. package/dist/maps/node.js.map +1 -1
  115. package/dist/maps/overlay.d.ts +72 -9
  116. package/dist/maps/overlay.d.ts.map +1 -1
  117. package/dist/maps/overlay.js +108 -28
  118. package/dist/maps/overlay.js.map +1 -1
  119. package/dist/maps/paint.d.ts +3 -0
  120. package/dist/maps/paint.d.ts.map +1 -1
  121. package/dist/maps/paint.js.map +1 -1
  122. package/dist/maps/proj.d.ts +14 -0
  123. package/dist/maps/proj.d.ts.map +1 -1
  124. package/dist/maps/proj.js +28 -0
  125. package/dist/maps/proj.js.map +1 -1
  126. package/dist/maps/renderer.d.ts +69 -0
  127. package/dist/maps/renderer.d.ts.map +1 -0
  128. package/dist/maps/renderer.js +66 -0
  129. package/dist/maps/renderer.js.map +1 -0
  130. package/dist/maps/sources.d.ts +25 -3
  131. package/dist/maps/sources.d.ts.map +1 -1
  132. package/dist/maps/sources.js +17 -0
  133. package/dist/maps/sources.js.map +1 -1
  134. package/dist/maps/theme.d.ts +19 -0
  135. package/dist/maps/theme.d.ts.map +1 -0
  136. package/dist/maps/theme.js +44 -0
  137. package/dist/maps/theme.js.map +1 -0
  138. package/dist/maps/tiles.d.ts +117 -17
  139. package/dist/maps/tiles.d.ts.map +1 -1
  140. package/dist/maps/tiles.js +163 -30
  141. package/dist/maps/tiles.js.map +1 -1
  142. package/dist/maps/types.d.ts +188 -20
  143. package/dist/maps/types.d.ts.map +1 -1
  144. package/dist/markdown/expressions.d.ts +1 -1
  145. package/dist/markdown/expressions.d.ts.map +1 -1
  146. package/dist/markdown/expressions.js.map +1 -1
  147. package/dist/markdown/index.d.ts +7 -6
  148. package/dist/markdown/index.d.ts.map +1 -1
  149. package/dist/markdown/index.js +9 -7
  150. package/dist/markdown/index.js.map +1 -1
  151. package/dist/markdown/spans.d.ts +1 -1
  152. package/dist/markdown/spans.d.ts.map +1 -1
  153. package/dist/media-player/index.d.ts +11 -3
  154. package/dist/media-player/index.d.ts.map +1 -1
  155. package/dist/media-player/index.js +13 -2
  156. package/dist/media-player/index.js.map +1 -1
  157. package/dist/rich-text-editor/clipboard.d.ts +23 -0
  158. package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
  159. package/dist/rich-text-editor/clipboard.js +307 -0
  160. package/dist/rich-text-editor/clipboard.js.map +1 -0
  161. package/dist/rich-text-editor/collab.d.ts +32 -0
  162. package/dist/rich-text-editor/collab.d.ts.map +1 -0
  163. package/dist/rich-text-editor/collab.js +55 -0
  164. package/dist/rich-text-editor/collab.js.map +1 -0
  165. package/dist/rich-text-editor/commands.d.ts +50 -0
  166. package/dist/rich-text-editor/commands.d.ts.map +1 -0
  167. package/dist/rich-text-editor/commands.js +376 -0
  168. package/dist/rich-text-editor/commands.js.map +1 -0
  169. package/dist/rich-text-editor/drag.d.ts +68 -0
  170. package/dist/rich-text-editor/drag.d.ts.map +1 -0
  171. package/dist/rich-text-editor/drag.js +148 -0
  172. package/dist/rich-text-editor/drag.js.map +1 -0
  173. package/dist/rich-text-editor/html.d.ts +145 -0
  174. package/dist/rich-text-editor/html.d.ts.map +1 -0
  175. package/dist/rich-text-editor/html.js +516 -0
  176. package/dist/rich-text-editor/html.js.map +1 -0
  177. package/dist/rich-text-editor/index.d.ts +263 -0
  178. package/dist/rich-text-editor/index.d.ts.map +1 -0
  179. package/dist/rich-text-editor/index.js +822 -0
  180. package/dist/rich-text-editor/index.js.map +1 -0
  181. package/dist/rich-text-editor/inline.d.ts +94 -0
  182. package/dist/rich-text-editor/inline.d.ts.map +1 -0
  183. package/dist/rich-text-editor/inline.js +252 -0
  184. package/dist/rich-text-editor/inline.js.map +1 -0
  185. package/dist/rich-text-editor/keymap.d.ts +71 -0
  186. package/dist/rich-text-editor/keymap.d.ts.map +1 -0
  187. package/dist/rich-text-editor/keymap.js +393 -0
  188. package/dist/rich-text-editor/keymap.js.map +1 -0
  189. package/dist/rich-text-editor/keys.d.ts +29 -0
  190. package/dist/rich-text-editor/keys.d.ts.map +1 -0
  191. package/dist/rich-text-editor/keys.js +93 -0
  192. package/dist/rich-text-editor/keys.js.map +1 -0
  193. package/dist/rich-text-editor/look.d.ts +71 -0
  194. package/dist/rich-text-editor/look.d.ts.map +1 -0
  195. package/dist/rich-text-editor/look.js +342 -0
  196. package/dist/rich-text-editor/look.js.map +1 -0
  197. package/dist/rich-text-editor/markdown.d.ts +18 -0
  198. package/dist/rich-text-editor/markdown.d.ts.map +1 -0
  199. package/dist/rich-text-editor/markdown.js +496 -0
  200. package/dist/rich-text-editor/markdown.js.map +1 -0
  201. package/dist/rich-text-editor/nodes.d.ts +135 -0
  202. package/dist/rich-text-editor/nodes.d.ts.map +1 -0
  203. package/dist/rich-text-editor/nodes.js +397 -0
  204. package/dist/rich-text-editor/nodes.js.map +1 -0
  205. package/dist/rich-text-editor/render.d.ts +68 -0
  206. package/dist/rich-text-editor/render.d.ts.map +1 -0
  207. package/dist/rich-text-editor/render.js +500 -0
  208. package/dist/rich-text-editor/render.js.map +1 -0
  209. package/dist/rich-text-editor/schema.d.ts +16 -0
  210. package/dist/rich-text-editor/schema.d.ts.map +1 -0
  211. package/dist/rich-text-editor/schema.js +386 -0
  212. package/dist/rich-text-editor/schema.js.map +1 -0
  213. package/dist/rich-text-editor/store.d.ts +11 -0
  214. package/dist/rich-text-editor/store.d.ts.map +1 -0
  215. package/dist/rich-text-editor/store.js +43 -0
  216. package/dist/rich-text-editor/store.js.map +1 -0
  217. package/dist/rich-text-editor/suggest.d.ts +127 -0
  218. package/dist/rich-text-editor/suggest.d.ts.map +1 -0
  219. package/dist/rich-text-editor/suggest.js +0 -0
  220. package/dist/rich-text-editor/suggest.js.map +1 -0
  221. package/dist/rich-text-editor/tables.d.ts +41 -0
  222. package/dist/rich-text-editor/tables.d.ts.map +1 -0
  223. package/dist/rich-text-editor/tables.js +204 -0
  224. package/dist/rich-text-editor/tables.js.map +1 -0
  225. package/dist/rich-text-editor/toolbar.d.ts +58 -0
  226. package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
  227. package/dist/rich-text-editor/toolbar.js +548 -0
  228. package/dist/rich-text-editor/toolbar.js.map +1 -0
  229. package/dist/rich-text-editor/view.d.ts +368 -0
  230. package/dist/rich-text-editor/view.d.ts.map +1 -0
  231. package/dist/rich-text-editor/view.js +1968 -0
  232. package/dist/rich-text-editor/view.js.map +1 -0
  233. package/dist/rich-text-editor/virtual.d.ts +54 -0
  234. package/dist/rich-text-editor/virtual.d.ts.map +1 -0
  235. package/dist/rich-text-editor/virtual.js +262 -0
  236. package/dist/rich-text-editor/virtual.js.map +1 -0
  237. package/dist/richtext/node.d.ts +25 -2
  238. package/dist/richtext/node.d.ts.map +1 -1
  239. package/dist/richtext/node.js +46 -24
  240. package/dist/richtext/node.js.map +1 -1
  241. package/dist/terminal/backends.d.ts +4 -1
  242. package/dist/terminal/backends.d.ts.map +1 -1
  243. package/dist/terminal/backends.js.map +1 -1
  244. package/dist/terminal/index.d.ts +18 -7
  245. package/dist/terminal/index.d.ts.map +1 -1
  246. package/dist/terminal/index.js +28 -11
  247. package/dist/terminal/index.js.map +1 -1
  248. package/package.json +23 -4
  249. package/src/charts/node.ts +6 -2
  250. package/src/embed/client.ts +102 -8
  251. package/src/embed/index.ts +2 -0
  252. package/src/html/node.ts +7 -5
  253. package/src/index.ts +2 -0
  254. package/src/{markdown → internal/markdown}/ast.ts +24 -7
  255. package/src/{markdown → internal/markdown}/parse.ts +134 -24
  256. package/src/internal/markdown/stringify.ts +772 -0
  257. package/src/internal/scroll.ts +5 -3
  258. package/src/maps/anchors.ts +607 -0
  259. package/src/maps/controller.ts +999 -0
  260. package/src/maps/gl/buckets.ts +457 -0
  261. package/src/maps/gl/build-worker.ts +55 -0
  262. package/src/maps/gl/color.ts +62 -0
  263. package/src/maps/gl/cover.ts +170 -0
  264. package/src/maps/gl/index.ts +23 -0
  265. package/src/maps/gl/markers.ts +83 -0
  266. package/src/maps/gl/overlays.ts +379 -0
  267. package/src/maps/gl/pane.ts +42 -0
  268. package/src/maps/gl/placement.ts +816 -0
  269. package/src/maps/gl/renderer.ts +1576 -0
  270. package/src/maps/gl/shaders.ts +486 -0
  271. package/src/maps/gl/store.ts +566 -0
  272. package/src/maps/gl/text.ts +495 -0
  273. package/src/maps/gl/view.ts +1224 -0
  274. package/src/maps/index.ts +270 -81
  275. package/src/maps/labels.ts +214 -121
  276. package/src/maps/node.ts +763 -629
  277. package/src/maps/overlay.ts +148 -43
  278. package/src/maps/paint.ts +3 -0
  279. package/src/maps/proj.ts +31 -0
  280. package/src/maps/renderer.ts +155 -0
  281. package/src/maps/sources.ts +38 -3
  282. package/src/maps/theme.ts +51 -0
  283. package/src/maps/tiles.ts +191 -33
  284. package/src/maps/types.ts +188 -20
  285. package/src/markdown/expressions.ts +5 -1
  286. package/src/markdown/index.ts +14 -11
  287. package/src/markdown/spans.ts +1 -1
  288. package/src/media-player/index.ts +18 -4
  289. package/src/rich-text-editor/clipboard.ts +439 -0
  290. package/src/rich-text-editor/collab.ts +91 -0
  291. package/src/rich-text-editor/commands.ts +448 -0
  292. package/src/rich-text-editor/dom-globals.d.ts +23 -0
  293. package/src/rich-text-editor/drag.ts +211 -0
  294. package/src/rich-text-editor/html.ts +619 -0
  295. package/src/rich-text-editor/index.ts +1331 -0
  296. package/src/rich-text-editor/inline.ts +345 -0
  297. package/src/rich-text-editor/keymap.ts +510 -0
  298. package/src/rich-text-editor/keys.ts +119 -0
  299. package/src/rich-text-editor/look.ts +427 -0
  300. package/src/rich-text-editor/markdown.ts +653 -0
  301. package/src/rich-text-editor/nodes.ts +497 -0
  302. package/src/rich-text-editor/render.ts +800 -0
  303. package/src/rich-text-editor/schema.ts +431 -0
  304. package/src/rich-text-editor/store.ts +39 -0
  305. package/src/rich-text-editor/suggest.ts +597 -0
  306. package/src/rich-text-editor/tables.ts +272 -0
  307. package/src/rich-text-editor/toolbar.ts +724 -0
  308. package/src/rich-text-editor/view.ts +2287 -0
  309. package/src/rich-text-editor/virtual.ts +347 -0
  310. package/src/richtext/node.ts +59 -30
  311. package/src/terminal/backends.ts +4 -1
  312. package/src/terminal/index.ts +43 -15
  313. package/dist/markdown/ast.d.ts.map +0 -1
  314. package/dist/markdown/ast.js +0 -17
  315. package/dist/markdown/ast.js.map +0 -1
  316. package/dist/markdown/parse.d.ts.map +0 -1
  317. package/dist/markdown/parse.js.map +0 -1
  318. package/dist/markdown/tags.d.ts.map +0 -1
  319. package/dist/markdown/tags.js.map +0 -1
  320. /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
  321. /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
  322. /package/dist/{markdown → internal/markdown}/tags.js +0 -0
  323. /package/src/{markdown → internal/markdown}/tags.ts +0 -0
@@ -0,0 +1,743 @@
1
+ import { markerAt, markerRect } from './overlay.js';
2
+ import { DEFAULT_TILE_SIZE, boundsOf, cameraForBounds, projectLngLat, transformFor, unprojectPoint, visibleBounds, } from './proj.js';
3
+ /** Where a map with no camera given starts. */
4
+ export const DEFAULT_CAMERA = {
5
+ center: { lon: 0, lat: 20 },
6
+ zoom: 2,
7
+ };
8
+ /** Logical pixels the pointer may travel before a press becomes a drag. */
9
+ export const DRAG_THRESHOLD = 3;
10
+ /**
11
+ * How long after the last camera change the map counts as moving.
12
+ *
13
+ * One window for both renderers, and both ask it the same question — is this
14
+ * a gesture? The retained renderer rasterizes nothing inside it, so a drag or
15
+ * a wheel is composites only; the GL renderer draws at reduced detail inside
16
+ * it when adaptive quality asks, and admits no new labels. `onMoveEnd` fires
17
+ * when it closes. Long enough that a wheel zoom's many notches count as one
18
+ * gesture, short enough that the map sharpens before the user has finished
19
+ * looking. (The retained renderer had 140 ms and the GL proof of concept
20
+ * 180; the retained number was the one its tests had pinned.)
21
+ */
22
+ export const SETTLE_MS = 140;
23
+ /**
24
+ * Zoom is quantized to this, for the reason react-x11's docs/scale.md gives
25
+ * about a gesture-driven `scale`: every distinct value is a distinct set of
26
+ * font sizes to shape, and a wheel feeding a raw accumulator makes a new set
27
+ * per frame. A sixteenth of a level is finer than the eye reads as stepping.
28
+ */
29
+ const ZOOM_STEP = 1 / 16;
30
+ /** A zoom on the {@link ZOOM_STEP} grid — also what a label placement is
31
+ * keyed on, so a placement and the zooms a wheel produces agree. */
32
+ export function quantizeZoom(zoom) {
33
+ return Math.round(zoom / ZOOM_STEP) * ZOOM_STEP;
34
+ }
35
+ /** A wheel notch is this much zoom. */
36
+ const WHEEL_ZOOM = 1 / 2.5;
37
+ /**
38
+ * How fast a wheel's zoom closes on where the notches asked it to go — the
39
+ * time constant of the exponential the glide follows, so it is all but over
40
+ * after three of these.
41
+ *
42
+ * A notch is 0.384 of a level, which is six of {@link ZOOM_STEP}, and
43
+ * applying it the moment it arrives is a jump: the map is one size, and a
44
+ * frame later it is another. The device cannot help with that. A wheel
45
+ * *clicks* — the core protocol reports it as a button, and even over XI2 a
46
+ * mouse reports one notch at a time — so the smoothness has to come from
47
+ * the map spreading a notch over the frames after it, which is what every
48
+ * map client does with one. `ev.smooth` tells that device from the one that
49
+ * measured the scroll; see {@link MapController.wheel}.
50
+ */
51
+ const WHEEL_EASE_MS = 55;
52
+ /** What the wheel event's own step is worth, in the absence of a frame to
53
+ * measure: one at 60Hz. The gesture has to move *now* — a wheel that waits
54
+ * for the next frame to do anything reads as lag — and this is the distance
55
+ * a frame would have moved it. */
56
+ const EASE_FIRST_MS = 16;
57
+ /**
58
+ * How often a glide steps: a 60Hz frame.
59
+ *
60
+ * A timer rather than the frame the step is drawn in, because a step *is* a
61
+ * camera move and the views already know what to do with one — the retained
62
+ * renderer claims the pane, the GL one asks for a frame — and that is the
63
+ * path a drag's every pointer step takes. Claiming from inside a frame is
64
+ * not: the claim lands on the frame being painted and brings nothing after
65
+ * it, which left a notch delivering its first step and then waiting for the
66
+ * settle timer to wake the map up.
67
+ *
68
+ * Nothing here outruns the display. The step is exponential in the *time*
69
+ * since the last one, so a view that paints at half this rate sees every
70
+ * other step and the same glide; what the timer decides is the finest the
71
+ * map can move, not how fast it gets there.
72
+ */
73
+ const GLIDE_TICK_MS = 16;
74
+ const timers = globalThis;
75
+ /**
76
+ * The clock a glide is measured on.
77
+ *
78
+ * `performance.now()` rather than the `Date.now()` the settle window uses:
79
+ * this one is compared across two frames of a 60Hz display, and `Date.now()`
80
+ * counts whole milliseconds, so a sixth of the step would be error. Reached
81
+ * through `globalThis` because `src/` compiles with `types: []`.
82
+ */
83
+ function now() {
84
+ return timers.performance?.now() ?? Date.now();
85
+ }
86
+ /**
87
+ * The settle timer, unref'd where the runtime allows it.
88
+ *
89
+ * A map that has just been panned holds a timer, and an unref'd one does not
90
+ * keep a process alive on its own — which matters for a script or a test that
91
+ * renders a map and expects to exit, and is the call core's caret blink makes
92
+ * for the same reason.
93
+ */
94
+ function arm(tick, ms) {
95
+ const handle = timers.setTimeout?.(tick, ms) ?? null;
96
+ handle?.unref?.();
97
+ return handle;
98
+ }
99
+ function clamp(value, low, high) {
100
+ return value < low ? low : value > high ? high : value;
101
+ }
102
+ const EMPTY_PANE = { x: 0, y: 0, width: 0, height: 0 };
103
+ export class MapController {
104
+ /**
105
+ * What `<Map>`'s ref hands out. Made once, so it is the **same object**
106
+ * for the life of the mount — across a renderer switch too, which is
107
+ * what lets an application hold it in a ref and never look again.
108
+ */
109
+ handle;
110
+ /**
111
+ * Set by the view for the length of a frame. A camera change made inside
112
+ * one — the fit that a `fitBounds` asked for before layout, applied at
113
+ * the first frame with a size — is already what the frame draws, so it
114
+ * claims nothing and starts no gesture.
115
+ */
116
+ painting = false;
117
+ _props = {};
118
+ /** The camera, when the application does not control it. */
119
+ _camera;
120
+ _view = null;
121
+ _gesture = null;
122
+ _hover = null;
123
+ /** Wall-clock time the settle window closes at. */
124
+ _settleAt = 0;
125
+ _settleTimer = null;
126
+ _glide = null;
127
+ _glideTimer = null;
128
+ _pendingFit = null;
129
+ constructor(seed = DEFAULT_CAMERA) {
130
+ this._camera = { center: { ...seed.center }, zoom: seed.zoom };
131
+ this.handle = {
132
+ getCamera: () => this.getCamera(),
133
+ setCamera: (camera) => this.setCamera(camera),
134
+ panBy: (dx, dy) => this.panBy(dx, dy),
135
+ zoomIn: (step) => this.zoomIn(step),
136
+ zoomOut: (step) => this.zoomOut(step),
137
+ zoomTo: (zoom) => this.zoomTo(zoom),
138
+ fitBounds: (bounds, options) => this.fitBounds(bounds, options),
139
+ fitMarkers: (ids, options) => this.fitMarkers(ids, options),
140
+ getBounds: () => this.getBounds(),
141
+ project: (position) => this.project(position),
142
+ unproject: (x, y) => this.unproject(x, y),
143
+ markerAt: (x, y) => this.markerAt(x, y),
144
+ refresh: () => this._view?.refresh(),
145
+ stats: () => this._view?.stats() ?? null,
146
+ };
147
+ }
148
+ // --- props and views --------------------------------------------------------
149
+ /** The props to read from here on. Read lazily, at event and frame time,
150
+ * so a new object per render costs an assignment. */
151
+ setProps(props) {
152
+ this._props = props;
153
+ }
154
+ get props() {
155
+ return this._props;
156
+ }
157
+ /** The renderer drawing the map from now on. */
158
+ attach(view) {
159
+ this._view = view;
160
+ }
161
+ /** …and letting go, if it still is: during a renderer switch the new
162
+ * view can attach before the old one is torn down. */
163
+ detach(view) {
164
+ if (this._view === view)
165
+ this._view = null;
166
+ }
167
+ get view() {
168
+ return this._view;
169
+ }
170
+ /**
171
+ * Stop the timers — the settle window's, and a wheel glide in flight.
172
+ * They are the only things held that outlive a frame. The camera and the
173
+ * view stay, so a mount that React only pretends to tear down (a
174
+ * strict-mode double effect) comes back to a working controller; the next
175
+ * camera move arms the settle timer again, and the next notch a glide.
176
+ */
177
+ dispose() {
178
+ if (this._settleTimer !== null)
179
+ timers.clearTimeout?.(this._settleTimer);
180
+ this._settleTimer = null;
181
+ if (this._glideTimer !== null)
182
+ timers.clearTimeout?.(this._glideTimer);
183
+ this._glideTimer = null;
184
+ this._glide = null;
185
+ }
186
+ _minZoom() {
187
+ return this._props.minZoom ?? 0;
188
+ }
189
+ _maxZoom() {
190
+ return this._props.maxZoom ?? 22;
191
+ }
192
+ _interactive() {
193
+ return this._props.interactive !== false;
194
+ }
195
+ _markers() {
196
+ return this._props.markers ?? [];
197
+ }
198
+ // --- the camera ----------------------------------------------------------
199
+ /** The camera in effect: the application's when it controls one. */
200
+ camera() {
201
+ return this._props.camera ?? this._camera;
202
+ }
203
+ /** The camera this controller keeps for itself — the one in effect
204
+ * whenever the application is not controlling it. */
205
+ ownCamera() {
206
+ return this._camera;
207
+ }
208
+ /** The pane, in the window's logical pixels — empty with no view. */
209
+ pane() {
210
+ return this._view?.pane() ?? EMPTY_PANE;
211
+ }
212
+ /** A camera resolved against the pane. */
213
+ transform(camera = this.camera()) {
214
+ const pane = this.pane();
215
+ return transformFor(camera, { width: pane.width, height: pane.height }, DEFAULT_TILE_SIZE);
216
+ }
217
+ /**
218
+ * Move the camera — the one place it changes.
219
+ *
220
+ * Clamped (the zoom to the range, the latitude to what Web Mercator can
221
+ * represent; the longitude is not, because the map wraps and a camera just
222
+ * past the antimeridian is a camera in the next copy of the world),
223
+ * reported, and the settle window restarted: every camera move is a
224
+ * gesture, not only a pointer one, because an application animating the
225
+ * camera with `panBy` in a loop wants what a drag wants.
226
+ *
227
+ * A camera the application controls is only *asked* to move:
228
+ * `onCameraChange` is the request, and the view hears about the move when
229
+ * the new `camera` prop arrives. Telling it here as well was a double
230
+ * shift — the retained renderer blitted the pane for the request and then
231
+ * again for the prop, and `scrollContents` adds two shifts in a frame
232
+ * together.
233
+ */
234
+ apply(next, blit) {
235
+ const previous = this.camera();
236
+ const camera = {
237
+ center: { lon: next.center.lon, lat: clamp(next.center.lat, -85, 85) },
238
+ zoom: clamp(next.zoom, this._minZoom(), this._maxZoom()),
239
+ };
240
+ if (camera.zoom === previous.zoom &&
241
+ camera.center.lon === previous.center.lon &&
242
+ camera.center.lat === previous.center.lat) {
243
+ return;
244
+ }
245
+ const controlled = this._props.camera !== undefined;
246
+ if (!controlled)
247
+ this._camera = camera;
248
+ this._props.onCameraChange?.(camera);
249
+ if (this.painting)
250
+ return;
251
+ this.touch();
252
+ if (!controlled)
253
+ this._view?.moved(previous, camera, blit);
254
+ }
255
+ getCamera() {
256
+ const camera = this.camera();
257
+ return { center: { ...camera.center }, zoom: camera.zoom };
258
+ }
259
+ setCamera(camera) {
260
+ const current = this.camera();
261
+ this.apply({
262
+ center: camera.center ?? current.center,
263
+ zoom: camera.zoom ?? current.zoom,
264
+ }, false);
265
+ }
266
+ /** Move by a distance in pane-local logical pixels. */
267
+ panBy(dx, dy) {
268
+ const t = this.transform();
269
+ this.apply({
270
+ zoom: t.zoom,
271
+ center: unprojectPoint(t, t.paneX + dx, t.paneY + dy),
272
+ }, true);
273
+ }
274
+ zoomIn(step = 1) {
275
+ const pane = this.pane();
276
+ this.zoomAbout(step, pane.width / 2, pane.height / 2);
277
+ }
278
+ zoomOut(step = 1) {
279
+ this.zoomIn(-step);
280
+ }
281
+ zoomTo(zoom) {
282
+ this.setCamera({ zoom });
283
+ }
284
+ /**
285
+ * Zoom by `delta` levels about a pane-local point that must not move —
286
+ * the pointer under a wheel, the pane's centre for a key.
287
+ */
288
+ zoomAbout(delta, x, y) {
289
+ const camera = this.camera();
290
+ const zoom = clamp(quantizeZoom(camera.zoom + delta), this._minZoom(), this._maxZoom());
291
+ if (zoom === camera.zoom)
292
+ return;
293
+ const before = this.transform(camera);
294
+ const anchor = unprojectPoint(before, x, y);
295
+ const after = this.transform({ center: camera.center, zoom });
296
+ // Where the anchor would land at the new zoom, and how far the centre
297
+ // has to move so it lands where it already is.
298
+ const moved = projectLngLat(after, anchor);
299
+ const dx = (moved.x - x) / after.world;
300
+ const dy = (moved.y - y) / after.world;
301
+ this.apply({
302
+ zoom,
303
+ center: unprojectPoint({
304
+ ...after,
305
+ centerX: after.centerX + dx,
306
+ centerY: after.centerY + dy,
307
+ }, after.paneX, after.paneY),
308
+ }, false);
309
+ }
310
+ /**
311
+ * A scroll, as a zoom: where the wheel wants the camera, and how the
312
+ * frames after it get there.
313
+ *
314
+ * **Two devices arrive here and they want opposite things.** A wheel
315
+ * *clicks*: one event carrying a whole notch, with nothing in between, so
316
+ * the notch becomes a target the frames after it glide towards. A touchpad
317
+ * *measures*: a stream of fractions that is already as smooth as the hand
318
+ * making it, which wants applying the moment it arrives and only needs
319
+ * keeping from rounding away. `ev.smooth` is core's word for which device
320
+ * this is — "a whole notch is worth easing towards, a stream of measured
321
+ * pixels is not" (react-x11's docs/events.md).
322
+ *
323
+ * Either way the *target* accumulates. A second notch arriving mid-glide
324
+ * lengthens the one gesture instead of starting another, and a fraction
325
+ * too small to move the quantized camera is still on the books when the
326
+ * next one arrives.
327
+ */
328
+ _wheelZoom(delta, x, y, smooth) {
329
+ const at = now();
330
+ const camera = this.camera();
331
+ const glide = this._glide;
332
+ // Where this delta counts from: the target the wheel has already asked
333
+ // for, or the camera when there is nothing outstanding. A record whose
334
+ // `applied` is not where the camera is belongs to a camera something
335
+ // else has since moved, and is not this gesture's to add to.
336
+ //
337
+ // Nothing expires it by age. A glide that is still owed frames is still
338
+ // owed them however long the frames take, and what is left of a
339
+ // touchpad's is under half a step of zoom — too little to be a jump
340
+ // whenever it is spent.
341
+ const from = glide && glide.applied === camera.zoom ? glide.target : camera.zoom;
342
+ // Clamped here as well as in `apply`: an accumulator allowed past the
343
+ // limit records notches the map cannot answer, and the scroll back out
344
+ // then does nothing until they are spent.
345
+ this._glide = {
346
+ target: clamp(from + delta, this._minZoom(), this._maxZoom()),
347
+ applied: camera.zoom,
348
+ x,
349
+ y,
350
+ at,
351
+ easing: !smooth,
352
+ };
353
+ // The event's own step, so the map moves under the notch that asked
354
+ // rather than on the frame after it — and then the rest, a frame at a
355
+ // time, until the target is reached.
356
+ this.stepZoom(at, EASE_FIRST_MS);
357
+ this._armGlide();
358
+ }
359
+ /** Keep stepping a glide until it arrives. Idempotent: a notch arriving
360
+ * mid-glide moves the target and finds the timer already running. */
361
+ _armGlide() {
362
+ if (this._glideTimer !== null || this._glide?.easing !== true)
363
+ return;
364
+ const tick = () => {
365
+ this._glideTimer = null;
366
+ const glide = this._glide;
367
+ if (glide?.easing !== true)
368
+ return;
369
+ const at = now();
370
+ // `stepZoom` clears the record when the target is reached or the
371
+ // camera refuses a step, and this re-arms only while one is left.
372
+ this.stepZoom(at, at - glide.at);
373
+ this._armGlide();
374
+ };
375
+ this._glideTimer = arm(tick, GLIDE_TICK_MS);
376
+ }
377
+ /**
378
+ * One step of a glide: how far the camera moves this frame.
379
+ *
380
+ * Exponential rather than a curve with a duration, because the distance is
381
+ * not known when it starts — a second notch moves the target while the
382
+ * first is still being delivered — and because `elapsed` being the real
383
+ * gap between two frames is what makes the glide the same speed on a
384
+ * display that paints 120 frames a second and one that manages 30.
385
+ *
386
+ * Returns whether the camera moved. The move itself is what brings the
387
+ * frame that shows it: `apply` tells the view, exactly as a drag's every
388
+ * pointer step does.
389
+ */
390
+ stepZoom(at, elapsed) {
391
+ const glide = this._glide;
392
+ if (!glide)
393
+ return false;
394
+ const camera = this.camera();
395
+ if (glide.applied !== camera.zoom) {
396
+ // The camera moved without this gesture: an application that owns it
397
+ // answering a wheel with something of its own, a `setCamera`, a fit.
398
+ // Whatever it was is more current than a notch from before it.
399
+ this._glide = null;
400
+ return false;
401
+ }
402
+ const remaining = glide.target - camera.zoom;
403
+ let step = glide.easing
404
+ ? remaining * (1 - Math.exp(-elapsed / WHEEL_EASE_MS))
405
+ : remaining;
406
+ // Never a step the quantized camera cannot show. The tail of an
407
+ // exponential is arbitrarily small, and a step that rounds to nothing
408
+ // would leave the glide asking for frames it has no use for — so the
409
+ // tail is walked a grid step at a time, and "the camera did not move" is
410
+ // left meaning what it says below.
411
+ if (Math.abs(step) < ZOOM_STEP) {
412
+ step =
413
+ Math.abs(remaining) < ZOOM_STEP
414
+ ? remaining
415
+ : Math.sign(remaining) * ZOOM_STEP;
416
+ }
417
+ const before = camera.zoom;
418
+ this.zoomAbout(step, glide.x, glide.y);
419
+ const zoom = this.camera().zoom;
420
+ glide.applied = zoom;
421
+ glide.at = at;
422
+ if (zoom === before) {
423
+ // The camera refused a step it cannot have rounded away — an
424
+ // application owns it and did not answer, or it is against its limit.
425
+ // A wheel's glide has nothing further to deliver; a touchpad's record
426
+ // stays behind as the accumulator, and the next fraction moves it on.
427
+ if (glide.easing)
428
+ this._glide = null;
429
+ return false;
430
+ }
431
+ return true;
432
+ }
433
+ fitBounds(bounds, options) {
434
+ const pane = this.pane();
435
+ if (pane.width <= 0 || pane.height <= 0) {
436
+ // Asked before layout has run — which `fitBounds` in an effect always
437
+ // is. Remembered, and applied at the first frame that has a size.
438
+ this._pendingFit = { bounds, options };
439
+ return;
440
+ }
441
+ this._pendingFit = null;
442
+ this.apply(cameraForBounds(bounds, { width: pane.width, height: pane.height }, {
443
+ padding: options?.padding ?? 24,
444
+ tileSize: DEFAULT_TILE_SIZE,
445
+ minZoom: this._minZoom(),
446
+ maxZoom: options?.maxZoom ?? this._maxZoom(),
447
+ }), false);
448
+ }
449
+ fitMarkers(ids, options) {
450
+ const wanted = ids ? new Set(ids) : null;
451
+ const positions = [];
452
+ for (const marker of this._markers()) {
453
+ if (wanted && !wanted.has(marker.id))
454
+ continue;
455
+ positions.push(marker.position);
456
+ }
457
+ const bounds = boundsOf(positions);
458
+ if (bounds)
459
+ this.fitBounds(bounds, options);
460
+ }
461
+ /** What the pane can see — the whole world before it has a size. */
462
+ getBounds() {
463
+ const pane = this.pane();
464
+ if (pane.width <= 0 || pane.height <= 0) {
465
+ return { west: -180, south: -85, east: 180, north: 85 };
466
+ }
467
+ return visibleBounds(this.transform());
468
+ }
469
+ project(position) {
470
+ return projectLngLat(this.transform(), position);
471
+ }
472
+ unproject(x, y) {
473
+ return unprojectPoint(this.transform(), x, y);
474
+ }
475
+ markerAt(x, y) {
476
+ return markerAt(this._markers(), this.transform(), x, y);
477
+ }
478
+ /**
479
+ * A view is about to draw a frame: a `fitBounds` that arrived before the
480
+ * pane had a size is applied now, so the frame draws it.
481
+ */
482
+ beginFrame() {
483
+ const fit = this._pendingFit;
484
+ if (!fit)
485
+ return;
486
+ const pane = this.pane();
487
+ if (pane.width <= 0 || pane.height <= 0)
488
+ return;
489
+ this.fitBounds(fit.bounds, fit.options);
490
+ }
491
+ // --- the settle window ---------------------------------------------------
492
+ /** Whether the camera moved within the settle window. */
493
+ get moving() {
494
+ return Date.now() < this._settleAt;
495
+ }
496
+ /** …or a press is still down: what the retained renderer holds
497
+ * rasterization off for. */
498
+ get gesturing() {
499
+ return this._gesture !== null || this.moving;
500
+ }
501
+ /** Whether a pan gesture is under way — a press that may yet drag. */
502
+ get dragging() {
503
+ return this._gesture?.kind === 'pan';
504
+ }
505
+ /**
506
+ * Restart the settle window. When it closes the camera has held still for
507
+ * {@link SETTLE_MS}: `onMoveEnd` fires — the moment an application fetches
508
+ * what is now on screen — and the view is told it may sharpen.
509
+ *
510
+ * Wall-clock time rather than a frame clock: this is a window a hundred
511
+ * and forty milliseconds long compared inside a timer callback, and a
512
+ * timer is the one thing that runs when no frame does.
513
+ */
514
+ touch() {
515
+ this._settleAt = Date.now() + SETTLE_MS;
516
+ if (this._settleTimer !== null)
517
+ return;
518
+ const tick = () => {
519
+ this._settleTimer = null;
520
+ const left = this._settleAt - Date.now();
521
+ if (left > 0) {
522
+ this._settleTimer = arm(tick, left);
523
+ return;
524
+ }
525
+ this._props.onMoveEnd?.(this.camera());
526
+ this._view?.settled();
527
+ };
528
+ this._settleTimer = arm(tick, SETTLE_MS);
529
+ }
530
+ // --- input ---------------------------------------------------------------
531
+ /**
532
+ * A press. Answers what it began, for the view to capture the pointer — and
533
+ * for a pan, to take the focus — or null when nothing is.
534
+ *
535
+ * A press on a marker is a marker gesture: its release is a marker click.
536
+ * A press anywhere else is a pan, or on a map that does not move a
537
+ * `'frozen'` press, which pans nothing and is still a click when it is
538
+ * released — an application listening for clicks on a frozen map gets
539
+ * them.
540
+ */
541
+ pointerDown(input) {
542
+ const marker = markerAt(this._markers(), this.transform(), input.x, input.y);
543
+ if (marker) {
544
+ this._gesture = {
545
+ kind: 'marker',
546
+ id: marker.id,
547
+ startX: input.x,
548
+ startY: input.y,
549
+ };
550
+ return 'marker';
551
+ }
552
+ this._gesture = {
553
+ kind: 'pan',
554
+ startX: input.x,
555
+ startY: input.y,
556
+ lastX: input.x,
557
+ lastY: input.y,
558
+ moved: false,
559
+ };
560
+ return this._interactive() ? 'pan' : 'frozen';
561
+ }
562
+ /** The pointer moved with the press still down. */
563
+ pointerDrag(input) {
564
+ const gesture = this._gesture;
565
+ if (!gesture || gesture.kind !== 'pan')
566
+ return;
567
+ if (!this._interactive())
568
+ return;
569
+ if (!gesture.moved &&
570
+ Math.abs(input.x - gesture.startX) < DRAG_THRESHOLD &&
571
+ Math.abs(input.y - gesture.startY) < DRAG_THRESHOLD) {
572
+ return;
573
+ }
574
+ gesture.moved = true;
575
+ // Whole device pixels, because that is what the retained renderer's
576
+ // blit can shift — a fractional pan would decline it every frame and
577
+ // repaint the pane. Invisible on the GL renderer, which draws any shift.
578
+ const scale = this._view?.scale() ?? 1;
579
+ const dx = Math.round((input.x - gesture.lastX) * scale) / scale;
580
+ const dy = Math.round((input.y - gesture.lastY) * scale) / scale;
581
+ if (dx === 0 && dy === 0)
582
+ return;
583
+ gesture.lastX += dx;
584
+ gesture.lastY += dy;
585
+ this.touch();
586
+ this.panBy(-dx, -dy);
587
+ }
588
+ /**
589
+ * The press let go. A release within the drag threshold of a marker press
590
+ * is that marker's click, then the map's; one that ended no drag is the
591
+ * map's click. And the second release of a double click zooms in about the
592
+ * pointer, Shift for out — the convention every map client has.
593
+ */
594
+ pointerUp(input) {
595
+ const gesture = this._gesture;
596
+ this._gesture = null;
597
+ if (!gesture)
598
+ return;
599
+ if (gesture.kind === 'marker') {
600
+ const marker = this._markers().find((m) => m.id === gesture.id);
601
+ if (marker &&
602
+ Math.abs(input.x - gesture.startX) < DRAG_THRESHOLD &&
603
+ Math.abs(input.y - gesture.startY) < DRAG_THRESHOLD) {
604
+ const event = this.pointerEvent(input, marker);
605
+ this._props.onMarkerClick?.(marker, event);
606
+ this._props.onMapClick?.(event);
607
+ }
608
+ return;
609
+ }
610
+ if (gesture.moved) {
611
+ this.touch();
612
+ return;
613
+ }
614
+ this._props.onMapClick?.(this.pointerEvent(input, null));
615
+ if (input.detail === 2 && this._interactive()) {
616
+ this.zoomAbout(input.shiftKey ? -1 : 1, input.x, input.y);
617
+ }
618
+ }
619
+ /** The pointer moved with nothing pressed: the marker under it, if that
620
+ * changed, is `onMarkerHover`'s. */
621
+ pointerMove(input) {
622
+ const notify = this._props.onMarkerHover;
623
+ if (!notify)
624
+ return;
625
+ const marker = markerAt(this._markers(), this.transform(), input.x, input.y);
626
+ const id = marker?.id ?? null;
627
+ if (id === this._hover)
628
+ return;
629
+ this._hover = id;
630
+ notify(marker, this.pointerEvent(input, marker));
631
+ }
632
+ /**
633
+ * The pointer left the map. No event: there is no position on the map to
634
+ * report, and inventing one would be worse than saying so.
635
+ */
636
+ pointerLeave() {
637
+ if (this._hover === null)
638
+ return;
639
+ this._hover = null;
640
+ this._props.onMarkerHover?.(null, null);
641
+ }
642
+ /**
643
+ * The wheel, which is a zoom about the pointer. True when it was consumed
644
+ * — always, on a map that moves: a wheel over a map is never meant for
645
+ * whatever is behind it.
646
+ */
647
+ wheel(input, deltaY, smooth = false) {
648
+ if (!this._interactive())
649
+ return false;
650
+ this.touch();
651
+ this._wheelZoom(-deltaY * WHEEL_ZOOM * 0.02, input.x, input.y, smooth);
652
+ return true;
653
+ }
654
+ /** A key: the arrows pan, +/− zoom about the centre. True when it was one
655
+ * of those, on a map that moves. */
656
+ keyDown(keysym, shiftKey) {
657
+ if (!this._interactive())
658
+ return false;
659
+ const pane = this.pane();
660
+ const step = shiftKey ? 200 : 60;
661
+ switch (keysym) {
662
+ case 0xff51: // XK_Left
663
+ this.panBy(-step, 0);
664
+ return true;
665
+ case 0xff53: // XK_Right
666
+ this.panBy(step, 0);
667
+ return true;
668
+ case 0xff52: // XK_Up
669
+ this.panBy(0, -step);
670
+ return true;
671
+ case 0xff54: // XK_Down
672
+ this.panBy(0, step);
673
+ return true;
674
+ case 0x002b: // XK_plus
675
+ case 0x003d: // XK_equal
676
+ case 0xffab: // XK_KP_Add
677
+ this.zoomAbout(1, pane.width / 2, pane.height / 2);
678
+ return true;
679
+ case 0x002d: // XK_minus
680
+ case 0xffad: // XK_KP_Subtract
681
+ this.zoomAbout(-1, pane.width / 2, pane.height / 2);
682
+ return true;
683
+ default:
684
+ return false;
685
+ }
686
+ }
687
+ /** Where a pointer event happened, in every space that could be wanted. */
688
+ pointerEvent(input, marker) {
689
+ return {
690
+ lngLat: unprojectPoint(this.transform(), input.x, input.y),
691
+ x: input.x,
692
+ y: input.y,
693
+ marker,
694
+ shiftKey: input.shiftKey ?? false,
695
+ ctrlKey: input.ctrlKey ?? false,
696
+ altKey: input.altKey ?? false,
697
+ metaKey: input.metaKey ?? false,
698
+ button: input.button ?? 1,
699
+ };
700
+ }
701
+ /**
702
+ * What a screen reader meets: the markers in view, as buttons.
703
+ *
704
+ * A map is one painted rectangle to an assistive technology, and its
705
+ * markers are the only things in it that are *objects* rather than
706
+ * cartography — so those are the scene. Announcing every road would be
707
+ * worse than announcing none. The same whichever renderer draws.
708
+ */
709
+ markerScene() {
710
+ const pane = this.pane();
711
+ if (pane.width <= 0 || pane.height <= 0)
712
+ return [];
713
+ const transform = this.transform();
714
+ const scale = this._view?.scale() ?? 1;
715
+ const items = [];
716
+ for (const marker of this._markers()) {
717
+ const rect = markerRect(marker, transform);
718
+ if (rect.x + rect.width < 0 ||
719
+ rect.y + rect.height < 0 ||
720
+ rect.x > pane.width ||
721
+ rect.y > pane.height) {
722
+ continue;
723
+ }
724
+ items.push({
725
+ id: `marker:${marker.id}`,
726
+ // Device pixels in the owning window's coordinates, which is what
727
+ // an a11y scene rect is — the same space as `abs`.
728
+ rect: {
729
+ x: (pane.x + rect.x) * scale,
730
+ y: (pane.y + rect.y) * scale,
731
+ width: rect.width * scale,
732
+ height: rect.height * scale,
733
+ },
734
+ role: 'button',
735
+ name: marker.title ??
736
+ `${marker.position.lat.toFixed(4)}, ${marker.position.lon.toFixed(4)}`,
737
+ states: { selected: marker.selected ?? false },
738
+ });
739
+ }
740
+ return items;
741
+ }
742
+ }
743
+ //# sourceMappingURL=controller.js.map