@eeacms/volto-eea-map 0.1.7 → 0.1.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 CHANGED
@@ -4,8 +4,45 @@ 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
+ #### [0.1.10](https://github.com/eea/volto-eea-map/compare/0.1.9...0.1.10)
8
+
9
+ - EEA core metadata adaptation for map block to [`5fb5724`](https://github.com/eea/volto-eea-map/commit/5fb5724164e1e9b1ae5143f9bd565c28118be253)
10
+ - EEA core metadata adaptation [`03b62a3`](https://github.com/eea/volto-eea-map/commit/03b62a35165670743d649282ae697dbf71c56a56)
11
+
12
+ #### [0.1.9](https://github.com/eea/volto-eea-map/compare/0.1.8...0.1.9)
13
+
14
+ > 12 August 2022
15
+
16
+ - Query builder for map layers queries [`#9`](https://github.com/eea/volto-eea-map/pull/9)
17
+ - pinpoint query builder [`6184b0e`](https://github.com/eea/volto-eea-map/commit/6184b0e1dcee48e7d34bf56bc1c878438d07f987)
18
+ - build query widget [`7a9d74a`](https://github.com/eea/volto-eea-map/commit/7a9d74a6fdac32a8b424c5ca97edd42df436cbe2)
19
+
20
+ #### [0.1.8](https://github.com/eea/volto-eea-map/compare/0.1.7...0.1.8)
21
+
22
+ > 11 August 2022
23
+
24
+ - Integration & new block for Map Visualization content-type [`#8`](https://github.com/eea/volto-eea-map/pull/8)
25
+ - remove title on map edit widget [`1d00707`](https://github.com/eea/volto-eea-map/commit/1d0070716a743ea7f26c5f877b723492319fcecd)
26
+ - update query by stored value [`c0f6ddd`](https://github.com/eea/volto-eea-map/commit/c0f6dddadaaf28de5b9914214acf735a166fae88)
27
+ - stylelint [`511845a`](https://github.com/eea/volto-eea-map/commit/511845a319137dfe4e1c0c9846ee5ccb7656b32b)
28
+ - visual candy [`2b24975`](https://github.com/eea/volto-eea-map/commit/2b249758f30133d8080793e892d0771e9b63dea5)
29
+ - responsive hoc only keep device size [`444f8b3`](https://github.com/eea/volto-eea-map/commit/444f8b3df2dd58a1f1e5bd6c6a90c1e9919696a1)
30
+ - responsive improvements on edit widget [`1dcdda0`](https://github.com/eea/volto-eea-map/commit/1dcdda077f5aaa8ddd256d2bec2f1018a15e066d)
31
+ - map is sticky [`64f3874`](https://github.com/eea/volto-eea-map/commit/64f3874648f0ede23502998e9165e86538e44728)
32
+ - simplified layer edit [`b64348c`](https://github.com/eea/volto-eea-map/commit/b64348c962647cdfc48d3328c5effb3b02b6217c)
33
+ - no consoles [`1387128`](https://github.com/eea/volto-eea-map/commit/1387128037abb314798326fa3c47b52a13d0415f)
34
+ - show if zoom/print position [`c4d7107`](https://github.com/eea/volto-eea-map/commit/c4d7107e874616c2246ec883d07eb59fad76b21e)
35
+ - lint fix [`cc497bd`](https://github.com/eea/volto-eea-map/commit/cc497bd8fe61d8867b74a8a16bc4868ccf088d55)
36
+ - fixes on webmap [`1c4506b`](https://github.com/eea/volto-eea-map/commit/1c4506b227d3a0ad0f919e3ff826700304b0620a)
37
+ - new embed eea map block to work with visualizations [`be4f2b0`](https://github.com/eea/volto-eea-map/commit/be4f2b00f92ea9dedc359e8c2620a9a31f4b5b6b)
38
+ - remove vis_url logic, will be moved in a new block [`13ebe94`](https://github.com/eea/volto-eea-map/commit/13ebe94c6c339fc827c019aab0a698aa431c5b78)
39
+ - Updates for map visualization [`895d463`](https://github.com/eea/volto-eea-map/commit/895d46313e9b65c6a5f420284fb9459296ed83f4)
40
+
7
41
  #### [0.1.7](https://github.com/eea/volto-eea-map/compare/0.1.6...0.1.7)
8
42
 
43
+ > 9 August 2022
44
+
45
+ - Develop [`#7`](https://github.com/eea/volto-eea-map/pull/7)
9
46
  - fix height coming through in view [`39d4d28`](https://github.com/eea/volto-eea-map/commit/39d4d287a4fe86890f5fad8d9050130b496dfe70)
10
47
  - Connected visualization view-edit [`52a8e21`](https://github.com/eea/volto-eea-map/commit/52a8e2187cb2e3a7db660ae4b6b5610c88be5083)
11
48
  - map_view is map_editor_widget, add view component for content-type [`dc227f9`](https://github.com/eea/volto-eea-map/commit/dc227f9bbb259b59739a3694dc69f0fcca661137)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-map",
3
- "version": "0.1.7",
3
+ "version": "0.1.10",
4
4
  "description": "@eeacms/volto-eea-map: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -24,6 +24,7 @@
24
24
  "@eeacms/volto-embed": "^2.0.1",
25
25
  "@plone/scripts": "*",
26
26
  "esri-loader": "3.5.0",
27
+ "react-querybuilder": "4.2.3",
27
28
  "volto-slate": "*"
28
29
  },
29
30
  "devDependencies": {
package/src/actions.js ADDED
@@ -0,0 +1,12 @@
1
+ import { GET_MAP_VISUALIZATION } from './constants';
2
+
3
+ export function getVisualization(path) {
4
+ return {
5
+ type: GET_MAP_VISUALIZATION,
6
+ path,
7
+ request: {
8
+ op: 'get',
9
+ path: `${path}/@map-visualization`,
10
+ },
11
+ };
12
+ }
@@ -7,13 +7,12 @@ import { addPrivacyProtectionToSchema } from '@eeacms/volto-embed';
7
7
  import './styles/map.css';
8
8
 
9
9
  const Edit = (props) => {
10
- const { block, data, onChangeBlock, selected } = props;
10
+ const { block, data, onChangeBlock, selected, id } = props;
11
11
  const schema = React.useMemo(() => Schema(props), [props]);
12
12
 
13
- if (__SERVER__) return '';
14
13
  return (
15
14
  <div>
16
- <View data={data} />
15
+ <View data={data} id={id} />
17
16
  <SidebarPortal selected={selected}>
18
17
  <BlockDataForm
19
18
  block={block}
@@ -1,4 +1,4 @@
1
- export const Schema = () => {
1
+ export const Schema = (props) => {
2
2
  return {
3
3
  title: 'EEA Map Block',
4
4
  fieldsets: [
@@ -6,17 +6,21 @@ export const Schema = () => {
6
6
  id: 'default',
7
7
  title: 'Default',
8
8
  fields: [
9
+ 'map_data',
9
10
  'description',
10
11
  'height',
11
- 'map_data',
12
- 'show_sources',
13
12
  'show_legend',
14
13
  'show_download',
15
14
  'show_viewer',
15
+ 'show_sources',
16
16
  ],
17
17
  },
18
18
  ],
19
19
  properties: {
20
+ show_sources: {
21
+ title: 'Show sources',
22
+ type: 'boolean',
23
+ },
20
24
  height: {
21
25
  title: 'Height',
22
26
  type: 'number',
@@ -30,10 +34,6 @@ export const Schema = () => {
30
34
  description: 'Open the map customization interface',
31
35
  widget: 'map_edit_widget',
32
36
  },
33
- show_sources: {
34
- title: 'Show sources',
35
- type: 'boolean',
36
- },
37
37
  show_legend: {
38
38
  title: 'Show legend',
39
39
  type: 'boolean',
@@ -1,22 +1,43 @@
1
1
  import React from 'react';
2
+
3
+ import { connect } from 'react-redux';
4
+ import { compose } from 'redux';
5
+
2
6
  import Webmap from './components/Webmap';
3
7
  import ExtraViews from './components/widgets/ExtraViews';
4
8
  import { PrivacyProtection } from '@eeacms/volto-embed';
9
+ import { getContent } from '@plone/volto/actions';
5
10
 
6
11
  const View = (props) => {
7
- const { data } = props || {};
8
-
12
+ const { data, id, path, data_provenance } = props || {};
9
13
  const { map_data = {}, height = '' } = data;
10
14
 
11
- if (__SERVER__) return '';
15
+ React.useEffect(() => {
16
+ // get content from document
17
+ if (path) {
18
+ props.getContent(path, null, id);
19
+ }
20
+ // eslint-disable-next-line react-hooks/exhaustive-deps
21
+ }, [path]);
22
+
12
23
  return (
13
24
  <div>
14
25
  <PrivacyProtection data={data} {...props}>
15
- <Webmap data={map_data} height={height} />
16
- <ExtraViews data={data} />
26
+ <Webmap data={map_data} height={height} id={id} />
27
+ <ExtraViews data={{ ...data, data_provenance }} />
17
28
  </PrivacyProtection>
18
29
  </div>
19
30
  );
20
31
  };
21
32
 
22
- export default View;
33
+ export default compose(
34
+ connect(
35
+ (state, props) => ({
36
+ data_provenance:
37
+ state.content.subrequests?.[props.id]?.data?.data_provenance,
38
+ }),
39
+ {
40
+ getContent,
41
+ },
42
+ ),
43
+ )(View);
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable */
2
2
  import React from 'react';
3
+ import { withDeviceSize } from '@eeacms/volto-eea-map/hocs';
3
4
  import { loadModules } from 'esri-loader';
5
+ import { formatQuery } from 'react-querybuilder';
4
6
 
5
7
  const MODULES = [
6
8
  'esri/Map',
@@ -15,9 +17,10 @@ const MODULES = [
15
17
  ];
16
18
 
17
19
  const Webmap = (props) => {
18
- const { editMode, height } = props;
20
+ const { editMode, height, id } = props;
19
21
 
20
22
  const data = React.useMemo(() => props.data || {}, [props.data]);
23
+ const device = React.useMemo(() => props.device || {}, [props.device]);
21
24
 
22
25
  const { base = {}, layers = {}, legend = {}, general = {} } = data;
23
26
 
@@ -89,7 +92,6 @@ const Webmap = (props) => {
89
92
  .filter(({ map_service_url, layer }) => map_service_url && layer)
90
93
  .map(({ map_service_url, layer, query = '' }) => {
91
94
  const url = `${map_service_url}/${layer}`;
92
-
93
95
  let mapLayer;
94
96
  switch (layer.type) {
95
97
  case 'Raster Layer':
@@ -100,7 +102,7 @@ const Webmap = (props) => {
100
102
  case 'Feature Layer':
101
103
  mapLayer = new FeatureLayer({
102
104
  url,
103
- definitionExpression: query,
105
+ definitionExpression: query ? formatQuery(query, 'sql') : '',
104
106
  });
105
107
  break;
106
108
  case 'Group Layer':
@@ -139,9 +141,10 @@ const Webmap = (props) => {
139
141
  });
140
142
  }
141
143
 
142
- if (general?.show_zoom) {
143
- const zoomPosition =
144
- general && general.zoom_position ? general.zoom_position : 'top-right';
144
+ const zoomPosition =
145
+ general && general.zoom_position ? general.zoom_position : '';
146
+
147
+ if (zoomPosition) {
145
148
  const zoomWidget = new Zoom({
146
149
  view: view,
147
150
  });
@@ -168,11 +171,10 @@ const Webmap = (props) => {
168
171
  view.ui.add(legendWidget, legendPosition);
169
172
  }
170
173
 
171
- if (general?.show_print) {
172
- const printPosition =
173
- general && general.print_position
174
- ? general.print_position
175
- : 'top-right';
174
+ const printPosition =
175
+ general && general.print_position ? general.print_position : '';
176
+
177
+ if (printPosition) {
176
178
  const printWidget = new Expand({
177
179
  content: new Print({
178
180
  view: view,
@@ -197,7 +199,12 @@ const Webmap = (props) => {
197
199
  <div>
198
200
  <div
199
201
  style={{
200
- height: height && !editMode ? `${height}px` : '500px',
202
+ height:
203
+ height && !editMode
204
+ ? `${height}px`
205
+ : device === 'tablet' || device === 'mobile'
206
+ ? '300px'
207
+ : '500px',
201
208
  }}
202
209
  ref={mapRef}
203
210
  className="esri-map"
@@ -206,4 +213,4 @@ const Webmap = (props) => {
206
213
  );
207
214
  };
208
215
 
209
- export default React.memo(Webmap);
216
+ export default withDeviceSize(React.memo(Webmap));
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Button } from 'semantic-ui-react';
3
- import { Icon } from '@plone/volto/components';
3
+ import { Icon, UniversalLink } from '@plone/volto/components';
4
4
 
5
5
  import LegendWidget from './LegendWidget';
6
6
  import { serializeNodes } from 'volto-slate/editor/render';
@@ -15,46 +15,69 @@ const ExtraViews = ({ data }) => {
15
15
  show_legend,
16
16
  show_download,
17
17
  show_viewer,
18
+ show_sources,
19
+ data_provenance = {},
18
20
  } = data;
19
-
20
21
  return (
21
22
  <div className="extra-eea-map-content">
22
- {map_data.layers?.map_layers[0] && (show_download || show_viewer) && (
23
- <div
24
- style={{ display: 'flex', justifyContent: 'end', margin: '10px 0' }}
25
- >
26
- {show_download && (
27
- <a
28
- target="_blank"
29
- rel="noreferrer"
30
- href={`${map_data.layers.map_layers[0].map_layer.map_service_url}?f=lyr&v=9.3`}
31
- >
32
- <Button size="tiny">
33
- <Button.Content>
34
- <Icon name={downloadSVG} title="Download" size="25px" />
35
- </Button.Content>
36
- </Button>
37
- </a>
38
- )}
39
- {show_viewer && (
40
- <a
41
- target="_blank"
42
- rel="noreferrer"
43
- href={
44
- `https://www.arcgis.com/home/webmap/viewer.html?url=` +
45
- `${map_data.layers.map_layers[0].map_layer.map_service_url}&source=sd`
46
- }
47
- >
48
- <Button size="tiny">
49
- <Button.Content>
50
- <Icon name={worldSVG} title="View map" size="25px" />
51
- </Button.Content>
52
- </Button>
53
- </a>
23
+ {map_data &&
24
+ map_data.layers?.map_layers[0] &&
25
+ (show_download || show_viewer) && (
26
+ <div
27
+ style={{ display: 'flex', justifyContent: 'end', margin: '10px 0' }}
28
+ >
29
+ {show_download && (
30
+ <a
31
+ target="_blank"
32
+ rel="noreferrer"
33
+ href={`${map_data.layers.map_layers[0].map_layer.map_service_url}?f=lyr&v=9.3`}
34
+ >
35
+ <Button size="tiny">
36
+ <Button.Content>
37
+ <Icon name={downloadSVG} title="Download" size="25px" />
38
+ </Button.Content>
39
+ </Button>
40
+ </a>
41
+ )}
42
+ {show_viewer && (
43
+ <a
44
+ target="_blank"
45
+ rel="noreferrer"
46
+ href={
47
+ `https://www.arcgis.com/home/webmap/viewer.html?url=` +
48
+ `${map_data.layers.map_layers[0].map_layer.map_service_url}&source=sd`
49
+ }
50
+ >
51
+ <Button size="tiny">
52
+ <Button.Content>
53
+ <Icon name={worldSVG} title="View map" size="25px" />
54
+ </Button.Content>
55
+ </Button>
56
+ </a>
57
+ )}
58
+ </div>
59
+ )}
60
+ {show_legend && map_data && <LegendWidget data={map_data} />}
61
+ {show_sources && (
62
+ <>
63
+ {data_provenance &&
64
+ data_provenance.data &&
65
+ data_provenance.data.length > 0 ? (
66
+ <div>
67
+ <h3>Sources:</h3>
68
+ {data_provenance.data.map((data, i) => (
69
+ <div key={i}>
70
+ <p className="map_source_title">{data.title}</p>
71
+ <p className="map_source_description">{data.organisation}</p>
72
+ <UniversalLink href={data.link}>{data.link} </UniversalLink>
73
+ </div>
74
+ ))}
75
+ </div>
76
+ ) : (
77
+ <p>Data provenance is not set for visualization used or page</p>
54
78
  )}
55
- </div>
79
+ </>
56
80
  )}
57
- {show_legend && <LegendWidget data={map_data} />}
58
81
  {description && serializeNodes(description)}
59
82
  </div>
60
83
  );
@@ -1,10 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Icon } from '@plone/volto/components';
3
3
  import { Input, Select, Button, Grid } from 'semantic-ui-react';
4
+ import { QueryBuilder } from 'react-querybuilder';
5
+ import 'react-querybuilder/dist/query-builder.css';
4
6
 
5
7
  import checkSVG from '@plone/volto/icons/check.svg';
6
8
  import closeSVG from '@plone/volto/icons/clear.svg';
7
9
  import aheadSVG from '@plone/volto/icons/ahead.svg';
10
+ import resetSVG from '@plone/volto/icons/reset.svg';
8
11
 
9
12
  import { fetchArcgisData } from '../../utils';
10
13
 
@@ -30,7 +33,8 @@ const LayerSelectWidget = (props) => {
30
33
  const [availableLayers, setAvailableLayers] = React.useState(
31
34
  available_layers,
32
35
  );
33
- const [layerQuery, setLayerQuery] = React.useState(query);
36
+
37
+ const [builtQuery, setBuiltQuery] = React.useState(query);
34
38
 
35
39
  const handleServiceUrlCheck = async () => {
36
40
  // fetch url, save it, populate layers options
@@ -78,14 +82,34 @@ const LayerSelectWidget = (props) => {
78
82
  map_data: mapData,
79
83
  query: '',
80
84
  });
81
- setLayerQuery('');
85
+ setBuiltQuery('');
82
86
  };
83
87
 
84
88
  const handleQueryLayer = () => {
85
- onChange(id, {
86
- ...value,
87
- query: layerQuery,
88
- });
89
+ if (builtQuery) {
90
+ onChange(id, {
91
+ ...value,
92
+ query: builtQuery,
93
+ });
94
+ }
95
+ };
96
+
97
+ const handleChangeServiceUrl = (value) => {
98
+ setServiceUrlError('');
99
+ setCheckColor('');
100
+ setServiceUrl(value);
101
+ setAvailableLayers('');
102
+ setBuiltQuery('');
103
+ setSelectedLayer('');
104
+ };
105
+
106
+ const handleReset = () => {
107
+ setServiceUrlError('');
108
+ setServiceUrl(map_service_url);
109
+ setCheckColor('');
110
+ setAvailableLayers(available_layers);
111
+ setBuiltQuery('');
112
+ setSelectedLayer(layer);
89
113
  };
90
114
 
91
115
  return (
@@ -95,74 +119,112 @@ const LayerSelectWidget = (props) => {
95
119
  }}
96
120
  >
97
121
  <Grid>
98
- <h4>Service URL</h4>
122
+ <h5 style={{ padding: '0', margin: '15px 0px 5px 0px' }}>
123
+ Service URL
124
+ </h5>
99
125
  <Grid.Row>
100
126
  <Input
101
127
  type="text"
102
- onChange={(e, { value }) => setServiceUrl(value)}
103
- style={{ width: '70%' }}
128
+ onChange={(e, { value }) => handleChangeServiceUrl(value)}
129
+ style={{ width: '100%' }}
130
+ error={serviceUrlError}
104
131
  value={serviceUrl}
105
- action
106
- actionPosition="right"
107
- >
108
- <input />
132
+ // action
133
+ // actionPosition="right"
134
+ ></Input>
135
+
136
+ <span style={{ fontSize: '12px', color: 'darkred' }}>
137
+ {serviceUrlError.error}
138
+ </span>
139
+ </Grid.Row>
140
+ {serviceUrl && (
141
+ <Grid.Row>
142
+ {serviceUrl !== map_service_url && (
143
+ <Button
144
+ size="small"
145
+ compact
146
+ className="layer-reset-button"
147
+ onClick={handleReset}
148
+ >
149
+ <Icon name={resetSVG} title="Reset" size="20px" />
150
+ </Button>
151
+ )}
109
152
  <Button
110
- type="submit"
111
- size="tiny"
112
- compact
153
+ size="small"
113
154
  color={checkColor}
155
+ compact
156
+ className="layer-check-button"
114
157
  onClick={handleServiceUrlCheck}
115
158
  >
116
159
  <Icon
117
160
  name={serviceUrlError ? closeSVG : checkSVG}
118
- title="Check Url"
119
- size="17px"
161
+ title="Submit"
162
+ size="20px"
120
163
  />
121
164
  </Button>
122
- </Input>
123
- </Grid.Row>
124
- <h4>Layer</h4>
125
- <Grid.Row>
126
- <Select
127
- onChange={(e, { value }) => handleSelectLayer(value)}
128
- options={availableLayers}
129
- style={{ width: '100%' }}
130
- placeholder="Select layer"
131
- value={selectedLayer}
132
- />
133
- </Grid.Row>
134
- <h4>Query Layer</h4>
135
- <Grid.Row stretched>
136
- <Input
137
- type="text"
138
- onChange={(e, { value }) => setLayerQuery(value)}
139
- style={{ width: '70%' }}
140
- value={layerQuery}
141
- action
142
- actionPosition="right"
143
- >
144
- <input />
145
- <Button
146
- type="submit"
147
- size="tiny"
148
- compact
149
- color={'green'}
150
- onClick={handleQueryLayer}
151
- >
152
- <Icon name={aheadSVG} title="Check Url" size="17px" />
153
- </Button>
154
- </Input>
155
- </Grid.Row>
156
- <Grid.Row>
157
- <p style={{ fontSize: '12px', fontWeight: 'bold' }}>
158
- Available Fields:
159
- </p>
160
- </Grid.Row>
161
- {fields &&
162
- fields.length > 0 &&
163
- fields.map((field, id) => (
164
- <p style={{ fontSize: '12px' }}>{field.alias}</p>
165
- ))}
165
+ </Grid.Row>
166
+ )}
167
+ {availableLayers && availableLayers.length > 0 && (
168
+ <>
169
+ <h5 style={{ padding: '0', margin: '15px 0px 5px 0px' }}>Layer</h5>
170
+ <Grid.Row>
171
+ <Select
172
+ onChange={(e, { value }) => handleSelectLayer(value)}
173
+ options={availableLayers}
174
+ style={{ width: '100%' }}
175
+ placeholder="Select layer"
176
+ value={selectedLayer}
177
+ />
178
+ </Grid.Row>
179
+ </>
180
+ )}
181
+ {availableLayers && fields && fields.length > 0 && (
182
+ <>
183
+ <h5 style={{ padding: '0', margin: '15px 0px 5px 0px' }}>
184
+ Query Layer
185
+ </h5>
186
+ <Grid.Row>
187
+ <QueryBuilder
188
+ fields={fields.map((fi, i) => {
189
+ return { name: fi.name, label: fi.name };
190
+ })}
191
+ query={builtQuery}
192
+ onQueryChange={(q) => setBuiltQuery(q)}
193
+ enableDragAndDrop={false}
194
+ />
195
+ </Grid.Row>
196
+ {builtQuery && (
197
+ <Grid.Row>
198
+ <Button
199
+ type="submit"
200
+ size="tiny"
201
+ compact
202
+ className="layer-submit-button "
203
+ color={'green'}
204
+ onClick={handleQueryLayer}
205
+ >
206
+ <Icon name={aheadSVG} title="Check Url" size="20px" />
207
+ </Button>
208
+ </Grid.Row>
209
+ )}
210
+ <Grid.Row>
211
+ <p
212
+ style={{
213
+ fontSize: '13px',
214
+ fontWeight: 'bold',
215
+ color: 'darkgray',
216
+ }}
217
+ >
218
+ Available Fields:
219
+ </p>
220
+ </Grid.Row>
221
+ {fields.map((field, id) => (
222
+ <p style={{ fontSize: '12px', padding: '0 5px' }}>
223
+ <strong>{field.alias}</strong> ({field.type})
224
+ </p>
225
+ ))}
226
+ </>
227
+ )}
166
228
  </Grid>
167
229
  </div>
168
230
  );
@@ -42,7 +42,7 @@ const MapEditorWidget = (props) => {
42
42
  <FormFieldWrapper {...props}>
43
43
  <Modal
44
44
  id="map-editor-modal"
45
- style={{ width: '95% !important' }}
45
+ className="map-editor-modal"
46
46
  onClose={handleClose}
47
47
  onOpen={() => setOpen(true)}
48
48
  open={open}
@@ -53,35 +53,38 @@ const MapEditorWidget = (props) => {
53
53
  }
54
54
  >
55
55
  <Modal.Content scrolling>
56
- <Grid>
57
- <Grid.Row>
58
- <Grid.Column width={4}>
59
- <InlineForm
60
- block={block}
61
- title={schema.title}
62
- schema={schema}
63
- onChangeField={(id, value) => {
64
- handleChangeField(id, value);
65
- }}
66
- formData={dataForm}
67
- />
68
- </Grid.Column>
69
- <Grid.Column width={8}>
56
+ <Grid stackable reversed="mobile vertically tablet vertically">
57
+ <Grid.Column
58
+ mobile={12}
59
+ tablet={12}
60
+ computer={5}
61
+ className="map-editor-column"
62
+ >
63
+ <InlineForm
64
+ block={block}
65
+ schema={schema}
66
+ onChangeField={(id, value) => {
67
+ handleChangeField(id, value);
68
+ }}
69
+ formData={dataForm}
70
+ />
71
+ </Grid.Column>
72
+ <Grid.Column mobile={12} tablet={12} computer={7}>
73
+ <div className="webmap-container">
70
74
  <Webmap data={intValue} editMode={true} />
71
- </Grid.Column>
72
- </Grid.Row>
75
+ </div>
76
+ </Grid.Column>
73
77
  </Grid>
74
78
  </Modal.Content>
75
79
  <Modal.Actions>
76
80
  <Grid>
77
81
  <Grid.Row>
78
- <Grid.Column width={8}></Grid.Column>
79
- <Grid.Column width={4}>
82
+ <div className="map-edit-actions-container">
80
83
  <Button onClick={handleClose}>Close</Button>
81
84
  <Button color="green" onClick={handleApplyChanges}>
82
85
  Apply changes
83
86
  </Button>
84
- </Grid.Column>
87
+ </div>
85
88
  </Grid.Row>
86
89
  </Grid>
87
90
  </Modal.Actions>
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
  import { Menu, Tab } from 'semantic-ui-react';
3
3
  import { ObjectWidget } from '@plone/volto/components';
4
+ import { withDeviceSize } from '@eeacms/volto-eea-map/hocs';
4
5
 
5
6
  export const ObjectTypesWidget = (props) => {
6
- const { schemas, value = {}, onChange, errors = {}, id } = props;
7
+ const { schemas, value = {}, onChange, errors = {}, id, device } = props;
7
8
  const objectId = id;
8
9
 
9
10
  const defaultActiveTab = 0;
@@ -11,6 +12,7 @@ export const ObjectTypesWidget = (props) => {
11
12
  const [activeTab, setActiveTab] = React.useState(
12
13
  defaultActiveTab > -1 ? defaultActiveTab : 0,
13
14
  );
15
+
14
16
  const createTab = ({ schema, id, icon }, index) => {
15
17
  return {
16
18
  menuItem: () => (
@@ -42,7 +44,13 @@ export const ObjectTypesWidget = (props) => {
42
44
 
43
45
  return (
44
46
  <Tab
45
- menu={{ fluid: true, vertical: true, tabular: true }}
47
+ menu={{
48
+ vertical:
49
+ device === 'computer' || device === 'tablet' || device === 'mobile'
50
+ ? false
51
+ : true,
52
+ tabular: true,
53
+ }}
46
54
  panes={schemas.map(createTab)}
47
55
  activeIndex={activeTab}
48
56
  grid={{ paneWidth: 9, tabWidth: 3 }}
@@ -50,4 +58,4 @@ export const ObjectTypesWidget = (props) => {
50
58
  );
51
59
  };
52
60
 
53
- export default ObjectTypesWidget;
61
+ export default withDeviceSize(ObjectTypesWidget);
@@ -4,11 +4,11 @@ import Webmap from '../Webmap';
4
4
  const VisualizationView = (props) => {
5
5
  const { content = {} } = props;
6
6
 
7
- const { map_editor_widget = {} } = content;
7
+ const { map_visualization_data = {} } = content;
8
8
 
9
9
  return (
10
10
  <div>
11
- <Webmap data={map_editor_widget} />
11
+ <Webmap data={map_visualization_data} />
12
12
  </div>
13
13
  );
14
14
  };
@@ -66,25 +66,20 @@ const GeneralSchema = ({ data = {} }) => {
66
66
  id: 'default',
67
67
  title: 'Zoom',
68
68
  fields: [
69
- 'show_print',
70
69
  'print_position',
71
- 'show_zoom',
72
- 'centerOnExtent',
73
70
  'zoom_position',
71
+ 'centerOnExtent',
74
72
  ...(!centerOnExtent ? ['zoom_level', 'long', 'lat'] : []),
75
73
  ],
76
74
  },
77
75
  ],
78
76
  properties: {
79
- show_zoom: {
80
- title: 'Show zoom',
81
- type: 'boolean',
82
- },
83
77
  centerOnExtent: {
84
78
  title: 'Center on extent',
85
79
  type: 'boolean',
86
80
  description:
87
81
  'This will override latitude/longitude/zoom level and will lock zoom/moving the map.',
82
+ default: true,
88
83
  },
89
84
  zoom_position: {
90
85
  title: 'Zoom position',
@@ -106,10 +101,7 @@ const GeneralSchema = ({ data = {} }) => {
106
101
  title: 'Latitude',
107
102
  type: 'number',
108
103
  },
109
- show_print: {
110
- title: 'Show print',
111
- type: 'boolean',
112
- },
104
+
113
105
  print_position: {
114
106
  title: 'Print position',
115
107
  choices: ['bottom-right', 'bottom-left', 'top-right', 'top-left'].map(
@@ -20,3 +20,109 @@
20
20
  display: flex;
21
21
  padding: 1rem 0;
22
22
  }
23
+
24
+ .map-editor-modal {
25
+ width: '95% !important';
26
+ }
27
+
28
+ .webmap-container {
29
+ position: sticky;
30
+ top: 10px;
31
+ }
32
+
33
+ .map-edit-actions-container {
34
+ padding: 5px 0;
35
+ margin-right: 10px;
36
+ margin-left: auto;
37
+ }
38
+
39
+ .layer-reset-button {
40
+ margin-top: 5px !important;
41
+ animation: fadeDown 0.2s ease-in;
42
+ }
43
+
44
+ .layer-check-button {
45
+ margin-top: 5px !important;
46
+ margin-left: auto !important;
47
+ animation: fadeDown 0.2s ease-in;
48
+ }
49
+
50
+ .layer-submit-button {
51
+ margin-top: 5px !important;
52
+ margin-left: auto !important;
53
+ animation: fadeDown 0.2s ease-in;
54
+ }
55
+
56
+ .ruleGroup {
57
+ border: 1px solid lightgray !important;
58
+ margin: 10px 0;
59
+ background-color: transparent !important;
60
+ }
61
+
62
+ .ruleGroup-addRule {
63
+ padding: 7px 10px !important;
64
+ background-color: darkgray !important;
65
+ border-radius: 5px;
66
+ color: white;
67
+ cursor: pointer;
68
+ font-size: 12px;
69
+ font-weight: bold;
70
+ }
71
+
72
+ .ruleGroup-addGroup {
73
+ padding: 7px 10px !important;
74
+ background-color: darkgray !important;
75
+ border-radius: 5px;
76
+ color: white;
77
+ cursor: pointer;
78
+ font-size: 12px;
79
+ font-weight: bold;
80
+ }
81
+
82
+ .ruleGroup-combinators {
83
+ padding: 0.3em 0 !important;
84
+ }
85
+
86
+ .rule-operators {
87
+ padding: 0.3em 0 !important;
88
+ }
89
+
90
+ .rule-value {
91
+ padding: 0.3em 0 !important;
92
+ }
93
+
94
+ .rule-fields {
95
+ padding: 0.3em 0 !important;
96
+ }
97
+
98
+ .rule-remove {
99
+ padding: 1px 6px !important;
100
+ border: 2px solid #d02144 !important;
101
+ border-radius: 50px;
102
+ color: #d02144;
103
+ cursor: pointer;
104
+ }
105
+
106
+ .map_source_title {
107
+ margin-top: 15px !important;
108
+ margin-bottom: 5px !important;
109
+ font-size: 18px;
110
+ font-weight: bold;
111
+ }
112
+
113
+ .map_source_description {
114
+ margin: 5px 0 !important;
115
+ font-size: 14px;
116
+ }
117
+
118
+ @keyframes fadeDown {
119
+ from {
120
+ opacity: 0;
121
+ transform: translate3d(0, -20%, 0);
122
+ }
123
+
124
+ to {
125
+ opacity: 1;
126
+ transform: translate3d(0, 0, 0);
127
+ }
128
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { SidebarPortal } from '@plone/volto/components';
3
+ import BlockDataForm from '@plone/volto/components/manage/Form/BlockDataForm';
4
+ import { Schema } from './Schema';
5
+ import View from './View';
6
+ import { addPrivacyProtectionToSchema } from '@eeacms/volto-embed';
7
+ import './styles/map.css';
8
+
9
+ const Edit = (props) => {
10
+ const { block, data, onChangeBlock, selected, id } = props;
11
+ const schema = React.useMemo(() => Schema(props), [props]);
12
+
13
+ return (
14
+ <div>
15
+ <View data={data} id={id} />
16
+ <SidebarPortal selected={selected}>
17
+ <BlockDataForm
18
+ block={block}
19
+ title={schema.title}
20
+ schema={addPrivacyProtectionToSchema(schema)}
21
+ onChangeField={(id, value) => {
22
+ onChangeBlock(block, {
23
+ ...data,
24
+ [id]: value,
25
+ });
26
+ }}
27
+ formData={data}
28
+ />
29
+ </SidebarPortal>
30
+ </div>
31
+ );
32
+ };
33
+
34
+ export default Edit;
@@ -0,0 +1,51 @@
1
+ export const Schema = (props) => {
2
+ return {
3
+ title: 'Embed EEA Map Block',
4
+ fieldsets: [
5
+ {
6
+ id: 'default',
7
+ title: 'Default',
8
+ fields: [
9
+ 'vis_url',
10
+ 'description',
11
+ 'height',
12
+ 'show_legend',
13
+ 'show_download',
14
+ 'show_viewer',
15
+ 'show_sources',
16
+ ],
17
+ },
18
+ ],
19
+ properties: {
20
+ vis_url: {
21
+ widget: 'object_by_path',
22
+ title: 'Visualization',
23
+ },
24
+ height: {
25
+ title: 'Height',
26
+ type: 'number',
27
+ },
28
+ description: {
29
+ title: 'Description',
30
+ widget: 'slate',
31
+ },
32
+ show_sources: {
33
+ title: 'Show sources',
34
+ type: 'boolean',
35
+ },
36
+ show_legend: {
37
+ title: 'Show legend',
38
+ type: 'boolean',
39
+ },
40
+ show_download: {
41
+ title: 'Show download',
42
+ type: 'boolean',
43
+ },
44
+ show_viewer: {
45
+ title: 'Show web viewer',
46
+ type: 'boolean',
47
+ },
48
+ },
49
+ required: [],
50
+ };
51
+ };
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+
3
+ import { connect } from 'react-redux';
4
+ import { compose } from 'redux';
5
+
6
+ import { PrivacyProtection } from '@eeacms/volto-embed';
7
+ import Webmap from '../EEAMap/components/Webmap';
8
+ import ExtraViews from '../EEAMap/components/widgets/ExtraViews';
9
+ import { getContent } from '@plone/volto/actions';
10
+
11
+ const View = (props) => {
12
+ const { data, viz_content = {}, id } = props || {};
13
+ const { height = '', vis_url = '' } = data;
14
+
15
+ const { map_visualization_data = '', data_provenance = {} } =
16
+ viz_content || {};
17
+
18
+ React.useEffect(() => {
19
+ if (vis_url) {
20
+ props.getContent(vis_url, null, id);
21
+ }
22
+ // eslint-disable-next-line react-hooks/exhaustive-deps
23
+ }, [vis_url]);
24
+
25
+ return (
26
+ <div>
27
+ <PrivacyProtection data={data} {...props}>
28
+ {map_visualization_data && (
29
+ <div>
30
+ <Webmap data={map_visualization_data} height={height} />
31
+ <ExtraViews
32
+ data={{
33
+ ...data,
34
+ data_provenance,
35
+ map_data: map_visualization_data,
36
+ }}
37
+ />
38
+ </div>
39
+ )}
40
+ {!map_visualization_data && (
41
+ <p>No map view to show. Set visualization in block configuration.</p>
42
+ )}
43
+ </PrivacyProtection>
44
+ </div>
45
+ );
46
+ };
47
+
48
+ export default compose(
49
+ connect(
50
+ (state, props) => ({
51
+ viz_content: state.content.subrequests?.[props.id]?.data,
52
+ }),
53
+ {
54
+ getContent,
55
+ },
56
+ ),
57
+ )(View);
@@ -0,0 +1,22 @@
1
+ .map-edit-container {
2
+ display: flex;
3
+ }
4
+
5
+ .map-modal-trigger-button {
6
+ margin-bottom: 10px !important;
7
+ }
8
+
9
+ #map-editor-modal {
10
+ top: auto;
11
+ left: auto !important;
12
+ width: 95% !important;
13
+ }
14
+
15
+ #map-widget-toggle {
16
+ color: blue !important;
17
+ }
18
+
19
+ .map-text-view {
20
+ display: flex;
21
+ padding: 1rem 0;
22
+ }
@@ -1,4 +1,7 @@
1
1
  import EEAMapView from './Blocks/EEAMap/View';
2
2
  import EEAMapEdit from './Blocks/EEAMap/Edit';
3
3
 
4
- export { EEAMapEdit, EEAMapView };
4
+ import EmbedMapView from './Blocks/EmbedEEAMap/View';
5
+ import EmbedMapEdit from './Blocks/EmbedEEAMap/Edit';
6
+
7
+ export { EEAMapEdit, EEAMapView, EmbedMapView, EmbedMapEdit };
@@ -0,0 +1 @@
1
+ export const GET_MAP_VISUALIZATION = 'GET_MAP_VISUALIZATION';
@@ -0,0 +1,3 @@
1
+ import withDeviceSize from './withDeviceSize';
2
+
3
+ export { withDeviceSize };
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+
3
+ export default function withDeviceSize(WrappedComponent) {
4
+ return (props) => {
5
+ const [device, setDevice] = React.useState(null);
6
+
7
+ const updateScreenSize = () => {
8
+ if (__CLIENT__) {
9
+ const screenWidth =
10
+ window.innerWidth ||
11
+ document.documentElement.clientWidth ||
12
+ document.body.clientWidth ||
13
+ 0;
14
+
15
+ setDevice(getDeviceConfig(screenWidth));
16
+ }
17
+ };
18
+
19
+ const getDeviceConfig = (width) => {
20
+ // semantic ui breakpoints
21
+ if (width < 320) {
22
+ return 'mobile';
23
+ } else if (width >= 320 && width < 768) {
24
+ return 'tablet';
25
+ } else if (width >= 768 && width < 992) {
26
+ return 'computer';
27
+ } else if (width >= 992 && width < 1280) {
28
+ return 'large';
29
+ } else if (width >= 1280) {
30
+ return 'widescreen';
31
+ }
32
+ };
33
+
34
+ React.useEffect(() => {
35
+ updateScreenSize();
36
+ window.addEventListener('resize', updateScreenSize);
37
+ return () => {
38
+ window.removeEventListener('resize', updateScreenSize);
39
+ };
40
+ /* eslint-disable-next-line */
41
+ }, []);
42
+
43
+ return <WrappedComponent {...props} device={device} />;
44
+ };
45
+ }
package/src/index.js CHANGED
@@ -1,4 +1,9 @@
1
- import { EEAMapEdit, EEAMapView } from '@eeacms/volto-eea-map/components';
1
+ import {
2
+ EEAMapEdit,
3
+ EEAMapView,
4
+ EmbedMapView,
5
+ EmbedMapEdit,
6
+ } from '@eeacms/volto-eea-map/components';
2
7
  import world from '@plone/volto/icons/world.svg';
3
8
  import LayerSelectWidget from './components/Blocks/EEAMap/components/widgets/LayerSelectWidget';
4
9
  import MapEditorWidget from './components/Blocks/EEAMap/components/widgets/MapEditorWidget';
@@ -6,6 +11,9 @@ import ObjectTypesWidget from './components/Blocks/EEAMap/components/widgets/Obj
6
11
  import VisualizationEditorWidget from './components/Blocks/EEAMap/components/widgets/VisualizationEditorWidget';
7
12
  import VisualizationView from './components/Blocks/EEAMap/components/widgets/VisualizationView';
8
13
 
14
+ import { data_visualizations } from './middlewares';
15
+ import * as addonReducers from './reducers';
16
+
9
17
  export default (config) => {
10
18
  config.settings.allowed_cors_destinations = [
11
19
  ...(config.settings.allowed_cors_destinations || []),
@@ -50,14 +58,52 @@ export default (config) => {
50
58
  ],
51
59
  };
52
60
 
61
+ config.blocks.blocksConfig.embed_eea_map_block = {
62
+ id: 'embed_eea_map_block', // The name (id) of the block
63
+ title: 'Embed EEA Map', // The display name of the block
64
+ icon: world, // The icon used in the block chooser
65
+ group: 'common', // The group (blocks can be grouped, displayed in the chooser)
66
+ view: EmbedMapView, // The view mode component
67
+ edit: EmbedMapEdit, // The edit mode component
68
+ sidebarTab: 1, // The sidebar tab you want to be selected when selecting the block
69
+ security: {
70
+ addPermission: [], // Future proof (not implemented yet) add user permission role(s)
71
+ view: [], // Future proof (not implemented yet) view user role(s)
72
+ },
73
+ variations: [
74
+ {
75
+ id: 'default',
76
+ title: 'EEA Map (default)',
77
+ isDefault: true,
78
+ view: EEAMapView,
79
+ },
80
+ {
81
+ id: 'extra',
82
+ title: 'Extra variation (expand if needed)',
83
+ isDefault: true,
84
+ view: EEAMapView,
85
+ },
86
+ ],
87
+ };
88
+
53
89
  config.widgets.widget.map_edit_widget = MapEditorWidget;
54
90
  config.widgets.widget.map_layers_widget = LayerSelectWidget;
55
91
  config.widgets.widget.object_types_widget = ObjectTypesWidget;
56
92
 
57
93
  //map editor for the visualization(content-type)
58
- config.widgets.id.map_editor_widget = VisualizationEditorWidget;
94
+ config.widgets.id.map_visualization_data = VisualizationEditorWidget;
59
95
  //map viewer for the visualization(content-type)
60
96
  config.views.contentTypesViews.map_visualization = VisualizationView;
61
97
 
98
+ config.settings.storeExtenders = [
99
+ ...(config.settings.storeExtenders || []),
100
+ data_visualizations,
101
+ ];
102
+
103
+ config.addonReducers = {
104
+ ...config.addonReducers,
105
+ ...addonReducers,
106
+ };
107
+
62
108
  return config;
63
109
  };
@@ -0,0 +1,14 @@
1
+ import { GET_MAP_VISUALIZATION } from '@eeacms/volto-eea-map/constants';
2
+
3
+ export const data_visualizations = (middlewares) => [
4
+ (store) => (next) => (action) => {
5
+ if (action.type === GET_MAP_VISUALIZATION) {
6
+ store.dispatch({
7
+ type: `${GET_MAP_VISUALIZATION}_PENDING`,
8
+ path: action.path,
9
+ });
10
+ }
11
+ return next(action);
12
+ },
13
+ ...middlewares,
14
+ ];
@@ -0,0 +1 @@
1
+ export * from './map_visualizations';
@@ -0,0 +1,3 @@
1
+ import map_visualizations from './map_visualizations';
2
+
3
+ export { map_visualizations };
@@ -0,0 +1,48 @@
1
+ import { GET_MAP_VISUALIZATION } from '@eeacms/volto-eea-map/constants';
2
+
3
+ const initialState = {
4
+ data: {},
5
+ error: null,
6
+ loaded: false,
7
+ loading: false,
8
+ };
9
+
10
+ export default function data_providers(state = initialState, action = {}) {
11
+ const path = action.path
12
+ ? action.path.replace(`/@map-visualization`, '')
13
+ : undefined;
14
+
15
+ switch (action.type) {
16
+ case `${GET_MAP_VISUALIZATION}_PENDING`:
17
+ return {
18
+ ...state,
19
+ error: null,
20
+ loaded: false,
21
+ loading: true,
22
+ };
23
+
24
+ case `${GET_MAP_VISUALIZATION}_SUCCESS`:
25
+ return {
26
+ ...state,
27
+ error: null,
28
+ data: {
29
+ ...state.data,
30
+ [path]: action.result.map_visualization,
31
+ },
32
+ loaded: true,
33
+ loading: false,
34
+ };
35
+
36
+ case `${GET_MAP_VISUALIZATION}_FAIL`:
37
+ return {
38
+ ...state,
39
+ error: action.error,
40
+ data: { ...state.data },
41
+ loaded: false,
42
+ loading: false,
43
+ };
44
+
45
+ default:
46
+ return state;
47
+ }
48
+ }