@eeacms/volto-eea-map 4.1.0 → 5.0.1

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 (73) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/package.json +5 -2
  3. package/src/Arcgis/Editor/Editor.jsx +130 -0
  4. package/src/Arcgis/Editor/EditorContext.jsx +2 -0
  5. package/src/Arcgis/Editor/Fold/Fold.jsx +56 -0
  6. package/src/Arcgis/Editor/Panels/Panel.jsx +8 -0
  7. package/src/Arcgis/Editor/Panels/SettingsGeneralPanel.jsx +217 -0
  8. package/src/Arcgis/Editor/Panels/SettingsLayersPanel.jsx +216 -0
  9. package/src/Arcgis/Editor/Panels/StructureBaseLayerPanel.jsx +60 -0
  10. package/src/Arcgis/Editor/Panels/StructureLayersPanel.jsx +394 -0
  11. package/src/Arcgis/Editor/Panels/StructureWidgetsPanel.jsx +181 -0
  12. package/src/Arcgis/Editor/Panels/index.js +6 -0
  13. package/src/Arcgis/Editor/SidebarGroup.jsx +62 -0
  14. package/src/Arcgis/Layer/Layer.jsx +247 -0
  15. package/src/Arcgis/Map/Map.jsx +287 -0
  16. package/src/Arcgis/Map/MapBuilder.jsx +111 -0
  17. package/src/Arcgis/Map/MapContext.jsx +3 -0
  18. package/src/Arcgis/Widget/Widget.jsx +170 -0
  19. package/src/Arcgis/helpers.js +140 -0
  20. package/src/Blocks/EmbedEEAMap/Edit.jsx +40 -0
  21. package/src/Blocks/EmbedEEAMap/View.jsx +122 -0
  22. package/src/Blocks/EmbedEEAMap/helpers.js +12 -0
  23. package/src/Blocks/EmbedEEAMap/schema.js +126 -0
  24. package/src/{components → Toolbar}/Share.jsx +1 -1
  25. package/src/{components/ExtraViews.jsx → Toolbar/Toolbar.jsx} +14 -16
  26. package/src/{components/visualization → Views}/VisualizationView.jsx +8 -9
  27. package/src/Widgets/ArcgisColorPickerWidget.jsx +95 -0
  28. package/src/Widgets/ArcgisRendererWidget/ArcgisRendererWidget.jsx +106 -0
  29. package/src/Widgets/ArcgisRendererWidget/RendererEditor/ClassBreaks.jsx +8 -0
  30. package/src/Widgets/ArcgisRendererWidget/RendererEditor/Dictionary.jsx +8 -0
  31. package/src/Widgets/ArcgisRendererWidget/RendererEditor/DotDensity.jsx +8 -0
  32. package/src/Widgets/ArcgisRendererWidget/RendererEditor/Heatmap.jsx +8 -0
  33. package/src/Widgets/ArcgisRendererWidget/RendererEditor/PieChart.jsx +8 -0
  34. package/src/Widgets/ArcgisRendererWidget/RendererEditor/Simple.jsx +109 -0
  35. package/src/Widgets/ArcgisRendererWidget/RendererEditor/UniqueValue.jsx +8 -0
  36. package/src/Widgets/ArcgisRendererWidget/RendererEditor/_Editor.jsx +29 -0
  37. package/src/Widgets/ArcgisRendererWidget/RendererEditor/_EditorModal.jsx +88 -0
  38. package/src/Widgets/ArcgisRendererWidget/RendererEditor/_defaults.js +30 -0
  39. package/src/Widgets/ArcgisSliderWidget.jsx +79 -0
  40. package/src/Widgets/ArcgisViewpointWidget.jsx +112 -0
  41. package/src/{components/visualization → Widgets}/VisualizationViewWidget.jsx +11 -10
  42. package/src/Widgets/VisualizationWidget.jsx +200 -0
  43. package/src/arcgis.js +48 -0
  44. package/src/constants.js +225 -7
  45. package/src/hocs/withArcgis.jsx +27 -0
  46. package/src/hooks/useChangedProps.jsx +24 -0
  47. package/src/hooks/useClass.jsx +17 -0
  48. package/src/hooks/useCopyToClipboard.jsx +25 -0
  49. package/src/index.js +16 -16
  50. package/src/jsoneditor.js +72 -0
  51. package/src/styles/editor.less +446 -0
  52. package/src/styles/map.less +3 -0
  53. package/src/components/Blocks/EmbedEEAMap/Edit.jsx +0 -161
  54. package/src/components/Blocks/EmbedEEAMap/Schema.js +0 -161
  55. package/src/components/Blocks/EmbedEEAMap/View.jsx +0 -79
  56. package/src/components/Blocks/EmbedEEAMap/helpers.js +0 -45
  57. package/src/components/LegendView.jsx +0 -150
  58. package/src/components/Webmap.jsx +0 -371
  59. package/src/components/index.js +0 -6
  60. package/src/components/visualization/VisualizationEditorWidget.jsx +0 -122
  61. package/src/components/visualization/panelsSchema.js +0 -229
  62. package/src/components/widgets/DataQueryWidget.jsx +0 -51
  63. package/src/components/widgets/LayerSelectWidget.jsx +0 -463
  64. package/src/components/widgets/LayersPanelWidget.jsx +0 -59
  65. package/src/components/widgets/SimpleColorPickerWidget.jsx +0 -121
  66. package/src/hocs/index.js +0 -3
  67. package/src/hocs/withDeviceSize.jsx +0 -45
  68. package/src/less/global.less +0 -253
  69. package/src/less/variables.less +0 -5
  70. package/src/utils.js +0 -151
  71. /package/src/{components → Toolbar}/FigureNote.jsx +0 -0
  72. /package/src/{components → Toolbar}/MoreInfoLink.jsx +0 -0
  73. /package/src/{components → Toolbar}/Sources.jsx +0 -0
