@eeacms/volto-eea-map 0.1.34 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import { FormFieldWrapper, Icon } from '@plone/volto/components';
3
3
  import { Button } from 'semantic-ui-react';
4
4
  import loadable from '@loadable/component';
5
5
  import clearSVG from '@plone/volto/icons/clear.svg';
6
+ import checkSVG from '@plone/volto/icons/check.svg';
6
7
 
7
8
  const ReactColor = loadable.lib(() => import('react-color'));
8
9
 
@@ -10,17 +11,33 @@ export default (props) => {
10
11
  const { id, value, onChange, available_colors } = props;
11
12
  const [showPicker, setShowPicker] = React.useState(false);
12
13
 
14
+ const [color, setColor] = React.useState(value);
15
+
16
+ const handleChangeColor = (valColor) => {
17
+ setColor(valColor);
18
+ };
19
+
20
+ const handleConfirmColor = () => {
21
+ onChange(id, color);
22
+ setShowPicker(false);
23
+ };
24
+
25
+ const handleAbortColor = () => {
26
+ setColor(value);
27
+ setShowPicker(false);
28
+ };
29
+
13
30
  return (
14
31
  <FormFieldWrapper
15
32
  {...props}
16
33
  draggable={false}
17
34
  className="simple-color-picker-widget"
18
35
  >
19
- <div>
36
+ <div style={{ position: 'relative' }}>
20
37
  <Button.Group fluid>
21
38
  <Button
22
- color={value}
23
- style={{ backgroundColor: value }}
39
+ color={value?.hex ? value?.hex : 'black'}
40
+ style={{ backgroundColor: value?.hex ? value?.hex : 'black' }}
24
41
  onClick={() => setShowPicker(true)}
25
42
  size="small"
26
43
  fluid
@@ -35,27 +52,67 @@ export default (props) => {
35
52
  >
36
53
  <Icon name={clearSVG} size="18px" color="red" />
37
54
  </Button>
55
+ {showPicker ? (
56
+ <div
57
+ style={{
58
+ position: 'absolute',
59
+ top: '0',
60
+ zIndex: '77',
61
+ background: 'lightgrey',
62
+ display: 'flex',
63
+ flexDirection: 'column',
64
+ padding: '5px',
65
+ borderRadius: '2px',
66
+ }}
67
+ >
68
+ <ReactColor>
69
+ {({ SketchPicker }) => {
70
+ return (
71
+ <SketchPicker
72
+ width="180px"
73
+ colors={available_colors}
74
+ color={
75
+ color?.rgb
76
+ ? color.rgb
77
+ : {
78
+ r: 0,
79
+ g: 0,
80
+ b: 0,
81
+ a: 1,
82
+ }
83
+ }
84
+ onChangeComplete={(value) => {
85
+ handleChangeColor(value);
86
+ }}
87
+ ></SketchPicker>
88
+ );
89
+ }}
90
+ </ReactColor>
91
+ <Button.Group>
92
+ <Button
93
+ size="tiny"
94
+ compact
95
+ title="Reset color"
96
+ style={{ paddingLeft: '8px', paddingRight: '0px' }}
97
+ onClick={() => handleAbortColor()}
98
+ >
99
+ <Icon name={clearSVG} size="18px" />
100
+ </Button>
101
+ <Button
102
+ onClick={() => handleConfirmColor()}
103
+ color="green"
104
+ compact
105
+ size="tiny"
106
+ title="Confirm color"
107
+ >
108
+ <Icon name={checkSVG} size="18px" />
109
+ </Button>
110
+ </Button.Group>
111
+ </div>
112
+ ) : (
113
+ ''
114
+ )}
38
115
  </Button.Group>
39
-
40
- {showPicker ? (
41
- <ReactColor>
42
- {({ SketchPicker }) => {
43
- return (
44
- <SketchPicker
45
- width="180px"
46
- colors={available_colors}
47
- color={value || '#000'}
48
- onChangeComplete={(value) => {
49
- setShowPicker(false);
50
- onChange(id, value.hex);
51
- }}
52
- ></SketchPicker>
53
- );
54
- }}
55
- </ReactColor>
56
- ) : (
57
- ''
58
- )}
59
116
  </div>
60
117
  </FormFieldWrapper>
61
118
  );
package/src/index.js CHANGED
@@ -2,16 +2,20 @@ import EmbedMapView from './components/Blocks/EmbedEEAMap/View';
2
2
  import EmbedMapEdit from './components/Blocks/EmbedEEAMap/Edit';
3
3
 
4
4
  import world from '@plone/volto/icons/world.svg';
5
+
5
6
  import DataQueryWidget from './components/widgets/DataQueryWidget';
6
7
  import LayerSelectWidget from './components/widgets/LayerSelectWidget';
7
- import MapEditorWidget from './components/widgets/MapEditorWidget';
8
+
8
9
  import VisualizationEditorWidget from './components/visualization/VisualizationEditorWidget';
9
10
  import VisualizationView from './components/visualization/VisualizationView';
11
+
10
12
  import SimpleColorPickerWidget from './components/widgets/SimpleColorPickerWidget';
11
13
 
12
14
  import { data_visualizations } from './middlewares';
13
15
  import * as addonReducers from './reducers';
14
16
 
17
+ import './less/global.less';
18
+
15
19
  export default (config) => {
16
20
  config.settings.allowed_cors_destinations = [
17
21
  ...(config.settings.allowed_cors_destinations || []),
@@ -51,7 +55,6 @@ export default (config) => {
51
55
  ],
52
56
  };
53
57
 
54
- config.widgets.widget.map_edit_widget = MapEditorWidget;
55
58
  config.widgets.widget.map_layers_widget = LayerSelectWidget;
56
59
  config.widgets.widget.data_query_widget = DataQueryWidget;
57
60
  config.widgets.widget.simple_color_picker_widget = SimpleColorPickerWidget;
@@ -1,3 +1,21 @@
1
+ @import (multiple, reference, optional) '../../theme.config';
2
+
3
+ /* Enables customization of addons */
4
+ .loadAddonOverrides() {
5
+ @import (optional)
6
+ '@{siteFolder}/../addons/@{addon}/@{addontype}s/@{addonelement}.overrides';
7
+ }
8
+
9
+ /* Helper to load variables */
10
+ .loadAddonVariables() {
11
+ @import (optional) '@{addonelement}.variables';
12
+ @import (optional)
13
+ '@{siteFolder}/../addons/@{addon}/@{addontype}s/@{addonelement}.variables';
14
+ }
15
+
16
+ @import './variables.less';
17
+
18
+ // TODO: pull out colors and dimensions into variables
1
19
  .map-edit-container {
2
20
  display: flex;
3
21
  }
@@ -162,6 +180,8 @@
162
180
  }
163
181
 
164
182
  .legend-container {
183
+ display: flex;
184
+ flex-direction: column;
165
185
  margin: 15px 0;
166
186
  }
167
187
 
@@ -179,10 +199,20 @@
179
199
  }
180
200
 
181
201
  .legend-action {
182
- padding: 10px 0;
202
+ z-index: 22;
203
+ display: inline-flex;
204
+ align-items: center;
205
+ padding-bottom: 3px;
183
206
  border: none;
184
207
  background: transparent;
208
+ background-color: transparent;
209
+ color: #3d5265;
185
210
  cursor: pointer;
211
+ font-weight: bold;
212
+
213
+ &:hover {
214
+ color: @secondaryColor;
215
+ }
186
216
  }
187
217
 
188
218
  .map-number-input {
@@ -195,6 +225,46 @@
195
225
  color: green;
196
226
  }
197
227
 
228
+ /* sources */
229
+
230
+ .eea-map-sources-list {
231
+ margin: 0;
232
+ list-style: decimal inside;
233
+ padding-inline-start: 0;
234
+ }
235
+
236
+ #eea-map-sources-popup .ui.popup {
237
+ min-width: 600px;
238
+ background-color: #f9f9f9;
239
+ box-shadow: rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;
240
+ }
241
+
242
+ .eea-map-sources-button {
243
+ display: inline-flex;
244
+ align-items: center;
245
+ padding-top: 10px;
246
+ padding-bottom: 3px;
247
+ border: none;
248
+ background-color: transparent;
249
+ color: @textColor;
250
+ cursor: pointer;
251
+ font-weight: bold;
252
+
253
+ .icon {
254
+ margin-left: 0.5rem;
255
+ }
256
+
257
+ &:hover {
258
+ color: @secondaryColor;
259
+ }
260
+
261
+ &.expanded {
262
+ padding-bottom: 0;
263
+ border-bottom: 3px solid @secondaryColor;
264
+ color: @secondaryColor;
265
+ }
266
+ }
267
+
198
268
  @keyframes fadeDown {
199
269
  from {
200
270
  opacity: 0;
@@ -212,4 +282,9 @@
212
282
  max-width: 0.2cm;
213
283
  height: 0.2cm;
214
284
  }
285
+
286
+ .eea-map-sources-container,
287
+ #eea-map-sources-popup {
288
+ display: none;
289
+ }
215
290
  }
