@guardian/interactive-component-library 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/components/molecules/canvas-map/lib/Feature.d.ts +4 -1
  2. package/dist/components/molecules/canvas-map/lib/Feature.js +3 -0
  3. package/dist/components/molecules/canvas-map/lib/layers/TextLayer.d.ts +49 -15
  4. package/dist/components/molecules/canvas-map/lib/layers/TextLayer.js +4 -1
  5. package/dist/components/molecules/canvas-map/lib/layers/VectorLayer.d.ts +36 -10
  6. package/dist/components/molecules/canvas-map/lib/layers/VectorLayer.js +6 -3
  7. package/dist/components/molecules/canvas-map/lib/renderers/MapRenderer.d.ts +14 -0
  8. package/dist/components/molecules/canvas-map/lib/renderers/MapRenderer.js +52 -8
  9. package/dist/components/molecules/canvas-map/lib/renderers/TextLayerRenderer.d.ts +50 -5
  10. package/dist/components/molecules/canvas-map/lib/renderers/TextLayerRenderer.js +171 -24
  11. package/dist/components/molecules/canvas-map/lib/renderers/VectorLayerRenderer.d.ts +13 -6
  12. package/dist/components/molecules/canvas-map/lib/renderers/VectorLayerRenderer.js +12 -42
  13. package/dist/components/molecules/canvas-map/lib/sources/VectorSource.d.ts +24 -6
  14. package/dist/components/molecules/canvas-map/lib/sources/VectorSource.js +18 -0
  15. package/dist/components/molecules/canvas-map/lib/styles/Style.d.ts +40 -11
  16. package/dist/components/molecules/canvas-map/lib/styles/Style.js +7 -0
  17. package/dist/components/molecules/canvas-map/lib/styles/Text.d.ts +184 -11
  18. package/dist/components/molecules/canvas-map/lib/styles/Text.js +18 -3
  19. package/dist/components/molecules/result-summary/index.d.ts +2 -1
  20. package/dist/components/molecules/result-summary/index.js +30 -5
  21. package/dist/components/molecules/result-summary/style.module.css.js +12 -3
  22. package/dist/components/organisms/ticker/index.d.ts +2 -1
  23. package/dist/components/organisms/ticker/index.js +4 -1
  24. package/dist/components/organisms/ticker/lib/TickerControlsDesktop.js +4 -4
  25. package/dist/components/organisms/ticker/lib/TickerControlsMobileVertical.js +5 -5
  26. package/dist/components/organisms/ticker/style.module.scss.js +2 -2
  27. package/dist/style.css +21 -2
  28. package/package.json +1 -1
