@guardian/interactive-component-library 0.4.0 → 0.4.2

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.
@@ -1,3 +1,5 @@
1
+ import { Layer } from '../lib/layers';
2
+ import { ReactNode } from 'preact/compat';
1
3
  /**
2
4
  * @param {Object} params
3
5
  * @param {import('../lib/Map').Map} params.map
@@ -8,10 +10,18 @@ export function MapProvider({ map, children }: {
8
10
  children: import('preact').ComponentChildren;
9
11
  }): import("preact").JSX.Element;
10
12
  export type MapContext = {
11
- registerLayer: ((layer: import('../lib/layers').Layer) => void) | null;
13
+ registerLayer: ((layer: Layer, comp: ReactNode) => void) | null;
14
+ unregisterLayer: ((layer: Layer) => void) | null;
12
15
  };
13
16
  /**
14
- * @typedef {{ registerLayer: ((layer: import("../lib/layers").Layer) => void) | null }} MapContext
17
+ * @import { Layer } from "../lib/layers"
18
+ * @import { ReactNode } from "preact/compat"
19
+ */
20
+ /**
21
+ * @typedef {{
22
+ * registerLayer: ((layer: Layer, comp: ReactNode) => void) | null,
23
+ * unregisterLayer: ((layer: Layer) => void) | null
24
+ * }} MapContext
15
25
  */
16
26
  /**
17
27
  * @type {import('preact').Context<MapContext | null>}
@@ -1,18 +1,52 @@
1
1
  import { jsx } from "preact/jsx-runtime";
2
2
  import { createContext } from "preact";
3
- import { useEffect } from "preact/hooks";
3
+ import { useState, useEffect } from "preact/hooks";
4
4
  const MapContext = createContext(null);
5
5
  function MapProvider({ map, children }) {
6
- const registeredLayers = [];
7
- const registerLayer = (layer) => {
8
- registeredLayers.push(layer);
6
+ const [layers, setLayers] = useState([]);
7
+ const registerLayer = (layer, comp) => {
8
+ if (!layers.includes(layer)) {
9
+ const position = getCompTreePosition(comp, children);
10
+ if (position === null) return;
11
+ setLayers((prevLayers) => {
12
+ const newLayers = [...prevLayers];
13
+ newLayers.splice(position, 0, layer);
14
+ return newLayers;
15
+ });
16
+ }
17
+ };
18
+ const unregisterLayer = (layerToRemove) => {
19
+ setLayers(
20
+ (prevLayers) => prevLayers.filter((layer) => layer !== layerToRemove)
21
+ );
9
22
  };
10
23
  useEffect(() => {
11
- if (map && !map.hasLayers(registeredLayers)) {
12
- map.setLayers(registeredLayers);
24
+ if (!map) return;
25
+ map.setLayers(layers);
26
+ }, [map, layers]);
27
+ return /* @__PURE__ */ jsx(MapContext.Provider, { value: { registerLayer, unregisterLayer }, children });
28
+ }
29
+ function getCompTreePosition(targetComponent, children) {
30
+ let index = 0;
31
+ function traverse(nodes) {
32
+ var _a, _b;
33
+ for (const node of nodes) {
34
+ const childNodes = (_b = (_a = node == null ? void 0 : node.__c) == null ? void 0 : _a.__v) == null ? void 0 : _b.__k;
35
+ if (childNodes && childNodes.length > 1 && childNodes[0] !== null) {
36
+ const result = traverse(childNodes);
37
+ if (result !== null) {
38
+ return result;
39
+ }
40
+ } else {
41
+ if ((node == null ? void 0 : node.__c) === targetComponent) {
42
+ return index;
43
+ }
44
+ index++;
45
+ }
13
46
  }
14
- }, [map, children]);
15
- return /* @__PURE__ */ jsx(MapContext.Provider, { value: { registerLayer }, children });
47
+ return null;
48
+ }
49
+ return traverse(Array.isArray(children) ? children : [children]);
16
50
  }