@@ -0,0 +1,181 @@
1
+ import { useState } from 'react';
2
+ import { Icon, InlineForm } from '@plone/volto/components';
3
+ import { withVariationSchemaEnhancer } from '@plone/volto/helpers';
4
+ import addSVG from '@plone/volto/icons/add.svg';
5
+ import Panel from './Panel';
6
+ import Fold from '../Fold/Fold';
7
+ import { debounce, getWidgets } from '../../helpers';
8
+ import {
9
+ expandKeys,
10
+ positions,
11
+ widgets as widgetsOptions,
12
+ getDefaultWidgets,
13
+ widgetsSchema,
14
+ } from '@eeacms/volto-eea-map/constants';
15
+
16
+ const Form = withVariationSchemaEnhancer(InlineForm);
17
+
18
+ export default function StructureWidgetsPanel({ value, onChangeValue }) {
19
+ const [widgets, setWidgets] = useState(getWidgets(value));
20
+
21
+ return (
22
+ <Panel
23
+ header={
24
+ <div style={{ width: '100%', textAlign: 'right' }}>
25
+ <button
26
+ className="btn-primary"
27
+ onClick={() => {
28
+ const newWidgets = [
29
+ ...widgets,
30
+ { name: null, position: positions[0] },
31
+ ];
32
+ setWidgets(newWidgets);
33
+ debounce(
34
+ () => {
35
+ onChangeValue({
36
+ ...value,
37
+ widgets: newWidgets,
38
+ });
39
+ },
40
+ 600,
41
+ 'widgets:update',
42
+ );
43
+ }}
44
+ >
45
+ <Icon name={addSVG} size="16px" /> Widget
46
+ </button>
47
+ </div>
48
+ }
49
+ content={
50
+ <>
51
+ {widgets.map((widget, index) => {
52
+ const title = `Widget ${index + 1}${
53
+ widget.name ? ` (${widget.name})` : ''
54
+ }`;
55
+ const ExpandProperties = widget.ExpandProperties || {};
56
+ return (
57
+ <Fold
58
+ key={index}
59
+ title={title}
60
+ onDelete={() => {
61
+ const newWidgets = widgets.filter((_, i) => i !== index);
62
+ setWidgets(newWidgets);
63
+ debounce(
64
+ () => {
65
+ onChangeValue({
66
+ ...value,
67
+ widgets: newWidgets,
68
+ });
69
+ },
70
+ 600,
71
+ 'widgets:update',
72
+ );
73
+ }}
74
+ foldable
75
+ deletable
76
+ >
77
+ <Form
78
+ schema={{
79
+ title,
80
+ fieldsets: [
81
+ {
82
+ id: 'default',
83
+ title: 'Default',
84
+ fields: ['name', 'position', 'expand'],
85
+ },
86
+ ],
87
+ properties: {
88
+ name: {
89
+ title: 'Name',
90
+ choices: widgetsOptions,
91
+ },
92
+ position: {
93
+ title: 'Position',
94
+ choices: positions,
95
+ },
96
+ expand: {
97
+ title: 'Expand',
98
+ type: 'boolean',
99
+ },
100
+ expandTooltip: {
101
+ title: 'Expand tooltip',
102
+ },
103
+ },
104
+ required: [],
105
+ }}
106
+ blocksConfig={{
107
+ widget: {
108
+ schemaEnhancer: ({ schema, formData }) => {
109
+ return {
110
+ ...schema,
111
+ fieldsets: [
112
+ {
113
+ id: 'default',
114
+ title: 'Default',
115
+ fields: [
116
+ 'name',
117
+ 'position',
118
+ ...Object.keys(
119
+ widgetsSchema[formData.name] || {},
120
+ ),
121
+ 'expand',
122
+ ...(formData.expand ? ['expandTooltip'] : []),
123
+ ],
124
+ },
125
+ ],
126
+ properties: {
127
+ ...schema.properties,
128
+ ...(widgetsSchema[formData.name] || {}),
129
+ },
130
+ };
131
+ },
132
+ },
133
+ }}
134
+ formData={{
135
+ '@type': 'widget',
136
+ ...widget,
137
+ ...ExpandProperties,
138
+ }}
139
+ onChangeField={(id, fieldValue) => {
140
+ const newWidget =
141
+ id === 'name'
142
+ ? getDefaultWidgets(
143
+ value.settings?.map?.dimension,
144
+ ).find(($widget) => $widget.name === fieldValue) || {}
145
+ : {};
146
+ const newWidgets = widgets.map(($widget, i) => {
147
+ if (i !== index) return $widget;
148
+ return {
149
+ ...$widget,
150
+ ...newWidget,
151
+ ...(expandKeys.includes(id)
152
+ ? {
153
+ ExpandProperties: {
154
+ ...ExpandProperties,
155
+ [id]: fieldValue,
156
+ },
157
+ }
158
+ : { [id]: fieldValue }),
159
+ };
160
+ });
161
+ setWidgets(newWidgets);
162
+ debounce(
163
+ () => {
164
+ onChangeValue({
165
+ ...value,
166
+ widgets: newWidgets,
167
+ });
168
+ },
169
+ 600,
170
+ 'widgets:update',
171
+ );
172
+ }}
173
+ />
174
+ </Fold>
175
+ );
176
+ })}
177
+ </>
178
+ }
179
+ />
180
+ );
181
+ }
@@ -0,0 +1,6 @@
1
+ export { default as Panel } from './Panel';
2
+ export { default as StructureBaseLayerPanel } from './StructureBaseLayerPanel';
3
+ export { default as StructureLayersPanel } from './StructureLayersPanel';
4
+ export { default as StructureWidgetsPanel } from './StructureWidgetsPanel';
5
+ export { default as SettingsGeneralPanel } from './SettingsGeneralPanel';
6
+ export { default as SettingsLayersPanel } from './SettingsLayersPanel';
@@ -0,0 +1,62 @@
1
+ import { useEffect, useState, useMemo } from 'react';
2
+ import cx from 'classnames';
3
+
4
+ import { Icon } from '@plone/volto/components';
5
+
6
+ import upKeySVG from '@plone/volto/icons/up-key.svg';
7
+
8
+ export default function SidebarGroup({ title, items, active, setActive }) {
9
+ const [expanded, setExpanded] = useState(false);
10
+ const isActive = useMemo(
11
+ () =>
12
+ active.sidebar === title &&
13
+ items.filter((item) => active.panel.title === item.title).length === 1,
14
+ [active, title, items],
15
+ );
16
+
17
+ useEffect(() => {
18
+ if (isActive) {
19
+ setExpanded(true);
20
+ }
21
+ }, [isActive]);
22
+
23
+ return (
24
+ <div
25
+ className={cx('sidebar-group', {
26
+ 'sidebar-group--expanded': expanded,
27
+ 'sidebar-group--is-active': isActive,
28
+ })}
29
+ >
30
+ <div
31
+ role="button"
32
+ tabIndex={0}
33
+ className="sidebar-group--title"
34
+ onClick={() => setExpanded(!expanded)}
35
+ onKeyDown={() => {}}
36
+ >
37
+ <div className="sidebar-group--title__icon">
38
+ <Icon name={upKeySVG} size="20px" />
39
+ </div>
40
+ <div className="sidebar-group--title__label">{title}</div>
41
+ </div>
42
+ {expanded &&
43
+ items.map((item) => (
44
+ <div
45
+ role="button"
46
+ tabIndex={0}
47
+ key={item.title}
48
+ className={cx('sidebar-group--item', {
49
+ 'sidebar-group--item__is-active':
50
+ isActive && active.panel.title === item.title,
51
+ })}
52
+ onClick={() => {
53
+ setActive({ sidebar: title, panel: item });
54
+ }}
55
+ onKeyDown={() => {}}
56
+ >
57
+ {item.title}
58
+ </div>
59
+ ))}
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,247 @@
1
+ import { useContext, useEffect, useMemo, memo } from 'react';
2
+ import { EventEmitter } from 'events';
3
+ import { uniq, isObject, isNaN } from 'lodash';
4
+
5
+ import useClass from '@eeacms/volto-eea-map/hooks/useClass';
6
+ import useChangedProps from '@eeacms/volto-eea-map/hooks/useChangedProps';
7
+
8
+ import { omitBy } from '@eeacms/volto-eea-map/Arcgis/helpers';
9
+ import { layersMapping, withSublayers } from '@eeacms/volto-eea-map/constants';
10
+
11
+ import MapContext from '@eeacms/volto-eea-map/Arcgis/Map/MapContext';
12
+
13
+ let modules = {};
14
+
15
+ class $Layer extends EventEmitter {
16
+ #isReady = false;
17
+ #props = {};
18
+ #layer = null;
19
+ #modulesLoaded = false;
20
+
21
+ constructor(props = {}) {
22
+ super();
23
+
24
+ this.#props = props;
25
+ }
26
+
27
+ get props() {
28
+ return this.#props;
29
+ }
30
+
31
+ get isReady() {
32
+ return this.#isReady && !!this.#layer;
33
+ }
34
+
35
+ get layer() {
36
+ return this.#layer;
37
+ }
38
+
39
+ set props(props = {}) {
40
+ this.#props = props;
41
+ }
42
+
43
+ getUrl(id) {
44
+ return this.#props.url
45
+ ? `${this.#props.url}${id ? `/${id}` : ''}`
46
+ : undefined;
47
+ }
48
+
49
+ getType(type) {
50
+ return layersMapping[type] || type?.replaceAll(' ', '');
51
+ }
52
+
53
+ getLayersTypes(layer) {
54
+ return uniq([
55
+ this.getType(layer.type),
56
+ ...(layer.subLayers || []).reduce((acc, layer) => {
57
+ acc.push(...this.getLayersTypes(layer));
58
+ return acc;
59
+ }, []),
60
+ ]);
61
+ }
62
+
63
+ getRenderer(renderer) {
64
+ const { $map } = this.#props;
65
+ const agJsonUtils = $map.modules.agJsonUtils;
66
+
67
+ if (!renderer) return;
68
+
69
+ if (renderer.autocast) {
70
+ return renderer;
71
+ }
72
+ return agJsonUtils.fromJSON(renderer);
73
+ }
74
+
75
+ async loadModules() {
76
+ const $arcgis = __CLIENT__ ? window.$arcgis : null;
77
+ if (__SERVER__ || !$arcgis) return Promise.reject();
78
+ if (!this.#modulesLoaded) {
79
+ const types = this.getLayersTypes(this.#props);
80
+ for (const type of types) {
81
+ if (!modules[`Ag${type}`] && type) {
82
+ modules[`Ag${type}`] = await $arcgis.import(`esri/layers/${type}`);
83
+ }
84
+ }
85
+ this.#modulesLoaded = true;
86
+ }
87
+ return Promise.resolve();
88
+ }
89
+
90
+ createLayer(props) {
91
+ const type = this.getType(props.type);
92
+
93
+ if (!type) return null;
94
+
95
+ const AgLayer = modules[`Ag${type}`];
96
+
97
+ if (!AgLayer) {
98
+ throw new Error('$Layer modules not loaded');
99
+ }
100
+
101
+ const renderer = this.getRenderer(props.renderer);
102
+
103
+ const layerProps = omitBy(props || {}, [
104
+ '$map',
105
+ 'type',
106
+ 'url',
107
+ 'id',
108
+ 'renderer',
109
+ ]);
110
+
111
+ if (renderer) {
112
+ layerProps.renderer = renderer;
113
+ }
114
+
115
+ const layer = new AgLayer(
116
+ withSublayers.includes(type)
117
+ ? {
118
+ url: this.getUrl(),
119
+ sublayers: props.sublayers || [layerProps],
120
+ }
121
+ : {
122
+ url: this.getUrl(props.id),
123
+ ...layerProps,
124
+ },
125
+ );
126
+
127
+ if (props.subLayers) {
128
+ props.subLayers.forEach((subLayer) => {
129
+ layer.add(this.createLayer(subLayer));
130
+ });
131
+ }
132
+
133
+ return layer;
134
+ }
135
+
136
+ init() {
137
+ const $map = this.#props.$map;
138
+ if (!$map.isReady || (!this.#props.url && !this.#props.source)) return;
139
+ if (!this.#modulesLoaded) {
140
+ throw new Error('$Layer modules not loaded');
141
+ }
142
+
143
+ this.#layer = this.createLayer(this.#props);
144
+
145
+ if (this.#layer?.queryExtent && this.#props.zoomToExtent) {
146
+ this.#layer.when(async () => {
147
+ const data = await this.#layer.queryExtent();
148
+ if (!$map.view) return;
149
+ $map.view.goTo(data.extent).then(() => {
150
+ const homeWidget = $map.view.ui.find('Home');
151
+ if (!homeWidget) return;
152
+ homeWidget.viewpoint = new $map.modules.AgViewpoint({
153
+ center: $map.view.center,
154
+ zoom: $map.view.zoom,
155
+ });
156
+ });
157
+ });
158
+ }
159
+
160
+ if (this.#layer) {
161
+ $map.map.add(this.#layer);
162
+ }
163
+
164
+ this.#isReady = true;
165
+
166
+ this.emit('connected');
167
+ }
168
+
169
+ updateProps(props) {
170
+ if (isNaN(props) || !isObject(props)) return;
171
+ Object.keys(props).forEach((key) => {
172
+ if (key === '$map') return;
173
+ this.#props[key] = props[key];
174
+ });
175
+ this.update(props);
176
+ }
177
+
178
+ update(props) {
179
+ const { $map } = this.#props;
180
+ if (!this.isReady || !$map.isReady) return;
181
+
182
+ Object.keys(
183
+ omitBy(props || this.#props || {}, ['$map', 'id', 'type', 'url']),
184
+ ).forEach((key) => {
185
+ switch (key) {
186
+ case 'renderer':
187
+ const renderer = this.getRenderer(this.#props[key]);
188
+ if (renderer) {
189
+ this.#layer.renderer = renderer;
190
+ }
191
+ break;
192
+ default:
193
+ this.#layer[key] = this.#props[key];
194
+ break;
195
+ }
196
+ });
197
+ }
198
+
199
+ connect() {
200
+ this.loadModules().then(() => {
201
+ this.init();
202
+ });
203
+ }
204
+
205
+ disconnect() {
206
+ this.#modulesLoaded = false;
207
+ if (!this.#isReady) return;
208
+ if (this.#layer) {
209
+ this.#layer.destroy();
210
+ }
211
+ this.#layer = null;
212
+ this.#isReady = false;
213
+ this.emit('disconnected');
214
+ }
215
+ }
216
+
217
+ // https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-Layer.html
218
+ function Layer(props) {
219
+ const { $map } = useContext(MapContext);
220
+ const context = useMemo(() => ({ ...props, $map }), [props, $map]);
221
+
222
+ const $layer = useClass($Layer, context);
223
+
224
+ useChangedProps((props) => {
225
+ if (!$layer || !$map || !$map.isReady) return;
226
+ $layer.updateProps(props);
227
+ }, props);
228
+
229
+ useEffect(() => {
230
+ if (!$layer) return;
231
+
232
+ $layer.props = context;
233
+
234
+ $layer.connect();
235
+
236
+ return () => {
237
+ if (!$layer) return;
238
+ $layer.disconnect();
239
+ };
240
+ // We handle the props change in the useEffect above.
241
+ /* eslint-disable-next-line */
242
+ }, [$map, $layer, context.id, context.type, context.url, context.source]);
243
+
244
+ return null;
245
+ }
246
+
247
+ export default memo(Layer);