@guardian/interactive-component-library 0.7.1 → 0.7.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.
@@ -10,30 +10,30 @@ export class Map {
10
10
  * @constructor
11
11
  * @param {Object} config - The configuration for the map.
12
12
  * @param {Object} config.view - The view configuration for the map.
13
- * @param {boolean} config.debug - Whether to enable debug mode or not.
13
+ * @param {boolean} [config.debug] - Whether to enable debug mode or not.
14
14
  * @param {boolean} config.allowZoomPan - Whether to enable zoom and pan functionality or not.
15
- * @param {HTMLElement} config.target - The target element to render the map into.
15
+ * @param {HTMLElement} [config.target] - The target element to render the map into.
16
16
  */
17
17
  constructor(config: {
18
18
  view: any;
19
- debug: boolean;
19
+ debug?: boolean;
20
20
  allowZoomPan: boolean;
21
- target: HTMLElement;
21
+ target?: HTMLElement;
22
22
  });
23
23
  allowZoomPan: boolean;
24
24
  options: {
25
25
  view: any;
26
- debug: boolean;
26
+ debug?: boolean;
27
27
  allowZoomPan: boolean;
28
- target: HTMLElement;
28
+ target?: HTMLElement;
29
29
  };
30
30
  view: View;
31
31
  target: HTMLElement;
32
32
  layers: any[];
33
33
  dispatcher: Dispatcher;
34
34
  _viewport: HTMLDivElement;
35
- _renderer: MapRenderer;
36
35
  _resizeObserver: ResizeObserver;
36
+ _renderer: MapRenderer;
37
37
  destroy(): void;
38
38
  /** PUBLIC GETTERS */
39
39
  get size(): number[];
@@ -79,7 +79,8 @@ export class Map {
79
79
  _zoomBehaviour: any;
80
80
  _requestRender(): void;
81
81
  _animationFrameRequestID: number;
82
- _renderFrame(): void;
82
+ _renderFrame(_: any, canvas: any): void;
83
+ renderToCanvas(canvas: any): void;
83
84
  }
84
85
  export namespace Map {
85
86
  let Component: any;
@@ -14,9 +14,9 @@ class Map {
14
14
  * @constructor
15
15
  * @param {Object} config - The configuration for the map.
16
16
  * @param {Object} config.view - The view configuration for the map.
17
- * @param {boolean} config.debug - Whether to enable debug mode or not.
17
+ * @param {boolean} [config.debug] - Whether to enable debug mode or not.
18
18
  * @param {boolean} config.allowZoomPan - Whether to enable zoom and pan functionality or not.
19
- * @param {HTMLElement} config.target - The target element to render the map into.
19
+ * @param {HTMLElement} [config.target] - The target element to render the map into.
20
20
  */
21
21
  constructor(config) {
22
22
  if (config.debug) {
@@ -28,27 +28,31 @@ class Map {
28
28
  this.target = config.target;
29
29
  this.layers = [];
30
30
  this.dispatcher = new Dispatcher(this);
31
- this._viewport = document.createElement("div");
32
- this._viewport.className = "gv-map";
33
- this._viewport.style.position = "relative";
34
- this._viewport.style.overflow = "hidden";
35
- this._viewport.style.width = "100%";
36
- this._viewport.style.height = "100%";
37
- this.target.appendChild(this._viewport);
31
+ const createViewport = () => {
32
+ this._viewport = document.createElement("div");
33
+ this._viewport.className = "gv-map";
34
+ this._viewport.style.position = "relative";
35
+ this._viewport.style.overflow = "hidden";
36
+ this._viewport.style.width = "100%";
37
+ this._viewport.style.height = "100%";
38
+ this.target.appendChild(this._viewport);
39
+ this._viewport.addEventListener("touchmove", (event) => {
40
+ if (event.targetTouches.length < 2 && this._collaborativeGesturesEnabled) {
41
+ this._filterEventCallback(true);
42
+ }
43
+ });
44
+ this._resizeObserver = new ResizeObserver(() => {
45
+ this._updateSize();
46
+ });
47
+ this._resizeObserver.observe(this.target);
48
+ };
49
+ if (this.target) createViewport();
38
50
  this._renderer = new MapRenderer(this);
39
- this._resizeObserver = new ResizeObserver(() => {
40
- this._updateSize();
41
- });
42
- this._resizeObserver.observe(this.target);
43
- this._viewport.addEventListener("touchmove", (event) => {
44
- if (event.targetTouches.length < 2 && this._collaborativeGesturesEnabled) {
45
- this._filterEventCallback(true);
46
- }
47
- });
48
51
  }
49
52
  destroy() {
53
+ var _a;
50
54
  this._resizeObserver.disconnect();
51
- this._viewport.remove();
55
+ (_a = this._viewport) == null ? void 0 : _a.remove();
52
56
  }
53
57
  /** PUBLIC GETTERS */
54
58
  get size() {
@@ -223,7 +227,8 @@ class Map {
223
227
  this._zoomBypassKey = navigator.userAgent.indexOf("Mac") !== -1 ? "metaKey" : "ctrlKey";
224
228
  this._zoomBehaviour = zoom().extent([[0, 0], viewPortSize]).translateExtent([[0, 0], viewPortSize]).scaleExtent(this.view.scaleExtent).filter((event) => {
225
229
  const filterEvent = (filter) => {
226
- this._filterEventCallback(filter);
230
+ var _a;
231
+ (_a = this._filterEventCallback) == null ? void 0 : _a.call(this, filter);
227
232
  return !filter;
228
233
  };
229
234
  if (event.type === "wheel" && !event[this._zoomBypassKey]) {
@@ -250,21 +255,25 @@ class Map {
250
255
  select(this._viewport).call(this._zoomBehaviour);
251
256
  }
252
257
  _requestRender() {
253
- if (!this._renderer || !!this._animationFrameRequestID || this._isTransitioning)
258
+ if (!this._renderer || !!this._animationFrameRequestID || this._isTransitioning || typeof window === "undefined")
254
259
  return;
255
260
  this._animationFrameRequestID = requestAnimationFrame(
256
261
  this._renderFrame.bind(this)
257
262
  );
258
263
  }
259
- _renderFrame() {
264
+ _renderFrame(_, canvas) {
260
265
  const frameState = {
261
266
  size: this.size,
262
267
  viewState: this.view.getState(),
263
268
  debug: this.options.debug || false
264
269
  };
265
- this._renderer.renderFrame(frameState);
270
+ this._renderer.renderFrame(frameState, canvas);
266
271
  this._animationFrameRequestID = null;
267
272
  }
273
+ renderToCanvas(canvas) {
274
+ this.view.viewPortSize = [canvas.width, canvas.height];
275
+ this._renderFrame(null, canvas);
276
+ }
268
277
  }
269
278
  Map.Component = void 0;
270
279
  export {
@@ -29,7 +29,7 @@ class View {
29
29
  this._transform = zoomIdentity;
30
30
  this._padding = padding;
31
31
  this._viewPortSize = [0, 0];
32
- this.pixelRatio = window.devicePixelRatio;
32
+ this.pixelRatio = typeof window !== "undefined" ? window.devicePixelRatio : 1;
33
33
  }
34
34
  set viewPortSize(size) {
35
35
  const previousSize = this._viewPortSize;
@@ -73,7 +73,7 @@ export class VectorLayer {
73
73
  */
74
74
  getExtent(): import('../util').Extent | null;
75
75
  findFeatures(coordinate: any): any;
76
- renderFrame(frameState: any, targetElement: any): any;
76
+ renderFrame(frameState: any, canvas: any): HTMLCanvasElement;
77
77
  }
78
78
  export namespace VectorLayer {
79
79
  /** @param {VectorLayerComponentProps} props */
@@ -135,8 +135,8 @@ class VectorLayer {
135
135
  if (!this.hitDetectionEnabled) return;
136
136
  return this.source.getFeaturesAtCoordinate(coordinate);
137
137
  }
138
- renderFrame(frameState, targetElement) {
139
- return this.renderer.renderFrame(frameState, targetElement);
138
+ renderFrame(frameState, canvas) {
139
+ return this.renderer.renderFrame(frameState, canvas);
140
140
  }
141
141
  }
142
142
  VectorLayer.Component.displayName = "VectorLayer";
@@ -10,7 +10,7 @@ export class MapRenderer {
10
10
  constructor(map: any);
11
11
  map: any;
12
12
  _element: HTMLDivElement;
13
- renderFrame(frameState: any): void;
13
+ renderFrame(frameState: any, canvas: any): void;
14
14
  }
15
15
  export type CanvasSingleton = {
16
16
  getContext2d: () => CanvasRenderingContext2D;
@@ -3,35 +3,37 @@ import RBush from "rbush";
3
3
  class MapRenderer {
4
4
  constructor(map) {
5
5
  this.map = map;
6
- this._element = document.createElement("div");
7
- this._element.className = "gv-layer-container";
8
- const style = this._element.style;
9
- style.position = "absolute";
10
- style.width = "100%";
11
- style.height = "100%";
12
- style.zIndex = "0";
13
6
  const container = map.viewPort;
14
- container.insertBefore(this._element, container.firstChild || null);
7
+ if (container) {
8
+ this._element = document.createElement("div");
9
+ this._element.className = "gv-layer-container";
10
+ const style = this._element.style;
11
+ style.position = "absolute";
12
+ style.width = "100%";
13
+ style.height = "100%";
14
+ style.zIndex = "0";
15
+ container.insertBefore(this._element, container.firstChild || null);
16
+ }
15
17
  }
16
- renderFrame(frameState) {
18
+ renderFrame(frameState, canvas) {
17
19
  const { zoomLevel, projection, sizeInPixels } = frameState.viewState;
18
20
  const layers = this.map.layers;
19
21
  const mapElements = [];
22
+ const canvasElement = canvas || createCanvas(sizeInPixels);
20
23
  const visibleLayers = layers.filter((layer) => {
21
24
  return zoomLevel > (layer.minZoom || 0);
22
25
  });
23
- const canvasSingleton = makeCanvasSingleton(sizeInPixels);
24
26
  const renderLayer = (layer, declutterTree2) => {
25
27
  const viewState = frameState.viewState;
26
28
  if (layer.projection) {
27
29
  viewState.projection = layer.projection;
28
30
  }
29
- const newContainer = layer.renderFrame(
31
+ const element = layer.renderFrame(
30
32
  { ...frameState, viewState, declutterTree: declutterTree2 },
31
- canvasSingleton
33
+ canvasElement
32
34
  );
33
- if (newContainer) {
34
- mapElements.push(newContainer);
35
+ if (element) {
36
+ mapElements.push(element);
35
37
  }
36
38
  viewState.projection = projection;
37
39
  };
@@ -43,53 +45,20 @@ class MapRenderer {
43
45
  renderLayer(layer);
44
46
  }
45
47
  }
46
- if (canvasSingleton.isInitialised()) {
47
- replaceChildren(this._element, [
48
- canvasSingleton.getContainer(),
49
- ...mapElements
50
- ]);
51
- } else {
52
- replaceChildren(this._element, mapElements);
48
+ if (this._element) {
49
+ replaceChildren(this._element, [canvasElement, ...mapElements]);
53
50
  }
54
51
  }
55
52
  }
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%";
53
+ function createCanvas(sizeInPixels) {
84
54
  const canvas = document.createElement("canvas");
85
- style = canvas.style;
86
- style.position = "absolute";
87
- style.width = "100%";
88
- style.height = "100%";
55
+ canvas.className = "gv-map-layer";
56
+ canvas.style.position = "absolute";
57
+ canvas.style.width = "100%";
58
+ canvas.style.height = "100%";
89
59
  canvas.width = sizeInPixels[0];
90
60
  canvas.height = sizeInPixels[1];
91
- container.appendChild(canvas);
92
- return container;
61
+ return canvas;
93
62
  }
94
63
  export {
95
64
  MapRenderer
@@ -12,10 +12,16 @@ export class TextLayerRenderer {
12
12
  _element: HTMLDivElement;
13
13
  _mouseInteractionsEnabled: boolean | ((feature: import('../Feature').Feature, event: MouseEvent) => void);
14
14
  /**
15
- * @param {import("./MapRenderer").CanvasSingleton} canvasSingleton
15
+ * @param {HTMLCanvasElement} canvas
16
16
  */
17
- renderFrame(frameState: any, canvasSingleton: import('./MapRenderer').CanvasSingleton): HTMLDivElement;
18
- getTextElementWithID(id: any): Element;
17
+ renderFrame(frameState: any, canvas: HTMLCanvasElement): HTMLDivElement;
18
+ /**
19
+ * Function that gets or creates a text element.
20
+ *
21
+ * @param {String} id
22
+ * @returns {HTMLDivElement}
23
+ */
24
+ getTextElementWithID(id: string): HTMLDivElement;
19
25
  /**
20
26
  * @param {HTMLDivElement} element
21
27
  * @param {import("../styles/Text").Text} textStyle
@@ -20,9 +20,9 @@ class TextLayerRenderer {
20
20
  this.attachClickAndHoverListeners();
21
21
  }
22
22
  /**
23
- * @param {import("./MapRenderer").CanvasSingleton} canvasSingleton
23
+ * @param {HTMLCanvasElement} canvas
24
24
  */
25
- renderFrame(frameState, canvasSingleton) {
25
+ renderFrame(frameState, canvas) {
26
26
  var _a, _b;
27
27
  if (this.layer.opacity === 0) return null;
28
28
  const { declutterTree } = frameState;
@@ -85,7 +85,7 @@ class TextLayerRenderer {
85
85
  const callout = (_a = featureStyle == null ? void 0 : featureStyle.text) == null ? void 0 : _a.callout;
86
86
  const icon = (_b = featureStyle == null ? void 0 : featureStyle.text) == null ? void 0 : _b.icon;
87
87
  if (callout || icon) {
88
- canvasCtx ?? (canvasCtx = canvasSingleton.getContext2d());
88
+ canvasCtx ?? (canvasCtx = canvas.getContext("2d"));
89
89
  }
90
90
  if (callout) {
91
91
  const [originalX, originalY] = transform.apply(point.coordinates);
@@ -128,6 +128,12 @@ class TextLayerRenderer {
128
128
  replaceChildren(this._element, textElements);
129
129
  return this._element;
130
130
  }
131
+ /**
132
+ * Function that gets or creates a text element.
133
+ *
134
+ * @param {String} id
135
+ * @returns {HTMLDivElement}
136
+ */
131
137
  getTextElementWithID(id) {
132
138
  const elementId = `text-feature-${id}`;
133
139
  let textElement = this._element.querySelector(`#${elementId}`);
@@ -11,7 +11,7 @@ export class VectorLayerRenderer {
11
11
  layer: import('../layers').VectorLayer;
12
12
  featureRenderer: FeatureRenderer;
13
13
  /**
14
- * @param {import("./MapRenderer").CanvasSingleton} canvasSingleton
14
+ * @param {HTMLCanvasElement} canvas
15
15
  */
16
- renderFrame(frameState: any, canvasSingleton: import('./MapRenderer').CanvasSingleton): any;
16
+ renderFrame(frameState: any, canvas: HTMLCanvasElement): HTMLCanvasElement;
17
17
  }
@@ -9,12 +9,12 @@ class VectorLayerRenderer {
9
9
  this.featureRenderer = new FeatureRenderer();
10
10
  }
11
11
  /**
12
- * @param {import("./MapRenderer").CanvasSingleton} canvasSingleton
12
+ * @param {HTMLCanvasElement} canvas
13
13
  */
14
- renderFrame(frameState, canvasSingleton) {
15
- if (this.layer.opacity === 0) return null;
14
+ renderFrame(frameState, canvas) {
15
+ if (this.layer.opacity === 0) return canvas;
16
16
  const { projection, visibleExtent, transform } = frameState.viewState;
17
- const context = canvasSingleton.getContext2d();
17
+ const context = canvas.getContext("2d");
18
18
  context.save();
19
19
  context.translate(transform.x, transform.y);
20
20
  context.scale(transform.k, transform.k);
@@ -1,10 +1,16 @@
1
1
  /** @typedef { ("vertical" | "horizontal") } OptionLayoutDirection */
2
+ /**
3
+ * @typedef { Object } Option
4
+ * @prop { String } title
5
+ * @prop { String } [description]
6
+ * @prop { String | import("preact").JSX.Element } icon
7
+ */
2
8
  /**
3
9
  * OptionPicker component
4
10
  *
5
11
  * @param {Object} props
6
12
  * @param {string} props.title
7
- * @param {Array<{ title: string, description: string, icon: string | JSX.Element }>} props.options
13
+ * @param {Option[]} props.options
8
14
  * @param {OptionLayoutDirection} [props.layoutDirection="horizontal"]
9
15
  * @param {number} [props.initialSelectedIndex=0]
10
16
  * @param {(index: number, option: Object) => void} [props.onSelect]
@@ -13,14 +19,15 @@
13
19
  */
14
20
  export function OptionPicker({ title, options, layoutDirection, initialSelectedIndex, onSelect, styles, }: {
15
21
  title: string;
16
- options: Array<{
17
- title: string;
18
- description: string;
19
- icon: string | JSX.Element;
20
- }>;
22
+ options: Option[];
21
23
  layoutDirection?: OptionLayoutDirection;
22
24
  initialSelectedIndex?: number;
23
25
  onSelect?: (index: number, option: any) => void;
24
26
  styles?: any;
25
27
  }): import("preact").JSX.Element;
26
28
  export type OptionLayoutDirection = ("vertical" | "horizontal");
29
+ export type Option = {
30
+ title: string;
31
+ description?: string;
32
+ icon: string | import("preact").JSX.Element;
33
+ };
package/dist/style.css CHANGED
@@ -1359,14 +1359,14 @@ body {
1359
1359
  --new-group-01: #333333;
1360
1360
  --dem: #093ca3;
1361
1361
  --gop: #c70000;
1362
- --oth: #707070;
1362
+ --oth: #4d4d4d;
1363
1363
  --ind-dem: #7378d8;
1364
1364
  --lib: #c6b716;
1365
1365
  --grn: #0da498;
1366
1366
  --ken: #ef6f07;
1367
1367
  --dem-2: #dad7f5;
1368
1368
  --gop-2: #ffdbd4;
1369
- --oth-2: #e7e7e7;
1369
+ --oth-2: #cccccc;
1370
1370
  --ind-dem-2: #dad7f5;
1371
1371
  --lib-2: #f4eac2;
1372
1372
  --grn-2: #cfedea;
@@ -1471,14 +1471,14 @@ body {
1471
1471
  --new-group-01: #cccccc;
1472
1472
  --dem: #3261db;
1473
1473
  --gop: #c70000;
1474
- --oth: #707070;
1474
+ --oth: #cccccc;
1475
1475
  --ind-dem: #7389d8;
1476
1476
  --lib: #c6b716;
1477
1477
  --grn: #23b4a9;
1478
1478
  --ken: #ef6f07;
1479
1479
  --dem-2: #20366d;
1480
1480
  --gop-2: #5a0b0b;
1481
- --oth-2: #333333;
1481
+ --oth-2: #4d4d4d;
1482
1482
  --ind-dem-2: #394261;
1483
1483
  --lib-2: #524d13;
1484
1484
  --grn-2: #19524e;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@guardian/interactive-component-library",
3
3
  "private": false,
4
- "version": "0.7.1",
4
+ "version": "0.7.3",
5
5
  "packageManager": "pnpm@8.4.0",
6
6
  "repository": {
7
7
  "type": "git",