@react-x11/components 0.7.1 → 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.
- package/README.md +295 -51
- package/dist/charts/node.d.ts +1 -1
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +6 -2
- package/dist/charts/node.js.map +1 -1
- package/dist/color-picker/ColorPicker.d.ts.map +1 -1
- package/dist/color-picker/ColorPicker.js +43 -33
- package/dist/color-picker/ColorPicker.js.map +1 -1
- package/dist/embed/client.d.ts +48 -3
- package/dist/embed/client.d.ts.map +1 -1
- package/dist/embed/client.js +79 -5
- package/dist/embed/client.js.map +1 -1
- package/dist/embed/index.d.ts +1 -1
- package/dist/embed/index.d.ts.map +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/embed/index.js.map +1 -1
- package/dist/html/node.d.ts +3 -1
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +7 -5
- package/dist/html/node.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{markdown → internal/markdown}/ast.d.ts +12 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -0
- package/dist/internal/markdown/ast.js +22 -0
- package/dist/internal/markdown/ast.js.map +1 -0
- package/dist/internal/markdown/parse.d.ts.map +1 -0
- package/dist/{markdown → internal/markdown}/parse.js +112 -22
- package/dist/internal/markdown/parse.js.map +1 -0
- package/dist/internal/markdown/stringify.d.ts +4 -0
- package/dist/internal/markdown/stringify.d.ts.map +1 -0
- package/dist/internal/markdown/stringify.js +684 -0
- package/dist/internal/markdown/stringify.js.map +1 -0
- package/dist/internal/markdown/tags.d.ts.map +1 -0
- package/dist/internal/markdown/tags.js.map +1 -0
- package/dist/internal/scroll.d.ts +3 -2
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +33 -15
- package/dist/internal/scroll.js.map +1 -1
- package/dist/internal/units.d.ts +2 -0
- package/dist/internal/units.d.ts.map +1 -0
- package/dist/internal/units.js +35 -0
- package/dist/internal/units.js.map +1 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +4 -2
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +98 -0
- package/dist/maps/anchors.d.ts.map +1 -0
- package/dist/maps/anchors.js +531 -0
- package/dist/maps/anchors.js.map +1 -0
- package/dist/maps/controller.d.ts +283 -0
- package/dist/maps/controller.d.ts.map +1 -0
- package/dist/maps/controller.js +743 -0
- package/dist/maps/controller.js.map +1 -0
- package/dist/maps/gl/buckets.d.ts +95 -0
- package/dist/maps/gl/buckets.d.ts.map +1 -0
- package/dist/maps/gl/buckets.js +362 -0
- package/dist/maps/gl/buckets.js.map +1 -0
- package/dist/maps/gl/build-worker.d.ts +13 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -0
- package/dist/maps/gl/build-worker.js +44 -0
- package/dist/maps/gl/build-worker.js.map +1 -0
- package/dist/maps/gl/color.d.ts +8 -0
- package/dist/maps/gl/color.d.ts.map +1 -0
- package/dist/maps/gl/color.js +57 -0
- package/dist/maps/gl/color.js.map +1 -0
- package/dist/maps/gl/cover.d.ts +51 -0
- package/dist/maps/gl/cover.d.ts.map +1 -0
- package/dist/maps/gl/cover.js +124 -0
- package/dist/maps/gl/cover.js.map +1 -0
- package/dist/maps/gl/index.d.ts +3 -0
- package/dist/maps/gl/index.d.ts.map +1 -0
- package/dist/maps/gl/index.js +20 -0
- package/dist/maps/gl/index.js.map +1 -0
- package/dist/maps/gl/markers.d.ts +28 -0
- package/dist/maps/gl/markers.d.ts.map +1 -0
- package/dist/maps/gl/markers.js +60 -0
- package/dist/maps/gl/markers.js.map +1 -0
- package/dist/maps/gl/overlays.d.ts +68 -0
- package/dist/maps/gl/overlays.d.ts.map +1 -0
- package/dist/maps/gl/overlays.js +300 -0
- package/dist/maps/gl/overlays.js.map +1 -0
- package/dist/maps/gl/pane.d.ts +12 -0
- package/dist/maps/gl/pane.d.ts.map +1 -0
- package/dist/maps/gl/pane.js +37 -0
- package/dist/maps/gl/pane.js.map +1 -0
- package/dist/maps/gl/placement.d.ts +111 -0
- package/dist/maps/gl/placement.d.ts.map +1 -0
- package/dist/maps/gl/placement.js +567 -0
- package/dist/maps/gl/placement.js.map +1 -0
- package/dist/maps/gl/renderer.d.ts +352 -0
- package/dist/maps/gl/renderer.d.ts.map +1 -0
- package/dist/maps/gl/renderer.js +1127 -0
- package/dist/maps/gl/renderer.js.map +1 -0
- package/dist/maps/gl/shaders.d.ts +120 -0
- package/dist/maps/gl/shaders.d.ts.map +1 -0
- package/dist/maps/gl/shaders.js +451 -0
- package/dist/maps/gl/shaders.js.map +1 -0
- package/dist/maps/gl/store.d.ts +115 -0
- package/dist/maps/gl/store.d.ts.map +1 -0
- package/dist/maps/gl/store.js +469 -0
- package/dist/maps/gl/store.js.map +1 -0
- package/dist/maps/gl/text.d.ts +154 -0
- package/dist/maps/gl/text.d.ts.map +1 -0
- package/dist/maps/gl/text.js +382 -0
- package/dist/maps/gl/text.js.map +1 -0
- package/dist/maps/gl/view.d.ts +54 -0
- package/dist/maps/gl/view.d.ts.map +1 -0
- package/dist/maps/gl/view.js +974 -0
- package/dist/maps/gl/view.js.map +1 -0
- package/dist/maps/index.d.ts +1 -1
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +216 -52
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +37 -16
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +188 -119
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +162 -56
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +607 -515
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/overlay.d.ts +72 -9
- package/dist/maps/overlay.d.ts.map +1 -1
- package/dist/maps/overlay.js +108 -28
- package/dist/maps/overlay.js.map +1 -1
- package/dist/maps/paint.d.ts +3 -0
- package/dist/maps/paint.d.ts.map +1 -1
- package/dist/maps/paint.js.map +1 -1
- package/dist/maps/proj.d.ts +14 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +28 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/renderer.d.ts +69 -0
- package/dist/maps/renderer.d.ts.map +1 -0
- package/dist/maps/renderer.js +66 -0
- package/dist/maps/renderer.js.map +1 -0
- package/dist/maps/sources.d.ts +25 -3
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +17 -0
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/theme.d.ts +19 -0
- package/dist/maps/theme.d.ts.map +1 -0
- package/dist/maps/theme.js +44 -0
- package/dist/maps/theme.js.map +1 -0
- package/dist/maps/tiles.d.ts +117 -17
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +163 -30
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +188 -20
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/expressions.d.ts +1 -1
- package/dist/markdown/expressions.d.ts.map +1 -1
- package/dist/markdown/expressions.js.map +1 -1
- package/dist/markdown/index.d.ts +7 -6
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +9 -7
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/media-player/index.d.ts +11 -3
- package/dist/media-player/index.d.ts.map +1 -1
- package/dist/media-player/index.js +13 -2
- package/dist/media-player/index.js.map +1 -1
- package/dist/rich-text-editor/clipboard.d.ts +23 -0
- package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
- package/dist/rich-text-editor/clipboard.js +307 -0
- package/dist/rich-text-editor/clipboard.js.map +1 -0
- package/dist/rich-text-editor/collab.d.ts +32 -0
- package/dist/rich-text-editor/collab.d.ts.map +1 -0
- package/dist/rich-text-editor/collab.js +55 -0
- package/dist/rich-text-editor/collab.js.map +1 -0
- package/dist/rich-text-editor/commands.d.ts +50 -0
- package/dist/rich-text-editor/commands.d.ts.map +1 -0
- package/dist/rich-text-editor/commands.js +376 -0
- package/dist/rich-text-editor/commands.js.map +1 -0
- package/dist/rich-text-editor/drag.d.ts +68 -0
- package/dist/rich-text-editor/drag.d.ts.map +1 -0
- package/dist/rich-text-editor/drag.js +148 -0
- package/dist/rich-text-editor/drag.js.map +1 -0
- package/dist/rich-text-editor/html.d.ts +145 -0
- package/dist/rich-text-editor/html.d.ts.map +1 -0
- package/dist/rich-text-editor/html.js +516 -0
- package/dist/rich-text-editor/html.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +263 -0
- package/dist/rich-text-editor/index.d.ts.map +1 -0
- package/dist/rich-text-editor/index.js +822 -0
- package/dist/rich-text-editor/index.js.map +1 -0
- package/dist/rich-text-editor/inline.d.ts +94 -0
- package/dist/rich-text-editor/inline.d.ts.map +1 -0
- package/dist/rich-text-editor/inline.js +252 -0
- package/dist/rich-text-editor/inline.js.map +1 -0
- package/dist/rich-text-editor/keymap.d.ts +71 -0
- package/dist/rich-text-editor/keymap.d.ts.map +1 -0
- package/dist/rich-text-editor/keymap.js +393 -0
- package/dist/rich-text-editor/keymap.js.map +1 -0
- package/dist/rich-text-editor/keys.d.ts +29 -0
- package/dist/rich-text-editor/keys.d.ts.map +1 -0
- package/dist/rich-text-editor/keys.js +93 -0
- package/dist/rich-text-editor/keys.js.map +1 -0
- package/dist/rich-text-editor/look.d.ts +71 -0
- package/dist/rich-text-editor/look.d.ts.map +1 -0
- package/dist/rich-text-editor/look.js +342 -0
- package/dist/rich-text-editor/look.js.map +1 -0
- package/dist/rich-text-editor/markdown.d.ts +18 -0
- package/dist/rich-text-editor/markdown.d.ts.map +1 -0
- package/dist/rich-text-editor/markdown.js +496 -0
- package/dist/rich-text-editor/markdown.js.map +1 -0
- package/dist/rich-text-editor/nodes.d.ts +135 -0
- package/dist/rich-text-editor/nodes.d.ts.map +1 -0
- package/dist/rich-text-editor/nodes.js +397 -0
- package/dist/rich-text-editor/nodes.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts +68 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -0
- package/dist/rich-text-editor/render.js +500 -0
- package/dist/rich-text-editor/render.js.map +1 -0
- package/dist/rich-text-editor/schema.d.ts +16 -0
- package/dist/rich-text-editor/schema.d.ts.map +1 -0
- package/dist/rich-text-editor/schema.js +386 -0
- package/dist/rich-text-editor/schema.js.map +1 -0
- package/dist/rich-text-editor/store.d.ts +11 -0
- package/dist/rich-text-editor/store.d.ts.map +1 -0
- package/dist/rich-text-editor/store.js +43 -0
- package/dist/rich-text-editor/store.js.map +1 -0
- package/dist/rich-text-editor/suggest.d.ts +127 -0
- package/dist/rich-text-editor/suggest.d.ts.map +1 -0
- package/dist/rich-text-editor/suggest.js +0 -0
- package/dist/rich-text-editor/suggest.js.map +1 -0
- package/dist/rich-text-editor/tables.d.ts +41 -0
- package/dist/rich-text-editor/tables.d.ts.map +1 -0
- package/dist/rich-text-editor/tables.js +204 -0
- package/dist/rich-text-editor/tables.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts +58 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
- package/dist/rich-text-editor/toolbar.js +548 -0
- package/dist/rich-text-editor/toolbar.js.map +1 -0
- package/dist/rich-text-editor/view.d.ts +368 -0
- package/dist/rich-text-editor/view.d.ts.map +1 -0
- package/dist/rich-text-editor/view.js +1968 -0
- package/dist/rich-text-editor/view.js.map +1 -0
- package/dist/rich-text-editor/virtual.d.ts +54 -0
- package/dist/rich-text-editor/virtual.d.ts.map +1 -0
- package/dist/rich-text-editor/virtual.js +262 -0
- package/dist/rich-text-editor/virtual.js.map +1 -0
- package/dist/richtext/node.d.ts +25 -2
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +46 -24
- package/dist/richtext/node.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +14 -5
- package/dist/table/index.js.map +1 -1
- package/dist/terminal/backends.d.ts +4 -1
- package/dist/terminal/backends.d.ts.map +1 -1
- package/dist/terminal/backends.js.map +1 -1
- package/dist/terminal/index.d.ts +18 -7
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +28 -11
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/node.d.ts +56 -3
- package/dist/terminal/vt/node.d.ts.map +1 -1
- package/dist/terminal/vt/node.js +106 -26
- package/dist/terminal/vt/node.js.map +1 -1
- package/dist/terminal/vt/renderer.d.ts +4 -0
- package/dist/terminal/vt/renderer.d.ts.map +1 -1
- package/dist/terminal/vt/renderer.js +14 -5
- package/dist/terminal/vt/renderer.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +6 -4
- package/dist/tree/index.js.map +1 -1
- package/package.json +23 -4
- package/src/charts/node.ts +6 -2
- package/src/color-picker/ColorPicker.ts +107 -81
- package/src/embed/client.ts +102 -8
- package/src/embed/index.ts +2 -0
- package/src/html/node.ts +7 -5
- package/src/index.ts +2 -0
- package/src/{markdown → internal/markdown}/ast.ts +24 -7
- package/src/{markdown → internal/markdown}/parse.ts +134 -24
- package/src/internal/markdown/stringify.ts +772 -0
- package/src/internal/scroll.ts +39 -18
- package/src/internal/units.ts +34 -0
- package/src/internal/window.ts +4 -2
- package/src/maps/anchors.ts +607 -0
- package/src/maps/controller.ts +999 -0
- package/src/maps/gl/buckets.ts +457 -0
- package/src/maps/gl/build-worker.ts +55 -0
- package/src/maps/gl/color.ts +62 -0
- package/src/maps/gl/cover.ts +170 -0
- package/src/maps/gl/index.ts +23 -0
- package/src/maps/gl/markers.ts +83 -0
- package/src/maps/gl/overlays.ts +379 -0
- package/src/maps/gl/pane.ts +42 -0
- package/src/maps/gl/placement.ts +816 -0
- package/src/maps/gl/renderer.ts +1576 -0
- package/src/maps/gl/shaders.ts +486 -0
- package/src/maps/gl/store.ts +566 -0
- package/src/maps/gl/text.ts +495 -0
- package/src/maps/gl/view.ts +1224 -0
- package/src/maps/index.ts +270 -81
- package/src/maps/labels.ts +214 -121
- package/src/maps/node.ts +763 -629
- package/src/maps/overlay.ts +148 -43
- package/src/maps/paint.ts +3 -0
- package/src/maps/proj.ts +31 -0
- package/src/maps/renderer.ts +155 -0
- package/src/maps/sources.ts +38 -3
- package/src/maps/theme.ts +51 -0
- package/src/maps/tiles.ts +191 -33
- package/src/maps/types.ts +188 -20
- package/src/markdown/expressions.ts +5 -1
- package/src/markdown/index.ts +14 -11
- package/src/markdown/spans.ts +1 -1
- package/src/media-player/index.ts +18 -4
- package/src/rich-text-editor/clipboard.ts +439 -0
- package/src/rich-text-editor/collab.ts +91 -0
- package/src/rich-text-editor/commands.ts +448 -0
- package/src/rich-text-editor/dom-globals.d.ts +23 -0
- package/src/rich-text-editor/drag.ts +211 -0
- package/src/rich-text-editor/html.ts +619 -0
- package/src/rich-text-editor/index.ts +1331 -0
- package/src/rich-text-editor/inline.ts +345 -0
- package/src/rich-text-editor/keymap.ts +510 -0
- package/src/rich-text-editor/keys.ts +119 -0
- package/src/rich-text-editor/look.ts +427 -0
- package/src/rich-text-editor/markdown.ts +653 -0
- package/src/rich-text-editor/nodes.ts +497 -0
- package/src/rich-text-editor/render.ts +800 -0
- package/src/rich-text-editor/schema.ts +431 -0
- package/src/rich-text-editor/store.ts +39 -0
- package/src/rich-text-editor/suggest.ts +597 -0
- package/src/rich-text-editor/tables.ts +272 -0
- package/src/rich-text-editor/toolbar.ts +724 -0
- package/src/rich-text-editor/view.ts +2287 -0
- package/src/rich-text-editor/virtual.ts +347 -0
- package/src/richtext/node.ts +59 -30
- package/src/table/index.ts +14 -5
- package/src/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +43 -15
- package/src/terminal/vt/node.ts +128 -29
- package/src/terminal/vt/renderer.ts +27 -5
- package/src/tree/index.ts +6 -4
- package/dist/markdown/ast.d.ts.map +0 -1
- package/dist/markdown/ast.js +0 -17
- package/dist/markdown/ast.js.map +0 -1
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js.map +0 -1
- package/dist/markdown/tags.d.ts.map +0 -1
- package/dist/markdown/tags.js.map +0 -1
- /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.js +0 -0
- /package/src/{markdown → internal/markdown}/tags.ts +0 -0
package/src/maps/overlay.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { clipOf, clipRing, clipSegment } from './clip.js';
|
|
|
18
18
|
import type { ClipRect } from './clip.js';
|
|
19
19
|
import type { MapCanvas } from './paint.js';
|
|
20
20
|
import { projectLngLat } from './proj.js';
|
|
21
|
-
import type { LngLat, Transform } from './proj.js';
|
|
21
|
+
import type { LngLat, ScreenRect, Transform } from './proj.js';
|
|
22
22
|
|
|
23
23
|
/** A point on the map the user can click. */
|
|
24
24
|
export interface MapMarker {
|
|
@@ -37,9 +37,11 @@ export interface MapMarker {
|
|
|
37
37
|
/** The ring around it, so a marker stays visible on any basemap. The
|
|
38
38
|
* theme's background by default. */
|
|
39
39
|
outline?: string;
|
|
40
|
-
/** Drawn above unselected markers
|
|
40
|
+
/** Drawn above the unselected markers of its `zIndex`, and given the
|
|
41
|
+
* selected ring: 2.5 logical pixels of the theme's text colour. */
|
|
41
42
|
selected?: boolean;
|
|
42
|
-
/** Higher draws later. Ties break on
|
|
43
|
+
/** Higher draws later, and is hit first. Ties break on `selected`, then
|
|
44
|
+
* on array order. */
|
|
43
45
|
zIndex?: number;
|
|
44
46
|
/** Skipped by hit testing — for a marker that is decoration. */
|
|
45
47
|
interactive?: boolean;
|
|
@@ -104,12 +106,56 @@ export interface OverlayPalette {
|
|
|
104
106
|
text: string;
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
/** The attribution's text size, in logical pixels. */
|
|
110
|
+
export const ATTRIBUTION_SIZE = 9;
|
|
111
|
+
/** The opacity of the box of the theme's background the attribution sits
|
|
112
|
+
* in — enough to read it over any basemap, not so much that it hides one. */
|
|
113
|
+
export const ATTRIBUTION_OPACITY = 0.72;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Where the attribution goes: a box in the pane's bottom-right corner with 4
|
|
117
|
+
* logical pixels of padding either side of the text and 2 above and below
|
|
118
|
+
* it, and the text's top-left corner inside it — in device pixels, on whole
|
|
119
|
+
* ones. Both renderers lay it out with this, so it sits on the same pixels
|
|
120
|
+
* whichever of them draws the map.
|
|
121
|
+
*
|
|
122
|
+
* `text` is the set string's box and `pane` the pane, in logical pixels.
|
|
123
|
+
*/
|
|
124
|
+
export function attributionLayout(
|
|
125
|
+
text: { width: number; height: number },
|
|
126
|
+
pane: ScreenRect,
|
|
127
|
+
scale: number,
|
|
128
|
+
): { box: ScreenRect; x: number; y: number } {
|
|
129
|
+
const padding = 4;
|
|
130
|
+
const width = text.width + padding * 2;
|
|
131
|
+
const height = text.height + padding;
|
|
132
|
+
const x = pane.x + pane.width - width;
|
|
133
|
+
const y = pane.y + pane.height - height;
|
|
134
|
+
return {
|
|
135
|
+
box: {
|
|
136
|
+
x: Math.round(x * scale),
|
|
137
|
+
y: Math.round(y * scale),
|
|
138
|
+
width: Math.ceil(width * scale),
|
|
139
|
+
height: Math.ceil(height * scale),
|
|
140
|
+
},
|
|
141
|
+
x: Math.round((x + padding) * scale),
|
|
142
|
+
y: Math.round((y + padding / 2) * scale),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
107
146
|
function byZ<T extends { zIndex?: number }>(items: readonly T[]): T[] {
|
|
108
147
|
// A stable sort — the language guarantees it — so items with no `zIndex`
|
|
109
148
|
// keep the order the application listed them in.
|
|
110
149
|
return [...items].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
111
150
|
}
|
|
112
151
|
|
|
152
|
+
/** The order overlays are drawn in, bottom first: by `zIndex`, and
|
|
153
|
+
* otherwise the order the application listed them in. Both renderers
|
|
154
|
+
* draw in it. */
|
|
155
|
+
export function overlayOrder(overlays: readonly MapOverlay[]): MapOverlay[] {
|
|
156
|
+
return byZ(overlays);
|
|
157
|
+
}
|
|
158
|
+
|
|
113
159
|
/**
|
|
114
160
|
* Draw the overlays.
|
|
115
161
|
*
|
|
@@ -314,22 +360,87 @@ function appendRing(ctx: MapCanvas, ring: readonly number[]): boolean {
|
|
|
314
360
|
return true;
|
|
315
361
|
}
|
|
316
362
|
|
|
317
|
-
/**
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
export
|
|
363
|
+
/** A marker's size when it names none, in logical pixels. */
|
|
364
|
+
export const MARKER_SIZE = 14;
|
|
365
|
+
/** A pin is this many times as tall as it is wide. */
|
|
366
|
+
export const PIN_HEIGHT = 1.4;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The order markers are drawn in, bottom first: by `zIndex`, a selected
|
|
370
|
+
* marker above the unselected ones of its `zIndex`, and otherwise the order
|
|
371
|
+
* the application listed them in. The hit test walks it from the top, so a
|
|
372
|
+
* press lands on the marker drawn over the others. Both renderers draw in
|
|
373
|
+
* it.
|
|
374
|
+
*/
|
|
375
|
+
export function markerOrder(markers: readonly MapMarker[]): MapMarker[] {
|
|
376
|
+
// A stable sort, so equal keys keep the application's order.
|
|
377
|
+
return [...markers].sort(
|
|
378
|
+
(a, b) =>
|
|
379
|
+
(a.zIndex ?? 0) - (b.zIndex ?? 0) ||
|
|
380
|
+
Number(a.selected === true) - Number(b.selected === true),
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** A marker's colours and ring, the theme's where it names none — what
|
|
385
|
+
* both renderers paint it with. `ringWidth` is in device pixels. */
|
|
386
|
+
export function markerPaint(
|
|
321
387
|
marker: MapMarker,
|
|
322
|
-
|
|
323
|
-
|
|
388
|
+
palette: OverlayPalette,
|
|
389
|
+
scale: number,
|
|
390
|
+
): { fill: string; ring: string; ringWidth: number } {
|
|
391
|
+
return {
|
|
392
|
+
fill: marker.color ?? palette.accent,
|
|
393
|
+
ring: marker.selected
|
|
394
|
+
? palette.text
|
|
395
|
+
: (marker.outline ?? palette.background),
|
|
396
|
+
ringWidth: Math.max(1, (marker.selected ? 2.5 : 1.5) * scale),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Where a marker's ink lands: its box, and the point it marks. */
|
|
401
|
+
export interface MarkerRect {
|
|
324
402
|
x: number;
|
|
325
403
|
y: number;
|
|
326
404
|
width: number;
|
|
327
405
|
height: number;
|
|
328
406
|
tipX: number;
|
|
329
407
|
tipY: number;
|
|
330
|
-
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* The markers a pane shows, bottom first, with where each one's ink lands
|
|
412
|
+
* — what both renderers draw. A marker wholly outside the pane is left out.
|
|
413
|
+
*/
|
|
414
|
+
export function markersInView(
|
|
415
|
+
markers: readonly MapMarker[],
|
|
416
|
+
transform: Transform,
|
|
417
|
+
pane: { width: number; height: number },
|
|
418
|
+
): { marker: MapMarker; rect: MarkerRect }[] {
|
|
419
|
+
const shown: { marker: MapMarker; rect: MarkerRect }[] = [];
|
|
420
|
+
for (const marker of markerOrder(markers)) {
|
|
421
|
+
const rect = markerRect(marker, transform);
|
|
422
|
+
if (
|
|
423
|
+
rect.x + rect.width < 0 ||
|
|
424
|
+
rect.y + rect.height < 0 ||
|
|
425
|
+
rect.x > pane.width ||
|
|
426
|
+
rect.y > pane.height
|
|
427
|
+
) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
shown.push({ marker, rect });
|
|
431
|
+
}
|
|
432
|
+
return shown;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** Where a marker's ink lands, in pane-local logical pixels. Shared by the
|
|
436
|
+
* drawing and the hit test, so the two cannot disagree — the bug that
|
|
437
|
+
* makes a marker unclickable a few pixels from where it looks. */
|
|
438
|
+
export function markerRect(
|
|
439
|
+
marker: MapMarker,
|
|
440
|
+
transform: Transform,
|
|
441
|
+
): MarkerRect {
|
|
331
442
|
const point = projectLngLat(transform, marker.position);
|
|
332
|
-
const size = marker.size ??
|
|
443
|
+
const size = marker.size ?? MARKER_SIZE;
|
|
333
444
|
if ((marker.shape ?? 'pin') === 'circle') {
|
|
334
445
|
return {
|
|
335
446
|
x: point.x - size / 2,
|
|
@@ -343,7 +454,7 @@ export function markerRect(
|
|
|
343
454
|
// A pin *stands on* its position: the point is the tip, and the head is
|
|
344
455
|
// above it. Getting this the other way round puts every marker half its
|
|
345
456
|
// own height north of where it belongs, which on a city map is a street.
|
|
346
|
-
const height = size *
|
|
457
|
+
const height = size * PIN_HEIGHT;
|
|
347
458
|
return {
|
|
348
459
|
x: point.x - size / 2,
|
|
349
460
|
y: point.y - height,
|
|
@@ -354,7 +465,7 @@ export function markerRect(
|
|
|
354
465
|
};
|
|
355
466
|
}
|
|
356
467
|
|
|
357
|
-
/** Draw the markers,
|
|
468
|
+
/** Draw the markers, in {@link markerOrder}. */
|
|
358
469
|
export function drawMarkers(
|
|
359
470
|
ctx: MapCanvas,
|
|
360
471
|
markers: readonly MapMarker[],
|
|
@@ -363,27 +474,17 @@ export function drawMarkers(
|
|
|
363
474
|
scale: number,
|
|
364
475
|
palette: OverlayPalette,
|
|
365
476
|
): number {
|
|
366
|
-
|
|
367
|
-
for (const marker of
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
rect.x + rect.width < 0 ||
|
|
371
|
-
rect.y + rect.height < 0 ||
|
|
372
|
-
rect.x > pane.width ||
|
|
373
|
-
rect.y > pane.height
|
|
374
|
-
) {
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
const size = marker.size ?? 14;
|
|
378
|
-
const fill = marker.color ?? palette.accent;
|
|
379
|
-
const ring = marker.outline ?? palette.background;
|
|
477
|
+
const shown = markersInView(markers, transform, pane);
|
|
478
|
+
for (const { marker, rect } of shown) {
|
|
479
|
+
const size = marker.size ?? MARKER_SIZE;
|
|
480
|
+
const paint = markerPaint(marker, palette, scale);
|
|
380
481
|
const x = (pane.x + rect.tipX) * scale;
|
|
381
482
|
const y = (pane.y + rect.tipY) * scale;
|
|
382
483
|
const r = (size / 2) * scale;
|
|
383
484
|
ctx.save();
|
|
384
|
-
ctx.lineWidth =
|
|
385
|
-
ctx.strokeStyle =
|
|
386
|
-
ctx.fillStyle = fill;
|
|
485
|
+
ctx.lineWidth = paint.ringWidth;
|
|
486
|
+
ctx.strokeStyle = paint.ring;
|
|
487
|
+
ctx.fillStyle = paint.fill;
|
|
387
488
|
if ((marker.shape ?? 'pin') === 'circle') {
|
|
388
489
|
ctx.beginPath();
|
|
389
490
|
ctx.moveTo(x + r, y);
|
|
@@ -391,12 +492,14 @@ export function drawMarkers(
|
|
|
391
492
|
ctx.fill();
|
|
392
493
|
ctx.stroke();
|
|
393
494
|
} else {
|
|
394
|
-
// A teardrop: a circle for the head and two
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
|
|
495
|
+
// A teardrop: a circle for the head and two straight sides down to
|
|
496
|
+
// the tip, meeting the circle where they are tangent to it — at
|
|
497
|
+
// acos(r / d) either side of the line to the tip, seen from the
|
|
498
|
+
// centre — so the outline is smooth rather than two lines crossing
|
|
499
|
+
// an arc.
|
|
500
|
+
const cy = y - size * PIN_HEIGHT * scale + r;
|
|
398
501
|
const d = y - cy;
|
|
399
|
-
const angle = Math.
|
|
502
|
+
const angle = Math.acos(Math.min(1, r / d));
|
|
400
503
|
ctx.beginPath();
|
|
401
504
|
ctx.arc(x, cy, r, Math.PI / 2 + angle, Math.PI / 2 - angle);
|
|
402
505
|
ctx.lineTo(x, y);
|
|
@@ -405,9 +508,8 @@ export function drawMarkers(
|
|
|
405
508
|
ctx.stroke();
|
|
406
509
|
}
|
|
407
510
|
ctx.restore();
|
|
408
|
-
drawn++;
|
|
409
511
|
}
|
|
410
|
-
return
|
|
512
|
+
return shown.length;
|
|
411
513
|
}
|
|
412
514
|
|
|
413
515
|
/** The marker under a pane-local logical point, topmost first, or null. */
|
|
@@ -418,7 +520,7 @@ export function markerAt(
|
|
|
418
520
|
y: number,
|
|
419
521
|
slop = 2,
|
|
420
522
|
): MapMarker | null {
|
|
421
|
-
const ordered =
|
|
523
|
+
const ordered = markerOrder(markers);
|
|
422
524
|
for (let i = ordered.length - 1; i >= 0; i--) {
|
|
423
525
|
const marker = ordered[i];
|
|
424
526
|
if (marker.interactive === false) continue;
|
|
@@ -623,13 +725,16 @@ export function geoJsonOverlays(
|
|
|
623
725
|
*/
|
|
624
726
|
const MAX_ARC_RADIUS = 8192;
|
|
625
727
|
|
|
728
|
+
/** How many segments a circle of this radius, in pixels, is drawn with as
|
|
729
|
+
* a ring: enough to hold the sagitta under half a pixel. */
|
|
730
|
+
export function circleSegments(radius: number): number {
|
|
731
|
+
const step = 2 * Math.acos(Math.max(-1, 1 - 0.5 / radius));
|
|
732
|
+
return Math.min(4096, Math.max(24, Math.ceil((Math.PI * 2) / step)));
|
|
733
|
+
}
|
|
734
|
+
|
|
626
735
|
/** A circle as a ring, with the sagitta held under half a pixel. */
|
|
627
736
|
function circleRing(cx: number, cy: number, radius: number): number[] {
|
|
628
|
-
const
|
|
629
|
-
const segments = Math.min(
|
|
630
|
-
4096,
|
|
631
|
-
Math.max(24, Math.ceil((Math.PI * 2) / step)),
|
|
632
|
-
);
|
|
737
|
+
const segments = circleSegments(radius);
|
|
633
738
|
const out: number[] = [];
|
|
634
739
|
for (let i = 0; i < segments; i++) {
|
|
635
740
|
const angle = (i / segments) * Math.PI * 2;
|
package/src/maps/paint.ts
CHANGED
|
@@ -72,6 +72,9 @@ export interface MapCanvas {
|
|
|
72
72
|
* scale bar are drawn with. */
|
|
73
73
|
fillRects?(rects: readonly number[]): void;
|
|
74
74
|
setLineDash?(segments: readonly number[]): void;
|
|
75
|
+
/** What a street name set along its street is drawn with. */
|
|
76
|
+
translate?(x: number, y: number): void;
|
|
77
|
+
rotate?(angle: number): void;
|
|
75
78
|
roundRect?(x: number, y: number, w: number, h: number, radii: number): void;
|
|
76
79
|
drawImage?(image: unknown, ...args: number[]): void;
|
|
77
80
|
/** Straight RGBA in, for a raster tile the application decoded. */
|
package/src/maps/proj.ts
CHANGED
|
@@ -441,6 +441,37 @@ export function subTileOf(
|
|
|
441
441
|
};
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
/**
|
|
445
|
+
* Where the data tile that serves a cover entry lands on screen: the entry
|
|
446
|
+
* itself until the cover goes deeper than the source cuts, and past that
|
|
447
|
+
* the whole of the ancestor it is one cell of — `span` times its size,
|
|
448
|
+
* starting `sub.x` cells to its left and `sub.y` above it.
|
|
449
|
+
*
|
|
450
|
+
* What a **raster** tile is drawn over past its source's depth. A vector
|
|
451
|
+
* cell is rasterized from its data tile's features through the cell, which
|
|
452
|
+
* is where overzoom's sharpness comes from; an image has nothing finer in
|
|
453
|
+
* it, so it is drawn whole, once, over this square. Placed from the entry
|
|
454
|
+
* rather than from the camera, and it lands where {@link tileCover} at the
|
|
455
|
+
* source's own depth puts the same tile.
|
|
456
|
+
*/
|
|
457
|
+
export function dataSquareOf(
|
|
458
|
+
entry: TileCoverEntry,
|
|
459
|
+
maxZoom: number,
|
|
460
|
+
): TileCoverEntry {
|
|
461
|
+
const sub = subTileOf(entry.tile, maxZoom);
|
|
462
|
+
if (sub.span === 1) return entry;
|
|
463
|
+
return {
|
|
464
|
+
tile: dataTileFor(entry.tile, maxZoom),
|
|
465
|
+
worldCopy: entry.worldCopy,
|
|
466
|
+
x: entry.x - sub.x * entry.size,
|
|
467
|
+
y: entry.y - sub.y * entry.size,
|
|
468
|
+
size: entry.size * sub.span,
|
|
469
|
+
// The cell's: a square takes its place in the cover's order from the
|
|
470
|
+
// cell it was reached through.
|
|
471
|
+
distance: entry.distance,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
444
475
|
/**
|
|
445
476
|
* How large to rasterize a tile whose pyramid level is `z` at camera `zoom`.
|
|
446
477
|
*
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Which of `<Map>`'s two renderers draws a map, and bringing the GL one in.
|
|
2
|
+
//
|
|
3
|
+
// The retained renderer (`./node.ts`) runs everywhere a 2D context does —
|
|
4
|
+
// indirect GLX, a remote X server, Xvfb in CI, the headless harness — and
|
|
5
|
+
// is visible to window capture. The GL renderer (`./gl/`) needs direct GL,
|
|
6
|
+
// and draws every frame from vector data at the display's rate. Neither is
|
|
7
|
+
// deprecated in favour of the other: `docs/prd-maps-gl.md`, "One `<Map>`,
|
|
8
|
+
// two renderers", has why the retained one stays, and this file is the
|
|
9
|
+
// decision between them — pure, so the whole of it is a unit test.
|
|
10
|
+
//
|
|
11
|
+
// `'auto'` decides in this order:
|
|
12
|
+
//
|
|
13
|
+
// 1. An explicit `renderer`, or the environment's, over both props and
|
|
14
|
+
// policy — the way `REACT_X11_BACKEND` works for the backend.
|
|
15
|
+
// 2. Whether this connection draws through the direct backend at all —
|
|
16
|
+
// `useSupports('shaders')`, which on X11 needs `glPolicy: 'auto'` and a
|
|
17
|
+
// DRI3 or Apple-DRI server, and on the Cocoa backend is always true once
|
|
18
|
+
// its probe has answered.
|
|
19
|
+
// 3. At run time, a GL failure moves the map to the retained renderer, with
|
|
20
|
+
// its camera and its handle intact. That one is sticky.
|
|
21
|
+
//
|
|
22
|
+
// A map that asked for `'gl'` by name never falls back: it gets GL, or it
|
|
23
|
+
// gets `onError`.
|
|
24
|
+
import type {
|
|
25
|
+
MapRenderer,
|
|
26
|
+
MapRendererReason,
|
|
27
|
+
MapRendererRequest,
|
|
28
|
+
} from './types.js';
|
|
29
|
+
|
|
30
|
+
/** The environment variable that overrides every map's renderer. */
|
|
31
|
+
export const RENDERER_ENV = 'REACT_X11_MAP_RENDERER';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* What a map asks for when it does not say: GL where this connection draws
|
|
35
|
+
* through the direct backend, the retained renderer everywhere else — which
|
|
36
|
+
* is still every indirect GLX connection, every remote X server and every
|
|
37
|
+
* headless test run. `'retained'` until the GL renderer had soaked on both
|
|
38
|
+
* backends (`docs/prd-maps-gl.md`, "The soak").
|
|
39
|
+
*/
|
|
40
|
+
export const DEFAULT_RENDERER: MapRendererRequest = 'auto';
|
|
41
|
+
|
|
42
|
+
const REQUESTS: readonly string[] = ['auto', 'gl', 'retained'];
|
|
43
|
+
|
|
44
|
+
/** Everything the decision reads. */
|
|
45
|
+
export interface RendererInput {
|
|
46
|
+
/** The `renderer` prop. */
|
|
47
|
+
requested?: MapRendererRequest;
|
|
48
|
+
/** `REACT_X11_MAP_RENDERER`, as the environment has it — anything but
|
|
49
|
+
* the three names is ignored. */
|
|
50
|
+
env?: string;
|
|
51
|
+
/** `useSupports('shaders')`: does this connection draw through the
|
|
52
|
+
* direct backend? */
|
|
53
|
+
shaders: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether the answer to that is still to come. The Cocoa backend settles
|
|
56
|
+
* it on the first ask rather than at connect, so the first render of a map
|
|
57
|
+
* there reads `false` for an answer that is about to be `true`; drawing
|
|
58
|
+
* the retained renderer for those few milliseconds would fetch the view's
|
|
59
|
+
* tiles twice. A map in that state shows its background and waits.
|
|
60
|
+
*/
|
|
61
|
+
probing?: boolean;
|
|
62
|
+
/** Whether GL has already failed on this map. */
|
|
63
|
+
failed: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The decision: a renderer — or `'pending'` while the connection's answer
|
|
67
|
+
* is still to come — and why, when it is not what was asked for. */
|
|
68
|
+
export interface RendererChoice {
|
|
69
|
+
renderer: MapRenderer | 'pending';
|
|
70
|
+
/** Null when the map got what it asked for. */
|
|
71
|
+
reason: MapRendererReason | null;
|
|
72
|
+
/** What was asked for, the environment's word included. */
|
|
73
|
+
asked: MapRendererRequest;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function requestOf(value: string | undefined): MapRendererRequest | null {
|
|
77
|
+
const v = value?.trim().toLowerCase();
|
|
78
|
+
return v && REQUESTS.includes(v) ? (v as MapRendererRequest) : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What a map asks for: the environment's word over the prop's, the prop's
|
|
83
|
+
* over the default — and whether the environment is what changed it.
|
|
84
|
+
*/
|
|
85
|
+
export function rendererRequest(
|
|
86
|
+
requested: MapRendererRequest | undefined,
|
|
87
|
+
env: string | undefined,
|
|
88
|
+
): { asked: MapRendererRequest; forced: boolean } {
|
|
89
|
+
const fromEnv = requestOf(env);
|
|
90
|
+
const own = requested ?? DEFAULT_RENDERER;
|
|
91
|
+
return { asked: fromEnv ?? own, forced: fromEnv !== null && fromEnv !== own };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Choose a map's renderer. */
|
|
95
|
+
export function chooseRenderer(input: RendererInput): RendererChoice {
|
|
96
|
+
const { asked, forced: byEnv } = rendererRequest(input.requested, input.env);
|
|
97
|
+
if (asked === 'retained') {
|
|
98
|
+
return { renderer: 'retained', reason: byEnv ? 'forced' : null, asked };
|
|
99
|
+
}
|
|
100
|
+
if (asked === 'gl') {
|
|
101
|
+
return { renderer: 'gl', reason: byEnv ? 'forced' : null, asked };
|
|
102
|
+
}
|
|
103
|
+
if (input.failed) return { renderer: 'retained', reason: 'gl-failed', asked };
|
|
104
|
+
if (!input.shaders) {
|
|
105
|
+
return input.probing
|
|
106
|
+
? { renderer: 'pending', reason: null, asked }
|
|
107
|
+
: { renderer: 'retained', reason: 'no-direct-gl', asked };
|
|
108
|
+
}
|
|
109
|
+
return { renderer: 'gl', reason: null, asked };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The environment's override, read off `globalThis` because `src/` compiles
|
|
113
|
+
* with `types: []`. */
|
|
114
|
+
export function rendererFromEnv(): string | undefined {
|
|
115
|
+
const g = globalThis as {
|
|
116
|
+
process?: { env?: Record<string, string | undefined> };
|
|
117
|
+
};
|
|
118
|
+
return g.process?.env?.[RENDERER_ENV];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --- the GL renderer, loaded when it is chosen --------------------------------
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The GL renderer's module, once it has loaded.
|
|
125
|
+
*
|
|
126
|
+
* Reached by dynamic `import()` and never statically: a static import from
|
|
127
|
+
* `<Map>` would put all of `./gl/` — the shaders, the bucket builder, the
|
|
128
|
+
* label atlas — into every bundle that uses a map, which is the tree-shaking
|
|
129
|
+
* promise broken for an application that never draws one through GL.
|
|
130
|
+
* `test/treeshake.test.ts` holds it to that.
|
|
131
|
+
*/
|
|
132
|
+
export type GlRendererModule = typeof import('./gl/index.js');
|
|
133
|
+
|
|
134
|
+
let loaded: GlRendererModule | null = null;
|
|
135
|
+
let loading: Promise<GlRendererModule> | null = null;
|
|
136
|
+
|
|
137
|
+
/** The GL renderer, if it has loaded — so a second map mounts it at once. */
|
|
138
|
+
export function glRendererModule(): GlRendererModule | null {
|
|
139
|
+
return loaded;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Load the GL renderer — once per process, however many maps ask. */
|
|
143
|
+
export function loadGlRenderer(): Promise<GlRendererModule> {
|
|
144
|
+
loading ??= import('./gl/index.js').then(
|
|
145
|
+
(module) => (loaded = module),
|
|
146
|
+
(error: unknown) => {
|
|
147
|
+
// Asked again next time rather than failing forever on a cached
|
|
148
|
+
// rejection: a load that failed once is most likely to fail again,
|
|
149
|
+
// and the map that asked has already fallen back or reported it.
|
|
150
|
+
loading = null;
|
|
151
|
+
throw error;
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
return loading;
|
|
155
|
+
}
|
package/src/maps/sources.ts
CHANGED
|
@@ -27,7 +27,13 @@ export interface TileRequest extends TileId {
|
|
|
27
27
|
* loader. */
|
|
28
28
|
sourceId: string;
|
|
29
29
|
/**
|
|
30
|
-
* Aborted when the
|
|
30
|
+
* Aborted when the map stops wanting the tile before it has arrived: it
|
|
31
|
+
* has been panned or zoomed out of what the map is loading (the view and
|
|
32
|
+
* a margin around it), its source has been taken out of `sources`, or the
|
|
33
|
+
* map has unmounted. Whatever an aborted load answers after that, an
|
|
34
|
+
* `AbortError` included, is ignored rather than reported to
|
|
35
|
+
* `onTileError`, and a tile that comes back is asked for again with a new
|
|
36
|
+
* signal.
|
|
31
37
|
*
|
|
32
38
|
* **This is a real `AbortSignal`** on every runtime that has one, which
|
|
33
39
|
* is what makes `fetch(url, { signal })` work — `fetch` rejects anything
|
|
@@ -63,8 +69,17 @@ export type TileData =
|
|
|
63
69
|
|
|
64
70
|
/** A tile source. */
|
|
65
71
|
export interface MapSource {
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
72
|
+
/**
|
|
73
|
+
* What this source is called: the `sourceId` its `load` requests carry
|
|
74
|
+
* and `onTileError` reports. `source-<index>` by default.
|
|
75
|
+
*
|
|
76
|
+
* **Not** what its tiles are cached under — that is the source object
|
|
77
|
+
* itself, because a name is not a provider: two sources may share one,
|
|
78
|
+
* and a map switched between them must not draw one's tiles as the
|
|
79
|
+
* other's. The flip side is that a source made anew on every render
|
|
80
|
+
* starts from an empty cache on every render, so make it once — at
|
|
81
|
+
* module scope, or in `useMemo`.
|
|
82
|
+
*/
|
|
68
83
|
id?: string;
|
|
69
84
|
/** Fetch, or answer from a cache, or decline. May be async. */
|
|
70
85
|
load(request: TileRequest): TileData | Promise<TileData>;
|
|
@@ -87,6 +102,26 @@ export interface MapSource {
|
|
|
87
102
|
attribution?: string;
|
|
88
103
|
}
|
|
89
104
|
|
|
105
|
+
/**
|
|
106
|
+
* What a map says in its corner: the `attribution` prop when it is given —
|
|
107
|
+
* `''` is the application saying it has put the attribution somewhere else —
|
|
108
|
+
* and otherwise every source's own, each once, in order. Both renderers draw
|
|
109
|
+
* this string, because for open data it is a licence condition.
|
|
110
|
+
*/
|
|
111
|
+
export function attributionOf(
|
|
112
|
+
attribution: string | undefined,
|
|
113
|
+
sources: readonly MapSource[],
|
|
114
|
+
): string {
|
|
115
|
+
if (attribution !== undefined) return attribution;
|
|
116
|
+
const parts: string[] = [];
|
|
117
|
+
for (const source of sources) {
|
|
118
|
+
if (source.attribution && !parts.includes(source.attribution)) {
|
|
119
|
+
parts.push(source.attribution);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return parts.join(' · ');
|
|
123
|
+
}
|
|
124
|
+
|
|
90
125
|
/** A source's pyramid, with the defaults filled in. */
|
|
91
126
|
export function pyramidOf(source: MapSource): {
|
|
92
127
|
minZoom: number;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The theme's say in how a map looks: which of the default style's palettes
|
|
2
|
+
// it gets, and the colours markers, overlays and the attribution fall back
|
|
3
|
+
// to. Both renderers read the theme through these, so a map in a dark
|
|
4
|
+
// application is dark whichever of them draws it.
|
|
5
|
+
import type { OverlayPalette } from './overlay.js';
|
|
6
|
+
import type { MapStyle } from './style.js';
|
|
7
|
+
import { shortbreadStyle } from './styles.js';
|
|
8
|
+
|
|
9
|
+
const defaults = new Map<boolean, MapStyle>();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The style a map with no `mapStyle` is drawn in: `shortbreadStyle()` in the
|
|
13
|
+
* theme's light or dark palette. Made once each — a style's identity is
|
|
14
|
+
* what decides whether the GL renderer rebuilds its tiles.
|
|
15
|
+
*/
|
|
16
|
+
export function defaultStyleFor(dark: boolean): MapStyle {
|
|
17
|
+
let style = defaults.get(dark);
|
|
18
|
+
if (!style) {
|
|
19
|
+
style = shortbreadStyle({ dark });
|
|
20
|
+
defaults.set(dark, style);
|
|
21
|
+
}
|
|
22
|
+
return style;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Whether a theme is dark — the luminance of the surface the map sits on,
|
|
27
|
+
* the same reading `src/code-editor/`'s token themes make, rather than a flag
|
|
28
|
+
* nobody sets. A map inside a dark application that stays light is the thing
|
|
29
|
+
* everyone notices first.
|
|
30
|
+
*/
|
|
31
|
+
export function isDarkTheme(theme: unknown): boolean {
|
|
32
|
+
const background = (theme as Record<string, unknown> | undefined)?.background;
|
|
33
|
+
if (typeof background !== 'string') return false;
|
|
34
|
+
const hex = background.trim();
|
|
35
|
+
if (!hex.startsWith('#') || hex.length < 7) return false;
|
|
36
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
37
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
38
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
39
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b < 128;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The colours a marker, an overlay and the attribution use where they do
|
|
43
|
+
* not name one. */
|
|
44
|
+
export function overlayPalette(theme: unknown): OverlayPalette {
|
|
45
|
+
const t = theme as Record<string, unknown> | undefined;
|
|
46
|
+
return {
|
|
47
|
+
accent: (t?.accent as string) ?? '#2d6cdf',
|
|
48
|
+
background: (t?.background as string) ?? '#ffffff',
|
|
49
|
+
text: (t?.text as string) ?? '#111111',
|
|
50
|
+
};
|
|
51
|
+
}
|