17
51
  export {
18
52
  MapContext,
@@ -6,7 +6,7 @@ import { Dispatcher } from '../events/Dispatcher';
6
6
  /** @typedef {TextLayerOptions & { features: import("../Feature").Feature[] | import("../FeatureCollection").FeatureCollection }} TextLayerComponentProps */
7
7
  export class TextLayer {
8
8
  /** @param {TextLayerComponentProps} props */
9
- static Component({ features: featureCollection, style, minZoom, opacity, declutter, drawCollisionBoxes, }: TextLayerComponentProps): any;
9
+ static Component({ features: featureCollection, style, minZoom, opacity, declutter, drawCollisionBoxes, }: TextLayerComponentProps): boolean;
10
10
  /**
11
11
  * @param {import("../Feature").Feature[]} features
12
12
  * @param {TextLayerOptions} options
@@ -18,7 +18,7 @@ class TextLayer {
18
18
  declutter,
19
19
  drawCollisionBoxes
20
20
  }) {
21
- const { registerLayer } = useContext(MapContext);
21
+ const { registerLayer, unregisterLayer } = useContext(MapContext);
22
22
  const layer = useMemo(
23
23
  () => {
24
24
  const features = featureCollection instanceof FeatureCollection ? featureCollection.features : (
@@ -36,11 +36,16 @@ class TextLayer {
36
36
  // eslint-disable-next-line react-hooks/exhaustive-deps
37
37
  [featureCollection, minZoom, opacity, declutter, drawCollisionBoxes]
38
38
  );
39
- registerLayer(layer);
39
+ useEffect(() => {
40
+ registerLayer(layer, this);
41
+ return () => {
42
+ unregisterLayer(layer);
43
+ };
44
+ }, [layer]);
40
45
  useEffect(() => {
41
46
  layer.style = style;
42
47
  }, [style]);
43
- return null;
48
+ return false;
44
49
  }
45
50
  /**
46
51
  * @param {import("../Feature").Feature[]} features
@@ -6,7 +6,7 @@ import { VectorSource } from '../sources/VectorSource';
6
6
  /** @typedef {VectorLayerOptions & { features: import("../Feature").Feature[] | import("../FeatureCollection").FeatureCollection }} VectorLayerComponentProps */
7
7
  export class VectorLayer {
8
8
  /** @param {VectorLayerComponentProps} props */
9
- static Component({ features: featureCollection, style, minZoom, opacity, hitDetectionEnabled, }: VectorLayerComponentProps): any;
9
+ static Component({ features: featureCollection, style, minZoom, opacity, hitDetectionEnabled, }: VectorLayerComponentProps): boolean;
10
10
  /**
11
11
  * @param {import("../Feature").Feature[]} features
12
12
  * @param {VectorLayerOptions} options
@@ -17,7 +17,7 @@ class VectorLayer {
17
17
  opacity,
18
18
  hitDetectionEnabled = true
19
19
  }) {
20
- const { registerLayer } = useContext(MapContext);
20
+ const { registerLayer, unregisterLayer } = useContext(MapContext);
21
21
  const layer = useMemo(
22
22
  () => {
23
23
  const features = featureCollection instanceof FeatureCollection ? featureCollection.features : (
@@ -34,11 +34,16 @@ class VectorLayer {
34
34
  // eslint-disable-next-line react-hooks/exhaustive-deps
35
35
  [featureCollection, minZoom, opacity, hitDetectionEnabled]
36
36
  );
37
- registerLayer(layer);
37
+ useEffect(() => {
38
+ registerLayer(layer, this);
39
+ return () => {
40
+ unregisterLayer(layer);
41
+ };
42
+ }, [layer]);
38
43
  useEffect(() => {
39
44
  layer.style = style;
40
45
  }, [style]);
41
- return null;
46
+ return false;
42
47
  }
43
48
  /**
44
49
  * @param {import("../Feature").Feature[]} features
@@ -1,6 +1,14 @@
1
- export function CircleIcon({ color, pulse, diameter, styles }: {
1
+ export function CircleIcon({ color, pulse, diameter, styles, splitColor, }: {
2
2
  color: any;
3
3
  pulse?: boolean;
4
4
  diameter?: number;
5
5
  styles: any;
6
+ splitColor: any;
6
7
  }): import("preact").JSX.Element;
8
+ export type CircleIconProps = {
9
+ color: string;
10
+ pulse?: boolean;
11
+ diameter?: number;
12
+ splitColor?: string;
13
+ styles?: any;
14
+ };
@@ -1,11 +1,17 @@
1
- import { jsx } from "preact/jsx-runtime";
1
+ import { jsxs, jsx } from "preact/jsx-runtime";
2
2
  import defaultStyles from "./style.module.css.js";
3
3
  import { mergeStyles } from "../../../styles/helpers/mergeStyles.js";
4
- const CircleIcon = ({ color, pulse = false, diameter = 11, styles }) => {
4
+ const CircleIcon = ({
5
+ color,
6
+ pulse = false,
7
+ diameter = 11,
8
+ styles,
9
+ splitColor
10
+ }) => {
5
11
  styles = mergeStyles(defaultStyles, styles);
6
12
  let radius = diameter / 2;
7
13
  let padding = 2;
8
- return /* @__PURE__ */ jsx(
14
+ return /* @__PURE__ */ jsxs(
9
15
  "svg",
10
16
  {
11
17
  style: styles.svg,
@@ -14,16 +20,26 @@ const CircleIcon = ({ color, pulse = false, diameter = 11, styles }) => {
14
20
  viewBox: `0 0 ${diameter + padding} ${diameter + padding}`,
15
21
  width: diameter + padding,
16
22
  xmlns: "http://www.w3.org/2000/svg",
17
- children: /* @__PURE__ */ jsx(
18
- "circle",
19
- {
20
- className: [styles.circle, pulse && styles.pulse].join(" "),
21
- style: { fill: color },
22
- r: radius,
23
- cx: radius + padding / 2,
24
- cy: radius + padding / 2
25
- }
26
- )
23
+ children: [
24
+ /* @__PURE__ */ jsx(
25
+ "circle",
26
+ {
27
+ className: [styles.circle, pulse && styles.pulse].join(" "),
28
+ style: { fill: color },
29
+ r: radius,
30
+ cx: radius + padding / 2,
31
+ cy: radius + padding / 2
32
+ }
33
+ ),
34
+ splitColor && /* @__PURE__ */ jsx(
35
+ "path",
36
+ {
37
+ d: `M ${radius + padding / 2} ${padding / 2} A ${radius} ${radius} 0 0 1 ${radius + padding / 2} ${diameter + padding / 2} L ${radius + padding / 2} ${radius + padding / 2} Z`,
38
+ fill: splitColor,
39
+ transform: `rotate(45 ${radius + padding / 2} ${radius + padding / 2})`
40
+ }
41
+ )
42
+ ]
27
43
  }
28
44
  );
29
45
  };
@@ -8,7 +8,11 @@ function useContainerSize(containerRef) {
8
8
  for (let entry of entries) {
9
9
  setContainerSize({
10
10
  width: entry.contentRect.width,
11
- height: entry.contentRect.height
11
+ height: entry.contentRect.height,
12
+ left: entry.contentRect.left,
13
+ right: entry.contentRect.right,
14
+ bottom: entry.contentRect.bottom,
15
+ top: entry.contentRect.top
12
16
  });
13
17
  }
14
18
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@guardian/interactive-component-library",
3
3
  "private": false,
4
- "version": "0.4.0",
4
+ "version": "0.4.2",
5
5
  "packageManager": "pnpm@8.4.0",
6
6
  "repository": {
7
7
  "type": "git",