@eeacms/volto-eea-map 0.1.6 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/package.json +2 -1
  3. package/src/actions.js +12 -0
  4. package/src/components/Blocks/EEAMap/Edit.jsx +2 -3
  5. package/src/components/Blocks/EEAMap/Schema.js +13 -7
  6. package/src/components/Blocks/EEAMap/View.jsx +4 -6
  7. package/src/components/Blocks/EEAMap/components/Webmap.jsx +20 -13
  8. package/src/components/Blocks/EEAMap/components/widgets/LayerSelectWidget.jsx +139 -62
  9. package/src/components/Blocks/EEAMap/components/widgets/MapEditorWidget.jsx +23 -20
  10. package/src/components/Blocks/EEAMap/components/widgets/ObjectTypesWidget.jsx +11 -3
  11. package/src/components/Blocks/EEAMap/components/widgets/VisualizationEditorWidget.jsx +94 -0
  12. package/src/components/Blocks/EEAMap/components/widgets/VisualizationView.jsx +16 -0
  13. package/src/components/Blocks/EEAMap/components/widgets/panelsSchema.js +3 -11
  14. package/src/components/Blocks/EEAMap/styles/map.css +94 -0
  15. package/src/components/Blocks/EmbedEEAMap/Edit.jsx +34 -0
  16. package/src/components/Blocks/EmbedEEAMap/Schema.js +46 -0
  17. package/src/components/Blocks/EmbedEEAMap/View.jsx +48 -0
  18. package/src/components/Blocks/EmbedEEAMap/styles/map.css +22 -0
  19. package/src/components/index.js +4 -1
  20. package/src/constants.js +1 -0
  21. package/src/hocs/index.js +3 -0
  22. package/src/hocs/withDeviceSize.jsx +45 -0
  23. package/src/index.js +54 -1
  24. package/src/middlewares/index.js +14 -0
  25. package/src/middlewares/map_visualizations.js +1 -0
  26. package/src/reducers/index.js +3 -0
  27. package/src/reducers/map_visualizations.js +48 -0
