@hisptz/dhis2-analytics 2.1.36 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/components/Map/DHIS2Map.js +3 -1
  2. package/dist/components/Map/DHIS2Map.js.map +1 -1
  3. package/dist/components/Map/components/MapControls/components/TimelineControl/index.js +236 -0
  4. package/dist/components/Map/components/MapControls/components/TimelineControl/index.js.map +1 -0
  5. package/dist/components/Map/components/MapControls/index.js +17 -12
  6. package/dist/components/Map/components/MapControls/index.js.map +1 -1
  7. package/dist/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.js +62 -55
  8. package/dist/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.js.map +1 -1
  9. package/dist/components/Map/components/MapProvider/components/MapLayerProvider/index.js +14 -2
  10. package/dist/components/Map/components/MapProvider/components/MapLayerProvider/index.js.map +1 -1
  11. package/dist/components/Map/components/MapProvider/components/MapPeriodFilterProvider/index.js +19 -0
  12. package/dist/components/Map/components/MapProvider/components/MapPeriodFilterProvider/index.js.map +1 -0
  13. package/dist/components/Map/components/MapProvider/hooks/index.js +4 -0
  14. package/dist/components/Map/components/MapProvider/hooks/index.js.map +1 -1
  15. package/dist/components/Map/components/MapProvider/index.js +22 -5
  16. package/dist/components/Map/components/MapProvider/index.js.map +1 -1
  17. package/dist/components/Map/index.js +21 -0
  18. package/dist/components/Map/state/index.js +9 -0
  19. package/dist/components/Map/state/index.js.map +1 -1
  20. package/dist/components/Map/utils/helpers.js +74 -0
  21. package/dist/components/Map/utils/helpers.js.map +1 -1
  22. package/dist/esm/components/Map/DHIS2Map.js +3 -1
  23. package/dist/esm/components/Map/DHIS2Map.js.map +1 -1
  24. package/dist/esm/components/Map/components/MapControls/components/TimelineControl/index.js +234 -0
  25. package/dist/esm/components/Map/components/MapControls/components/TimelineControl/index.js.map +1 -0
  26. package/dist/esm/components/Map/components/MapControls/index.js +17 -12
  27. package/dist/esm/components/Map/components/MapControls/index.js.map +1 -1
  28. package/dist/esm/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.js +66 -59
  29. package/dist/esm/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.js.map +1 -1
  30. package/dist/esm/components/Map/components/MapProvider/components/MapLayerProvider/index.js +15 -3
  31. package/dist/esm/components/Map/components/MapProvider/components/MapLayerProvider/index.js.map +1 -1
  32. package/dist/esm/components/Map/components/MapProvider/components/MapPeriodFilterProvider/index.js +17 -0
  33. package/dist/esm/components/Map/components/MapProvider/components/MapPeriodFilterProvider/index.js.map +1 -0
  34. package/dist/esm/components/Map/components/MapProvider/hooks/index.js +5 -2
  35. package/dist/esm/components/Map/components/MapProvider/hooks/index.js.map +1 -1
  36. package/dist/esm/components/Map/components/MapProvider/index.js +22 -5
  37. package/dist/esm/components/Map/components/MapProvider/index.js.map +1 -1
  38. package/dist/esm/components/Map/index.js +3 -0
  39. package/dist/esm/components/Map/state/index.js +9 -1
  40. package/dist/esm/components/Map/state/index.js.map +1 -1
  41. package/dist/esm/components/Map/utils/helpers.js +72 -1
  42. package/dist/esm/components/Map/utils/helpers.js.map +1 -1
  43. package/dist/types/components/Map/DHIS2Map.d.ts.map +1 -1
  44. package/dist/types/components/Map/components/MapArea/interfaces/index.d.ts +5 -2
  45. package/dist/types/components/Map/components/MapArea/interfaces/index.d.ts.map +1 -1
  46. package/dist/types/components/Map/components/MapControls/components/TimelineControl/index.d.ts +26 -0
  47. package/dist/types/components/Map/components/MapControls/components/TimelineControl/index.d.ts.map +1 -0
  48. package/dist/types/components/Map/components/MapControls/index.d.ts +3 -4
  49. package/dist/types/components/Map/components/MapControls/index.d.ts.map +1 -1
  50. package/dist/types/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.d.ts.map +1 -1
  51. package/dist/types/components/Map/components/MapProvider/components/MapLayerProvider/index.d.ts.map +1 -1
  52. package/dist/types/components/Map/components/MapProvider/components/MapPeriodFilterProvider/index.d.ts +8 -0
  53. package/dist/types/components/Map/components/MapProvider/components/MapPeriodFilterProvider/index.d.ts.map +1 -0
  54. package/dist/types/components/Map/components/MapProvider/hooks/index.d.ts +1 -0
  55. package/dist/types/components/Map/components/MapProvider/hooks/index.d.ts.map +1 -1
  56. package/dist/types/components/Map/components/MapProvider/index.d.ts +2 -2
  57. package/dist/types/components/Map/components/MapProvider/index.d.ts.map +1 -1
  58. package/dist/types/components/Map/index.d.ts +3 -0
  59. package/dist/types/components/Map/index.d.ts.map +1 -1
  60. package/dist/types/components/Map/interfaces/index.d.ts +2 -0
  61. package/dist/types/components/Map/interfaces/index.d.ts.map +1 -1
  62. package/dist/types/components/Map/state/index.d.ts +8 -0
  63. package/dist/types/components/Map/state/index.d.ts.map +1 -1
  64. package/dist/types/components/Map/utils/helpers.d.ts +7 -0
  65. package/dist/types/components/Map/utils/helpers.d.ts.map +1 -1
  66. package/package.json +10 -6
