@eeacms/volto-bise-policy 1.3.4 → 1.3.6
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 +39 -13
- package/package.json +2 -1
- package/src/components/Widgets/EUNISObjectListWidget.jsx +8 -6
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyExplorerEdit.jsx +22 -1
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyExplorerView.jsx +17 -12
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyExplorerView.test.jsx +1 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyFilters.jsx +59 -13
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyListing.jsx +28 -16
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyListing.test.jsx +54 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyMap.jsx +68 -35
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyMap.test.jsx +37 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/FeatureInteraction.jsx +41 -39
- package/src/components/manage/Blocks/CaseStudyExplorer/FeatureInteraction.test.jsx +39 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/index.js +2 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/schema.js +19 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/styles.less +33 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/utils.js +171 -93
- package/src/components/manage/Blocks/CaseStudyExplorer/utils.test.js +96 -1
- package/theme/globals/eunismarinehabitat.less +26 -10
|
@@ -16,7 +16,13 @@ import InfoOverlay from './InfoOverlay';
|
|
|
16
16
|
import FeatureInteraction from './FeatureInteraction';
|
|
17
17
|
import CaseStudyList from './CaseStudyListing';
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
centerAndResetMapZoom,
|
|
21
|
+
CLUSTER_COLOR,
|
|
22
|
+
getFeatures,
|
|
23
|
+
getSelectInteraction,
|
|
24
|
+
scrollToElement,
|
|
25
|
+
} from './utils';
|
|
20
26
|
|
|
21
27
|
const styleCache = {};
|
|
22
28
|
const MapContextGateway = ({ setMap }) => {
|
|
@@ -69,32 +75,43 @@ function CaseStudyMap(props) {
|
|
|
69
75
|
}),
|
|
70
76
|
);
|
|
71
77
|
|
|
78
|
+
// `ol` is a fresh object literal on every render (see withOpenLayers); keep
|
|
79
|
+
// the latest reference in a ref so effects can depend only on values that
|
|
80
|
+
// actually change. Refreshing the points source regenerates the cluster
|
|
81
|
+
// features, which drops the currently selected feature from the rendered set.
|
|
82
|
+
const olRef = React.useRef(ol);
|
|
83
|
+
olRef.current = ol;
|
|
84
|
+
|
|
72
85
|
React.useEffect(() => {
|
|
73
86
|
if (activeItems) {
|
|
74
87
|
pointsSource.clear();
|
|
75
|
-
pointsSource.addFeatures(
|
|
88
|
+
pointsSource.addFeatures(
|
|
89
|
+
getFeatures({ cases: activeItems, ol: olRef.current }),
|
|
90
|
+
);
|
|
76
91
|
}
|
|
77
|
-
}, [activeItems, pointsSource
|
|
92
|
+
}, [activeItems, pointsSource]);
|
|
78
93
|
|
|
79
94
|
React.useEffect(() => {
|
|
80
95
|
if (!map) return;
|
|
81
96
|
|
|
82
|
-
const moveendListener = (
|
|
83
|
-
// console.log('map.getView()', map.getView());
|
|
84
|
-
// console.log('selectedCase', selectedCase);
|
|
97
|
+
const moveendListener = () => {
|
|
85
98
|
const mapZoom = Math.round(map.getView().getZoom() * 10) / 10;
|
|
86
99
|
const mapCenter = map.getView().getCenter();
|
|
100
|
+
const selectInteraction = getSelectInteraction(map);
|
|
87
101
|
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
if (selectInteraction) {
|
|
103
|
+
if (selectedCase) {
|
|
104
|
+
const coords = selectedCase.geometry.flatCoordinates;
|
|
105
|
+
const pixel = map.getPixelFromCoordinate(coords);
|
|
106
|
+
const selectedFeature = map.getFeaturesAtPixel(pixel)[0];
|
|
107
|
+
|
|
108
|
+
selectInteraction.getFeatures().clear();
|
|
109
|
+
if (selectedFeature) {
|
|
110
|
+
selectInteraction.getFeatures().push(selectedFeature);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
selectInteraction.getFeatures().clear();
|
|
114
|
+
}
|
|
98
115
|
}
|
|
99
116
|
|
|
100
117
|
if (
|
|
@@ -113,12 +130,9 @@ function CaseStudyMap(props) {
|
|
|
113
130
|
return () => {
|
|
114
131
|
map.un('moveend', moveendListener);
|
|
115
132
|
};
|
|
116
|
-
}, [map, selectedCase,
|
|
133
|
+
}, [map, selectedCase, ol.proj, setResetMapButtonClass]);
|
|
117
134
|
|
|
118
|
-
const clusterStyle = React.useMemo(
|
|
119
|
-
() => selectedClusterStyle({ selectedCase, ol }),
|
|
120
|
-
[selectedCase, ol],
|
|
121
|
-
);
|
|
135
|
+
const clusterStyle = React.useMemo(() => selectedClusterStyle({ ol }), [ol]);
|
|
122
136
|
|
|
123
137
|
const MapWithSelection = React.useMemo(() => Map, []);
|
|
124
138
|
// console.log('render');
|
|
@@ -148,7 +162,7 @@ function CaseStudyMap(props) {
|
|
|
148
162
|
scrollToElement('search-input');
|
|
149
163
|
onSelectedCase(null);
|
|
150
164
|
centerAndResetMapZoom({ map, ol });
|
|
151
|
-
map
|
|
165
|
+
getSelectInteraction(map)?.getFeatures().clear();
|
|
152
166
|
}}
|
|
153
167
|
>
|
|
154
168
|
<span className="result-info-title">Reset map</span>
|
|
@@ -164,7 +178,6 @@ function CaseStudyMap(props) {
|
|
|
164
178
|
<FeatureInteraction
|
|
165
179
|
onFeatureSelect={onSelectedCase}
|
|
166
180
|
hideFilters={hideFilters}
|
|
167
|
-
selectedCase={selectedCase}
|
|
168
181
|
/>
|
|
169
182
|
<Layer.Tile source={tileWMSSources[0]} zIndex={0} />
|
|
170
183
|
<Layer.Vector
|
|
@@ -176,20 +189,41 @@ function CaseStudyMap(props) {
|
|
|
176
189
|
</Layers>
|
|
177
190
|
</MapWithSelection>
|
|
178
191
|
{hideFilters ? null : (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
192
|
+
<>
|
|
193
|
+
<div
|
|
194
|
+
className="case-study-status-legend"
|
|
195
|
+
aria-label="Case study status legend"
|
|
196
|
+
>
|
|
197
|
+
<span className="legend-title">Current status</span>
|
|
198
|
+
{[
|
|
199
|
+
['planned', '#006bb8'],
|
|
200
|
+
['ongoing', '#ff9933'],
|
|
201
|
+
['completed', '#00a390'],
|
|
202
|
+
].map(([status, color]) => (
|
|
203
|
+
<span className="legend-item" key={status}>
|
|
204
|
+
<span
|
|
205
|
+
className="legend-dot"
|
|
206
|
+
style={{ backgroundColor: color }}
|
|
207
|
+
/>
|
|
208
|
+
{status}
|
|
209
|
+
</span>
|
|
210
|
+
))}
|
|
211
|
+
</div>
|
|
212
|
+
<CaseStudyList
|
|
213
|
+
map={map}
|
|
214
|
+
activeItems={activeItems}
|
|
215
|
+
selectedCase={selectedCase}
|
|
216
|
+
onSelectedCase={onSelectedCase}
|
|
217
|
+
pointsSource={pointsSource}
|
|
218
|
+
searchInput={searchInput}
|
|
219
|
+
/>
|
|
220
|
+
</>
|
|
187
221
|
)}
|
|
188
222
|
</div>
|
|
189
223
|
) : null;
|
|
190
224
|
}
|
|
191
225
|
|
|
192
|
-
const selectedClusterStyle = ({
|
|
226
|
+
const selectedClusterStyle = ({ ol }) => {
|
|
193
227
|
function _clusterStyle(feature) {
|
|
194
228
|
const size = feature.get('features').length;
|
|
195
229
|
let style = styleCache[size];
|
|
@@ -202,7 +236,7 @@ const selectedClusterStyle = ({ selectedFeature, ol }) => {
|
|
|
202
236
|
color: '#fff',
|
|
203
237
|
}),
|
|
204
238
|
fill: new ol.style.Fill({
|
|
205
|
-
color:
|
|
239
|
+
color: CLUSTER_COLOR,
|
|
206
240
|
}),
|
|
207
241
|
}),
|
|
208
242
|
text: new ol.style.Text({
|
|
@@ -216,8 +250,7 @@ const selectedClusterStyle = ({ selectedFeature, ol }) => {
|
|
|
216
250
|
}
|
|
217
251
|
|
|
218
252
|
if (size === 1) {
|
|
219
|
-
|
|
220
|
-
let color = '#289588'; // #0083E0 #50B0A4
|
|
253
|
+
const color = feature.values_.features[0].get('color');
|
|
221
254
|
|
|
222
255
|
return new ol.style.Style({
|
|
223
256
|
image: new ol.style.Circle({
|
|
@@ -31,6 +31,7 @@ jest.mock('./CaseStudyListing', () => () => (
|
|
|
31
31
|
|
|
32
32
|
jest.mock('./utils', () => ({
|
|
33
33
|
getFeatures: jest.fn(),
|
|
34
|
+
getSelectInteraction: jest.fn(() => null),
|
|
34
35
|
centerAndResetMapZoom: jest.fn(),
|
|
35
36
|
scrollToElement: jest.fn(),
|
|
36
37
|
}));
|
|
@@ -85,6 +86,42 @@ describe('CaseStudyMap', () => {
|
|
|
85
86
|
).not.toBeInTheDocument();
|
|
86
87
|
});
|
|
87
88
|
|
|
89
|
+
it('does not refresh the points source when only the ol wrapper identity changes', () => {
|
|
90
|
+
getFeatures.mockReturnValue([{ id: 1 }]);
|
|
91
|
+
const activeItems = [{ id: 1 }];
|
|
92
|
+
|
|
93
|
+
const { rerender } = render(
|
|
94
|
+
<CaseStudyMap
|
|
95
|
+
items={[{ id: 1 }]}
|
|
96
|
+
activeItems={activeItems}
|
|
97
|
+
ol={mockOl}
|
|
98
|
+
setMap={jest.fn()}
|
|
99
|
+
map={mockMap}
|
|
100
|
+
onSelectedCase={jest.fn()}
|
|
101
|
+
/>,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const pointsSource = mockOl.source.Vector.mock.results[0].value;
|
|
105
|
+
const clearCount = pointsSource.clear.mock.calls.length;
|
|
106
|
+
expect(clearCount).toBeGreaterThan(0);
|
|
107
|
+
|
|
108
|
+
// withOpenLayers builds a fresh `ol` object literal on every render;
|
|
109
|
+
// refreshing the points source would regenerate the cluster features and
|
|
110
|
+
// drop the currently selected feature from the rendered set.
|
|
111
|
+
rerender(
|
|
112
|
+
<CaseStudyMap
|
|
113
|
+
items={[{ id: 1 }]}
|
|
114
|
+
activeItems={activeItems}
|
|
115
|
+
ol={{ ...mockOl }}
|
|
116
|
+
setMap={jest.fn()}
|
|
117
|
+
map={mockMap}
|
|
118
|
+
onSelectedCase={jest.fn()}
|
|
119
|
+
/>,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(pointsSource.clear.mock.calls.length).toBe(clearCount);
|
|
123
|
+
});
|
|
124
|
+
|
|
88
125
|
it('clicking Reset map calls utils and clears features', () => {
|
|
89
126
|
getFeatures.mockReturnValueOnce([{ id: 1 }]);
|
|
90
127
|
const onSelectedCase = jest.fn();
|
|
@@ -18,79 +18,81 @@ export const useStyles = ({ ol }) => {
|
|
|
18
18
|
}),
|
|
19
19
|
}),
|
|
20
20
|
}),
|
|
21
|
-
[ol],
|
|
21
|
+
[ol.style],
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
-
const selectStyle = React.useCallback(
|
|
25
|
-
(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// console.log(color);
|
|
29
|
-
selected.image_.getFill().setColor(color);
|
|
30
|
-
return selected;
|
|
31
|
-
},
|
|
32
|
-
[selected],
|
|
33
|
-
);
|
|
24
|
+
const selectStyle = React.useCallback(() => {
|
|
25
|
+
selected.image_.getFill().setColor('#0A99FF');
|
|
26
|
+
return selected;
|
|
27
|
+
}, [selected]);
|
|
34
28
|
|
|
35
29
|
return { selected, selectStyle };
|
|
36
30
|
};
|
|
37
31
|
|
|
38
|
-
function FeatureInteraction({
|
|
39
|
-
onFeatureSelect,
|
|
40
|
-
hideFilters,
|
|
41
|
-
selectedCase,
|
|
42
|
-
ol,
|
|
43
|
-
}) {
|
|
44
|
-
// console.log('featureinteraction', selectedCase);
|
|
32
|
+
function FeatureInteraction({ onFeatureSelect, hideFilters, ol }) {
|
|
45
33
|
const { map } = useMapContext();
|
|
46
34
|
const { selectStyle } = useStyles({ ol });
|
|
47
35
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
// Keep callbacks current without recreating the OpenLayers interaction.
|
|
37
|
+
const onFeatureSelectRef = React.useRef(onFeatureSelect);
|
|
38
|
+
onFeatureSelectRef.current = onFeatureSelect;
|
|
39
|
+
const hideFiltersRef = React.useRef(hideFilters);
|
|
40
|
+
hideFiltersRef.current = hideFilters;
|
|
41
|
+
const olRef = React.useRef(ol);
|
|
42
|
+
olRef.current = ol;
|
|
43
|
+
|
|
44
|
+
// Select stores the selected rendered cluster feature and applies its style
|
|
45
|
+
// directly to that feature. Recreating it after selectedCase changes clears
|
|
46
|
+
// that collection and restores the feature's original style.
|
|
47
|
+
const selectRef = React.useRef(null);
|
|
48
|
+
if (!selectRef.current) {
|
|
49
|
+
selectRef.current = new ol.interaction.Select({
|
|
50
|
+
condition: ol.condition.click,
|
|
51
|
+
style: hideFilters ? null : selectStyle,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const select = selectRef.current;
|
|
52
55
|
|
|
53
56
|
React.useEffect(() => {
|
|
54
57
|
if (!map) return;
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
const onSelect = (e) => {
|
|
57
60
|
const features = e.target.getFeatures().getArray();
|
|
58
61
|
|
|
59
62
|
features.forEach((feature) => {
|
|
60
63
|
const subfeatures = feature.values_.features;
|
|
61
64
|
if (subfeatures.length === 1) {
|
|
62
65
|
const selectedFeature = subfeatures[0].values_;
|
|
63
|
-
if (
|
|
66
|
+
if (hideFiltersRef.current) {
|
|
64
67
|
const url = window.location.origin + selectedFeature.path;
|
|
65
68
|
// window.open(url);
|
|
66
69
|
window.location.href = url;
|
|
67
70
|
}
|
|
68
|
-
|
|
71
|
+
onFeatureSelectRef.current(selectedFeature);
|
|
69
72
|
scrollToElement('ol-map-container');
|
|
70
|
-
// map.getView().animate({
|
|
71
|
-
// duration: 10,
|
|
72
|
-
// center: selectedFeature.geometry.flatCoordinates,
|
|
73
|
-
// });
|
|
74
73
|
} else {
|
|
75
|
-
|
|
76
|
-
zoomMapToFeatures({ map, features: subfeatures, ol });
|
|
74
|
+
onFeatureSelectRef.current(null);
|
|
75
|
+
zoomMapToFeatures({ map, features: subfeatures, ol: olRef.current });
|
|
77
76
|
}
|
|
78
77
|
});
|
|
78
|
+
};
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
|
|
80
|
+
select.on('select', onSelect);
|
|
83
81
|
map.addInteraction(select);
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
const onPointerMove = (e) => {
|
|
86
84
|
const pixel = map.getEventPixel(e.originalEvent);
|
|
87
85
|
const hit = map.hasFeatureAtPixel(pixel);
|
|
88
86
|
map.getViewport().style.cursor = hit ? 'pointer' : '';
|
|
89
|
-
}
|
|
87
|
+
};
|
|
88
|
+
map.on('pointermove', onPointerMove);
|
|
90
89
|
|
|
91
|
-
return () =>
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
return () => {
|
|
91
|
+
select.un('select', onSelect);
|
|
92
|
+
map.un('pointermove', onPointerMove);
|
|
93
|
+
map.removeInteraction(select);
|
|
94
|
+
};
|
|
95
|
+
}, [map, select]);
|
|
94
96
|
|
|
95
97
|
return null;
|
|
96
98
|
}
|
|
@@ -35,6 +35,7 @@ const mockOl = createMockOl({
|
|
|
35
35
|
mockSelectCb = cb;
|
|
36
36
|
}
|
|
37
37
|
}),
|
|
38
|
+
un: jest.fn(),
|
|
38
39
|
})),
|
|
39
40
|
},
|
|
40
41
|
});
|
|
@@ -43,6 +44,7 @@ const mockMap = {
|
|
|
43
44
|
addInteraction,
|
|
44
45
|
removeInteraction,
|
|
45
46
|
on: onMap,
|
|
47
|
+
un: jest.fn(),
|
|
46
48
|
getEventPixel,
|
|
47
49
|
hasFeatureAtPixel,
|
|
48
50
|
getViewport,
|
|
@@ -66,6 +68,43 @@ describe('FeatureInteraction', () => {
|
|
|
66
68
|
mockSelectCb = null;
|
|
67
69
|
});
|
|
68
70
|
|
|
71
|
+
it('configures the selection style when filters are visible', () => {
|
|
72
|
+
render(
|
|
73
|
+
<FeatureInteraction
|
|
74
|
+
onFeatureSelect={jest.fn()}
|
|
75
|
+
hideFilters={false}
|
|
76
|
+
selectedCase={null}
|
|
77
|
+
ol={mockOl}
|
|
78
|
+
/>,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const options = mockOl.interaction.Select.mock.calls[0][0];
|
|
82
|
+
expect(options.style).toEqual(expect.any(Function));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('keeps one Select interaction across rerenders', () => {
|
|
86
|
+
const { rerender } = render(
|
|
87
|
+
<FeatureInteraction
|
|
88
|
+
onFeatureSelect={jest.fn()}
|
|
89
|
+
hideFilters={false}
|
|
90
|
+
selectedCase={null}
|
|
91
|
+
ol={mockOl}
|
|
92
|
+
/>,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
rerender(
|
|
96
|
+
<FeatureInteraction
|
|
97
|
+
onFeatureSelect={jest.fn()}
|
|
98
|
+
hideFilters={false}
|
|
99
|
+
selectedCase={{ path: '/selected' }}
|
|
100
|
+
ol={{ ...mockOl }}
|
|
101
|
+
/>,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect(mockOl.interaction.Select).toHaveBeenCalledTimes(1);
|
|
105
|
+
expect(addInteraction).toHaveBeenCalledTimes(1);
|
|
106
|
+
});
|
|
107
|
+
|
|
69
108
|
it('selects a single feature and calls onFeatureSelect + scrollToElement (hideFilters=false)', () => {
|
|
70
109
|
const onFeatureSelect = jest.fn();
|
|
71
110
|
const singleFeature = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import worldSVG from '@plone/volto/icons/world.svg';
|
|
2
2
|
import CaseStudyExplorerEdit from './CaseStudyExplorerEdit';
|
|
3
3
|
import CaseStudyExplorerView from './CaseStudyExplorerView';
|
|
4
|
+
import schema from './schema';
|
|
4
5
|
|
|
5
6
|
export default function installCaseStudyExplorerBlock(config) {
|
|
6
7
|
config.blocks.blocksConfig.caseStudyExplorer = {
|
|
@@ -10,6 +11,7 @@ export default function installCaseStudyExplorerBlock(config) {
|
|
|
10
11
|
group: 'custom_blocks',
|
|
11
12
|
edit: CaseStudyExplorerEdit,
|
|
12
13
|
view: CaseStudyExplorerView,
|
|
14
|
+
schema,
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
return config;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const schema = {
|
|
2
|
+
title: 'NRR Case Study Explorer',
|
|
3
|
+
fieldsets: [
|
|
4
|
+
{
|
|
5
|
+
id: 'default',
|
|
6
|
+
title: 'Default',
|
|
7
|
+
fields: ['filter_path'],
|
|
8
|
+
},
|
|
9
|
+
],
|
|
10
|
+
properties: {
|
|
11
|
+
filter_path: {
|
|
12
|
+
title: 'Filter path',
|
|
13
|
+
type: 'string',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
required: [],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default schema;
|
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
padding-top: 2em;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
.nrr-case-study-map-container .case-study-status-legend {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-wrap: wrap;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: flex-end;
|
|
10
|
+
margin: 0.75rem 0;
|
|
11
|
+
font-size: 0.9rem;
|
|
12
|
+
gap: 0.75rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.nrr-case-study-map-container .legend-title {
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.nrr-case-study-map-container .legend-item {
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: 0.3rem;
|
|
23
|
+
text-transform: capitalize;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.nrr-case-study-map-container .legend-dot {
|
|
27
|
+
display: inline-block;
|
|
28
|
+
width: 0.75rem;
|
|
29
|
+
height: 0.75rem;
|
|
30
|
+
border-radius: 50%;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.nrr-case-study-map-container .filter-wrapper .facet-btn:disabled {
|
|
34
|
+
cursor: not-allowed;
|
|
35
|
+
opacity: 0.55;
|
|
36
|
+
}
|
|
37
|
+
|
|
5
38
|
#csepopup > span {
|
|
6
39
|
display: 'block';
|
|
7
40
|
margin-bottom: '10px';
|