@@ -0,0 +1,5 @@
1
+ @type: extra;
2
+
3
+ @element: custom;
4
+
5
+ @grey-1: #f9f9f9;
package/src/utils.js CHANGED
@@ -30,4 +30,97 @@ const fetchArcGISData = async (url) => {
30
30
  return data;
31
31
  };
32
32
 
33
- export { setLegendColumns, fetchArcGISData };
33
+ const applyQueriesToMapLayers = (
34
+ map_visualization,
35
+ block_data_query_params,
36
+ enable_queries,
37
+ ) => {
38
+ //break reference to the original map_visualization object
39
+ //so i safely manipulate data
40
+ var altMapData = map_visualization
41
+ ? JSON.parse(JSON.stringify(map_visualization))
42
+ : '';
43
+
44
+ var rules = [];
45
+ if (
46
+ enable_queries &&
47
+ block_data_query_params &&
48
+ block_data_query_params.length > 0 &&
49
+ altMapData.layers &&
50
+ altMapData.layers.map_layers &&
51
+ altMapData.layers.map_layers.length > 0
52
+ ) {
53
+ altMapData.layers.map_layers.forEach((l, j) => {
54
+ block_data_query_params.forEach((param, i) => {
55
+ const matchingFields =
56
+ l.map_layer && l.map_layer.fields && l.map_layer.fields.length > 0
57
+ ? l.map_layer.fields.filter(
58
+ (field, k) =>
59
+ field.name === param.alias || field.name === param.i,
60
+ )
61
+ : [];
62
+ matchingFields.forEach((m, i) => {
63
+ const newRules = param.v
64
+ ? param.v.map((paramVal, i) => {
65
+ return {
66
+ field: m.name,
67
+ operator: '=',
68
+ value: paramVal,
69
+ };
70
+ })
71
+ : [];
72
+ const concatRules = rules.concat(newRules);
73
+ const filteredRules = concatRules.filter(
74
+ (v, i, a) =>
75
+ a.findLastIndex(
76
+ (v2) => v2.field === v.field && v2.value === v.value,
77
+ ) === i,
78
+ );
79
+ rules = filteredRules;
80
+ });
81
+ });
82
+ let autoQuery = {
83
+ combinator: 'or',
84
+ rules,
85
+ };
86
+ altMapData.layers.map_layers[j].map_layer.query = autoQuery;
87
+ });
88
+ }
89
+ return altMapData;
90
+ };
91
+
92
+ const updateBlockQueryFromPageQuery = (data_query, data_query_params) => {
93
+ var pageDataQuery = JSON.parse(JSON.stringify(data_query));
94
+ var blockDataQuery = data_query_params
95
+ ? JSON.parse(JSON.stringify(data_query_params))
96
+ : '';
97
+ var newDataQuery = pageDataQuery.map((parameter, index) => {
98
+ //check if the parameter exists in data and has value
99
+ // then get its alias value and update it
100
+ //check if data_query param value is changed
101
+ //and change it in block data
102
+ if (
103
+ blockDataQuery &&
104
+ blockDataQuery[index] &&
105
+ parameter.i &&
106
+ blockDataQuery[index].i &&
107
+ parameter.i === blockDataQuery[index].i
108
+ ) {
109
+ return {
110
+ ...parameter,
111
+ alias: blockDataQuery[index]?.alias ? blockDataQuery[index]?.alias : '',
112
+ v: parameter?.v ? parameter?.v : '',
113
+ };
114
+ }
115
+
116
+ return parameter;
117
+ });
118
+ return newDataQuery;
119
+ };
120
+
121
+ export {
122
+ setLegendColumns,
123
+ fetchArcGISData,
124
+ applyQueriesToMapLayers,
125
+ updateBlockQueryFromPageQuery,
126
+ };
@@ -1,65 +0,0 @@
1
- import React from 'react';
2
-
3
- import { connect } from 'react-redux';
4
- import { compose } from 'redux';
5
-
6
- import { SidebarPortal } from '@plone/volto/components';
7
- import BlockDataForm from '@plone/volto/components/manage/Form/BlockDataForm';
8
-
9
- import { Schema } from './Schema';
10
- import View from './View';
11
- import { getContent } from '@plone/volto/actions';
12
-
13
- import { addPrivacyProtectionToSchema } from '@eeacms/volto-embed';
14
- import '../../../styles/map.css';
15
-
16
- const Edit = (props) => {
17
- const { block, data, onChangeBlock, selected, id } = props;
18
- const schema = React.useMemo(() => Schema(props), [props]);
19
-
20
- React.useEffect(() => {
21
- if (
22
- !data.data_query_params ||
23
- props.data_query !== data.data_query_params
24
- ) {
25
- onChangeBlock(block, {
26
- ...data,
27
- data_query_params: props.data_query,
28
- });
29
- }
30
- // eslint-disable-next-line react-hooks/exhaustive-deps
31
- }, [props.data_query, block, data]);
32
-
33
- return (
34
- <div>
35
- <View data={data} id={id} />
36
- <SidebarPortal selected={selected}>
37
- <BlockDataForm
38
- block={block}
39
- title={schema.title}
40
- schema={addPrivacyProtectionToSchema(schema)}
41
- onChangeField={(id, value) => {
42
- onChangeBlock(block, {
43
- ...data,
44
- [id]: value,
45
- });
46
- }}
47
- formData={data}
48
- />
49
- </SidebarPortal>
50
- </div>
51
- );
52
- };
53
-
54
- export default compose(
55
- connect(
56
- (state, props) => ({
57
- data_query: state.content.data.data_query,
58
- data_provenance:
59
- state.content.subrequests?.[props.id]?.data?.data_provenance,
60
- }),
61
- {
62
- getContent,
63
- },
64
- ),
65
- )(Edit);
@@ -1,55 +0,0 @@
1
- export const Schema = (props) => {
2
- return {
3
- title: 'EEA Map Block',
4
- fieldsets: [
5
- {
6
- id: 'default',
7
- title: 'Default',
8
- fields: [
9
- 'map_data',
10
- 'description',
11
- 'height',
12
- 'show_legend',
13
- 'show_download',
14
- 'show_viewer',
15
- 'show_sources',
16
- 'data_query_params',
17
- ],
18
- },
19
- ],
20
- properties: {
21
- show_sources: {
22
- title: 'Show sources',
23
- type: 'boolean',
24
- },
25
- height: {
26
- title: 'Height',
27
- type: 'number',
28
- },
29
- description: {
30
- title: 'Description',
31
- widget: 'slate',
32
- },
33
- map_data: {
34
- title: 'Edit map',
35
- description: 'Open the map customization interface',
36
- widget: 'map_edit_widget',
37
- },
38
- show_legend: {
39
- title: 'Show legend',
40
- type: 'boolean',
41
- },
42
-
43
- show_viewer: {
44
- title: 'Show web viewer',
45
- type: 'boolean',
46
- },
47
- data_query_params: {
48
- title: 'Query parameters',
49
- description: 'ddafdfas',
50
- widget: 'data_query_widget',
51
- },
52
- },
53
- required: [],
54
- };
55
- };
@@ -1,44 +0,0 @@
1
- import React from 'react';
2
-
3
- import { connect } from 'react-redux';
4
- import { compose } from 'redux';
5
-
6
- import Webmap from '../../Webmap';
7
- import ExtraViews from '../../ExtraViews';
8
-
9
- import { PrivacyProtection } from '@eeacms/volto-embed';
10
- import { getContent } from '@plone/volto/actions';
11
-
12
- const View = (props) => {
13
- const { data, id, path } = props || {};
14
- const { map_data = {}, height = '' } = data;
15
-
16
- React.useEffect(() => {
17
- if (path) {
18
- props.getContent(path, null, id);
19
- }
20
- // eslint-disable-next-line react-hooks/exhaustive-deps
21
- }, [path]);
22
-
23
- return (
24
- <div>
25
- <PrivacyProtection data={data} {...props}>
26
- <Webmap data={map_data} height={height} id={id} />
27
- <ExtraViews data={data} />
28
- </PrivacyProtection>
29
- </div>
30
- );
31
- };
32
-
33
- export default compose(
34
- connect(
35
- (state, props) => ({
36
- data_query: state.content.subrequests?.[props.id]?.data?.data_query,
37
- data_provenance:
38
- state.content.subrequests?.[props.id]?.data?.data_provenance,
39
- }),
40
- {
41
- getContent,
42
- },
43
- ),
44
- )(View);
@@ -1,96 +0,0 @@
1
- import React from 'react';
2
- import { Modal, Button, Grid } from 'semantic-ui-react';
3
- import Webmap from '../Webmap';
4
-
5
- import { FormFieldWrapper, InlineForm } from '@plone/volto/components';
6
-
7
- import PanelsSchema from './panelsSchema';
8
-
9
- const MapEditorWidget = (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
-
21
- //set map data for screenshot
22
- // if (intValue.layers?.map_layers[0].map_layer?.map_service_url) {
23
- // onChange(
24
- // 'url',
25
- // `${intValue.layers?.map_layers[0].map_layer?.map_service_url}?f=jsapi`,
26
- // );
27
- // }
28
- setOpen(false);
29
- };
30
-
31
- const handleClose = () => {
32
- setIntValue(value);
33
- setOpen(false);
34
- };
35
-
36
- const handleChangeField = (id, fieldVal) => {
37
- setIntValue(fieldVal);
38
- };
39
-
40
- let schema = PanelsSchema({ data: dataForm });
41
- return (
42
- <FormFieldWrapper {...props}>
43
- <Modal
44
- id="map-editor-modal"
45
- className="map-editor-modal"
46
- onClose={handleClose}
47
- onOpen={() => setOpen(true)}
48
- open={open}
49
- trigger={
50
- <Button size="tiny" className="map-modal-trigger-button">
51
- Open Map Editor
52
- </Button>
53
- }
54
- >
55
- <Modal.Content scrolling>
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">
74
- <Webmap data={intValue} editMode={true} />
75
- </div>
76
- </Grid.Column>
77
- </Grid>
78
- </Modal.Content>
79
- <Modal.Actions>
80
- <Grid>
81
- <Grid.Row>
82
- <div className="map-edit-actions-container">
83
- <Button onClick={handleClose}>Close</Button>
84
- <Button color="green" onClick={handleApplyChanges}>
85
- Apply changes
86
- </Button>
87
- </div>
88
- </Grid.Row>
89
- </Grid>
90
- </Modal.Actions>
91
- </Modal>
92
- </FormFieldWrapper>
93
- );
94
- };
95
-
96
- export default MapEditorWidget;