@@ -32,7 +32,10 @@ export class Feature {
32
32
  */
33
33
  getExtent(): import('./util').Extent;
34
34
  getProjectedGeometries(projection: any): any;
35
- getStyleFunction(): any;
35
+ /**
36
+ * @returns {import("./styles/Style").StyleFunction}
37
+ */
38
+ getStyleFunction(): import('./styles/Style').StyleFunction;
36
39
  containsCoordinate(coordinate: any): boolean;
37
40
  clone(): Feature;
38
41
  /**
@@ -45,6 +45,9 @@ class Feature {
45
45
  getProjectedGeometries(projection) {
46
46
  return this._getProjectedGeometries(projection, projection.revision);
47
47
  }
48
+ /**
49
+ * @returns {import("./styles/Style").StyleFunction}
50
+ */
48
51
  getStyleFunction() {
49
52
  const style = this.style;
50
53
  if (!style) return null;
@@ -28,25 +28,59 @@ export class TextLayer {
28
28
  declutter?: boolean;
29
29
  drawCollisionBoxes?: boolean;
30
30
  });
31
- source: VectorSource;
32
- _style: Style | (() => Style);
33
- minZoom: number;
34
- opacity: number;
35
- declutter: boolean;
36
- drawCollisionBoxes: boolean;
37
- renderer: TextLayerRenderer;
38
- dispatcher: Dispatcher;
31
+ /**
32
+ * @type {VectorSource}
33
+ * @public
34
+ */
35
+ public source: VectorSource;
36
+ /**
37
+ * @type {Style|import("../styles").StyleFunction}
38
+ */
39
+ _style: Style | import('../styles').StyleFunction;
40
+ /**
41
+ * @type {number}
42
+ * @public
43
+ */
44
+ public minZoom: number;
45
+ /**
46
+ * @type {number}
47
+ * @public
48
+ */
49
+ public opacity: number;
50
+ /**
51
+ * @type {boolean}
52
+ * @public
53
+ */
54
+ public declutter: boolean;
55
+ /**
56
+ * @type {boolean}
57
+ * @public
58
+ */
59
+ public drawCollisionBoxes: boolean;
60
+ /**
61
+ * @type {TextLayerRenderer}
62
+ * @public
63
+ */
64
+ public renderer: TextLayerRenderer;
65
+ /**
66
+ * @type {Dispatcher}
67
+ * @public
68
+ */
69
+ public dispatcher: Dispatcher;
39
70
  tearDown(): void;
40
- set style(style: Style | (() => Style));
41
- get style(): Style | (() => Style);
42
- getExtent(): any;
43
- _extent: any;
44
- getStyleFunction(): () => Style;
45
- renderFrame(frameState: any, targetElement: any): any;
71
+ set style(style: Style | import('../styles').StyleFunction);
72
+ get style(): Style | import('../styles').StyleFunction;
73
+ getExtent(): import('../Feature').Feature;
74
+ _extent: import('../Feature').Feature;
75
+ /**
76
+ * @returns {import("../styles/Style").StyleFunction}
77
+ */
78
+ getStyleFunction(): import('../styles/Style').StyleFunction;
79
+ renderFrame(frameState: any, targetElement: any): HTMLDivElement;
46
80
  }
