@eeacms/volto-marine-policy 2.0.8 → 2.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/package.json +4 -3
- package/src/components/Blocks/DemoSitesExplorer/DemoSitesExplorerView.js +49 -15
- package/src/components/Blocks/DemoSitesExplorer/DemoSitesFilters.jsx +57 -40
- package/src/components/Blocks/DemoSitesExplorer/DemoSitesListing.jsx +2 -2
- package/src/components/Blocks/DemoSitesExplorer/DemoSitesMap.jsx +25 -6
- package/src/components/Blocks/DemoSitesExplorer/FeatureDisplay.jsx +24 -15
- package/src/components/Blocks/DemoSitesExplorer/FeatureInteraction.jsx +2 -2
- package/src/components/Blocks/DemoSitesExplorer/ObjectivesChart.jsx +255 -0
- package/src/components/Blocks/DemoSitesExplorer/styles.less +131 -5
- package/src/components/Blocks/DemoSitesExplorer/utils.js +85 -13
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [2.0.10](https://github.com/eea/volto-marine-policy/compare/2.0.9...2.0.10) - 9 May 2025
|
|
8
|
+
|
|
9
|
+
#### :rocket: Dependency updates
|
|
10
|
+
|
|
11
|
+
- Release @eeacms/volto-globalsearch@2.1.0 [EEA Jenkins - [`3b20bc2`](https://github.com/eea/volto-marine-policy/commit/3b20bc292d04ab81385732840ffe2c469f83fbd6)]
|
|
12
|
+
|
|
13
|
+
#### :house: Internal changes
|
|
14
|
+
|
|
15
|
+
- style: Automated code fix [eea-jenkins - [`69cc9c8`](https://github.com/eea/volto-marine-policy/commit/69cc9c86e32d35cce4f979b36bc7d20cc4b01e8f)]
|
|
16
|
+
- style: Automated code fix [eea-jenkins - [`d1a8da0`](https://github.com/eea/volto-marine-policy/commit/d1a8da01dfc0ddcc24058ba5aafb5d76be0d2ffc)]
|
|
17
|
+
|
|
18
|
+
#### :hammer_and_wrench: Others
|
|
19
|
+
|
|
20
|
+
- eslint [laszlocseh - [`8c27f84`](https://github.com/eea/volto-marine-policy/commit/8c27f84fcdc8adf9b12746b585c66f34b9e785a4)]
|
|
21
|
+
- further improvements to demo sites map [laszlocseh - [`b15e205`](https://github.com/eea/volto-marine-policy/commit/b15e2053256552c30f26fdab581330cba420b412)]
|
|
22
|
+
- eslint [laszlocseh - [`4514ee0`](https://github.com/eea/volto-marine-policy/commit/4514ee0a409e8dfe0d6975da989228f39033b343)]
|
|
23
|
+
- no results message [laszlocseh - [`06b7ae6`](https://github.com/eea/volto-marine-policy/commit/06b7ae62eb7b27a2ec9e2d3f9ec62aed42a85045)]
|
|
24
|
+
- fix eslint [laszlocseh - [`2a4605a`](https://github.com/eea/volto-marine-policy/commit/2a4605ac45b141d5e3f2b737a24c6afca0b5d884)]
|
|
25
|
+
- optimize demo sites map rendering [laszlocseh - [`2345b60`](https://github.com/eea/volto-marine-policy/commit/2345b609a95a00a53c46ce9b92686eeb598a14a6)]
|
|
26
|
+
### [2.0.9](https://github.com/eea/volto-marine-policy/compare/2.0.8...2.0.9) - 7 May 2025
|
|
27
|
+
|
|
28
|
+
#### :rocket: Dependency updates
|
|
29
|
+
|
|
30
|
+
- Release @eeacms/volto-searchlib@2.0.16 [EEA Jenkins - [`9d425b4`](https://github.com/eea/volto-marine-policy/commit/9d425b44fa813cf7d08d95d36af6c0f01d0e52ce)]
|
|
31
|
+
|
|
32
|
+
#### :house: Internal changes
|
|
33
|
+
|
|
34
|
+
- style: Automated code fix [eea-jenkins - [`1c3cbdc`](https://github.com/eea/volto-marine-policy/commit/1c3cbdcc74127d45e46db9ae4fd1ac3b237ad32c)]
|
|
35
|
+
- style: Automated code fix [eea-jenkins - [`b00b251`](https://github.com/eea/volto-marine-policy/commit/b00b2515e54b8ffdbc51bf301f0b0bb232b83efe)]
|
|
36
|
+
- style: Automated code fix [eea-jenkins - [`e02c515`](https://github.com/eea/volto-marine-policy/commit/e02c5156ac34e4cf2fef61a27dfe64d60aad97af)]
|
|
37
|
+
- style: Automated code fix [eea-jenkins - [`678b9bf`](https://github.com/eea/volto-marine-policy/commit/678b9bf2b8f83f9b3a6facaa42f5851a013462ad)]
|
|
38
|
+
- style: Automated code fix [eea-jenkins - [`b1cccfb`](https://github.com/eea/volto-marine-policy/commit/b1cccfbd674890392948ad5227bfe334f55a2721)]
|
|
39
|
+
|
|
40
|
+
#### :hammer_and_wrench: Others
|
|
41
|
+
|
|
42
|
+
- small fixes demmo sites map [laszlocseh - [`f83d070`](https://github.com/eea/volto-marine-policy/commit/f83d070d0b1c7a5bdf1136eb542782477c9266b0)]
|
|
43
|
+
- update objectives [laszlocseh - [`0c09eb1`](https://github.com/eea/volto-marine-policy/commit/0c09eb1b093ffba34deafa079127175629f3f2b9)]
|
|
44
|
+
- fix eslint [laszlocseh - [`128fbb0`](https://github.com/eea/volto-marine-policy/commit/128fbb04277cc11bc6406df8cc815ea4a41291d0)]
|
|
45
|
+
- fix eslint [laszlocseh - [`68e7168`](https://github.com/eea/volto-marine-policy/commit/68e7168d6322cb07fe7df7c2376a26e297cb015f)]
|
|
46
|
+
- fix eslint [laszlocseh - [`1be214a`](https://github.com/eea/volto-marine-policy/commit/1be214accdac3657f156691f09c149f9f678d344)]
|
|
47
|
+
- fix eslint [laszlocseh - [`fbed39c`](https://github.com/eea/volto-marine-policy/commit/fbed39c59d7841234cd8ffe699a9e84766a501bc)]
|
|
48
|
+
- fix eslint [laszlocseh - [`ed3a2d2`](https://github.com/eea/volto-marine-policy/commit/ed3a2d26194d18aed50b1781e2d84a3d1482b0ab)]
|
|
49
|
+
- fix eslint [laszlocseh - [`15805ce`](https://github.com/eea/volto-marine-policy/commit/15805ce9adb0ca66a99368a432d54d74fcbc8077)]
|
|
50
|
+
- WiP demo sites map [laszlocseh - [`ca84585`](https://github.com/eea/volto-marine-policy/commit/ca84585f692b03c7011d46393fa5b937fa985e4d)]
|
|
51
|
+
- WiP demo sites map [laszlocseh - [`b51ca84`](https://github.com/eea/volto-marine-policy/commit/b51ca842dba4fe0700100b5a6328d1bd4b962acb)]
|
|
7
52
|
### [2.0.8](https://github.com/eea/volto-marine-policy/compare/2.0.7...2.0.8) - 16 April 2025
|
|
8
53
|
|
|
9
54
|
#### :house: Internal changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-marine-policy",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.10",
|
|
4
4
|
"description": "@eeacms/volto-marine-policy: Volto add-on",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": "European Environment Agency: IDM2 A-Team",
|
|
@@ -34,15 +34,16 @@
|
|
|
34
34
|
"@eeacms/volto-eea-design-system": "*",
|
|
35
35
|
"@eeacms/volto-eea-website-theme": "*",
|
|
36
36
|
"@eeacms/volto-embed": "*",
|
|
37
|
-
"@eeacms/volto-globalsearch": "2.0
|
|
37
|
+
"@eeacms/volto-globalsearch": "2.1.0",
|
|
38
38
|
"@eeacms/volto-openlayers-map": "0.3.2",
|
|
39
|
-
"@eeacms/volto-searchlib": "2.0.
|
|
39
|
+
"@eeacms/volto-searchlib": "2.0.16",
|
|
40
40
|
"@eeacms/volto-tabs-block": "*",
|
|
41
41
|
"axios": "0.30.0",
|
|
42
42
|
"d3-array": "^2.12.1",
|
|
43
43
|
"jquery": "3.6.0",
|
|
44
44
|
"razzle-plugin-scss": "^4.2.18",
|
|
45
45
|
"react-lazy-load-image-component": "^1.4.0",
|
|
46
|
+
"react-plotly.js": "2.6.0",
|
|
46
47
|
"react-slick": "^0.24.0",
|
|
47
48
|
"slick-carousel": "^1.8.1"
|
|
48
49
|
},
|
|
@@ -2,9 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import { Grid } from 'semantic-ui-react'; // Dropdown,
|
|
3
3
|
import { addAppURL } from '@plone/volto/helpers';
|
|
4
4
|
import config from '@plone/volto/registry';
|
|
5
|
+
import { VisibilitySensor } from '@eeacms/volto-datablocks/components';
|
|
6
|
+
|
|
5
7
|
import DemoSitesMap from './DemoSitesMap';
|
|
6
8
|
import { ActiveFilters, DemoSitesFilters } from './DemoSitesFilters';
|
|
7
|
-
|
|
9
|
+
import ObjectivesChart from './ObjectivesChart';
|
|
8
10
|
import { filterCases, getFilters } from './utils';
|
|
9
11
|
import { useCases } from './hooks';
|
|
10
12
|
|
|
@@ -23,6 +25,7 @@ export default function DemoSitesExplorerView(props) {
|
|
|
23
25
|
|
|
24
26
|
const [activeFilters, setActiveFilters] = React.useState({
|
|
25
27
|
objective_filter: [],
|
|
28
|
+
target_filter: [],
|
|
26
29
|
indicator_filter: [],
|
|
27
30
|
project_filter: [],
|
|
28
31
|
country_filter: [],
|
|
@@ -31,6 +34,7 @@ export default function DemoSitesExplorerView(props) {
|
|
|
31
34
|
const [activeItems, setActiveItems] = React.useState(cases);
|
|
32
35
|
const [filters, setFilters] = React.useState([]);
|
|
33
36
|
const [map, setMap] = React.useState();
|
|
37
|
+
const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
|
|
34
38
|
|
|
35
39
|
React.useEffect(() => {
|
|
36
40
|
const _filters = getFilters(cases, indicatorOnly);
|
|
@@ -38,6 +42,7 @@ export default function DemoSitesExplorerView(props) {
|
|
|
38
42
|
}, [
|
|
39
43
|
cases,
|
|
40
44
|
activeFilters.objective_filter,
|
|
45
|
+
activeFilters.target_filter,
|
|
41
46
|
activeFilters.indicator_filter,
|
|
42
47
|
activeFilters.project_filter,
|
|
43
48
|
activeFilters.country_filter,
|
|
@@ -74,33 +79,62 @@ export default function DemoSitesExplorerView(props) {
|
|
|
74
79
|
<Grid.Row>
|
|
75
80
|
{cases.length ? (
|
|
76
81
|
<Grid columns={12}>
|
|
77
|
-
<Grid.Column mobile={
|
|
82
|
+
<Grid.Column mobile={8} tablet={8} computer={8}>
|
|
78
83
|
<DemoSitesMap
|
|
79
84
|
items={cases}
|
|
80
85
|
activeItems={activeItems}
|
|
86
|
+
setActiveFilters={setActiveFilters}
|
|
81
87
|
hideFilters={hideFilters}
|
|
82
88
|
selectedCase={selectedCase}
|
|
83
89
|
onSelectedCase={onSelectedCase}
|
|
84
90
|
map={map}
|
|
85
91
|
setMap={setMap}
|
|
92
|
+
highlightedIndex={highlightedIndex}
|
|
93
|
+
setHighlightedIndex={setHighlightedIndex}
|
|
86
94
|
/>
|
|
87
95
|
</Grid.Column>
|
|
88
|
-
<Grid.Column
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
<Grid.Column
|
|
97
|
+
mobile={4}
|
|
98
|
+
tablet={4}
|
|
99
|
+
computer={4}
|
|
100
|
+
className="right-side-filters"
|
|
101
|
+
>
|
|
102
|
+
<Grid.Row>
|
|
103
|
+
{!hideFilters ? (
|
|
104
|
+
<VisibilitySensor>
|
|
105
|
+
<ObjectivesChart
|
|
106
|
+
items={cases}
|
|
107
|
+
// activeItems={activeItems}
|
|
108
|
+
// filters={filters}
|
|
109
|
+
activeFilters={activeFilters}
|
|
110
|
+
setActiveFilters={setActiveFilters}
|
|
111
|
+
// hideFilters={hideFilters}
|
|
112
|
+
// map={map}
|
|
113
|
+
highlightedIndex={highlightedIndex}
|
|
114
|
+
setHighlightedIndex={setHighlightedIndex}
|
|
115
|
+
/>
|
|
116
|
+
</VisibilitySensor>
|
|
117
|
+
) : (
|
|
118
|
+
''
|
|
119
|
+
)}
|
|
120
|
+
</Grid.Row>
|
|
121
|
+
<Grid.Row>
|
|
122
|
+
<div className="legend">
|
|
123
|
+
{/* <div className="legend-row legend-subtitle">Legend</div> */}
|
|
124
|
+
<div className="legend-row">
|
|
125
|
+
<div className="circle">
|
|
126
|
+
<div className="dot-demosite"></div>
|
|
127
|
+
</div>
|
|
128
|
+
<div>Demo site</div>
|
|
94
129
|
</div>
|
|
95
|
-
<div
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<div
|
|
130
|
+
<div className="legend-row">
|
|
131
|
+
<div className="circle">
|
|
132
|
+
<div className="dot-region"></div>
|
|
133
|
+
</div>
|
|
134
|
+
<div>Associated region</div>
|
|
100
135
|
</div>
|
|
101
|
-
<div>Associated region</div>
|
|
102
136
|
</div>
|
|
103
|
-
</
|
|
137
|
+
</Grid.Row>
|
|
104
138
|
</Grid.Column>
|
|
105
139
|
</Grid>
|
|
106
140
|
) : null}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { clearFilters } from './utils';
|
|
4
4
|
|
|
5
5
|
const normalizeSearchInput = (searchInput) => {
|
|
6
6
|
let normInput = searchInput
|
|
@@ -20,7 +20,7 @@ export function DemoSitesFilter(props) {
|
|
|
20
20
|
activeFilters,
|
|
21
21
|
setActiveFilters,
|
|
22
22
|
filterName,
|
|
23
|
-
map,
|
|
23
|
+
// map,
|
|
24
24
|
} = props;
|
|
25
25
|
|
|
26
26
|
const customOrder = props?.customOrder || [];
|
|
@@ -103,7 +103,7 @@ export function DemoSitesFilter(props) {
|
|
|
103
103
|
}
|
|
104
104
|
setActiveFilters(temp);
|
|
105
105
|
// scrollToElement('search-input');
|
|
106
|
-
centerAndResetMapZoom(map);
|
|
106
|
+
// centerAndResetMapZoom(map);
|
|
107
107
|
}}
|
|
108
108
|
/>
|
|
109
109
|
<span>{label}</span>
|
|
@@ -132,7 +132,7 @@ export function DemoSitesFilters(props) {
|
|
|
132
132
|
|
|
133
133
|
return (
|
|
134
134
|
<>
|
|
135
|
-
{!hideFilters ? (
|
|
135
|
+
{/* {!hideFilters ? (
|
|
136
136
|
<DemoSitesFilter
|
|
137
137
|
filterTitle="Objective/Enabler"
|
|
138
138
|
filterName="objective_filter"
|
|
@@ -140,13 +140,19 @@ export function DemoSitesFilters(props) {
|
|
|
140
140
|
activeFilters={activeFilters}
|
|
141
141
|
setActiveFilters={setActiveFilters}
|
|
142
142
|
map={map}
|
|
143
|
-
customOrder={
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
customOrder={objectivesCustomOrder}
|
|
144
|
+
/>
|
|
145
|
+
) : (
|
|
146
|
+
''
|
|
147
|
+
)} */}
|
|
148
|
+
{!hideFilters ? (
|
|
149
|
+
<DemoSitesFilter
|
|
150
|
+
filterTitle="Target"
|
|
151
|
+
filterName="target_filter"
|
|
152
|
+
filters={filters}
|
|
153
|
+
activeFilters={activeFilters}
|
|
154
|
+
setActiveFilters={setActiveFilters}
|
|
155
|
+
map={map}
|
|
150
156
|
/>
|
|
151
157
|
) : (
|
|
152
158
|
''
|
|
@@ -184,7 +190,7 @@ export function DemoSitesFilters(props) {
|
|
|
184
190
|
}
|
|
185
191
|
|
|
186
192
|
export function SearchBox(props) {
|
|
187
|
-
const { setSearchInput,
|
|
193
|
+
const { setSearchInput, onSelectedCase } = props;
|
|
188
194
|
const [showClearButton, setShowClearButton] = React.useState(false);
|
|
189
195
|
|
|
190
196
|
return (
|
|
@@ -209,7 +215,7 @@ export function SearchBox(props) {
|
|
|
209
215
|
// popupOverlay.style.visibility = 'hidden';
|
|
210
216
|
setSearchInput(searchInput);
|
|
211
217
|
// scrollToElement('search-input');
|
|
212
|
-
centerAndResetMapZoom(map);
|
|
218
|
+
// centerAndResetMapZoom(map);
|
|
213
219
|
}}
|
|
214
220
|
></input>
|
|
215
221
|
<div className="terms-box-left">
|
|
@@ -227,7 +233,7 @@ export function SearchBox(props) {
|
|
|
227
233
|
setSearchInput('');
|
|
228
234
|
setShowClearButton(false);
|
|
229
235
|
// scrollToElement('search-input');
|
|
230
|
-
centerAndResetMapZoom(map);
|
|
236
|
+
// centerAndResetMapZoom(map);
|
|
231
237
|
}}
|
|
232
238
|
></i>
|
|
233
239
|
</div>
|
|
@@ -247,7 +253,7 @@ export function SearchBox(props) {
|
|
|
247
253
|
|
|
248
254
|
setSearchInput(searchInputVal);
|
|
249
255
|
// scrollToElement('search-input');
|
|
250
|
-
centerAndResetMapZoom(map);
|
|
256
|
+
// centerAndResetMapZoom(map);
|
|
251
257
|
}}
|
|
252
258
|
onKeyDown={() => {}}
|
|
253
259
|
tabIndex="0"
|
|
@@ -266,30 +272,16 @@ export function SearchBox(props) {
|
|
|
266
272
|
|
|
267
273
|
export function ActiveFilters(props) {
|
|
268
274
|
const { filters, activeFilters, setActiveFilters } = props;
|
|
269
|
-
const hasActiveFilters =
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const filterInputs = document.querySelectorAll(
|
|
280
|
-
'#cse-filter .filter-input input',
|
|
281
|
-
);
|
|
282
|
-
for (let i = 0; i < filterInputs.length; i++) {
|
|
283
|
-
filterInputs[i].checked = false;
|
|
284
|
-
}
|
|
285
|
-
setActiveFilters({
|
|
286
|
-
objective_filter: [],
|
|
287
|
-
indicator_filter: [],
|
|
288
|
-
project_filter: [],
|
|
289
|
-
country_filter: [],
|
|
290
|
-
});
|
|
291
|
-
// scrollToElement('search-input');
|
|
292
|
-
};
|
|
275
|
+
const hasActiveFilters = false;
|
|
276
|
+
// const hasActiveFilters = Object.entries(activeFilters).some(
|
|
277
|
+
// ([filterName, filterList]) => {
|
|
278
|
+
// return false;
|
|
279
|
+
// if (filterList.length > 0) {
|
|
280
|
+
// return true;
|
|
281
|
+
// }
|
|
282
|
+
// return false;
|
|
283
|
+
// },
|
|
284
|
+
// );
|
|
293
285
|
|
|
294
286
|
const removeFilter = (filterName, filterCode) => {
|
|
295
287
|
const temp = JSON.parse(JSON.stringify(activeFilters));
|
|
@@ -316,7 +308,7 @@ export function ActiveFilters(props) {
|
|
|
316
308
|
<div className="filter-list-header">
|
|
317
309
|
<h4 className="filter-list-title">Active filters</h4>
|
|
318
310
|
<button
|
|
319
|
-
onClick={clearFilters}
|
|
311
|
+
onClick={() => clearFilters(setActiveFilters)}
|
|
320
312
|
className="ui mini basic compact button clear-btn"
|
|
321
313
|
>
|
|
322
314
|
clear all
|
|
@@ -349,6 +341,31 @@ export function ActiveFilters(props) {
|
|
|
349
341
|
) : (
|
|
350
342
|
''
|
|
351
343
|
)}
|
|
344
|
+
{activeFilters.target_filter.length > 0 ? (
|
|
345
|
+
<div className="filter-wrapper">
|
|
346
|
+
<div className="filter-label">Target:</div>
|
|
347
|
+
{activeFilters.target_filter.map((filterCode) => {
|
|
348
|
+
const filterLabel = filters.target_filter[filterCode];
|
|
349
|
+
return (
|
|
350
|
+
<div className="ui basic label filter-value">
|
|
351
|
+
<span>{filterLabel}</span>
|
|
352
|
+
<i
|
|
353
|
+
tabIndex="0"
|
|
354
|
+
onKeyPress={() => {}}
|
|
355
|
+
onClick={() => {
|
|
356
|
+
removeFilter('target_filter', filterCode);
|
|
357
|
+
// scrollToElement('search-input');
|
|
358
|
+
}}
|
|
359
|
+
role="button"
|
|
360
|
+
className="close icon"
|
|
361
|
+
></i>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
})}
|
|
365
|
+
</div>
|
|
366
|
+
) : (
|
|
367
|
+
''
|
|
368
|
+
)}
|
|
352
369
|
{activeFilters.indicator_filter.length > 0 ? (
|
|
353
370
|
<div className="filter-wrapper">
|
|
354
371
|
<div className="filter-label">Indicator:</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { centerAndResetMapZoom,
|
|
2
|
+
import { centerAndResetMapZoom, isValidURL } from './utils';
|
|
3
3
|
|
|
4
4
|
const showPageNr = (pageNr, currentPage, numberOfPages) => {
|
|
5
5
|
// show first 5 pages
|
|
@@ -271,7 +271,7 @@ export default function DemoSitesList(props) {
|
|
|
271
271
|
// scroll to the map
|
|
272
272
|
// scrollToElement('ol-map-container');
|
|
273
273
|
|
|
274
|
-
zoomMapToFeatures(map, [item], 5000);
|
|
274
|
+
// zoomMapToFeatures(map, [item], 5000);
|
|
275
275
|
onSelectedCase(item.values_);
|
|
276
276
|
|
|
277
277
|
const popupOverlay =
|
|
@@ -9,7 +9,12 @@ import InfoOverlay from './InfoOverlay';
|
|
|
9
9
|
import FeatureInteraction from './FeatureInteraction';
|
|
10
10
|
import { useMapContext } from '@eeacms/volto-openlayers-map/api';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
centerAndResetMapZoom,
|
|
14
|
+
clearFilters,
|
|
15
|
+
getFeatures,
|
|
16
|
+
// zoomMapToFeatures,
|
|
17
|
+
} from './utils';
|
|
13
18
|
|
|
14
19
|
const styleCache = {};
|
|
15
20
|
const MapContextGateway = ({ setMap }) => {
|
|
@@ -24,11 +29,14 @@ export default function DemoSitesMap(props) {
|
|
|
24
29
|
const {
|
|
25
30
|
items,
|
|
26
31
|
activeItems,
|
|
32
|
+
setActiveFilters,
|
|
27
33
|
hideFilters,
|
|
28
34
|
selectedCase,
|
|
29
35
|
onSelectedCase,
|
|
30
36
|
map,
|
|
31
37
|
setMap,
|
|
38
|
+
highlightedIndex,
|
|
39
|
+
setHighlightedIndex,
|
|
32
40
|
} = props;
|
|
33
41
|
const features = getFeatures(items);
|
|
34
42
|
const [resetMapButtonClass, setResetMapButtonClass] =
|
|
@@ -65,7 +73,8 @@ export default function DemoSitesMap(props) {
|
|
|
65
73
|
if (activeItems) {
|
|
66
74
|
pointsSource.clear();
|
|
67
75
|
pointsSource.addFeatures(getFeatures(activeItems));
|
|
68
|
-
hideFilters && zoomMapToFeatures(map, getFeatures(activeItems));
|
|
76
|
+
// hideFilters && zoomMapToFeatures(map, getFeatures(activeItems));
|
|
77
|
+
// zoomMapToFeatures(map, getFeatures(activeItems));
|
|
69
78
|
}
|
|
70
79
|
}, [map, activeItems, pointsSource, hideFilters]);
|
|
71
80
|
|
|
@@ -122,13 +131,18 @@ export default function DemoSitesMap(props) {
|
|
|
122
131
|
view={{
|
|
123
132
|
center: ol.proj.fromLonLat([10, 54]),
|
|
124
133
|
showFullExtent: true,
|
|
125
|
-
zoom:
|
|
134
|
+
zoom: 3.4,
|
|
126
135
|
}}
|
|
127
136
|
pixelRatio={1}
|
|
128
137
|
// controls={ol.control.defaults({ attribution: false })}
|
|
129
138
|
>
|
|
130
139
|
<Controls attribution={false} />
|
|
131
140
|
<Layers>
|
|
141
|
+
{highlightedIndex > 0 && !activeItems.length && (
|
|
142
|
+
<div className="no-results-message">
|
|
143
|
+
No results found. Please refine your filters.
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
132
146
|
<button
|
|
133
147
|
className={cx(
|
|
134
148
|
'reset-map-button ui button secondary',
|
|
@@ -137,15 +151,19 @@ export default function DemoSitesMap(props) {
|
|
|
137
151
|
onClick={() => {
|
|
138
152
|
// scrollToElement('search-input');
|
|
139
153
|
onSelectedCase(null);
|
|
154
|
+
clearFilters(setActiveFilters);
|
|
155
|
+
setHighlightedIndex(5);
|
|
140
156
|
if (hideFilters) {
|
|
141
|
-
zoomMapToFeatures(map, getFeatures(activeItems));
|
|
157
|
+
// zoomMapToFeatures(map, getFeatures(activeItems));
|
|
158
|
+
centerAndResetMapZoom(map);
|
|
142
159
|
} else {
|
|
143
160
|
centerAndResetMapZoom(map);
|
|
161
|
+
// zoomMapToFeatures(map, getFeatures(activeItems));
|
|
144
162
|
}
|
|
145
163
|
map.getInteractions().array_[9].getFeatures().clear();
|
|
146
164
|
}}
|
|
147
165
|
>
|
|
148
|
-
<span className="result-info-title">Reset
|
|
166
|
+
<span className="result-info-title">Reset filters</span>
|
|
149
167
|
<i className="icon ri-map-2-line"></i>
|
|
150
168
|
</button>
|
|
151
169
|
<InfoOverlay
|
|
@@ -200,7 +218,8 @@ const selectedClusterStyle = (selectedFeature) => {
|
|
|
200
218
|
}
|
|
201
219
|
// set size === 1 to enable clusterization
|
|
202
220
|
if (size) {
|
|
203
|
-
let color = feature.values_.features[0].values_['color'];
|
|
221
|
+
// let color = feature.values_.features[0].values_['color'];
|
|
222
|
+
let color = '#0179cf';
|
|
204
223
|
let width = feature.values_.features[0].values_['width'];
|
|
205
224
|
let radius = feature.values_.features[0].values_['radius'];
|
|
206
225
|
// console.log(color)
|
|
@@ -6,9 +6,13 @@ export default function FeatureDisplay({ feature }) {
|
|
|
6
6
|
<div id="csepopup">
|
|
7
7
|
<h3>
|
|
8
8
|
<strong>
|
|
9
|
-
<
|
|
9
|
+
<span
|
|
10
|
+
// target="_blank"
|
|
11
|
+
// rel="noopener noreferrer"
|
|
12
|
+
// href={feature.path}
|
|
13
|
+
>
|
|
10
14
|
{truncateText(feature.title)}
|
|
11
|
-
</
|
|
15
|
+
</span>
|
|
12
16
|
</strong>
|
|
13
17
|
</h3>
|
|
14
18
|
{feature.info ? (
|
|
@@ -57,6 +61,19 @@ export default function FeatureDisplay({ feature }) {
|
|
|
57
61
|
''
|
|
58
62
|
)}
|
|
59
63
|
|
|
64
|
+
{feature.objective.length > 0 ? (
|
|
65
|
+
<div>
|
|
66
|
+
<span className="popup-title blue">Objective/Enabler</span>
|
|
67
|
+
<ul>
|
|
68
|
+
{feature.objective.map((item, index) => {
|
|
69
|
+
return <li key={index}>{item}</li>;
|
|
70
|
+
})}
|
|
71
|
+
</ul>
|
|
72
|
+
</div>
|
|
73
|
+
) : (
|
|
74
|
+
''
|
|
75
|
+
)}
|
|
76
|
+
|
|
60
77
|
{/* {feature.project_link ? (
|
|
61
78
|
<div>
|
|
62
79
|
<span className="popup-title blue">Project link: </span>
|
|
@@ -85,13 +102,13 @@ export default function FeatureDisplay({ feature }) {
|
|
|
85
102
|
{feature.indicators.map((item, index) => {
|
|
86
103
|
return (
|
|
87
104
|
<li key={index}>
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
<span
|
|
106
|
+
// target="_blank"
|
|
107
|
+
// rel="noopener noreferrer"
|
|
108
|
+
// href={item['path']}
|
|
92
109
|
>
|
|
93
110
|
{item['title']}
|
|
94
|
-
</
|
|
111
|
+
</span>
|
|
95
112
|
</li>
|
|
96
113
|
);
|
|
97
114
|
})}
|
|
@@ -100,14 +117,6 @@ export default function FeatureDisplay({ feature }) {
|
|
|
100
117
|
) : (
|
|
101
118
|
''
|
|
102
119
|
)}
|
|
103
|
-
{/* <div>
|
|
104
|
-
<h4>Indicators</h4>
|
|
105
|
-
<ul>
|
|
106
|
-
{feature.sectors.map((item, index) => {
|
|
107
|
-
return <li key={index}>{item}</li>;
|
|
108
|
-
})}
|
|
109
|
-
</ul>
|
|
110
|
-
</div> */}
|
|
111
120
|
</div>
|
|
112
121
|
) : null;
|
|
113
122
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { openlayers as ol } from '@eeacms/volto-openlayers-map';
|
|
3
3
|
import { useMapContext } from '@eeacms/volto-openlayers-map/api';
|
|
4
|
-
import { zoomMapToFeatures } from './utils';
|
|
4
|
+
// import { zoomMapToFeatures } from './utils';
|
|
5
5
|
|
|
6
6
|
export const useStyles = () => {
|
|
7
7
|
const selected = React.useMemo(
|
|
@@ -67,7 +67,7 @@ export default function FeatureInteraction({ onFeatureSelect }) {
|
|
|
67
67
|
// });
|
|
68
68
|
} else {
|
|
69
69
|
onFeatureSelect(null);
|
|
70
|
-
zoomMapToFeatures(map, subfeatures);
|
|
70
|
+
// zoomMapToFeatures(map, subfeatures);
|
|
71
71
|
}
|
|
72
72
|
});
|
|
73
73
|
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import loadable from '@loadable/component';
|
|
4
|
+
import { Grid } from 'semantic-ui-react'; // Dropdown,
|
|
5
|
+
|
|
6
|
+
import { objectivesCustomOrder } from './utils';
|
|
7
|
+
|
|
8
|
+
const Plot = loadable(() => import('react-plotly.js'));
|
|
9
|
+
|
|
10
|
+
const ObjectivesChart = ({
|
|
11
|
+
items,
|
|
12
|
+
// activeItems,
|
|
13
|
+
// filters,
|
|
14
|
+
activeFilters,
|
|
15
|
+
setActiveFilters,
|
|
16
|
+
// hideFilters,
|
|
17
|
+
// map,
|
|
18
|
+
highlightedIndex,
|
|
19
|
+
setHighlightedIndex,
|
|
20
|
+
}) => {
|
|
21
|
+
const initialized = useRef(false);
|
|
22
|
+
const chartRef = useRef(null);
|
|
23
|
+
const objectiveCounts = {};
|
|
24
|
+
|
|
25
|
+
for (const item of items) {
|
|
26
|
+
item.properties.objective.map((obj) => {
|
|
27
|
+
objectiveCounts[obj] = (objectiveCounts[obj] || 0) + 1;
|
|
28
|
+
return [];
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const _sorted = Object.entries(objectiveCounts).sort(
|
|
32
|
+
(a, b) =>
|
|
33
|
+
objectivesCustomOrder.indexOf(a[0]) - objectivesCustomOrder.indexOf(b[0]),
|
|
34
|
+
);
|
|
35
|
+
const objectives = Object.fromEntries(_sorted);
|
|
36
|
+
const objectivesLength = Object.keys(objectives).length;
|
|
37
|
+
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
// set the objectives and the count
|
|
40
|
+
if (!items) return;
|
|
41
|
+
if (initialized.current) return;
|
|
42
|
+
|
|
43
|
+
const interval = setInterval(() => {
|
|
44
|
+
setHighlightedIndex((prevIndex) => {
|
|
45
|
+
if (prevIndex + 1 >= objectivesLength + 1) {
|
|
46
|
+
clearInterval(interval); // Stop after last item
|
|
47
|
+
initialized.current = true;
|
|
48
|
+
return prevIndex;
|
|
49
|
+
}
|
|
50
|
+
return prevIndex + 1;
|
|
51
|
+
});
|
|
52
|
+
}, 2000);
|
|
53
|
+
// console.log('init');
|
|
54
|
+
return () => clearInterval(interval); // Cleanup on unmount
|
|
55
|
+
}, [items, objectivesLength, highlightedIndex, setHighlightedIndex]);
|
|
56
|
+
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
// if (!objectives) return;
|
|
59
|
+
const filterKey = 'objective_filter';
|
|
60
|
+
|
|
61
|
+
if (highlightedIndex === -1) {
|
|
62
|
+
const newValue = [undefined];
|
|
63
|
+
|
|
64
|
+
setActiveFilters((prev) => {
|
|
65
|
+
const currentValues = prev[filterKey];
|
|
66
|
+
// Don't update if no change
|
|
67
|
+
const isSame =
|
|
68
|
+
currentValues.length === newValue.length &&
|
|
69
|
+
currentValues.every((v, i) => v === newValue[i]);
|
|
70
|
+
|
|
71
|
+
if (isSame) return prev;
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
...prev,
|
|
75
|
+
[filterKey]: newValue,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const currentObjective = Object.keys(objectives)[highlightedIndex];
|
|
82
|
+
const newValue = currentObjective ? [currentObjective] : [];
|
|
83
|
+
|
|
84
|
+
setActiveFilters((prev) => {
|
|
85
|
+
const currentValues = prev[filterKey];
|
|
86
|
+
// Don't update if no change
|
|
87
|
+
const isSame =
|
|
88
|
+
currentValues.length === newValue.length &&
|
|
89
|
+
currentValues.every((v, i) => v === newValue[i]);
|
|
90
|
+
|
|
91
|
+
if (isSame) return prev;
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
...prev,
|
|
95
|
+
[filterKey]: newValue,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
// console.log(highlightedIndex, activeFilters);
|
|
99
|
+
}, [objectives, activeFilters, setActiveFilters, highlightedIndex]);
|
|
100
|
+
|
|
101
|
+
const handleClick = (event) => {
|
|
102
|
+
let label = '';
|
|
103
|
+
|
|
104
|
+
if (event.points) {
|
|
105
|
+
const point = event.points[0];
|
|
106
|
+
label = point.label;
|
|
107
|
+
// const value = point.value;
|
|
108
|
+
} else {
|
|
109
|
+
label = event.target.textContent;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const tempFilters = JSON.parse(JSON.stringify(activeFilters));
|
|
113
|
+
if (tempFilters['objective_filter'].includes(label)) {
|
|
114
|
+
tempFilters['objective_filter'] = tempFilters['objective_filter'].filter(
|
|
115
|
+
(i) => i !== label,
|
|
116
|
+
);
|
|
117
|
+
setHighlightedIndex(5);
|
|
118
|
+
} else {
|
|
119
|
+
tempFilters['objective_filter'] = [];
|
|
120
|
+
tempFilters['objective_filter'].push(label);
|
|
121
|
+
setHighlightedIndex(Object.keys(objectives).indexOf(label));
|
|
122
|
+
}
|
|
123
|
+
setActiveFilters(tempFilters);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const handleHover = () => {
|
|
127
|
+
if (chartRef.current) {
|
|
128
|
+
chartRef.current.style.cursor = 'pointer'; // Change cursor on hover
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const handleUnhover = () => {
|
|
133
|
+
if (chartRef.current) {
|
|
134
|
+
chartRef.current.style.cursor = 'default'; // Reset cursor
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const labels = Object.keys(objectives);
|
|
139
|
+
const values = Object.values(objectives);
|
|
140
|
+
const totalCount = values.reduce((acc, curr) => acc + curr, 0);
|
|
141
|
+
const customColors = ['#007b6c', '#fdaf20', '#004b7f', '#f9eb8a', '#9e83b6']; // Adjust colors as needed
|
|
142
|
+
const grayColor = '#d3d3d3';
|
|
143
|
+
// const pull = labels.map((_, i) => (i === highlightedIndex ? 0.1 : 0));
|
|
144
|
+
const inactiveColors = labels.map((_, i) =>
|
|
145
|
+
i === highlightedIndex ? customColors[i % customColors.length] : grayColor,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// console.log(highlightedIndex);
|
|
149
|
+
|
|
150
|
+
return highlightedIndex >= -1 ? (
|
|
151
|
+
<div className="objectives-chart fade-in">
|
|
152
|
+
<Grid.Row className="chart-title">
|
|
153
|
+
Demo sites and Associated regions
|
|
154
|
+
</Grid.Row>
|
|
155
|
+
<Grid.Row className="chart-title">Objective/Enabler</Grid.Row>
|
|
156
|
+
<Grid.Row className="chart-container">
|
|
157
|
+
<div ref={chartRef}>
|
|
158
|
+
<Plot
|
|
159
|
+
useResizeHandler
|
|
160
|
+
data={[
|
|
161
|
+
{
|
|
162
|
+
type: 'pie',
|
|
163
|
+
labels: labels,
|
|
164
|
+
values: values,
|
|
165
|
+
textinfo: 'none',
|
|
166
|
+
// textinfo: highlightedIndex === 5 ? 'value' : 'none',
|
|
167
|
+
hole: 0.4,
|
|
168
|
+
insidetextorientation: 'radial',
|
|
169
|
+
hoverinfo: 'label+value',
|
|
170
|
+
marker: {
|
|
171
|
+
colors:
|
|
172
|
+
highlightedIndex === 5 ? customColors : inactiveColors, // Apply custom colors here
|
|
173
|
+
line: {
|
|
174
|
+
color: '#e0e0e0', // light gray
|
|
175
|
+
width: 1, // thin border
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
direction: 'clockwise',
|
|
179
|
+
// pull,
|
|
180
|
+
},
|
|
181
|
+
]}
|
|
182
|
+
layout={{
|
|
183
|
+
width: 300,
|
|
184
|
+
height: 300,
|
|
185
|
+
// title: 'Objectives Distribution',
|
|
186
|
+
showlegend: false,
|
|
187
|
+
margin: {
|
|
188
|
+
t: 20, // Top margin
|
|
189
|
+
b: 20, // Bottom margin
|
|
190
|
+
l: 0, // Left margin
|
|
191
|
+
r: 0, // Right margin
|
|
192
|
+
},
|
|
193
|
+
annotations: [
|
|
194
|
+
{
|
|
195
|
+
text:
|
|
196
|
+
highlightedIndex === 5
|
|
197
|
+
? `${totalCount}`
|
|
198
|
+
: values[highlightedIndex] || '', // Display total count in the center
|
|
199
|
+
font: {
|
|
200
|
+
family:
|
|
201
|
+
"'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif",
|
|
202
|
+
size: 24, // Adjust font size as needed
|
|
203
|
+
weight: 'bold',
|
|
204
|
+
},
|
|
205
|
+
showarrow: false, // No arrow pointing to the center
|
|
206
|
+
x: 0.5, // Position in the center (horizontal)
|
|
207
|
+
y: 0.5, // Position in the center (vertical)
|
|
208
|
+
align: 'center',
|
|
209
|
+
valign: 'middle',
|
|
210
|
+
bgcolor: 'rgba(255, 255, 255, 0.6)', // Optional background color
|
|
211
|
+
borderpad: 10, // Padding around the text
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
}}
|
|
215
|
+
config={{
|
|
216
|
+
responsive: true,
|
|
217
|
+
displayModeBar: true,
|
|
218
|
+
modeBarButtonsToRemove: ['toImage'], // Removes download button
|
|
219
|
+
displaylogo: false, // Removes "Produced with Plotly.js"
|
|
220
|
+
}}
|
|
221
|
+
onClick={handleClick}
|
|
222
|
+
onHover={handleHover}
|
|
223
|
+
onUnhover={handleUnhover}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
</Grid.Row>
|
|
227
|
+
<Grid.Row>
|
|
228
|
+
<ul>
|
|
229
|
+
{Object.entries(objectives).map(([item, count], index) => (
|
|
230
|
+
<li
|
|
231
|
+
className={cx(
|
|
232
|
+
index === highlightedIndex ? 'active' : '',
|
|
233
|
+
customColors[index].replace('#', 'C'),
|
|
234
|
+
)}
|
|
235
|
+
>
|
|
236
|
+
<div
|
|
237
|
+
onClick={handleClick}
|
|
238
|
+
onKeyDown={() => {}}
|
|
239
|
+
tabIndex={index}
|
|
240
|
+
role="button"
|
|
241
|
+
key={item}
|
|
242
|
+
>
|
|
243
|
+
{item}
|
|
244
|
+
</div>
|
|
245
|
+
</li>
|
|
246
|
+
))}
|
|
247
|
+
</ul>
|
|
248
|
+
</Grid.Row>
|
|
249
|
+
</div>
|
|
250
|
+
) : (
|
|
251
|
+
''
|
|
252
|
+
);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export default ObjectivesChart;
|
|
@@ -12,6 +12,23 @@
|
|
|
12
12
|
.ol-map {
|
|
13
13
|
height: 600px;
|
|
14
14
|
min-height: 600px;
|
|
15
|
+
font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
|
16
|
+
|
|
17
|
+
.no-results-message {
|
|
18
|
+
position: absolute;
|
|
19
|
+
z-index: 9;
|
|
20
|
+
top: 46%;
|
|
21
|
+
right: 28%;
|
|
22
|
+
bottom: 47%;
|
|
23
|
+
left: 27%;
|
|
24
|
+
padding: 0.5em 1em;
|
|
25
|
+
border: 4px solid #dfdfdf;
|
|
26
|
+
border-radius: 2px;
|
|
27
|
+
background-color: #0088e9;
|
|
28
|
+
color: #ffffff;
|
|
29
|
+
opacity: 0.6;
|
|
30
|
+
text-shadow: none;
|
|
31
|
+
}
|
|
15
32
|
}
|
|
16
33
|
|
|
17
34
|
#csepopup {
|
|
@@ -29,6 +46,7 @@
|
|
|
29
46
|
}
|
|
30
47
|
|
|
31
48
|
h3 {
|
|
49
|
+
color: #069;
|
|
32
50
|
font-size: 1.4rem;
|
|
33
51
|
}
|
|
34
52
|
|
|
@@ -388,12 +406,12 @@
|
|
|
388
406
|
}
|
|
389
407
|
|
|
390
408
|
.legend {
|
|
391
|
-
position: absolute;
|
|
409
|
+
// position: absolute;
|
|
392
410
|
bottom: 2em;
|
|
393
411
|
display: flex;
|
|
394
|
-
flex-direction:
|
|
412
|
+
flex-direction: row;
|
|
395
413
|
align-items: flex-start;
|
|
396
|
-
gap:
|
|
414
|
+
gap: 1em;
|
|
397
415
|
|
|
398
416
|
.legend-row {
|
|
399
417
|
display: flex;
|
|
@@ -411,9 +429,9 @@
|
|
|
411
429
|
width: 22px;
|
|
412
430
|
min-width: 22px;
|
|
413
431
|
height: 22px;
|
|
414
|
-
/* dark green shade */
|
|
415
432
|
border-radius: 50%;
|
|
416
|
-
background-color: #007265;
|
|
433
|
+
// background-color: #007265;
|
|
434
|
+
background-color: #0179cf;
|
|
417
435
|
gap: 0.5em;
|
|
418
436
|
}
|
|
419
437
|
|
|
@@ -439,3 +457,111 @@
|
|
|
439
457
|
transform: translate(-50%, -50%);
|
|
440
458
|
}
|
|
441
459
|
}
|
|
460
|
+
|
|
461
|
+
.right-side-filters {
|
|
462
|
+
display: flex !important;
|
|
463
|
+
flex-direction: column;
|
|
464
|
+
align-items: center;
|
|
465
|
+
justify-content: space-around;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.objectives-chart {
|
|
469
|
+
font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
|
470
|
+
|
|
471
|
+
.js-plotly-plot .plotly,
|
|
472
|
+
.js-plotly-plot .plotly div {
|
|
473
|
+
font-family: 'Roboto', 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
&.fade-in {
|
|
477
|
+
animation: fadeIn 1s ease-in forwards;
|
|
478
|
+
opacity: 0;
|
|
479
|
+
/* Start invisible */
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
@keyframes fadeIn {
|
|
483
|
+
to {
|
|
484
|
+
opacity: 1;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.main-svg {
|
|
489
|
+
overflow: visible !important;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.hoverlayer {
|
|
493
|
+
overflow: visible !important;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.chart-title {
|
|
497
|
+
font-size: 1em;
|
|
498
|
+
font-weight: 500;
|
|
499
|
+
text-align: center;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.chart-container {
|
|
503
|
+
text-align: center;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
ul {
|
|
507
|
+
display: flex;
|
|
508
|
+
flex-direction: column;
|
|
509
|
+
padding-left: 1.3em;
|
|
510
|
+
gap: 0.5em;
|
|
511
|
+
list-style: none;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
li {
|
|
515
|
+
display: flex;
|
|
516
|
+
flex-direction: row;
|
|
517
|
+
align-items: center;
|
|
518
|
+
font-size: 0.8em;
|
|
519
|
+
line-height: 1.5em;
|
|
520
|
+
|
|
521
|
+
&:hover {
|
|
522
|
+
cursor: pointer;
|
|
523
|
+
font-weight: 500;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
&.C007b6c::before {
|
|
527
|
+
background-color: #007b6c;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
&.Cfdaf20::before {
|
|
531
|
+
background-color: #fdaf20;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
&.C004b7f::before {
|
|
535
|
+
background-color: #004b7f;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
&.Cf9eb8a::before {
|
|
539
|
+
background-color: #f9eb8a;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
&.C9e83b6::before {
|
|
543
|
+
background-color: #9e83b6;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
&::before {
|
|
547
|
+
width: 0.7em;
|
|
548
|
+
height: 0.7em;
|
|
549
|
+
flex-shrink: 0;
|
|
550
|
+
border-radius: 2px;
|
|
551
|
+
/* change as needed */
|
|
552
|
+
margin-right: 8px;
|
|
553
|
+
content: '';
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
&.active {
|
|
557
|
+
font-size: 1em;
|
|
558
|
+
// color: red;
|
|
559
|
+
font-weight: 500;
|
|
560
|
+
|
|
561
|
+
&::before {
|
|
562
|
+
width: 1em;
|
|
563
|
+
height: 1em;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { openlayers as ol } from '@eeacms/volto-openlayers-map';
|
|
2
2
|
|
|
3
|
+
export const objectivesCustomOrder = [
|
|
4
|
+
'Objective 1: Protect and restore marine and freshwater ecosystems and biodiversity',
|
|
5
|
+
'Objective 2: Prevent and eliminate pollution of our oceans, seas and waters',
|
|
6
|
+
'Objective 3: Make the sustainable blue economy carbon-neutral and circular',
|
|
7
|
+
'Enabler 1: Digital twin of the ocean',
|
|
8
|
+
'Enabler 2: Public mobilisation and engagement',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export const clearFilters = (setActiveFilters) => {
|
|
12
|
+
const filterInputs = document.querySelectorAll(
|
|
13
|
+
'#cse-filter .filter-input input',
|
|
14
|
+
);
|
|
15
|
+
for (let i = 0; i < filterInputs.length; i++) {
|
|
16
|
+
filterInputs[i].checked = false;
|
|
17
|
+
}
|
|
18
|
+
setActiveFilters({
|
|
19
|
+
objective_filter: [],
|
|
20
|
+
target_filter: [],
|
|
21
|
+
indicator_filter: [],
|
|
22
|
+
project_filter: [],
|
|
23
|
+
country_filter: [],
|
|
24
|
+
});
|
|
25
|
+
// scrollToElement('search-input');
|
|
26
|
+
};
|
|
27
|
+
|
|
3
28
|
export const truncateText = (str, max = 50) => {
|
|
4
29
|
if (str.length <= max) {
|
|
5
30
|
return str;
|
|
@@ -18,7 +43,7 @@ export function isValidURL(string) {
|
|
|
18
43
|
|
|
19
44
|
export function centerAndResetMapZoom(map) {
|
|
20
45
|
map.getView().animate({
|
|
21
|
-
zoom:
|
|
46
|
+
zoom: 3.4,
|
|
22
47
|
duration: 1000,
|
|
23
48
|
center: ol.proj.transform([10, 54], 'EPSG:4326', 'EPSG:3857'),
|
|
24
49
|
});
|
|
@@ -39,20 +64,42 @@ export function getExtentOfFeatures(features) {
|
|
|
39
64
|
|
|
40
65
|
export function zoomMapToFeatures(map, features, threshold = 500) {
|
|
41
66
|
const extent = getExtentOfFeatures(features);
|
|
42
|
-
|
|
67
|
+
|
|
68
|
+
// let extentBuffer = (extent[3] - extent[1] + extent[2] - extent[0]) / 4;
|
|
69
|
+
// extentBuffer = extentBuffer < threshold ? threshold : extentBuffer;
|
|
70
|
+
// const paddedExtent = ol.extent.buffer(extent, extentBuffer);
|
|
71
|
+
|
|
72
|
+
const width = extent[2] - extent[0];
|
|
73
|
+
const height = extent[3] - extent[1];
|
|
74
|
+
const bufferFactor = 0.05; // 5% buffer
|
|
75
|
+
|
|
76
|
+
let extentBuffer = Math.max(width, height) * bufferFactor;
|
|
43
77
|
extentBuffer = extentBuffer < threshold ? threshold : extentBuffer;
|
|
44
78
|
const paddedExtent = ol.extent.buffer(extent, extentBuffer);
|
|
45
|
-
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// map.getView().fit(paddedExtent, { ...map.getSize(), duration: 1000 });
|
|
82
|
+
map.getView().fit(paddedExtent, {
|
|
83
|
+
size: map.getSize(), // pass size explicitly
|
|
84
|
+
// padding: [0, 0, 0, 0], // top, right, bottom, left
|
|
85
|
+
duration: 1000,
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// no features available, zooming fails
|
|
89
|
+
}
|
|
46
90
|
}
|
|
47
91
|
|
|
48
92
|
export function getFeatures(cases) {
|
|
49
93
|
const Feature = ol.ol.Feature;
|
|
50
94
|
const colors = {
|
|
51
|
-
'
|
|
52
|
-
|
|
53
|
-
'Prevent and eliminate pollution of waters':
|
|
54
|
-
|
|
55
|
-
'
|
|
95
|
+
'Objective 1: Protect and restore marine and freshwater ecosystems and biodiversity':
|
|
96
|
+
'#007b6c',
|
|
97
|
+
'Objective 2: Prevent and eliminate pollution of our oceans, seas and waters':
|
|
98
|
+
'#fdaf20',
|
|
99
|
+
'Objective 3: Make the sustainable blue economy carbon-neutral and circular':
|
|
100
|
+
'#004b7f',
|
|
101
|
+
'Enabler 1: Digital twin of the ocean': '#f9eb8a',
|
|
102
|
+
'Enabler 2: Public mobilisation and engagement': '#9e83b6',
|
|
56
103
|
};
|
|
57
104
|
const width = {
|
|
58
105
|
'Demo site': 6,
|
|
@@ -85,10 +132,11 @@ export function getFeatures(cases) {
|
|
|
85
132
|
info: c.properties.info,
|
|
86
133
|
website: c.properties.website,
|
|
87
134
|
objective: c.properties.objective,
|
|
135
|
+
target: c.properties.target,
|
|
88
136
|
description: c.properties.description,
|
|
89
137
|
index: index,
|
|
90
138
|
path: c.properties.path,
|
|
91
|
-
color: colors[c.properties.objective] || '#B83230',
|
|
139
|
+
color: colors[c.properties.objective[0]] || '#B83230',
|
|
92
140
|
width: width[c.properties.type_is_region],
|
|
93
141
|
radius: radius[c.properties.type_is_region],
|
|
94
142
|
},
|
|
@@ -101,6 +149,7 @@ export function getFeatures(cases) {
|
|
|
101
149
|
export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
102
150
|
const data = cases.filter((_case) => {
|
|
103
151
|
let flag_objective = false;
|
|
152
|
+
let flag_target = false;
|
|
104
153
|
let flag_indicator = false;
|
|
105
154
|
let flag_project = false;
|
|
106
155
|
let flag_country = false;
|
|
@@ -122,7 +171,17 @@ export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
|
122
171
|
let objective = _case.properties.objective;
|
|
123
172
|
|
|
124
173
|
activeFilters.objective_filter.forEach((filter) => {
|
|
125
|
-
if (objective
|
|
174
|
+
if (objective?.includes(filter)) flag_objective = true;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!activeFilters.target_filter.length) {
|
|
179
|
+
flag_target = true;
|
|
180
|
+
} else {
|
|
181
|
+
let target = _case.properties.target;
|
|
182
|
+
|
|
183
|
+
activeFilters.target_filter.forEach((filter) => {
|
|
184
|
+
if (target?.includes(filter)) flag_target = true;
|
|
126
185
|
});
|
|
127
186
|
}
|
|
128
187
|
|
|
@@ -162,6 +221,7 @@ export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
|
162
221
|
|
|
163
222
|
return flag_indicatorOnly &&
|
|
164
223
|
flag_objective &&
|
|
224
|
+
flag_target &&
|
|
165
225
|
flag_indicator &&
|
|
166
226
|
flag_country &&
|
|
167
227
|
flag_project
|
|
@@ -175,6 +235,7 @@ export function filterCases(cases, activeFilters, indicatorOnly) {
|
|
|
175
235
|
export function getFilters(cases, indicatorOnly) {
|
|
176
236
|
let _filters = {
|
|
177
237
|
objective_filter: {},
|
|
238
|
+
target_filter: {},
|
|
178
239
|
indicator_filter: {},
|
|
179
240
|
project_filter: {},
|
|
180
241
|
country_filter: {},
|
|
@@ -197,9 +258,20 @@ export function getFilters(cases, indicatorOnly) {
|
|
|
197
258
|
});
|
|
198
259
|
|
|
199
260
|
let objective = _case.properties.objective;
|
|
200
|
-
|
|
201
|
-
_filters.objective_filter
|
|
202
|
-
|
|
261
|
+
objective.map((item) => {
|
|
262
|
+
if (item && !_filters.objective_filter.hasOwnProperty(item)) {
|
|
263
|
+
_filters.objective_filter[item] = item;
|
|
264
|
+
}
|
|
265
|
+
return [];
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
let target = _case.properties.target;
|
|
269
|
+
target.map((item) => {
|
|
270
|
+
if (item && !_filters.target_filter.hasOwnProperty(item)) {
|
|
271
|
+
_filters.target_filter[item] = item;
|
|
272
|
+
}
|
|
273
|
+
return [];
|
|
274
|
+
});
|
|
203
275
|
|
|
204
276
|
let project = _case.properties.project;
|
|
205
277
|
if (project && !_filters.project_filter.hasOwnProperty(project)) {
|