package/CHANGELOG.md CHANGED
@@ -4,8 +4,47 @@ 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.9](https://github.com/eea/volto-eea-map/compare/0.1.8...0.1.9)
8
+
9
+ - pinpoint query builder [`6184b0e`](https://github.com/eea/volto-eea-map/commit/6184b0e1dcee48e7d34bf56bc1c878438d07f987)
10
+ - build query widget [`7a9d74a`](https://github.com/eea/volto-eea-map/commit/7a9d74a6fdac32a8b424c5ca97edd42df436cbe2)
11
+
12
+ #### [0.1.8](https://github.com/eea/volto-eea-map/compare/0.1.7...0.1.8)
13
+
14
+ > 11 August 2022
15
+
16
+ - Integration & new block for Map Visualization content-type [`#8`](https://github.com/eea/volto-eea-map/pull/8)
17
+ - remove title on map edit widget [`1d00707`](https://github.com/eea/volto-eea-map/commit/1d0070716a743ea7f26c5f877b723492319fcecd)
18
+ - update query by stored value [`c0f6ddd`](https://github.com/eea/volto-eea-map/commit/c0f6dddadaaf28de5b9914214acf735a166fae88)
19
+ - stylelint [`511845a`](https://github.com/eea/volto-eea-map/commit/511845a319137dfe4e1c0c9846ee5ccb7656b32b)
20
+ - visual candy [`2b24975`](https://github.com/eea/volto-eea-map/commit/2b249758f30133d8080793e892d0771e9b63dea5)
21
+ - responsive hoc only keep device size [`444f8b3`](https://github.com/eea/volto-eea-map/commit/444f8b3df2dd58a1f1e5bd6c6a90c1e9919696a1)
22
+ - responsive improvements on edit widget [`1dcdda0`](https://github.com/eea/volto-eea-map/commit/1dcdda077f5aaa8ddd256d2bec2f1018a15e066d)
23
+ - map is sticky [`64f3874`](https://github.com/eea/volto-eea-map/commit/64f3874648f0ede23502998e9165e86538e44728)
24
+ - simplified layer edit [`b64348c`](https://github.com/eea/volto-eea-map/commit/b64348c962647cdfc48d3328c5effb3b02b6217c)
25
+ - no consoles [`1387128`](https://github.com/eea/volto-eea-map/commit/1387128037abb314798326fa3c47b52a13d0415f)
26
+ - show if zoom/print position [`c4d7107`](https://github.com/eea/volto-eea-map/commit/c4d7107e874616c2246ec883d07eb59fad76b21e)
27
+ - lint fix [`cc497bd`](https://github.com/eea/volto-eea-map/commit/cc497bd8fe61d8867b74a8a16bc4868ccf088d55)
28
+ - fixes on webmap [`1c4506b`](https://github.com/eea/volto-eea-map/commit/1c4506b227d3a0ad0f919e3ff826700304b0620a)
29
+ - new embed eea map block to work with visualizations [`be4f2b0`](https://github.com/eea/volto-eea-map/commit/be4f2b00f92ea9dedc359e8c2620a9a31f4b5b6b)
30
+ - remove vis_url logic, will be moved in a new block [`13ebe94`](https://github.com/eea/volto-eea-map/commit/13ebe94c6c339fc827c019aab0a698aa431c5b78)
31
+ - Updates for map visualization [`895d463`](https://github.com/eea/volto-eea-map/commit/895d46313e9b65c6a5f420284fb9459296ed83f4)
32
+
33
+ #### [0.1.7](https://github.com/eea/volto-eea-map/compare/0.1.6...0.1.7)
34
+
35
+ > 9 August 2022
36
+
37
+ - Develop [`#7`](https://github.com/eea/volto-eea-map/pull/7)
38
+ - fix height coming through in view [`39d4d28`](https://github.com/eea/volto-eea-map/commit/39d4d287a4fe86890f5fad8d9050130b496dfe70)
39
+ - Connected visualization view-edit [`52a8e21`](https://github.com/eea/volto-eea-map/commit/52a8e2187cb2e3a7db660ae4b6b5610c88be5083)
40
+ - map_view is map_editor_widget, add view component for content-type [`dc227f9`](https://github.com/eea/volto-eea-map/commit/dc227f9bbb259b59739a3694dc69f0fcca661137)
41
+ - Add map visualization widget [`affd84c`](https://github.com/eea/volto-eea-map/commit/affd84ca8753214c07f2f0756a13f08f5cf0a048)
42
+
7
43
  #### [0.1.6](https://github.com/eea/volto-eea-map/compare/0.1.5...0.1.6)
8
44
 
45
+ > 5 August 2022
46
+
47
+ - Develop [`#6`](https://github.com/eea/volto-eea-map/pull/6)
9
48
  - Lint fix [`f5561d1`](https://github.com/eea/volto-eea-map/commit/f5561d1581201bd635b4e53e8a46decaf684438f)
10
49
  - Map improvements [`c75d16c`](https://github.com/eea/volto-eea-map/commit/c75d16c265136d9f676e194540e613bb742fb51b)
11
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-map",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
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,10 +6,10 @@ export const Schema = () => {
6
6
  id: 'default',
7
7
  title: 'Default',
8
8
  fields: [
9
+ 'map_data',
10
+ //...(use_visualization ? ['vis_url'] : ['map_data']),
9
11
  'description',
10
12
  'height',
11
- 'map_data',
12
- 'show_sources',
13
13
  'show_legend',
14
14
  'show_download',
15
15
  'show_viewer',
@@ -17,6 +17,16 @@ export const Schema = () => {
17
17
  },
18
18
  ],
19
19
  properties: {
20
+ // use_visualization: {
21
+ // title: 'Use visualization',
22
+ // description:
23
+ // 'This setting will enable importing the map data from a visualization. If is enabled, editing the map manually will not be possible',
24
+ // type: 'boolean',
25
+ // },
26
+ // vis_url: {
27
+ // widget: 'object_by_path',
28
+ // title: 'Visualization',
29
+ // },
20
30
  height: {
21
31
  title: 'Height',
22
32
  type: 'number',
@@ -30,10 +40,6 @@ export const Schema = () => {
30
40
  description: 'Open the map customization interface',
31
41
  widget: 'map_edit_widget',
32
42
  },
33
- show_sources: {
34
- title: 'Show sources',
35
- type: 'boolean',
36
- },
37
43
  show_legend: {
38
44
  title: 'Show legend',
39
45
  type: 'boolean',
@@ -1,19 +1,17 @@
1
1
  import React from 'react';
2
+
2
3
  import Webmap from './components/Webmap';
3
4
  import ExtraViews from './components/widgets/ExtraViews';
4
5
  import { PrivacyProtection } from '@eeacms/volto-embed';
5
6
 
6
7
  const View = (props) => {
7
- const { data } = props || {};
8
-
9
- const { map_data = {} } = data;
10
-
11
- if (__SERVER__) return '';
8
+ const { data, id } = props || {};
9
+ const { map_data = {}, height = '' } = data;
12
10
 
13
11
  return (
14
12
  <div>
15
13
  <PrivacyProtection data={data} {...props}>
16
- <Webmap data={map_data} />
14
+ <Webmap data={map_data} height={height} id={id} />
17
15
  <ExtraViews data={data} />
18
16
  </PrivacyProtection>
19
17
  </div>
@@ -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,10 +1,17 @@
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';
6
+ // import {
7
+ // bootstrapControlClassnames,
8
+ // bootstrapControlElements,
9
+ // } from 'react-querybuilder/bootstrap';
4
10
 
5
11
  import checkSVG from '@plone/volto/icons/check.svg';
6
12
  import closeSVG from '@plone/volto/icons/clear.svg';
7
13
  import aheadSVG from '@plone/volto/icons/ahead.svg';
14
+ import resetSVG from '@plone/volto/icons/reset.svg';
8
15
 
9
16
  import { fetchArcgisData } from '../../utils';
10
17
 
@@ -30,7 +37,9 @@ const LayerSelectWidget = (props) => {
30
37
  const [availableLayers, setAvailableLayers] = React.useState(
31
38
  available_layers,
32
39
  );
33
- const [layerQuery, setLayerQuery] = React.useState(query);
40
+ //const [layerQuery, setLayerQuery] = React.useState(query);
41
+
42
+ const [builtQuery, setBuiltQuery] = React.useState(query);
34
43
 
35
44
  const handleServiceUrlCheck = async () => {
36
45
  // fetch url, save it, populate layers options
@@ -78,14 +87,34 @@ const LayerSelectWidget = (props) => {
78
87
  map_data: mapData,
79
88
  query: '',
80
89
  });
81
- setLayerQuery('');
90
+ setBuiltQuery('');
82
91
  };
83
92
 
84
93
  const handleQueryLayer = () => {
85
- onChange(id, {
86
- ...value,
87
- query: layerQuery,
88
- });
94
+ if (builtQuery) {
95
+ onChange(id, {
96
+ ...value,
97
+ query: builtQuery,
98
+ });
99
+ }
100
+ };
101
+
102
+ const handleChangeServiceUrl = (value) => {
103
+ setServiceUrlError('');
104
+ setCheckColor('');
105
+ setServiceUrl(value);
106
+ setAvailableLayers('');
107
+ setBuiltQuery('');
108
+ setSelectedLayer('');
109
+ };
110
+
111
+ const handleReset = () => {
112
+ setServiceUrlError('');
113
+ setServiceUrl(map_service_url);
114
+ setCheckColor('');
115
+ setAvailableLayers(available_layers);
116
+ setBuiltQuery('');
117
+ setSelectedLayer(layer);
89
118
  };
90
119
 
91
120
  return (
@@ -95,74 +124,122 @@ const LayerSelectWidget = (props) => {
95
124
  }}
96
125
  >
97
126
  <Grid>
98
- <h4>Service URL</h4>
127
+ <h5 style={{ padding: '0', margin: '15px 0px 5px 0px' }}>
128
+ Service URL
129
+ </h5>
99
130
  <Grid.Row>
100
131
  <Input
101
132
  type="text"
102
- onChange={(e, { value }) => setServiceUrl(value)}
103
- style={{ width: '70%' }}
133
+ onChange={(e, { value }) => handleChangeServiceUrl(value)}
134
+ style={{ width: '100%' }}
135
+ error={serviceUrlError}
104
136
  value={serviceUrl}
105
- action
106
- actionPosition="right"
107
- >
108
- <input />
137
+ // action
138
+ // actionPosition="right"
139
+ ></Input>
140
+
141
+ <span style={{ fontSize: '12px', color: 'darkred' }}>
142
+ {serviceUrlError.error}
143
+ </span>
144
+ </Grid.Row>
145
+ {serviceUrl && (
146
+ <Grid.Row>
147
+ {serviceUrl !== map_service_url && (
148
+ <Button
149
+ size="small"
150
+ compact
151
+ className="layer-reset-button"
152
+ onClick={handleReset}
153
+ >
154
+ <Icon name={resetSVG} title="Reset" size="20px" />
155
+ </Button>
156
+ )}
109
157
  <Button
110
- type="submit"
111
- size="tiny"
112
- compact
158
+ size="small"
113
159
  color={checkColor}
160
+ compact
161
+ className="layer-check-button"
114
162
  onClick={handleServiceUrlCheck}
115
163
  >
116
164
  <Icon
117
165
  name={serviceUrlError ? closeSVG : checkSVG}
118
- title="Check Url"
119
- size="17px"
166
+ title="Submit"
167
+ size="20px"
120
168
  />
121
169
  </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
- ))}
170
+ </Grid.Row>
171
+ )}
172
+ {availableLayers && availableLayers.length > 0 && (
173
+ <>
174
+ <h5 style={{ padding: '0', margin: '15px 0px 5px 0px' }}>Layer</h5>
175
+ <Grid.Row>
176
+ <Select
177
+ onChange={(e, { value }) => handleSelectLayer(value)}
178
+ options={availableLayers}
179
+ style={{ width: '100%' }}
180
+ placeholder="Select layer"
181
+ value={selectedLayer}
182
+ />
183
+ </Grid.Row>
184
+ </>
185
+ )}
186
+ {availableLayers && fields && fields.length > 0 && (
187
+ <>
188
+ <h5 style={{ padding: '0', margin: '15px 0px 5px 0px' }}>
189
+ Query Layer
190
+ </h5>
191
+ <Grid.Row stretched>
192
+ {/* <Input
193
+ type="text"
194
+ style={{ width: '100%' }}
195
+ onChange={(e, { value }) => setLayerQuery(value)}
196
+ value={layerQuery}
197
+ ></Input> */}
198
+ </Grid.Row>
199
+ <Grid.Row>
200
+ <QueryBuilder
201
+ fields={fields.map((fi, i) => {
202
+ return { name: fi.name, label: fi.name };
203
+ })}
204
+ query={builtQuery}
205
+ onQueryChange={(q) => setBuiltQuery(q)}
206
+ enableDragAndDrop={false}
207
+ //controlElements={bootstrapControlElements}
208
+ //controlClassnames={bootstrapControlClassnames}
209
+ />
210
+ </Grid.Row>
211
+ {builtQuery && (
212
+ <Grid.Row>
213
+ <Button
214
+ type="submit"
215
+ size="tiny"
216
+ compact
217
+ className="layer-submit-button "
218
+ color={'green'}
219
+ onClick={handleQueryLayer}
220
+ >
221
+ <Icon name={aheadSVG} title="Check Url" size="20px" />
222
+ </Button>
223
+ </Grid.Row>
224
+ )}
225
+ <Grid.Row>
226
+ <p
227
+ style={{
228
+ fontSize: '13px',
229
+ fontWeight: 'bold',
230
+ color: 'darkgray',
231
+ }}
232
+ >
233
+ Available Fields:
234
+ </p>
235
+ </Grid.Row>
236
+ {fields.map((field, id) => (
237
+ <p style={{ fontSize: '12px', padding: '0 5px' }}>
238
+ <strong>{field.alias}</strong> ({field.type})
239
+ </p>
240
+ ))}
241
+ </>
242
+ )}
166
243
  </Grid>
167
244
  </div>
168
245
  );
@@ -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);
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import { Modal, Button, Grid } from 'semantic-ui-react';
3
+ import Webmap from '../Webmap';
4
+ import { FormFieldWrapper, InlineForm } from '@plone/volto/components';
5
+ import { VisibilitySensor } from '@eeacms/volto-datablocks/components';
6
+
7
+ import PanelsSchema from './panelsSchema';
8
+
9
+ const VisualizationEditorWidget = (props) => {
10
+ const [open, setOpen] = React.useState(false);
11
+ const { onChange = {}, id } = props;
12
+ const block = React.useMemo(() => props.block, [props.block]);
13
+ const value = React.useMemo(() => props.value, [props.value]);
14
+
15
+ const [intValue, setIntValue] = React.useState(value);
16
+
17
+ const dataForm = { map_data: intValue };
18
+ const handleApplyChanges = () => {
19
+ onChange(id, intValue);
20
+ setOpen(false);
21
+ };
22
+
23
+ const handleClose = () => {
24
+ setIntValue(value);
25
+ setOpen(false);
26
+ };
27
+
28
+ const handleChangeField = (id, fieldVal) => {
29
+ setIntValue(fieldVal);
30
+ };
31
+
32
+ let schema = PanelsSchema({ data: dataForm });
33
+
34
+ return (
35
+ <FormFieldWrapper {...props}>
36
+ <Button
37
+ floated="right"
38
+ size="tiny"
39
+ onClick={(e) => {
40
+ e.preventDefault();
41
+ e.stopPropagation();
42
+ setOpen(true);
43
+ }}
44
+ >
45
+ Open Map Editor
46
+ </Button>
47
+ {open && (
48
+ <Modal
49
+ id="map-editor-modal"
50
+ style={{ width: '95% !important' }}
51
+ open={true}
52
+ >
53
+ <Modal.Content scrolling>
54
+ <Grid>
55
+ <Grid.Row>
56
+ <Grid.Column width={4}>
57
+ <InlineForm
58
+ block={block}
59
+ title={schema.title}
60
+ schema={schema}
61
+ onChangeField={(id, value) => {
62
+ handleChangeField(id, value);
63
+ }}
64
+ formData={dataForm}
65
+ />
66
+ </Grid.Column>
67
+ <Grid.Column width={8}>
68
+ <VisibilitySensor>
69
+ <Webmap data={intValue} editMode={true} />
70
+ </VisibilitySensor>
71
+ </Grid.Column>
72
+ </Grid.Row>
73
+ </Grid>
74
+ </Modal.Content>
75
+ <Modal.Actions>
76
+ <Grid>
77
+ <Grid.Row>
78
+ <Grid.Column width={8}></Grid.Column>
79
+ <Grid.Column width={4}>
80
+ <Button onClick={() => handleClose()}>Close</Button>
81
+ <Button color="green" onClick={() => handleApplyChanges()}>
82
+ Apply changes
83
+ </Button>
84
+ </Grid.Column>
85
+ </Grid.Row>
86
+ </Grid>
87
+ </Modal.Actions>
88
+ </Modal>
89
+ )}
90
+ </FormFieldWrapper>
91
+ );
92
+ };
93
+
94
+ export default VisualizationEditorWidget;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import Webmap from '../Webmap';
3
+
4
+ const VisualizationView = (props) => {
5
+ const { content = {} } = props;
6
+
7
+ const { map_visualization_data = {} } = content;
8
+
9
+ return (
10
+ <div>
11
+ <Webmap data={map_visualization_data} />
12
+ </div>
13
+ );
14
+ };
15
+
16
+ export default VisualizationView;
@@ -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,97 @@
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
+ @keyframes fadeDown {
107
+ from {
108
+ opacity: 0;
109
+ transform: translate3d(0, -20%, 0);
110
+ }
111
+
112
+ to {
113
+ opacity: 1;
114
+ transform: translate3d(0, 0, 0);
115
+ }
116
+ }
@@ -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 } = props;
11
+ const schema = React.useMemo(() => Schema(props), [props]);
12
+
13
+ return (
14
+ <div>
15
+ <View data={data} />
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,46 @@
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
+ ],
16
+ },
17
+ ],
18
+ properties: {
19
+ vis_url: {
20
+ widget: 'object_by_path',
21
+ title: 'Visualization',
22
+ },
23
+ height: {
24
+ title: 'Height',
25
+ type: 'number',
26
+ },
27
+ description: {
28
+ title: 'Description',
29
+ widget: 'slate',
30
+ },
31
+ show_legend: {
32
+ title: 'Show legend',
33
+ type: 'boolean',
34
+ },
35
+ show_download: {
36
+ title: 'Show download',
37
+ type: 'boolean',
38
+ },
39
+ show_viewer: {
40
+ title: 'Show web viewer',
41
+ type: 'boolean',
42
+ },
43
+ },
44
+ required: [],
45
+ };
46
+ };
@@ -0,0 +1,48 @@
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 { getVisualization } from '@eeacms/volto-eea-map/actions';
10
+
11
+ const View = (props) => {
12
+ const { data, map_visualization } = props || {};
13
+ const { height = '', vis_url = '' } = data;
14
+
15
+ React.useEffect(() => {
16
+ if (vis_url) {
17
+ props.getVisualization(vis_url);
18
+ }
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
20
+ }, [vis_url]);
21
+
22
+ return (
23
+ <div>
24
+ <PrivacyProtection data={data} {...props}>
25
+ {map_visualization && (
26
+ <div>
27
+ <Webmap data={map_visualization.data} height={height} />
28
+ <ExtraViews data={{ ...data, map_data: map_visualization.data }} />
29
+ </div>
30
+ )}
31
+ {!map_visualization && (
32
+ <p>No map view to show. Set visualization in block configuration.</p>
33
+ )}
34
+ </PrivacyProtection>
35
+ </div>
36
+ );
37
+ };
38
+
39
+ export default compose(
40
+ connect(
41
+ (state, props) => ({
42
+ map_visualization: state.map_visualizations.data[props.data.vis_url],
43
+ }),
44
+ {
45
+ getVisualization,
46
+ },
47
+ ),
48
+ )(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,8 +1,18 @@
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';
5
10
  import ObjectTypesWidget from './components/Blocks/EEAMap/components/widgets/ObjectTypesWidget';
11
+ import VisualizationEditorWidget from './components/Blocks/EEAMap/components/widgets/VisualizationEditorWidget';
12
+ import VisualizationView from './components/Blocks/EEAMap/components/widgets/VisualizationView';
13
+
14
+ import { data_visualizations } from './middlewares';
15
+ import * as addonReducers from './reducers';
6
16
 
7
17
  export default (config) => {
8
18
  config.settings.allowed_cors_destinations = [
@@ -48,9 +58,52 @@ export default (config) => {
48
58
  ],
49
59
  };
50
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
+
51
89
  config.widgets.widget.map_edit_widget = MapEditorWidget;
52
90
  config.widgets.widget.map_layers_widget = LayerSelectWidget;
53
91
  config.widgets.widget.object_types_widget = ObjectTypesWidget;
54
92
 
93
+ //map editor for the visualization(content-type)
94
+ config.widgets.id.map_visualization_data = VisualizationEditorWidget;
95
+ //map viewer for the visualization(content-type)
96
+ config.views.contentTypesViews.map_visualization = VisualizationView;
97
+
98
+ config.settings.storeExtenders = [
99
+ ...(config.settings.storeExtenders || []),
100
+ data_visualizations,
101
+ ];
102
+
103
+ config.addonReducers = {
104
+ ...config.addonReducers,
105
+ ...addonReducers,
106
+ };
107
+
55
108
  return config;
56
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
+ }