@@ -23,6 +23,7 @@ const MapComponent = ({
23
23
  legends,
24
24
  setRef,
25
25
  analyticsOptions,
26
+ renderingStrategy,
26
27
  base
27
28
  }) => {
28
29
  const sanitizedPointLayers = [
@@ -44,8 +45,9 @@ const MapComponent = ({
44
45
  return /* @__PURE__ */ jsxRuntime.jsx(
45
46
  MapProvider.MapProvider,
46
47
  {
47
- periodSelection,
48
48
  orgUnitSelection,
49
+ periodSelection,
50
+ renderingStrategy,
49
51
  children: /* @__PURE__ */ jsxRuntime.jsx(
50
52
  MapArea__default.default,
51
53
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Map/DHIS2Map.tsx"],"names":["jsx","MapProvider","MapArea"],"mappings":";;;;;;;;;;;AAUA,MAAM,eAAe,CAAC;AAAA,EACrB,gBAAA;AAAA,EACA,UAAA;AAAA,EACA,aAAA;AAAA,EACA,cAAA;AAAA,EACA,iBAAA;AAAA,EACA,eAAA;AAAA,EACA,UAAA;AAAA,EACA,GAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,gBAAA;AAAA,EACA;AACD,CAAA,KAAgB;AACf,EAAA,MAAM,oBAAA,GAA2C;AAAA,IAChD;AAAA,MACC,IAAA,EAAM,OAAA;AAAA,MACN,EAAA,EAAI,OAAA;AAAA,MACJ,OAAA,EAAS,YAAY,OAAA,IAAW,KAAA;AAAA,MAChC,GAAG;AAAA;AACJ,GACD;AACA,EAAA,MAAM,uBAAA,GAAiD;AAAA,IACtD;AAAA,MACC,GAAG,aAAA;AAAA,MACH,IAAA,EAAM,SAAA;AAAA,MACN,EAAA,EAAI,UAAA;AAAA,MACJ,OAAA,EAAS,eAAe,OAAA,IAAW;AAAA;AACpC,GACD;AAEA,EAAA,uBACCA,cAAA;AAAA,IAACC,uBAAA;AAAA,IAAA;AAAA,MACA,eAAA;AAAA,MACA,gBAAA;AAAA,MAEA,QAAA,kBAAAD,cAAA;AAAA,QAACE,wBAAA;AAAA,QAAA;AAAA,UACA,IAAA,EAAM;AAAA,YACL,GAAG;AAAA,WACJ;AAAA,UACA,MAAA,EAAQ;AAAA,YACP,cAAA;AAAA,YACA,iBAAA;AAAA,YACA,cAAA,EAAgB,uBAAA;AAAA,YAChB,WAAA,EAAa;AAAA,WACd;AAAA,UACA,eAAA;AAAA,UACA,gBAAA;AAAA,UACA,OAAA;AAAA,UACA,QAAA;AAAA,UAEA,GAAA,EAAK,MAAA;AAAA,UACL;AAAA,SAAA;AAAA,QAFK;AAAA;AAGN;AAAA,GACD;AAEF,CAAA;AACO,MAAM,QAAA,GAAyB;AAK/B,MAAM,GAAA,GAAM","file":"DHIS2Map.js","sourcesContent":["import MapArea from \"./components/MapArea/index.js\";\nimport {\n\tCustomBoundaryLayer,\n\tCustomPointLayer,\n} from \"./components/MapLayer/interfaces\";\nimport { MapProvider } from \"./components/MapProvider\";\nimport { MapProps } from \"./interfaces\";\nimport \"leaflet/dist/leaflet.css\";\nimport type { FC } from \"react\";\n\nconst MapComponent = ({\n\torgUnitSelection,\n\tpointLayer,\n\tboundaryLayer,\n\tthematicLayers,\n\tearthEngineLayers,\n\tperiodSelection,\n\tmapOptions,\n\tkey,\n\tcontrols,\n\tshowPeriodTitle,\n\tlegends,\n\tsetRef,\n\tanalyticsOptions,\n\tbase,\n}: MapProps) => {\n\tconst sanitizedPointLayers: CustomPointLayer[] = [\n\t\t{\n\t\t\ttype: \"point\",\n\t\t\tid: \"point\",\n\t\t\tenabled: pointLayer?.enabled ?? false,\n\t\t\t...pointLayer,\n\t\t},\n\t];\n\tconst sanitizedBoundaryLayers: CustomBoundaryLayer[] = [\n\t\t{\n\t\t\t...boundaryLayer,\n\t\t\ttype: \"overlay\",\n\t\t\tid: \"boundary\",\n\t\t\tenabled: boundaryLayer?.enabled ?? false,\n\t\t},\n\t];\n\n\treturn (\n\t\t<MapProvider\n\t\t\tperiodSelection={periodSelection}\n\t\t\torgUnitSelection={orgUnitSelection}\n\t\t>\n\t\t\t<MapArea\n\t\t\t\tbase={{\n\t\t\t\t\t...base,\n\t\t\t\t}}\n\t\t\t\tlayers={{\n\t\t\t\t\tthematicLayers,\n\t\t\t\t\tearthEngineLayers,\n\t\t\t\t\tboundaryLayers: sanitizedBoundaryLayers,\n\t\t\t\t\tpointLayers: sanitizedPointLayers,\n\t\t\t\t}}\n\t\t\t\tshowPeriodTitle={showPeriodTitle}\n\t\t\t\tanalyticsOptions={analyticsOptions}\n\t\t\t\tlegends={legends}\n\t\t\t\tcontrols={controls}\n\t\t\t\tkey={key}\n\t\t\t\tref={setRef}\n\t\t\t\tmapOptions={mapOptions}\n\t\t\t/>\n\t\t</MapProvider>\n\t);\n};\nexport const DHIS2Map: FC<MapProps> = MapComponent;\n\n/**\n * @deprecated since `v2`. Use `DHIS2Map` instead\n * */\nexport const Map = DHIS2Map;\n"]}
1
+ {"version":3,"sources":["../../../src/components/Map/DHIS2Map.tsx"],"names":["jsx","MapProvider","MapArea"],"mappings":";;;;;;;;;;;AAUA,MAAM,eAAe,CAAC;AAAA,EACrB,gBAAA;AAAA,EACA,UAAA;AAAA,EACA,aAAA;AAAA,EACA,cAAA;AAAA,EACA,iBAAA;AAAA,EACA,eAAA;AAAA,EACA,UAAA;AAAA,EACA,GAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,gBAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACD,CAAA,KAAgB;AACf,EAAA,MAAM,oBAAA,GAA2C;AAAA,IAChD;AAAA,MACC,IAAA,EAAM,OAAA;AAAA,MACN,EAAA,EAAI,OAAA;AAAA,MACJ,OAAA,EAAS,YAAY,OAAA,IAAW,KAAA;AAAA,MAChC,GAAG;AAAA;AACJ,GACD;AACA,EAAA,MAAM,uBAAA,GAAiD;AAAA,IACtD;AAAA,MACC,GAAG,aAAA;AAAA,MACH,IAAA,EAAM,SAAA;AAAA,MACN,EAAA,EAAI,UAAA;AAAA,MACJ,OAAA,EAAS,eAAe,OAAA,IAAW;AAAA;AACpC,GACD;AAEA,EAAA,uBACCA,cAAA;AAAA,IAACC,uBAAA;AAAA,IAAA;AAAA,MACA,gBAAA;AAAA,MACA,eAAA;AAAA,MACA,iBAAA;AAAA,MAEA,QAAA,kBAAAD,cAAA;AAAA,QAACE,wBAAA;AAAA,QAAA;AAAA,UACA,IAAA,EAAM;AAAA,YACL,GAAG;AAAA,WACJ;AAAA,UACA,MAAA,EAAQ;AAAA,YACP,cAAA;AAAA,YACA,iBAAA;AAAA,YACA,cAAA,EAAgB,uBAAA;AAAA,YAChB,WAAA,EAAa;AAAA,WACd;AAAA,UACA,eAAA;AAAA,UACA,gBAAA;AAAA,UACA,OAAA;AAAA,UACA,QAAA;AAAA,UAEA,GAAA,EAAK,MAAA;AAAA,UACL;AAAA,SAAA;AAAA,QAFK;AAAA;AAGN;AAAA,GACD;AAEF,CAAA;AACO,MAAM,QAAA,GAAyB;AAK/B,MAAM,GAAA,GAAM","file":"DHIS2Map.js","sourcesContent":["import MapArea from \"./components/MapArea/index.js\";\nimport {\n\tCustomBoundaryLayer,\n\tCustomPointLayer,\n} from \"./components/MapLayer/interfaces\";\nimport { MapProvider } from \"./components/MapProvider\";\nimport { MapProps } from \"./interfaces\";\nimport \"leaflet/dist/leaflet.css\";\nimport type { FC } from \"react\";\n\nconst MapComponent = ({\n\torgUnitSelection,\n\tpointLayer,\n\tboundaryLayer,\n\tthematicLayers,\n\tearthEngineLayers,\n\tperiodSelection,\n\tmapOptions,\n\tkey,\n\tcontrols,\n\tshowPeriodTitle,\n\tlegends,\n\tsetRef,\n\tanalyticsOptions,\n\trenderingStrategy,\n\tbase,\n}: MapProps) => {\n\tconst sanitizedPointLayers: CustomPointLayer[] = [\n\t\t{\n\t\t\ttype: \"point\",\n\t\t\tid: \"point\",\n\t\t\tenabled: pointLayer?.enabled ?? false,\n\t\t\t...pointLayer,\n\t\t},\n\t];\n\tconst sanitizedBoundaryLayers: CustomBoundaryLayer[] = [\n\t\t{\n\t\t\t...boundaryLayer,\n\t\t\ttype: \"overlay\",\n\t\t\tid: \"boundary\",\n\t\t\tenabled: boundaryLayer?.enabled ?? false,\n\t\t},\n\t];\n\n\treturn (\n\t\t<MapProvider\n\t\t\torgUnitSelection={orgUnitSelection}\n\t\t\tperiodSelection={periodSelection}\n\t\t\trenderingStrategy={renderingStrategy}\n\t\t>\n\t\t\t<MapArea\n\t\t\t\tbase={{\n\t\t\t\t\t...base,\n\t\t\t\t}}\n\t\t\t\tlayers={{\n\t\t\t\t\tthematicLayers,\n\t\t\t\t\tearthEngineLayers,\n\t\t\t\t\tboundaryLayers: sanitizedBoundaryLayers,\n\t\t\t\t\tpointLayers: sanitizedPointLayers,\n\t\t\t\t}}\n\t\t\t\tshowPeriodTitle={showPeriodTitle}\n\t\t\t\tanalyticsOptions={analyticsOptions}\n\t\t\t\tlegends={legends}\n\t\t\t\tcontrols={controls}\n\t\t\t\tkey={key}\n\t\t\t\tref={setRef}\n\t\t\t\tmapOptions={mapOptions}\n\t\t\t/>\n\t\t</MapProvider>\n\t);\n};\nexport const DHIS2Map: FC<MapProps> = MapComponent;\n\n/**\n * @deprecated since `v2`. Use `DHIS2Map` instead\n * */\nexport const Map = DHIS2Map;\n"]}
@@ -0,0 +1,236 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var d3Axis = require('d3-axis');
5
+ var d3Scale = require('d3-scale');
6
+ var d3Selection = require('d3-selection');
7
+ var leaflet = require('leaflet');
8
+ var luxon = require('luxon');
9
+ var reactDom = require('react-dom');
10
+ var react = require('react');
11
+ var reactLeaflet = require('react-leaflet');
12
+ var index_js = require('../../../../state/index.js');
13
+ var helpers_js = require('../../../../utils/helpers.js');
14
+
15
+ function resolveDates(tl) {
16
+ if ("step" in tl) {
17
+ const [start, end] = tl.range;
18
+ const dates = [];
19
+ let cur = luxon.DateTime.fromJSDate(start);
20
+ const endDt = luxon.DateTime.fromJSDate(end);
21
+ while (cur <= endDt) {
22
+ dates.push(cur.toJSDate());
23
+ cur = cur.plus(tl.step);
24
+ }
25
+ return dates;
26
+ }
27
+ return [...tl.range];
28
+ }
29
+ const ACCENT = "#2c6693";
30
+ const PADDING_LEFT = 40;
31
+ const PADDING_RIGHT = 20;
32
+ const LABEL_WIDTH = 80;
33
+ const PERIOD_RECT_HEIGHT = 8;
34
+ const PERIOD_RECT_GAP = 1;
35
+ const AXIS_OFFSET = 4;
36
+ function TimelineUI({
37
+ autoplay: initialAutoplay,
38
+ button,
39
+ dates,
40
+ interval,
41
+ onStep
42
+ }) {
43
+ const [index, setIndex] = react.useState(0);
44
+ const [playing, setPlaying] = react.useState(initialAutoplay);
45
+ const [svgWidth, setSvgWidth] = react.useState(null);
46
+ const svgRef = react.useRef(null);
47
+ const axisRef = react.useRef(null);
48
+ const intervalRef = react.useRef(null);
49
+ react.useEffect(() => {
50
+ const el = svgRef.current;
51
+ if (!el) return;
52
+ const measure = () => {
53
+ setSvgWidth(el.getBoundingClientRect().width);
54
+ };
55
+ measure();
56
+ const ro = new ResizeObserver(measure);
57
+ ro.observe(el);
58
+ return () => {
59
+ ro.disconnect();
60
+ };
61
+ }, []);
62
+ const trackWidth = svgWidth !== null ? svgWidth - PADDING_LEFT - PADDING_RIGHT : 0;
63
+ const timeScale = react.useMemo(() => {
64
+ if (!trackWidth || dates.length < 2) return null;
65
+ const periodMs = dates[1].getTime() - dates[0].getTime();
66
+ const domainEnd = new Date(dates[dates.length - 1].getTime() + periodMs);
67
+ return d3Scale.scaleTime().domain([dates[0], domainEnd]).range([0, trackWidth]);
68
+ }, [dates, trackWidth]);
69
+ react.useEffect(() => {
70
+ if (!timeScale || !axisRef.current || !trackWidth) return;
71
+ const maxTicks = Math.round(trackWidth / LABEL_WIDTH);
72
+ const axis = d3Axis.axisBottom(timeScale).ticks(maxTicks);
73
+ d3Selection.select(axisRef.current).call(axis);
74
+ d3Selection.select(axisRef.current).select(".domain").attr("stroke", "#ccc");
75
+ d3Selection.select(axisRef.current).selectAll(".tick line").attr("stroke", "#ccc");
76
+ d3Selection.select(axisRef.current).selectAll(".tick text").attr("fill", "#666").attr("font-size", "10px").attr("font-family", "sans-serif");
77
+ }, [timeScale, trackWidth]);
78
+ const stepTo = react.useCallback(
79
+ (idx) => {
80
+ setIndex(idx);
81
+ onStep(dates[idx]);
82
+ },
83
+ [dates, onStep]
84
+ );
85
+ react.useEffect(() => {
86
+ if (intervalRef.current) clearInterval(intervalRef.current);
87
+ if (!playing) return;
88
+ intervalRef.current = setInterval(() => {
89
+ setIndex((prev) => {
90
+ const next = prev + 1 >= dates.length ? 0 : prev + 1;
91
+ onStep(dates[next]);
92
+ return next;
93
+ });
94
+ }, interval);
95
+ return () => {
96
+ if (intervalRef.current) clearInterval(intervalRef.current);
97
+ };
98
+ }, [playing, interval, dates, onStep]);
99
+ const periodRects = react.useMemo(() => {
100
+ if (!timeScale || dates.length < 2) return null;
101
+ const unitWidth = timeScale(dates[1]) - timeScale(dates[0]);
102
+ return dates.map((date, i) => {
103
+ const x = timeScale(date);
104
+ const spanWidth = i < dates.length - 1 ? timeScale(dates[i + 1]) - x : unitWidth;
105
+ const w = Math.max(spanWidth - PERIOD_RECT_GAP, 1);
106
+ const capturedIndex = i;
107
+ return /* @__PURE__ */ jsxRuntime.jsx(
108
+ "rect",
109
+ {
110
+ fill: capturedIndex === index ? ACCENT : "#b0c4d8",
111
+ height: PERIOD_RECT_HEIGHT,
112
+ onClick: () => {
113
+ setPlaying(false);
114
+ stepTo(capturedIndex);
115
+ },
116
+ rx: 1,
117
+ style: { cursor: "pointer" },
118
+ width: w,
119
+ x,
120
+ y: 0
121
+ },
122
+ date.toISOString()
123
+ );
124
+ });
125
+ }, [timeScale, dates, index, stepTo]);
126
+ const RECT_ROW_Y = 6;
127
+ const svgHeight = RECT_ROW_Y + PERIOD_RECT_HEIGHT + AXIS_OFFSET + 24;
128
+ const playLabel = playing ? button?.playingText ?? "Pause" : button?.pausedText ?? "Play";
129
+ return /* @__PURE__ */ jsxRuntime.jsxs(
130
+ "svg",
131
+ {
132
+ "aria-label": "Timeline",
133
+ height: svgHeight,
134
+ ref: svgRef,
135
+ style: {
136
+ background: "white",
137
+ boxShadow: "0 -1px 4px rgba(0,0,0,0.15)",
138
+ display: "block",
139
+ width: "100%"
140
+ },
141
+ children: [
142
+ /* @__PURE__ */ jsxRuntime.jsxs(
143
+ "g",
144
+ {
145
+ "aria-label": playLabel,
146
+ onClick: () => {
147
+ setPlaying((p) => !p);
148
+ },
149
+ role: "button",
150
+ style: { cursor: "pointer" },
151
+ transform: `translate(7, ${RECT_ROW_Y - 8})`,
152
+ children: [
153
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M0 0h24v24H0z", fillOpacity: 0 }),
154
+ playing ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z", fill: ACCENT }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z", fill: ACCENT })
155
+ ]
156
+ }
157
+ ),
158
+ timeScale !== null && /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${PADDING_LEFT}, ${RECT_ROW_Y})`, children: periodRects }),
159
+ timeScale !== null && /* @__PURE__ */ jsxRuntime.jsx(
160
+ "g",
161
+ {
162
+ ref: axisRef,
163
+ transform: `translate(${PADDING_LEFT}, ${RECT_ROW_Y + PERIOD_RECT_HEIGHT + AXIS_OFFSET})`
164
+ }
165
+ )
166
+ ]
167
+ }
168
+ );
169
+ }
170
+ function TimelineControl({
171
+ autoplay = false,
172
+ button,
173
+ interval = 1e3,
174
+ periodType,
175
+ position = "bottom",
176
+ timeline
177
+ }) {
178
+ const map = reactLeaflet.useMap();
179
+ const {
180
+ periodType: contextPeriodType,
181
+ setActivePeriod,
182
+ setPeriodType
183
+ } = react.useContext(index_js.MapPeriodFilterContext);
184
+ const [container, setContainer] = react.useState(null);
185
+ react.useEffect(() => {
186
+ if (periodType) setPeriodType(periodType);
187
+ }, [periodType, setPeriodType]);
188
+ const resolvedPeriodType = periodType ?? contextPeriodType ?? "Monthly";
189
+ const dates = react.useMemo(() => resolveDates(timeline), [timeline]);
190
+ react.useEffect(() => {
191
+ const mapEl = map.getContainer();
192
+ const div = leaflet.DomUtil.create("div", "", mapEl);
193
+ leaflet.DomEvent.disableClickPropagation(div);
194
+ leaflet.DomEvent.disableScrollPropagation(div);
195
+ const isTop = position.startsWith("top");
196
+ Object.assign(div.style, {
197
+ bottom: isTop ? "auto" : "18px",
198
+ boxSizing: "border-box",
199
+ left: "0",
200
+ padding: isTop ? "8px 8px 0" : "0 8px 8px",
201
+ position: "absolute",
202
+ right: "0",
203
+ top: isTop ? "0" : "auto",
204
+ zIndex: "1000"
205
+ });
206
+ setContainer(div);
207
+ return () => {
208
+ div.remove();
209
+ setContainer(null);
210
+ };
211
+ }, [map, position]);
212
+ const handleStep = react.useCallback(
213
+ (date) => {
214
+ setActivePeriod(helpers_js.dateToDHIS2PeriodId(date, resolvedPeriodType));
215
+ },
216
+ [resolvedPeriodType, setActivePeriod]
217
+ );
218
+ if (!container) return null;
219
+ return reactDom.createPortal(
220
+ /* @__PURE__ */ jsxRuntime.jsx(
221
+ TimelineUI,
222
+ {
223
+ autoplay,
224
+ button,
225
+ dates,
226
+ interval,
227
+ onStep: handleStep
228
+ }
229
+ ),
230
+ container
231
+ );
232
+ }
233
+
234
+ exports.TimelineControl = TimelineControl;
235
+ //# sourceMappingURL=index.js.map
236
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../src/components/Map/components/MapControls/components/TimelineControl/index.tsx"],"names":["DateTime","useState","useRef","useEffect","useMemo","scaleTime","axisBottom","select","useCallback","jsx","jsxs","useMap","useContext","MapPeriodFilterContext","DomUtil","DomEvent","dateToDHIS2PeriodId","createPortal"],"mappings":"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","file":"index.js","sourcesContent":["import { axisBottom } from \"d3-axis\";\nimport { scaleTime } from \"d3-scale\";\nimport { select } from \"d3-selection\";\nimport { DomEvent, DomUtil } from \"leaflet\";\nimport { DateTime, type DurationObjectUnits } from \"luxon\";\nimport { createPortal } from \"react-dom\";\nimport {\n\ttype ReactElement,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { useMap } from \"react-leaflet\";\nimport { MapPeriodFilterContext } from \"../../../../state/index.js\";\nimport { dateToDHIS2PeriodId, type DHIS2PeriodType } from \"../../../../utils/helpers.js\";\n\nexport type { DHIS2PeriodType };\n\nexport type TimelineRange =\n\t| { range: [Date, Date]; step: DurationObjectUnits }\n\t| { range: [Date, Date, ...Date[]] };\n\nexport type TimelinePosition =\n\t| \"bottom\"\n\t| \"bottomleft\"\n\t| \"bottomright\"\n\t| \"bottomcenter\"\n\t| \"top\"\n\t| \"topleft\"\n\t| \"topright\"\n\t| \"topcenter\";\n\nexport interface TimelineControlOptions {\n\tautoplay?: boolean;\n\tbutton?: {\n\t\tpausedText?: string;\n\t\tplayingText?: string;\n\t};\n\tinterval?: number;\n\tperiodType?: DHIS2PeriodType;\n\tposition?: TimelinePosition;\n\ttimeline: {\n\t\tdateFormat: string;\n\t} & TimelineRange;\n}\n\nfunction resolveDates(tl: TimelineControlOptions[\"timeline\"]): Date[] {\n\tif (\"step\" in tl) {\n\t\tconst [start, end] = tl.range;\n\t\tconst dates: Date[] = [];\n\t\tlet cur = DateTime.fromJSDate(start);\n\t\tconst endDt = DateTime.fromJSDate(end);\n\t\twhile (cur <= endDt) {\n\t\t\tdates.push(cur.toJSDate());\n\t\t\tcur = cur.plus(tl.step);\n\t\t}\n\t\treturn dates;\n\t}\n\treturn [...tl.range];\n}\n\nconst ACCENT = \"#2c6693\";\nconst PADDING_LEFT = 40; // space for play/pause icon\nconst PADDING_RIGHT = 20;\nconst LABEL_WIDTH = 80; // min pixels per axis tick label\nconst PERIOD_RECT_HEIGHT = 8;\nconst PERIOD_RECT_GAP = 1; // gap between adjacent period rects\nconst AXIS_OFFSET = 4; // pixels between rect bottom and axis line\n\n\nfunction TimelineUI({\n\tautoplay: initialAutoplay,\n\tbutton,\n\tdates,\n\tinterval,\n\tonStep,\n}: {\n\tautoplay: boolean;\n\tbutton?: TimelineControlOptions[\"button\"];\n\tdates: Date[];\n\tinterval: number;\n\tonStep: (date: Date) => void;\n}): ReactElement {\n\tconst [index, setIndex] = useState(0);\n\tconst [playing, setPlaying] = useState(initialAutoplay);\n\tconst [svgWidth, setSvgWidth] = useState<number | null>(null);\n\tconst svgRef = useRef<SVGSVGElement>(null);\n\tconst axisRef = useRef<SVGGElement>(null);\n\tconst intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n\tuseEffect(() => {\n\t\tconst el = svgRef.current;\n\t\tif (!el) return;\n\t\tconst measure = (): void => { setSvgWidth(el.getBoundingClientRect().width); };\n\t\tmeasure();\n\t\tconst ro = new ResizeObserver(measure);\n\t\tro.observe(el);\n\t\treturn () => { ro.disconnect(); };\n\t}, []);\n\n\tconst trackWidth = svgWidth !== null ? svgWidth - PADDING_LEFT - PADDING_RIGHT : 0;\n\n\tconst timeScale = useMemo(() => {\n\t\tif (!trackWidth || dates.length < 2) return null;\n\t\tconst periodMs = dates[1].getTime() - dates[0].getTime();\n\t\tconst domainEnd = new Date(dates[dates.length - 1].getTime() + periodMs);\n\t\treturn scaleTime()\n\t\t\t.domain([dates[0], domainEnd])\n\t\t\t.range([0, trackWidth]);\n\t}, [dates, trackWidth]);\n\n\tuseEffect(() => {\n\t\tif (!timeScale || !axisRef.current || !trackWidth) return;\n\t\tconst maxTicks = Math.round(trackWidth / LABEL_WIDTH);\n\t\tconst axis = axisBottom<Date>(timeScale).ticks(maxTicks);\n\t\tselect<SVGGElement, unknown>(axisRef.current).call(axis);\n\t\tselect(axisRef.current).select(\".domain\").attr(\"stroke\", \"#ccc\");\n\t\tselect(axisRef.current).selectAll(\".tick line\").attr(\"stroke\", \"#ccc\");\n\t\tselect(axisRef.current)\n\t\t\t.selectAll<SVGTextElement, unknown>(\".tick text\")\n\t\t\t.attr(\"fill\", \"#666\")\n\t\t\t.attr(\"font-size\", \"10px\")\n\t\t\t.attr(\"font-family\", \"sans-serif\");\n\t}, [timeScale, trackWidth]);\n\n\tconst stepTo = useCallback(\n\t\t(idx: number) => {\n\t\t\tsetIndex(idx);\n\t\t\tonStep(dates[idx]);\n\t\t},\n\t\t[dates, onStep],\n\t);\n\n\t// Autoplay interval\n\tuseEffect(() => {\n\t\tif (intervalRef.current) clearInterval(intervalRef.current);\n\t\tif (!playing) return;\n\t\tintervalRef.current = setInterval(() => {\n\t\t\tsetIndex((prev) => {\n\t\t\t\tconst next = prev + 1 >= dates.length ? 0 : prev + 1;\n\t\t\t\tonStep(dates[next]);\n\t\t\t\treturn next;\n\t\t\t});\n\t\t}, interval);\n\t\treturn () => {\n\t\t\tif (intervalRef.current) clearInterval(intervalRef.current);\n\t\t};\n\t}, [playing, interval, dates, onStep]);\n\n\tconst periodRects = useMemo(() => {\n\t\tif (!timeScale || dates.length < 2) return null;\n\t\tconst unitWidth = timeScale(dates[1]) - timeScale(dates[0]);\n\t\treturn dates.map((date, i) => {\n\t\t\tconst x = timeScale(date);\n\t\t\tconst spanWidth = i < dates.length - 1\n\t\t\t\t? timeScale(dates[i + 1]) - x\n\t\t\t\t: unitWidth;\n\t\t\tconst w = Math.max(spanWidth - PERIOD_RECT_GAP, 1);\n\t\t\tconst capturedIndex = i;\n\t\t\treturn (\n\t\t\t\t<rect\n\t\t\t\t\tkey={date.toISOString()}\n\t\t\t\t\tfill={capturedIndex === index ? ACCENT : \"#b0c4d8\"}\n\t\t\t\t\theight={PERIOD_RECT_HEIGHT}\n\t\t\t\t\tonClick={() => { setPlaying(false); stepTo(capturedIndex); }}\n\t\t\t\t\trx={1}\n\t\t\t\t\tstyle={{ cursor: \"pointer\" }}\n\t\t\t\t\twidth={w}\n\t\t\t\t\tx={x}\n\t\t\t\t\ty={0}\n\t\t\t\t/>\n\t\t\t);\n\t\t});\n\t}, [timeScale, dates, index, stepTo]);\n\n\n\tconst RECT_ROW_Y = 6;\n\tconst svgHeight = RECT_ROW_Y + PERIOD_RECT_HEIGHT + AXIS_OFFSET + 24\n\n\tconst playLabel = playing\n\t\t? (button?.playingText ?? \"Pause\")\n\t\t: (button?.pausedText ?? \"Play\");\n\n\treturn (\n\t\t<svg\n\t\t\taria-label=\"Timeline\"\n\t\t\theight={svgHeight}\n\t\t\tref={svgRef}\n\t\t\tstyle={{\n\t\t\t\tbackground: \"white\",\n\t\t\t\tboxShadow: \"0 -1px 4px rgba(0,0,0,0.15)\",\n\t\t\t\tdisplay: \"block\",\n\t\t\t\twidth: \"100%\",\n\t\t\t}}\n\t\t>\n\t\t\t{/* Play / Pause — Material Design SVG icon */}\n\t\t\t<g\n\t\t\t\taria-label={playLabel}\n\t\t\t\tonClick={() => { setPlaying((p) => !p); }}\n\t\t\t\trole=\"button\"\n\t\t\t\tstyle={{ cursor: \"pointer\" }}\n\t\t\t\ttransform={`translate(7, ${RECT_ROW_Y - 8})`}\n\t\t\t>\n\t\t\t\t<path d=\"M0 0h24v24H0z\" fillOpacity={0} />\n\t\t\t\t{playing\n\t\t\t\t\t? <path d=\"M6 19h4V5H6v14zm8-14v14h4V5h-4z\" fill={ACCENT} />\n\t\t\t\t\t: <path d=\"M8 5v14l11-7z\" fill={ACCENT} />\n\t\t\t\t}\n\t\t\t</g>\n\n\t\t\t{/* Period rectangles */}\n\t\t\t{timeScale !== null && (\n\t\t\t\t<g transform={`translate(${PADDING_LEFT}, ${RECT_ROW_Y})`}>\n\t\t\t\t\t{periodRects}\n\t\t\t\t</g>\n\t\t\t)}\n\n\t\t\t{/* D3 x-axis */}\n\t\t\t{timeScale !== null && (\n\t\t\t\t<g\n\t\t\t\t\tref={axisRef}\n\t\t\t\t\ttransform={`translate(${PADDING_LEFT}, ${RECT_ROW_Y + PERIOD_RECT_HEIGHT + AXIS_OFFSET})`}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</svg>\n\t);\n}\n\n\nexport function TimelineControl({\n\tautoplay = false,\n\tbutton,\n\tinterval = 1000,\n\tperiodType,\n\tposition = \"bottom\",\n\ttimeline,\n}: TimelineControlOptions): ReactElement | null {\n\tconst map = useMap();\n\tconst {\n\t\tperiodType: contextPeriodType,\n\t\tsetActivePeriod,\n\t\tsetPeriodType,\n\t} = useContext(MapPeriodFilterContext);\n\tconst [container, setContainer] = useState<HTMLElement | null>(null);\n\n\tuseEffect(() => {\n\t\tif (periodType) setPeriodType(periodType);\n\t}, [periodType, setPeriodType]);\n\n\tconst resolvedPeriodType = periodType ?? contextPeriodType ?? \"Monthly\";\n\tconst dates = useMemo(() => resolveDates(timeline), [timeline]);\n\n\tuseEffect(() => {\n\t\tconst mapEl = map.getContainer();\n\t\tconst div = DomUtil.create(\"div\", \"\", mapEl);\n\t\tDomEvent.disableClickPropagation(div);\n\t\tDomEvent.disableScrollPropagation(div);\n\n\t\tconst isTop = position.startsWith(\"top\");\n\t\tObject.assign(div.style, {\n\t\t\tbottom: isTop ? \"auto\" : \"18px\",\n\t\t\tboxSizing: \"border-box\",\n\t\t\tleft: \"0\",\n\t\t\tpadding: isTop ? \"8px 8px 0\" : \"0 8px 8px\",\n\t\t\tposition: \"absolute\",\n\t\t\tright: \"0\",\n\t\t\ttop: isTop ? \"0\" : \"auto\",\n\t\t\tzIndex: \"1000\",\n\t\t});\n\n\t\tsetContainer(div);\n\t\treturn () => {\n\t\t\tdiv.remove();\n\t\t\tsetContainer(null);\n\t\t};\n\t}, [map, position]);\n\n\tconst handleStep = useCallback(\n\t\t(date: Date) => {\n\t\t\tsetActivePeriod(dateToDHIS2PeriodId(date, resolvedPeriodType));\n\t\t},\n\t\t[resolvedPeriodType, setActivePeriod],\n\t);\n\n\tif (!container) return null;\n\n\treturn createPortal(\n\t\t<TimelineUI\n\t\t\tautoplay={autoplay}\n\t\t\tbutton={button}\n\t\t\tdates={dates}\n\t\t\tinterval={interval}\n\t\t\tonStep={handleStep}\n\t\t/>,\n\t\tcontainer,\n\t);\n}\n"]}
@@ -4,34 +4,39 @@ var jsxRuntime = require('react/jsx-runtime');
4
4
  var reactLeaflet = require('react-leaflet');
5
5
  var FullscreenControl = require('./components/FullscreenControl/index.js');
6
6
  var DownloadControl = require('./components/DownloadControl/index.js');
7
+ var TimelineControl = require('./components/TimelineControl');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
10
11
  var FullscreenControl__default = /*#__PURE__*/_interopDefault(FullscreenControl);
11
12
  var DownloadControl__default = /*#__PURE__*/_interopDefault(DownloadControl);
12
13
 
13
- function MapControl({
14
- type,
15
- options,
16
- position,
17
- mapId
18
- }) {
14
+ function MapControl(props) {
15
+ const { type } = props;
19
16
  switch (type) {
20
17
  case "zoom":
21
- return /* @__PURE__ */ jsxRuntime.jsx(reactLeaflet.ZoomControl, { position, ...options });
18
+ return /* @__PURE__ */ jsxRuntime.jsx(reactLeaflet.ZoomControl, { position: props.position, ...props.options });
22
19
  case "scale":
23
- return /* @__PURE__ */ jsxRuntime.jsx(reactLeaflet.ScaleControl, { position, ...options });
20
+ return /* @__PURE__ */ jsxRuntime.jsx(reactLeaflet.ScaleControl, { position: props.position, ...props.options });
24
21
  case "fullscreen":
25
- return /* @__PURE__ */ jsxRuntime.jsx(FullscreenControl__default.default, { position, ...options });
22
+ return /* @__PURE__ */ jsxRuntime.jsx(
23
+ FullscreenControl__default.default,
24
+ {
25
+ position: props.position,
26
+ ...props.options
27
+ }
28
+ );
26
29
  case "print":
27
30
  return /* @__PURE__ */ jsxRuntime.jsx(
28
31
  DownloadControl__default.default,
29
32
  {
30
- mapId,
31
- position,
32
- options
33
+ mapId: props.mapId,
34
+ options: props.options,
35
+ position: props.position
33
36
  }
34
37
  );
38
+ case "timeline":
39
+ return /* @__PURE__ */ jsxRuntime.jsx(TimelineControl.TimelineControl, { ...props });
35
40
  default:
36
41
  return null;
37
42
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/components/Map/components/MapControls/index.tsx"],"names":["jsx","ZoomControl","ScaleControl","FullscreenControl","DownloadControl"],"mappings":";;;;;;;;;;;;AAUe,SAAR,UAAA,CAA4B;AAAA,EAClC,IAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA;AACD,CAAA,EAAoB;AACnB,EAAA,QAAQ,IAAA;AAAM,IACb,KAAK,MAAA;AACJ,MAAA,uBAAOA,cAAA,CAACC,wBAAA,EAAA,EAAY,QAAA,EAAqB,GAAG,OAAA,EAAS,CAAA;AAAA,IACtD,KAAK,OAAA;AACJ,MAAA,uBAAOD,cAAA,CAACE,yBAAA,EAAA,EAAa,QAAA,EAAqB,GAAG,OAAA,EAAS,CAAA;AAAA,IACvD,KAAK,YAAA;AACJ,MAAA,uBAAOF,cAAA,CAACG,kCAAA,EAAA,EAAkB,QAAA,EAAqB,GAAG,OAAA,EAAS,CAAA;AAAA,IAC5D,KAAK,OAAA;AACJ,MAAA,uBACCH,cAAA;AAAA,QAACI,gCAAA;AAAA,QAAA;AAAA,UACA,KAAA;AAAA,UACA,QAAA;AAAA,UACA;AAAA;AAAA,OACD;AAAA,IAEF;AACC,MAAA,OAAO,IAAA;AAAA;AAEV","file":"index.js","sourcesContent":["import React from \"react\";\nimport { ScaleControl, ZoomControl } from \"react-leaflet\";\nimport { MapControls } from \"../MapArea/interfaces/index.js\";\nimport FullscreenControl from \"./components/FullscreenControl/index.js\";\nimport DownloadControl from \"./components/DownloadControl/index.js\";\n\nexport interface MapControlProps extends MapControls {\n\tmapId: string;\n}\n\nexport default function MapControl({\n\ttype,\n\toptions,\n\tposition,\n\tmapId,\n}: MapControlProps) {\n\tswitch (type) {\n\t\tcase \"zoom\":\n\t\t\treturn <ZoomControl position={position} {...options} />;\n\t\tcase \"scale\":\n\t\t\treturn <ScaleControl position={position} {...options} />;\n\t\tcase \"fullscreen\":\n\t\t\treturn <FullscreenControl position={position} {...options} />;\n\t\tcase \"print\":\n\t\t\treturn (\n\t\t\t\t<DownloadControl\n\t\t\t\t\tmapId={mapId}\n\t\t\t\t\tposition={position}\n\t\t\t\t\toptions={options}\n\t\t\t\t/>\n\t\t\t);\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n"]}
1
+ {"version":3,"sources":["../../../../../src/components/Map/components/MapControls/index.tsx"],"names":["ZoomControl","ScaleControl","jsx","FullscreenControl","DownloadControl","TimelineControl"],"mappings":";;;;;;;;;;;;;AAOe,SAAR,WAA4B,KAAA,EAAwC;AAC1E,EAAA,MAAM,EAAE,MAAK,GAAI,KAAA;AACjB,EAAA,QAAQ,IAAA;AAAM,IACb,KAAK,MAAA;AACJ,MAAA,sCAAQA,wBAAA,EAAA,EAAY,QAAA,EAAU,MAAM,QAAA,EAAW,GAAG,MAAM,OAAA,EAAS,CAAA;AAAA,IAClE,KAAK,OAAA;AACJ,MAAA,sCACEC,yBAAA,EAAA,EAAa,QAAA,EAAU,MAAM,QAAA,EAAW,GAAG,MAAM,OAAA,EAAS,CAAA;AAAA,IAE7D,KAAK,YAAA;AACJ,MAAA,uBACCC,cAAA;AAAA,QAACC,kCAAA;AAAA,QAAA;AAAA,UACA,UAAU,KAAA,CAAM,QAAA;AAAA,UACf,GAAG,KAAA,CAAM;AAAA;AAAA,OACX;AAAA,IAEF,KAAK,OAAA;AACJ,MAAA,uBACCD,cAAA;AAAA,QAACE,gCAAA;AAAA,QAAA;AAAA,UACA,OAAO,KAAA,CAAM,KAAA;AAAA,UACb,SAAS,KAAA,CAAM,OAAA;AAAA,UACf,UAAU,KAAA,CAAM;AAAA;AAAA,OACjB;AAAA,IAEF,KAAK,UAAA;AACJ,MAAA,uBAAOF,cAAA,CAACG,+BAAA,EAAA,EAAiB,GAAG,KAAA,EAAO,CAAA;AAAA,IACpC;AACC,MAAA,OAAO,IAAA;AAAA;AAEV","file":"index.js","sourcesContent":["import React from \"react\";\nimport { ScaleControl, ZoomControl } from \"react-leaflet\";\nimport { MapControls } from \"../MapArea/interfaces\";\nimport FullscreenControl from \"./components/FullscreenControl/index.js\";\nimport DownloadControl from \"./components/DownloadControl/index.js\";\nimport { TimelineControl } from \"./components/TimelineControl\";\n\nexport default function MapControl(props: MapControls & { mapId: string }) {\n\tconst { type } = props;\n\tswitch (type) {\n\t\tcase \"zoom\":\n\t\t\treturn <ZoomControl position={props.position} {...props.options} />;\n\t\tcase \"scale\":\n\t\t\treturn (\n\t\t\t\t<ScaleControl position={props.position} {...props.options} />\n\t\t\t);\n\t\tcase \"fullscreen\":\n\t\t\treturn (\n\t\t\t\t<FullscreenControl\n\t\t\t\t\tposition={props.position}\n\t\t\t\t\t{...props.options}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"print\":\n\t\t\treturn (\n\t\t\t\t<DownloadControl\n\t\t\t\t\tmapId={props.mapId}\n\t\t\t\t\toptions={props.options}\n\t\t\t\t\tposition={props.position}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"timeline\":\n\t\t\treturn <TimelineControl {...props} />;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n"]}
@@ -6,6 +6,7 @@ var react = require('react');
6
6
  var utils = require('../../../../../utils');
7
7
  var appRuntime = require('@dhis2/app-runtime');
8
8
  var asyncEs = require('async-es');
9
+ var helpers_js = require('../../../../../utils/helpers.js');
9
10
  var colors_js = require('../../../../../utils/colors.js');
10
11
  var index_js = require('../../../../MapLayer/components/GoogleEngineLayer/hooks/index.js');
11
12
  var useBoundaryData_js = require('../../../../MapLayer/components/BoundaryLayer/hooks/useBoundaryData.js');
@@ -16,13 +17,13 @@ const analyticsQuery = {
16
17
  analytics: {
17
18
  resource: "analytics",
18
19
  params: ({ ou, pe, dx, startDate, endDate, analyticsOptions }) => {
19
- const peDimension = !lodash.isEmpty(pe) ? `pe:${pe?.join(";")}` : void 0;
20
+ const usingDateRange = !lodash.isEmpty(startDate) && !lodash.isEmpty(endDate);
21
+ const peDimension = !usingDateRange && !lodash.isEmpty(pe) ? `pe:${pe?.join(";")}` : void 0;
20
22
  const ouDimension = !lodash.isEmpty(ou) ? `ou:${ou?.join(";")}` : void 0;
21
23
  const dxDimension = !lodash.isEmpty(dx) ? `dx:${dx?.join(";")}` : void 0;
22
24
  return {
23
25
  dimension: lodash.compact([dxDimension, peDimension, ouDimension]),
24
- startDate,
25
- endDate,
26
+ ...usingDateRange ? { startDate, endDate } : {},
26
27
  displayProperty: "NAME",
27
28
  ...analyticsOptions ?? {}
28
29
  };
@@ -79,24 +80,32 @@ function useThematicLayers({
79
80
  const [loading, setLoading] = react.useState(false);
80
81
  const { orgUnits, orgUnitSelection } = hooks.useMapOrganisationUnit();
81
82
  const { periods, range } = hooks.useMapPeriods() ?? {};
83
+ const { activePeriod, periodType } = hooks.useMapPeriodFilter();
82
84
  const ou = react.useMemo(
83
85
  () => utils.getOrgUnitsSelection(orgUnitSelection),
84
86
  [orgUnitSelection]
85
87
  );
86
- const pe = react.useMemo(() => periods?.map((pe2) => pe2.id), [periods]);
88
+ const timelinePeriods = react.useMemo(() => {
89
+ if (!range || !periodType) return null;
90
+ return helpers_js.computeTimelinePeriods(range, periodType);
91
+ }, [range, periodType]);
92
+ const pe = react.useMemo(() => {
93
+ if (timelinePeriods) return timelinePeriods;
94
+ return periods?.map((pe2) => pe2.id);
95
+ }, [periods, timelinePeriods]);
96
+ const toISODate = (date) => date.toISOString().slice(0, 10);
87
97
  const { startDate, endDate } = react.useMemo(() => {
88
- if (!range) {
89
- return {
90
- startDate: void 0,
91
- endDate: void 0
92
- };
98
+ if (timelinePeriods || !range) {
99
+ return { startDate: void 0, endDate: void 0 };
93
100
  }
94
101
  return {
95
- startDate: utils.sanitizeDate(range.start.toDateString()),
96
- endDate: utils.sanitizeDate(range.end.toDateString())
102
+ startDate: toISODate(range.start),
103
+ endDate: toISODate(range.end)
97
104
  };
98
- }, [range]);
99
- const sanitizeData = (data, layer) => {
105
+ }, [range, timelinePeriods]);
106
+ const analyticsDataRef = react.useRef(null);
107
+ const legendSetsRef = react.useRef(/* @__PURE__ */ new Map());
108
+ const sanitizeData = (data, layer, currentPeriod) => {
100
109
  if (data) {
101
110
  const { analytics } = data;
102
111
  const rows = analytics?.rows;
@@ -109,18 +118,20 @@ function useThematicLayers({
109
118
  const valueIndex = analytics.headers.findIndex(
110
119
  (header) => header.name === "value"
111
120
  );
121
+ const peIndex = analytics.headers.findIndex(
122
+ (header) => header.name === "pe"
123
+ );
112
124
  if (!lodash.isEmpty(rows)) {
113
125
  return lodash.sortBy(
114
- orgUnits?.map((ou2) => {
115
- const row = rows.find(
116
- (row2) => row2[ouIndex] === ou2.id && row2[dxIndex] === layer.dataItem.id
126
+ orgUnits?.map((orgUnit) => {
127
+ const matchingRows = rows.filter(
128
+ (row) => row[ouIndex] === orgUnit.id && row[dxIndex] === layer.dataItem.id && (currentPeriod && peIndex >= 0 ? row[peIndex] === currentPeriod : true)
117
129
  );
130
+ const values = matchingRows.map((row) => parseFloat(row[valueIndex])).filter((v) => !isNaN(v));
118
131
  return {
119
- orgUnit: ou2,
120
- data: row ? parseFloat(row[valueIndex]) : void 0,
121
- dataItem: {
122
- ...layer.dataItem
123
- }
132
+ orgUnit,
133
+ data: values.length > 0 ? values.reduce((sum, v) => sum + v, 0) / values.length : void 0,
134
+ dataItem: { ...layer.dataItem }
124
135
  };
125
136
  }),
126
137
  ["data"]
@@ -137,15 +148,17 @@ function useThematicLayers({
137
148
  try {
138
149
  const legends = [];
139
150
  if (layer.dataItem.legendSet) {
140
- const legendSetData = await engine.query(
141
- legendSetsQuery,
142
- {
143
- variables: {
144
- id: layer.dataItem.legendSet
145
- }
151
+ let legendSet = legendSetsRef.current.get(layer.dataItem.legendSet);
152
+ if (!legendSet) {
153
+ const legendSetData = await engine.query(
154
+ legendSetsQuery,
155
+ { variables: { id: layer.dataItem.legendSet } }
156
+ );
157
+ legendSet = legendSetData?.legendSets;
158
+ if (legendSet) {
159
+ legendSetsRef.current.set(layer.dataItem.legendSet, legendSet);
146
160
  }
147
- );
148
- const legendSet = legendSetData?.legendSets;
161
+ }
149
162
  if (legendSet) {
150
163
  legends.push(...legendSet.legends);
151
164
  }
@@ -161,17 +174,11 @@ function useThematicLayers({
161
174
  const autoLegends = utils.generateLegends(
162
175
  lodash.last(sortedData)?.data ?? 0,
163
176
  lodash.head(sortedData)?.data ?? 0,
164
- {
165
- classesCount: scale,
166
- colorClass
167
- }
177
+ { classesCount: scale, colorClass }
168
178
  );
169
179
  legends.push(...autoLegends);
170
180
  }
171
- return {
172
- ...layer,
173
- legends
174
- };
181
+ return { ...layer, legends };
175
182
  } catch (e) {
176
183
  return layer;
177
184
  }
@@ -182,38 +189,25 @@ function useThematicLayers({
182
189
  try {
183
190
  setLoading(true);
184
191
  const layersWithoutData = layers?.filter((layer) => !layer.data);
185
- const layersWithData = lodash.differenceBy(
186
- layers,
187
- layersWithoutData,
188
- "id"
189
- );
192
+ const layersWithData = lodash.differenceBy(layers, layersWithoutData, "id");
190
193
  const dx = layersWithoutData.map((layer) => layer.dataItem.id);
191
194
  let sanitizedLayersWithData = [];
192
195
  if (!lodash.isEmpty(dx)) {
193
196
  const data = await engine.query(analyticsQuery, {
194
- variables: {
195
- dx,
196
- ou,
197
- pe,
198
- startDate,
199
- endDate,
200
- analyticsOptions
201
- }
197
+ variables: { dx, ou, pe, startDate, endDate, analyticsOptions }
202
198
  });
199
+ analyticsDataRef.current = data;
203
200
  sanitizedLayersWithData = layersWithoutData.map((layer) => ({
204
201
  ...layer,
205
202
  name: layer?.name ?? layer?.dataItem?.displayName ?? layer.id,
206
- data: sanitizeData(data, layer)
203
+ data: sanitizeData(data, layer, activePeriod)
207
204
  }));
208
205
  }
209
206
  const sanitizedLayersWithOrgUnits = layersWithData.map((layer) => ({
210
207
  ...layer,
211
208
  data: layer.data?.map((datum) => ({
212
209
  ...datum,
213
- orgUnit: lodash.find(orgUnits, [
214
- "id",
215
- datum.orgUnit
216
- ]),
210
+ orgUnit: lodash.find(orgUnits, ["id", datum.orgUnit]),
217
211
  dataItem: layer.dataItem,
218
212
  name: layer?.name ?? layer?.dataItem?.displayName ?? layer.id
219
213
  }))
@@ -229,8 +223,21 @@ function useThematicLayers({
229
223
  return [];
230
224
  }
231
225
  };
226
+ const refilterLayers = react.useCallback(
227
+ async (layers) => {
228
+ if (!analyticsDataRef.current) return [];
229
+ const processed = layers.filter((layer) => !layer.data).map((layer) => ({
230
+ ...layer,
231
+ name: layer?.name ?? layer?.dataItem?.displayName ?? layer.id,
232
+ data: sanitizeData(analyticsDataRef.current, layer, activePeriod)
233
+ }));
234
+ return sanitizeLegends(processed);
235
+ },
236
+ [activePeriod, orgUnits]
237
+ );
232
238
  return {
233
239
  sanitizeLayers,
240
+ refilterLayers,
234
241
  loading
235
242
  };
236
243
  }