47
81
  export namespace TextLayer {
48
82
  /** @param {TextLayerComponentProps} props */
49
- function Component({ features: featureCollection, style, minZoom, opacity, declutter, drawCollisionBoxes, }: TextLayerComponentProps): boolean;
83
+ function Component({ features: featureCollection, style, minZoom, opacity, declutter, drawCollisionBoxes, }: TextLayerComponentProps): any;
50
84
  namespace Component {
51
85
  let displayName: string;
52
86
  }
@@ -45,7 +45,7 @@ class TextLayer {
45
45
  useEffect(() => {
46
46
  layer.style = style;
47
47
  }, [style]);
48
- return false;
48
+ return null;
49
49
  }
50
50
  /**
51
51
  * @param {import("../Feature").Feature[]} features
@@ -107,6 +107,9 @@ class TextLayer {
107
107
  this._extent = extent;
108
108
  return extent;
109
109
  }
110
+ /**
111
+ * @returns {import("../styles/Style").StyleFunction}
112
+ */
110
113
  getStyleFunction() {
111
114
  const style = this.style;
112
115
  if (typeof style === "function") return style;
@@ -25,22 +25,48 @@ export class VectorLayer {
25
25
  opacity?: number;
26
26
  hitDetectionEnabled?: boolean;
27
27
  });
28
- dispatcher: Dispatcher;
29
- renderer: VectorLayerRenderer;
30
28
  set source(source: any);
31
29
  get source(): any;
32
- _style: Style | (() => Style);
33
- minZoom: number;
34
- opacity: number;
35
- hitDetectionEnabled: boolean;
30
+ /**
31
+ * @type {Dispatcher}
32
+ * @public
33
+ */
34
+ public dispatcher: Dispatcher;
35
+ /**
36
+ * @type {VectorLayerRenderer}
37
+ * @public
38
+ */
39
+ public renderer: VectorLayerRenderer;
40
+ /**
41
+ * @type {number}
42
+ * @public
43
+ */
44
+ public minZoom: number;
45
+ /**
46
+ * @type {number}
47
+ * @public
48
+ */
49
+ public opacity: number;
50
+ /**
51
+ * @type {boolean}
52
+ * @public
53
+ */
54
+ public hitDetectionEnabled: boolean;
55
+ /**
56
+ * @type {Style|import("../styles").StyleFunction}
57
+ */
58
+ _style: Style | import('../styles').StyleFunction;
36
59
  _source: any;
37
60
  _extent: any;
38
61
  setRawProjection(projection: any): void;
39
62
  projection: any;
40
63
  tearDown(): void;
41
- set style(style: Style | (() => Style));
42
- get style(): Style | (() => Style);
43
- getStyleFunction(): () => Style;
64
+ set style(style: Style | import('../styles').StyleFunction);
65
+ get style(): Style | import('../styles').StyleFunction;
66
+ /**
67
+ * @returns {import("../styles/Style").StyleFunction}
68
+ */
69
+ getStyleFunction(): import('../styles/Style').StyleFunction;
44
70
  /**
45
71
  * Get the extent of the features in the layer.
46
72
  * @returns {import("../util").Extent | null} The extent of the features in the layer, or null if the layer is empty.
@@ -51,7 +77,7 @@ export class VectorLayer {
51
77
  }
52
78
  export namespace VectorLayer {
53
79
  /** @param {VectorLayerComponentProps} props */
54
- function Component({ features: featureCollection, style, minZoom, opacity, hitDetectionEnabled, }: VectorLayerComponentProps): boolean;
80
+ function Component({ features: featureCollection, style, minZoom, opacity, hitDetectionEnabled, }: VectorLayerComponentProps): any;
55
81
  namespace Component {
56
82
  let displayName: string;
57
83
  }
@@ -43,7 +43,7 @@ class VectorLayer {
43
43
  useEffect(() => {
44
44
  layer.style = style;
45
45
  }, [style]);
46
- return false;
46
+ return null;
47
47
  }
48
48
  /**
49
49
  * @param {import("../Feature").Feature[]} features
@@ -68,13 +68,13 @@ class VectorLayer {
68
68
  opacity = 1,
69
69
  hitDetectionEnabled = true
70
70
  }) {
71
+ this.source = source;
71
72
  this.dispatcher = new Dispatcher(this);
72
73
  this.renderer = new VectorLayerRenderer(this);
73
- this.source = source;
74
- this._style = style;
75
74
  this.minZoom = minZoom;
76
75
  this.opacity = opacity;
77
76
  this.hitDetectionEnabled = hitDetectionEnabled;
77
+ this._style = style;
78
78
  }
79
79
  get source() {
80
80
  return this._source;
@@ -106,6 +106,9 @@ class VectorLayer {
106
106
  this._style = style;
107
107
  this.dispatcher.dispatch(MapEvent.CHANGE);
108
108
  }
109
+ /**
110
+ * @returns {import("../styles/Style").StyleFunction}
111
+ */
109
112
  getStyleFunction() {
110
113
  const style = this.style;
111
114
  if (typeof style === "function") return style;
@@ -1,6 +1,20 @@
1
+ /**
2
+ * @typedef {Object} CanvasSingleton
3
+ * @property {() => CanvasRenderingContext2D} getContext2d
4
+ * @property {() => HTMLDivElement} getContainer
5
+ * @property {() => boolean} isInitialised
6
+ *
7
+ * @typedef {Object} FrameState
8
+ */
1
9
  export class MapRenderer {
2
10
  constructor(map: any);
3
11
  map: any;
4
12
  _element: HTMLDivElement;
5
13
  renderFrame(frameState: any): void;
6
14
  }
15
+ export type CanvasSingleton = {
16
+ getContext2d: () => CanvasRenderingContext2D;
17
+ getContainer: () => HTMLDivElement;
18
+ isInitialised: () => boolean;
19
+ };
20
+ export type FrameState = any;
@@ -14,25 +14,24 @@ class MapRenderer {
14
14
  container.insertBefore(this._element, container.firstChild || null);
15
15
  }
16
16
  renderFrame(frameState) {
17
- const { zoomLevel, projection } = frameState.viewState;
17
+ const { zoomLevel, projection, sizeInPixels } = frameState.viewState;
18
18
  const layers = this.map.layers;
19
19
  const mapElements = [];
20
- let previousElement = null;
21
20
  const visibleLayers = layers.filter((layer) => {
22
21
  return zoomLevel > (layer.minZoom || 0);
23
22
  });
23
+ const canvasSingleton = makeCanvasSingleton(sizeInPixels);
24
24
  const renderLayer = (layer, declutterTree2) => {
25
25
  const viewState = frameState.viewState;
26
26
  if (layer.projection) {
27
27
  viewState.projection = layer.projection;
28
28
  }
29
- const element = layer.renderFrame(
29
+ const newContainer = layer.renderFrame(
30
30
  { ...frameState, viewState, declutterTree: declutterTree2 },
31
- previousElement
31
+ canvasSingleton
32
32
  );
33
- if (element !== previousElement) {
34
- mapElements.push(element);
35
- previousElement = element;
33
+ if (newContainer) {
34
+ mapElements.push(newContainer);
36
35
  }
37
36
  viewState.projection = projection;
38
37
  };
@@ -44,9 +43,54 @@ class MapRenderer {
44
43
  renderLayer(layer);
45
44
  }
46
45
  }
47
- replaceChildren(this._element, mapElements);
46
+ if (canvasSingleton.isInitialised()) {
47
+ replaceChildren(this._element, [
48
+ canvasSingleton.getContainer(),
49
+ ...mapElements
50
+ ]);
51
+ } else {
52
+ replaceChildren(this._element, mapElements);
53
+ }
48
54
  }
49
55
  }
56
+ function makeCanvasSingleton(sizeInPixels) {
57
+ let canvasLayer;
58
+ let canvasContext2d;
59
+ return {
60
+ getContext2d: () => {
61
+ if (!canvasLayer) {
62
+ canvasLayer = createCanvasMapLayer(sizeInPixels);
63
+ canvasContext2d = canvasLayer.firstElementChild.getContext("2d");
64
+ }
65
+ return canvasContext2d;
66
+ },
67
+ getContainer: () => {
68
+ if (!canvasLayer) {
69
+ canvasLayer = createCanvasMapLayer(sizeInPixels);
70
+ canvasContext2d = canvasLayer.firstElementChild.getContext("2d");
71
+ }
72
+ return canvasLayer;
73
+ },
74
+ isInitialised: () => !!canvasLayer
75
+ };
76
+ }
77
+ function createCanvasMapLayer(sizeInPixels) {
78
+ const container = document.createElement("div");
79
+ container.className = "gv-map-layer";
80
+ let style = container.style;
81
+ style.position = "absolute";
82
+ style.width = "100%";
83
+ style.height = "100%";
84
+ const canvas = document.createElement("canvas");
85
+ style = canvas.style;
86
+ style.position = "absolute";
87
+ style.width = "100%";
88
+ style.height = "100%";
89
+ canvas.width = sizeInPixels[0];
90
+ canvas.height = sizeInPixels[1];
91
+ container.appendChild(canvas);
92
+ return container;
93
+ }
50
94
  export {
51
95
  MapRenderer
52
96
  };
@@ -1,21 +1,66 @@
1
1
  import { FeatureRenderer } from './FeatureRenderer';
2
2
  export class TextLayerRenderer {
3
- constructor(layer: any);
4
- layer: any;
3
+ /**
4
+ * @param {import("../layers/TextLayer").TextLayer} layer
5
+ */
6
+ constructor(layer: import('../layers/TextLayer').TextLayer);
7
+ /**
8
+ * @type {import("../layers/TextLayer").TextLayer}
9
+ */
10
+ layer: import('../layers/TextLayer').TextLayer;
5
11
  featureRenderer: FeatureRenderer;
6
12
  _element: HTMLDivElement;
7
- renderFrame(frameState: any, targetElement: any): any;
13
+ /**
14
+ * @param {import("./MapRenderer").CanvasSingleton} canvasSingleton
15
+ */
16
+ renderFrame(frameState: any, canvasSingleton: import('./MapRenderer').CanvasSingleton): HTMLDivElement;
8
17
  getTextElementWithID(id: any): Element;
9
- styleTextElement(element: any, textStyle: any, position: any): void;
18
+ styleTextElement(element: any, textStyle: any, position: any): {
19
+ width: any;
20
+ height: any;
21
+ };
10
22
  getElementSize(element: any): {
11
23
  width: any;
12
24
  height: any;
13
25
  };
14
- getElementBBox(element: any, textStyle: any, position: any): {
26
+ /**
27
+ * @param {{ height: number, width: number }} dimens
28
+ * @param {import("../styles/Text").Text} textStyle
29
+ * @param {{x: number, y: number}} position
30
+ */
31
+ getElementBBox(dimens: {
32
+ height: number;
33
+ width: number;
34
+ }, textStyle: import('../styles/Text').Text, position: {
35
+ x: number;
36
+ y: number;
37
+ }): {
15
38
  minX: number;
16
39
  minY: number;
17
40
  maxX: number;
18
41
  maxY: number;
19
42
  };
43
+ /**
44
+ * @param {import("../styles/Text").Text} textStyle
45
+ * @param {{x: number, y: number}} position
46
+ */
47
+ getElementPosition(textStyle: import('../styles/Text').Text, position: {
48
+ x: number;
49
+ y: number;
50
+ }): {
51
+ left: string;
52
+ top: string;
53
+ };
20
54
  getCollisionBoxElement(bbox: any): HTMLDivElement;
55
+ /**
56
+ * Draws a `Text` element's icon on the canvas.
57
+ *
58
+ * Expects the canvas context to be translated to the text element's position.
59
+ *
60
+ * TODO: support more shapes?
61
+ *
62
+ * @param {CanvasRenderingContext2D} context
63
+ * @param {import("../styles/Text").IconOptions} icon
64
+ */
65
+ drawTextIcon(context: CanvasRenderingContext2D, icon: import('../styles/Text').IconOptions): void;
21
66
  }
@@ -1,6 +1,9 @@
1
1
  import { FeatureRenderer } from "./FeatureRenderer.js";
2
2
  import { replaceChildren } from "../util/dom.js";
3
3
  class TextLayerRenderer {
4
+ /**
5
+ * @param {import("../layers/TextLayer").TextLayer} layer
6
+ */
4
7
  constructor(layer) {
5
8
  this.layer = layer;
6
9
  this.featureRenderer = new FeatureRenderer();
@@ -13,13 +16,18 @@ class TextLayerRenderer {
13
16
  style.pointerEvents = "none";
14
17
  style.overflow = "hidden";
15
18
  }
16
- renderFrame(frameState, targetElement) {
17
- if (this.layer.opacity === 0) return targetElement;
19
+ /**
20
+ * @param {import("./MapRenderer").CanvasSingleton} canvasSingleton
21
+ */
22
+ renderFrame(frameState, canvasSingleton) {
23
+ var _a, _b;
24
+ if (this.layer.opacity === 0) return null;
18
25
  const { declutterTree } = frameState;
19
26
  const { projection, viewPortSize, sizeInPixels, visibleExtent, transform } = frameState.viewState;
20
- this._element.style.opacity = this.layer.opacity;
27
+ this._element.style.opacity = `${this.layer.opacity}`;
21
28
  const source = this.layer.source;
22
29
  const features = source.getFeaturesInExtent(visibleExtent);
30
+ let canvasCtx;
23
31
  const textElements = [];
24
32
  for (const feature of features) {
25
33
  const geometries = feature.getProjectedGeometries(projection);
@@ -30,16 +38,24 @@ class TextLayerRenderer {
30
38
  );
31
39
  }
32
40
  const styleFunction = feature.getStyleFunction() || this.layer.getStyleFunction();
33
- const featureStyle = styleFunction(feature);
41
+ const featureStyle = styleFunction(feature, transform.k);
34
42
  const textElement = this.getTextElementWithID(feature.uid);
35
43
  textElement.innerText = featureStyle.text.content;
36
- const [relativeX, relativeY] = transform.apply(point.coordinates).map((d, i) => d / sizeInPixels[i]);
37
- const position = {
38
- left: `${relativeX * 100}%`,
39
- top: `${relativeY * 100}%`
40
- };
41
- this.styleTextElement(textElement, featureStyle.text, position);
42
- const bbox = this.getElementBBox(textElement, featureStyle.text, {
44
+ const [canvasX, canvasY] = transform.apply(point.coordinates);
45
+ const [relativeX, relativeY] = [
46
+ canvasX / sizeInPixels[0],
47
+ canvasY / sizeInPixels[1]
48
+ ];
49
+ const position = this.getElementPosition(featureStyle.text, {
50
+ x: relativeX,
51
+ y: relativeY
52
+ });
53
+ const elementDimens = this.styleTextElement(
54
+ textElement,
55
+ featureStyle.text,
56
+ position
57
+ );
58
+ const bbox = this.getElementBBox(elementDimens, featureStyle.text, {
43
59
  x: relativeX * viewPortSize[0],
44
60
  y: relativeY * viewPortSize[1]
45
61
  });
@@ -49,6 +65,46 @@ class TextLayerRenderer {
49
65
  }
50
66
  declutterTree.insert(bbox);
51
67
  }
68
+ const callout = (_a = featureStyle == null ? void 0 : featureStyle.text) == null ? void 0 : _a.callout;
69
+ const icon = (_b = featureStyle == null ? void 0 : featureStyle.text) == null ? void 0 : _b.icon;
70
+ if (callout || icon) {
71
+ canvasCtx ?? (canvasCtx = canvasSingleton.getContext2d());
72
+ }
73
+ if (callout) {
74
+ const canvasOffsetX = (callout.offsetBy.x - callout.leaderGap) * window.devicePixelRatio;
75
+ const canvasOffsetY = callout.offsetBy.y * window.devicePixelRatio;
76
+ canvasCtx.beginPath();
77
+ canvasCtx.moveTo(canvasX, canvasY);
78
+ canvasCtx.lineTo(canvasX + canvasOffsetX / 2, canvasY + canvasOffsetY);
79
+ canvasCtx.moveTo(canvasX + canvasOffsetX / 2, canvasY + canvasOffsetY);
80
+ canvasCtx.lineTo(canvasX + canvasOffsetX, canvasY + canvasOffsetY);
81
+ canvasCtx.strokeStyle = callout.leaderColor;
82
+ canvasCtx.lineWidth = callout.leaderWidth;
83
+ canvasCtx.stroke();
84
+ canvasCtx.closePath();
85
+ }
86
+ if (icon) {
87
+ canvasCtx.beginPath();
88
+ canvasCtx.save();
89
+ let iconPosX = relativeX * viewPortSize[0];
90
+ let iconPosY = relativeY * viewPortSize[1];
91
+ if (callout) {
92
+ iconPosX += callout.offsetBy.x;
93
+ iconPosY += callout.offsetBy.y;
94
+ }
95
+ if (icon.position === "right") {
96
+ iconPosX += elementDimens.width;
97
+ } else if (icon.position === "left") {
98
+ iconPosX += icon.padding + icon.size / 2;
99
+ }
100
+ canvasCtx.translate(
101
+ iconPosX * window.devicePixelRatio,
102
+ iconPosY * window.devicePixelRatio
103
+ );
104
+ this.drawTextIcon(canvasCtx, icon);
105
+ canvasCtx.restore();
106
+ canvasCtx.closePath();
107
+ }
52
108
  if (this.layer.drawCollisionBoxes) {
53
109
  const collisionBoxDebugElement = this.getCollisionBoxElement(bbox);
54
110
  textElements.push(collisionBoxDebugElement);
@@ -82,6 +138,7 @@ class TextLayerRenderer {
82
138
  style.textShadow = textStyle.textShadow;
83
139
  const { width, height } = this.getElementSize(element);
84
140
  style.transform = textStyle.getTransform(width, height);
141
+ return { width, height };
85
142
  }
86
143
  getElementSize(element) {
87
144
  if (!element.parentElement) {
@@ -93,29 +150,85 @@ class TextLayerRenderer {
93
150
  }
94
151
  return { width, height };
95
152
  }
96
- getElementBBox(element, textStyle, position) {
97
- const collissionPadding = {
153
+ /**
154
+ * @param {{ height: number, width: number }} dimens
155
+ * @param {import("../styles/Text").Text} textStyle
156
+ * @param {{x: number, y: number}} position
157
+ */
158
+ getElementBBox(dimens, textStyle, position) {
159
+ const collisionPadding = {
98
160
  top: 2,
99
161
  right: 2,
100
162
  bottom: 2,
101
163
  left: 2
102
164
  };
103
- const { width, height } = this.getElementSize(element);
104
165
  const { x: translateX, y: translateY } = textStyle.getTranslation(
105
- width,
106
- height
166
+ dimens.width,
167
+ dimens.height
107
168
  );
108
- const minX = Math.floor(position.x + translateX - collissionPadding.left);
109
- const minY = Math.floor(position.y + translateY - collissionPadding.top);
169
+ let minX = position.x + translateX - collisionPadding.left;
170
+ let minY = position.y + translateY - collisionPadding.top;
171
+ let maxX = minX + dimens.width + collisionPadding.left + collisionPadding.right;
172
+ let maxY = minY + dimens.height + collisionPadding.top + collisionPadding.bottom;
173
+ if (textStyle.callout) {
174
+ minX += textStyle.callout.offsetBy.x;
175
+ minY += textStyle.callout.offsetBy.y;
176
+ maxX += textStyle.callout.offsetBy.x;
177
+ maxY += textStyle.callout.offsetBy.y;
178
+ }
179
+ if (textStyle.icon) {
180
+ if (textStyle.icon.position === "left" || textStyle.icon.position === "right") {
181
+ maxX += textStyle.icon.size + textStyle.icon.padding;
182
+ }
183
+ const iconSizeHeightDiff = textStyle.icon.size - dimens.height;
184
+ if (iconSizeHeightDiff > 0) {
185
+ minY -= iconSizeHeightDiff / 2;
186
+ maxY += iconSizeHeightDiff / 2;
187
+ }
188
+ }
189
+ minX = Math.floor(minX);
190
+ minY = Math.floor(minY);
191
+ maxX = Math.ceil(maxX);
192
+ maxY = Math.ceil(maxY);
110
193
  return {
111
194
  minX,
112
195
  minY,
113
- maxX: Math.ceil(
114
- minX + width + collissionPadding.left + collissionPadding.right
115
- ),
116
- maxY: Math.ceil(
117
- minY + height + collissionPadding.top + collissionPadding.bottom
118
- )
196
+ maxX,
197
+ maxY
198
+ };
199
+ }
200
+ /**
201
+ * @param {import("../styles/Text").Text} textStyle
202
+ * @param {{x: number, y: number}} position
203
+ */
204
+ getElementPosition(textStyle, position) {
205
+ if (textStyle.callout) {
206
+ if (textStyle.icon.position === "left") {
207
+ return {
208
+ left: `calc(${position.x * 100}% + ${textStyle.callout.offsetBy.x + (textStyle.icon.size + textStyle.icon.padding * 2)}px)`,
209
+ top: `calc(${position.y * 100}% + ${textStyle.callout.offsetBy.y}px)`
210
+ };
211
+ }
212
+ if (textStyle.icon.position === "right") {
213
+ return {
214
+ left: `calc(${position.x * 100}% + ${textStyle.callout.offsetBy.x}px)`,
215
+ top: `calc(${position.y * 100}% + ${textStyle.callout.offsetBy.y}px)`
216
+ };
217
+ }
218
+ return {
219
+ left: `calc(${position.x * 100}% + ${textStyle.callout.offsetBy.x}px)`,
220
+ top: `calc(${position.y * 100}% + ${textStyle.callout.offsetBy.y}px)`
221
+ };
222
+ }
223
+ if (textStyle.icon && (textStyle.icon.position === "left" || textStyle.icon.position === "right")) {
224
+ return {
225
+ left: `calc(${position.x * 100}% + ${textStyle.icon.size + textStyle.icon.padding * 2}px)`,
226
+ top: `${position.y * 100}%`
227
+ };
228
+ }
229
+ return {
230
+ left: `${position.x * 100}%`,
231
+ top: `${position.y * 100}%`
119
232
  };
120
233
  }
121
234
  getCollisionBoxElement(bbox) {
@@ -129,6 +242,40 @@ class TextLayerRenderer {
129
242
  style.border = "1px solid red";
130
243
  return element;
131
244
  }
245
+ /**
246
+ * Draws a `Text` element's icon on the canvas.
247
+ *
248
+ * Expects the canvas context to be translated to the text element's position.
249
+ *
250
+ * TODO: support more shapes?
251
+ *
252
+ * @param {CanvasRenderingContext2D} context
253
+ * @param {import("../styles/Text").IconOptions} icon
254
+ */
255
+ drawTextIcon(context, icon) {
256
+ var _a;
257
+ if (icon.shape === "circle") {
258
+ context.arc(
259
+ 0,
260
+ 0,
261
+ icon.size * devicePixelRatio / 2,
262
+ 0,
263
+ 2 * Math.PI,
264
+ false
265
+ );
266
+ if (icon.style) {
267
+ (_a = icon.style.fill) == null ? void 0 : _a.drawInContext(context);
268
+ if (icon.style.stroke) {
269
+ context.lineWidth = icon.style.stroke.width;
270
+ context.strokeStyle = icon.style.stroke.getRgba();
271
+ context.stroke();
272
+ }
273
+ } else if (icon.color) {
274
+ context.fillStyle = icon.color;
275
+ context.fill();
276
+ }
277
+ }
278
+ }
132
279
  }
133
280
  export {
134
281
  TextLayerRenderer
@@ -1,10 +1,17 @@
1
1
  import { FeatureRenderer } from './FeatureRenderer';
2
2
  export class VectorLayerRenderer {
3
- constructor(layer: any);
4
- layer: any;
3
+ /**
4
+ * @constructor
5
+ * @param {import('../layers').VectorLayer} layer
6
+ */
7
+ constructor(layer: import('../layers').VectorLayer);
8
+ /**
9
+ * @type {import('../layers').VectorLayer}
10
+ */
11
+ layer: import('../layers').VectorLayer;
5
12
  featureRenderer: FeatureRenderer;
6
- renderFrame(frameState: any, targetElement: any): any;
7
- getOrCreateContainer(targetElement: any, sizeInPixels: any): any;
8
- _container: any;
9
- createContainer(): HTMLDivElement;
13
+ /**
14
+ * @param {import("./MapRenderer").CanvasSingleton} canvasSingleton
15
+ */
16
+ renderFrame(frameState: any, canvasSingleton: import('./MapRenderer').CanvasSingleton): any;
10
17
  }