@eeacms/volto-bise-policy 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.
Files changed (141) hide show
  1. package/.coverage.babel.config.js +9 -0
  2. package/.i18n.babel.config.js +1 -0
  3. package/.project.eslintrc.js +47 -0
  4. package/.release-it.json +17 -0
  5. package/CHANGELOG.md +27 -0
  6. package/DEVELOP.md +53 -0
  7. package/LICENSE.md +9 -0
  8. package/README.md +84 -0
  9. package/RELEASE.md +74 -0
  10. package/babel.config.js +17 -0
  11. package/bootstrap +41 -0
  12. package/cypress.config.js +27 -0
  13. package/jest-addon.config.js +36 -0
  14. package/locales/bg/LC_MESSAGES/volto.po +14 -0
  15. package/locales/bg.json +1 -0
  16. package/locales/bs/LC_MESSAGES/volto.po +14 -0
  17. package/locales/bs.json +1 -0
  18. package/locales/cs/LC_MESSAGES/volto.po +14 -0
  19. package/locales/cs.json +1 -0
  20. package/locales/da/LC_MESSAGES/volto.po +14 -0
  21. package/locales/da.json +1 -0
  22. package/locales/de/LC_MESSAGES/volto.po +29 -0
  23. package/locales/de.json +1 -0
  24. package/locales/el/LC_MESSAGES/volto.po +14 -0
  25. package/locales/el.json +1 -0
  26. package/locales/en/LC_MESSAGES/volto.po +14 -0
  27. package/locales/en.json +1 -0
  28. package/locales/es/LC_MESSAGES/volto.po +24 -0
  29. package/locales/es.json +1 -0
  30. package/locales/et/LC_MESSAGES/volto.po +14 -0
  31. package/locales/et.json +1 -0
  32. package/locales/eu/LC_MESSAGES/volto.po +19 -0
  33. package/locales/eu.json +1 -0
  34. package/locales/fi/LC_MESSAGES/volto.po +14 -0
  35. package/locales/fi.json +1 -0
  36. package/locales/fr/LC_MESSAGES/volto.po +29 -0
  37. package/locales/fr.json +1 -0
  38. package/locales/ga/LC_MESSAGES/volto.po +14 -0
  39. package/locales/ga.json +1 -0
  40. package/locales/hr/LC_MESSAGES/volto.po +14 -0
  41. package/locales/hr.json +1 -0
  42. package/locales/hu/LC_MESSAGES/volto.po +14 -0
  43. package/locales/hu.json +1 -0
  44. package/locales/is/LC_MESSAGES/volto.po +14 -0
  45. package/locales/is.json +1 -0
  46. package/locales/it/LC_MESSAGES/volto.po +14 -0
  47. package/locales/it.json +1 -0
  48. package/locales/ja/LC_MESSAGES/volto.po +21 -0
  49. package/locales/ja.json +1 -0
  50. package/locales/lt/LC_MESSAGES/volto.po +14 -0
  51. package/locales/lt.json +1 -0
  52. package/locales/lv/LC_MESSAGES/volto.po +14 -0
  53. package/locales/lv.json +1 -0
  54. package/locales/mk/LC_MESSAGES/volto.po +14 -0
  55. package/locales/mk.json +1 -0
  56. package/locales/mt/LC_MESSAGES/volto.po +14 -0
  57. package/locales/mt.json +1 -0
  58. package/locales/nl/LC_MESSAGES/volto.po +33 -0
  59. package/locales/nl.json +1 -0
  60. package/locales/no/LC_MESSAGES/volto.po +14 -0
  61. package/locales/no.json +1 -0
  62. package/locales/pl/LC_MESSAGES/volto.po +14 -0
  63. package/locales/pl.json +1 -0
  64. package/locales/pt/LC_MESSAGES/volto.po +22 -0
  65. package/locales/pt.json +1 -0
  66. package/locales/pt_BR/LC_MESSAGES/volto.po +20 -0
  67. package/locales/pt_BR.json +1 -0
  68. package/locales/ro/LC_MESSAGES/volto.po +20 -0
  69. package/locales/ro.json +1 -0
  70. package/locales/sh/LC_MESSAGES/volto.po +14 -0
  71. package/locales/sh.json +1 -0
  72. package/locales/sk/LC_MESSAGES/volto.po +14 -0
  73. package/locales/sk.json +1 -0
  74. package/locales/sl/LC_MESSAGES/volto.po +14 -0
  75. package/locales/sl.json +1 -0
  76. package/locales/sq/LC_MESSAGES/volto.po +14 -0
  77. package/locales/sq.json +1 -0
  78. package/locales/sv/LC_MESSAGES/volto.po +14 -0
  79. package/locales/sv.json +1 -0
  80. package/locales/tr/LC_MESSAGES/volto.po +14 -0
  81. package/locales/tr.json +1 -0
  82. package/locales/volto.pot +16 -0
  83. package/package.json +57 -0
  84. package/razzle.extend.js +29 -0
  85. package/src/components/manage/Blocks/BodyClass/Edit.jsx +30 -0
  86. package/src/components/manage/Blocks/BodyClass/View.jsx +12 -0
  87. package/src/components/manage/Blocks/BodyClass/index.js +24 -0
  88. package/src/components/manage/Blocks/BodyClass/schema.js +20 -0
  89. package/src/components/manage/Blocks/FactsheetsListing/FactsheetsListingEdit.jsx +32 -0
  90. package/src/components/manage/Blocks/FactsheetsListing/FactsheetsListingView.jsx +81 -0
  91. package/src/components/manage/Blocks/FactsheetsListing/index.js +24 -0
  92. package/src/components/manage/Blocks/FactsheetsListing/schema.js +20 -0
  93. package/src/components/manage/Blocks/KeyFacts/KeyFactsEdit.jsx +34 -0
  94. package/src/components/manage/Blocks/KeyFacts/KeyFactsView.jsx +68 -0
  95. package/src/components/manage/Blocks/KeyFacts/index.js +22 -0
  96. package/src/components/manage/Blocks/KeyFacts/schema.jsx +74 -0
  97. package/src/components/manage/Blocks/MaesViewer/MaesViewerEdit.jsx +64 -0
  98. package/src/components/manage/Blocks/MaesViewer/MaesViewerView.jsx +136 -0
  99. package/src/components/manage/Blocks/MaesViewer/constants.js +1 -0
  100. package/src/components/manage/Blocks/MaesViewer/index.js +22 -0
  101. package/src/components/manage/Blocks/MaesViewer/schema.js +60 -0
  102. package/src/components/manage/Blocks/MaesViewer/style.css +64 -0
  103. package/src/components/manage/Blocks/MaesViewer/utils.js +473 -0
  104. package/src/components/manage/Blocks/Navigation/Edit.jsx +27 -0
  105. package/src/components/manage/Blocks/Navigation/View.jsx +43 -0
  106. package/src/components/manage/Blocks/Navigation/index.js +22 -0
  107. package/src/components/manage/Blocks/Navigation/schema.js +43 -0
  108. package/src/components/manage/Blocks/Navigation/styles.less +42 -0
  109. package/src/components/manage/Blocks/Redirect/Edit.jsx +29 -0
  110. package/src/components/manage/Blocks/Redirect/View.jsx +13 -0
  111. package/src/components/manage/Blocks/Redirect/index.js +24 -0
  112. package/src/components/manage/Blocks/Redirect/schema.js +20 -0
  113. package/src/components/manage/Blocks/index.js +17 -0
  114. package/src/components/manage/Styles/index.js +163 -0
  115. package/src/customizations/@eeacms/volto-block-style/StyleWrapper/schema.js +206 -0
  116. package/src/customizations/volto/components/manage/Toolbar/More.jsx +547 -0
  117. package/src/customizations/volto/components/theme/Footer/Footer.jsx +25 -0
  118. package/src/customizations/volto/components/theme/Header/Header.jsx +301 -0
  119. package/src/index.js +77 -0
  120. package/theme/assets/icons/plus.png +0 -0
  121. package/theme/assets/icons/plus.svg +6 -0
  122. package/theme/assets/icons/search.svg +3 -0
  123. package/theme/assets/images/Header/bise-logo-white.svg +47 -0
  124. package/theme/assets/images/Header/bise-logo.svg +132 -0
  125. package/theme/collections/table.variables +17 -0
  126. package/theme/extras/banner.variables +5 -0
  127. package/theme/extras/factsheets.less +76 -0
  128. package/theme/extras/footer.variables +6 -0
  129. package/theme/extras/header.overrides +30 -0
  130. package/theme/extras/header.variables +7 -0
  131. package/theme/extras/hero.overrides +4 -0
  132. package/theme/extras/inpageNavigation.variables +5 -0
  133. package/theme/extras/keyfacts.less +202 -0
  134. package/theme/extras/pluggables.less +191 -0
  135. package/theme/extras/stylemenu.less +48 -0
  136. package/theme/extras/tocnav.less +158 -0
  137. package/theme/globals/site.overrides +78 -0
  138. package/theme/globals/site.variables +22 -0
  139. package/theme/theme.config +135 -0
  140. package/theme/tokens/colors.less +15 -0
  141. package/theme/tokens/tokens.less +1 -0
@@ -0,0 +1,64 @@
1
+ .maes-viewer-grid {
2
+ display: grid;
3
+ margin: 3em 0;
4
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
5
+ }
6
+
7
+ .maes-viewer-grid .js-plotly-plot {
8
+ /* this would have been overriden by inline CSS inside the react-plotly.js
9
+ component if it was not marked !important */
10
+ /* this rule is needed to make the small charts inside the MAES Viewer
11
+ responsive */
12
+ display: block !important;
13
+ }
14
+
15
+ .maes-select-label {
16
+ display: inline-block;
17
+ margin-bottom: 1rem;
18
+ font-size: 19px;
19
+ font-weight: bold;
20
+ }
21
+
22
+ @media only screen and (min-width: 1440px) {
23
+ .maes-viewer-grid {
24
+ column-gap: 2rem;
25
+ }
26
+
27
+ .maes-viewer-select {
28
+ grid-column: 9/13;
29
+ grid-row: 1/2;
30
+ }
31
+
32
+ .maes-viewer-charts {
33
+ grid-column: 1/9;
34
+ grid-row: 1/2;
35
+ overflow-x: hidden;
36
+ }
37
+ }
38
+
39
+ @media only screen and (max-width: 1440px) {
40
+ .maes-viewer-grid {
41
+ grid-template-rows: auto auto;
42
+ /* grid-template-columns: 1fr; */
43
+ }
44
+
45
+ .maes-viewer-select {
46
+ margin-bottom: 3em;
47
+ grid-column: 1/5;
48
+ grid-row: 1/2;
49
+ }
50
+
51
+ .maes-viewer-charts {
52
+ grid-column: 1/13;
53
+ grid-row: 2/3;
54
+ overflow-x: hidden;
55
+ }
56
+ }
57
+
58
+ @media only screen and (max-width: 1282px) {
59
+ .maes-viewer-select {
60
+ width: 100%;
61
+ max-width: 320px;
62
+ grid-column: 1/13;
63
+ }
64
+ }
@@ -0,0 +1,473 @@
1
+ import _ from 'lodash';
2
+ import { defaultHoverTemplate } from './constants';
3
+
4
+ export const colorscale = [
5
+ 'rgb(166,206,227)',
6
+ 'rgb(31,120,180)',
7
+ 'rgb(178,223,138)',
8
+ 'rgb(51,160,44)',
9
+ 'rgb(251,154,153)',
10
+ 'rgb(227,26,28)',
11
+ 'rgb(160,26,51)',
12
+ 'rgb(227,178,223)',
13
+ 'rgb(31,227,28)',
14
+ 'rgb(227,120,180)',
15
+ ];
16
+
17
+ const mapLongLevelNameToShort = (x) => {
18
+ const obj = {
19
+ 'A - Marine habitats': 'Marine habitats',
20
+ 'B - Coastal habitats': 'Coastal habitats',
21
+ 'C - Inland surface waters': 'Inland waters',
22
+ 'D - Mires, bogs and fens': 'Mires, bogs and fens',
23
+ 'E - Grasslands and land dominated by forbs, mosses or lichens':
24
+ 'Grassland',
25
+ 'F - Heathland, scrub and tundra': 'Heathland',
26
+ 'G - Woodland, forest and other wooded land': 'Woodlands',
27
+ 'H - Inland unvegetated or sparsely vegetated habitats':
28
+ 'Inland unvegetated or sparsely vegetated habitats',
29
+ 'I - Arable land and market gardens': 'Cropland',
30
+ 'J - constructed, industrial and other artificial habitats': 'Urban',
31
+ };
32
+ return obj[x] || x;
33
+ };
34
+
35
+ const filterGettingOriginalIndices = (a, fn) => {
36
+ return a
37
+ .map((c, i) => [c, i])
38
+ .filter(([c, i]) => fn(c, i))
39
+ .map(([, i]) => i);
40
+ };
41
+
42
+ class ProviderData {
43
+ static ecosystemColumnName = 'Ecosystem_level2';
44
+ static countryColumnName = 'Country_name';
45
+
46
+ /**
47
+ * Takes an object like:
48
+ *
49
+ * { Area: [...], Source: [...] }
50
+ *
51
+ * where each item in the lists represents a table column value.
52
+ */
53
+ constructor(data) {
54
+ this.data = data;
55
+ }
56
+
57
+ getColumnData(name) {
58
+ return this.data[name];
59
+ }
60
+
61
+ getUniqueColumnData(name) {
62
+ return _.uniq(this.data[name]);
63
+ }
64
+
65
+ getEcosystems() {
66
+ return this.getUniqueColumnData(ProviderData.ecosystemColumnName);
67
+ }
68
+
69
+ getCountries() {
70
+ return this.getUniqueColumnData(ProviderData.countryColumnName).concat(
71
+ 'EU',
72
+ );
73
+ }
74
+
75
+ getCountriesColumn() {
76
+ return this.getColumnData(ProviderData.countryColumnName);
77
+ }
78
+
79
+ forEachCountryAndLevel(fn) {
80
+ const c = this.getCountries();
81
+ const e = this.getEcosystems();
82
+ c.forEach((country) => {
83
+ e.forEach((level) => {
84
+ fn(country, level);
85
+ });
86
+ });
87
+ }
88
+
89
+ getEcosystemForRowIndex(i) {
90
+ return this.data[ProviderData.ecosystemColumnName][i];
91
+ }
92
+
93
+ hasEcosystemAtRowIndex(i, ecosystem) {
94
+ return this.getEcosystemForRowIndex(i) === ecosystem;
95
+ }
96
+
97
+ getColumnNames() {
98
+ return _.keys(this.data);
99
+ }
100
+
101
+ getCellValue(columnName, rowIndex) {
102
+ return this.getColumnData(columnName)[rowIndex];
103
+ }
104
+
105
+ createRowObject(rowIndex) {
106
+ const obj = {};
107
+ this.getColumnNames().forEach((cn) => {
108
+ obj[cn] = this.getCellValue(cn, rowIndex);
109
+ });
110
+ return obj;
111
+ }
112
+
113
+ rowSatisfiesFilters(index, filters) {
114
+ const filtersResults = filters.map((f) => f(this.data, index));
115
+ return filtersResults.every((t) => t);
116
+ }
117
+
118
+ /**
119
+ * Filters the data. Given a country name and an array of filter functions
120
+ * which receive two parameters, provider_data and rowIndex, returns an array
121
+ * of objects containing the data for each matching row in the DataProvider.
122
+ */
123
+ query(country, filters) {
124
+ const countriesColumn = this.getCountriesColumn();
125
+ const filteredIndices = filterGettingOriginalIndices(
126
+ countriesColumn,
127
+ (c, i) => {
128
+ return c === country && this.rowSatisfiesFilters(i, filters);
129
+ },
130
+ );
131
+ const rowObjects = filteredIndices.map((i) => this.createRowObject(i));
132
+ return rowObjects;
133
+ }
134
+ }
135
+
136
+ export function mapByLevel(provider_data) {
137
+ const pd = new ProviderData(provider_data);
138
+
139
+ const byLevel = _.fromPairs(pd.getEcosystems().map((l) => [l, {}]));
140
+
141
+ pd.forEachCountryAndLevel((country, level) => {
142
+ const filters = [
143
+ (provider_data, index) => pd.hasEcosystemAtRowIndex(index, level),
144
+ ];
145
+ const country_data = pd.query(country, filters);
146
+ byLevel[level][country] = country_data;
147
+ });
148
+
149
+ return byLevel;
150
+ }
151
+
152
+ export function mapToAllEU(provider_data, byLevel) {
153
+ const level2 = new Set(provider_data['Ecosystem_level2']);
154
+ const countries = new Set(provider_data['Country_name']);
155
+ const EUByLevel = Object.fromEntries(Array.from(level2).map((l) => [l, {}]));
156
+
157
+ let totalArea = 0;
158
+
159
+ level2.forEach((level) => {
160
+ EUByLevel[level] = 0;
161
+
162
+ countries.forEach((country) => {
163
+ byLevel[level][country].forEach((x) => {
164
+ EUByLevel[level] += parseFloat(x['Area (m2)']);
165
+ });
166
+ });
167
+
168
+ totalArea += EUByLevel[level];
169
+ });
170
+
171
+ const EUByLevelPercents = _.fromPairs(
172
+ _.map(Array.from(level2), (l) => {
173
+ return [l, (EUByLevel[l] / totalArea) * 100];
174
+ }),
175
+ );
176
+
177
+ return { EUByLevel, EUByLevelPercents };
178
+ }
179
+
180
+ export function makeTrace(
181
+ level,
182
+ levelData,
183
+ index,
184
+ focusOn,
185
+ { hoverTemplate = defaultHoverTemplate },
186
+ ) {
187
+ const data = [
188
+ ...Object.entries(levelData).filter(([k, v]) => k !== focusOn),
189
+ [focusOn, levelData[focusOn]],
190
+ ];
191
+
192
+ const x = [...data.map(([k, v]) => v)];
193
+ const y = [];
194
+ y.length = x.length;
195
+ y.fill(0);
196
+ y[y.length - 1] = 0.25; // we "lift" this value to "highlight" it
197
+
198
+ // first element: country name
199
+ // second element: square kilometers (km2)
200
+ // third element: square megameters (Mm2)
201
+ const hovertext = _.map(data, (x) => [x[0], x[1] / 100, x[1] / 10000]);
202
+
203
+ const text = [
204
+ ...data.slice(0, data.length - 1).map((_) => null),
205
+ `<b>${focusOn}</b>`,
206
+ ];
207
+
208
+ const marker = {
209
+ symbol: [
210
+ ...data.slice(0, data.length - 1).map((_) => null),
211
+ 'big-dot',
212
+ // 'triangle-down',
213
+ ],
214
+ // See https://plotly.com/javascript/reference/scatter/#scatter-marker
215
+ size: [...data.slice(0, data.length - 1).map((_) => 10), 10],
216
+ opacity: [...data.slice(0, data.length - 1).map((_) => 0.8), 1],
217
+ color: [
218
+ ...data.slice(0, data.length - 1).map((_) => colorscale[index]),
219
+ '#182844',
220
+ ],
221
+ };
222
+ const ht = hoverTemplate
223
+ .replace(/\{(.*?)country(.*?)\}/, '{$1customdata[0]$2}')
224
+ .replace(/\{(.*?)km2(.*?)\}/, '{$1customdata[1]$2}')
225
+ .replace(/\{(.*?)mm2(.*?)\}/, '{$1customdata[2]$2}');
226
+ const res = {
227
+ x,
228
+ y,
229
+ // hovertext,
230
+ // See
231
+ // https://plotly.com/javascript/reference/scatter/#scatter-hovertemplate
232
+ hovertemplate: ht,
233
+ hoverinfo: 'y',
234
+ customdata: hovertext,
235
+ name: level,
236
+ type: 'scatter',
237
+ // needed for subplots!!
238
+ // xaxis: `x${index > 0 ? index + 1 : ''}`,
239
+ // yaxis: `y${index > 0 ? index + 1 : ''}`,
240
+ mode: 'markers+text',
241
+ text,
242
+ marker,
243
+ textposition: 'top center',
244
+ };
245
+ return res;
246
+ }
247
+
248
+ export function axesFromTemplate(prefix, count, tpl, getTitle) {
249
+ const res = {};
250
+ for (let x = 0; x < count; x++) {
251
+ const rec = `${prefix}${x > 0 ? x + 1 : x}`;
252
+ res[rec] = JSON.parse(JSON.stringify(tpl));
253
+ // res[rec].title = { text: getTitle(x) };
254
+ }
255
+ res[prefix] = JSON.parse(JSON.stringify(tpl));
256
+ return res;
257
+ }
258
+
259
+ /**
260
+ * @param {number} index
261
+ * @param {number} finalPercent
262
+ */
263
+ export function chartTileLayout(index, finalPercent) {
264
+ return {
265
+ height: 60,
266
+ showlegend: false,
267
+ margin: {
268
+ l: 0,
269
+ r: 0,
270
+ t: 0,
271
+ b: 0,
272
+ },
273
+ pad: 0,
274
+ xaxis: {
275
+ visible: true,
276
+ showline: false,
277
+ showgrid: false,
278
+ zeroline: false,
279
+ autorange: true,
280
+
281
+ // showticklabels: true,
282
+ // automargin: true,
283
+ // tickvals: [10000000000],
284
+ // tickmode: 'array',
285
+ // ticktext: ['0%'],
286
+ // ticks: 'outside',
287
+ },
288
+ yaxis: {
289
+ type: 'linear',
290
+ range: [-0.3, 1], // needed to show the current country as a dot above the line
291
+ visible: true,
292
+ ticks: '',
293
+ showgrid: false,
294
+ gridwidth: 2,
295
+ gridcolor: '#000',
296
+ showline: true,
297
+ zeroline: true,
298
+ zerolinewidth: 2,
299
+ zerolinecolor: '#000',
300
+ showticklabels: false,
301
+ // showdividers: true,
302
+ },
303
+ annotations: [
304
+ {
305
+ xref: 'x',
306
+ yref: 'paper',
307
+ x: 0,
308
+ y: 0.3,
309
+ xanchor: 'right',
310
+ yanchor: 'bottom',
311
+ text: '0%',
312
+ showarrow: false,
313
+ },
314
+ {
315
+ xref: 'paper',
316
+ yref: 'paper',
317
+ x: 1,
318
+ y: 0.3,
319
+ xanchor: 'right',
320
+ yanchor: 'bottom',
321
+ text: `${Math.round(finalPercent).toFixed(0)}%`,
322
+ showarrow: false,
323
+ },
324
+ ],
325
+ };
326
+ }
327
+
328
+ export function makeChartTiles(
329
+ provider_data,
330
+ focusOn,
331
+ focusEcosystem,
332
+ { hoverTemplate },
333
+ ) {
334
+ if (!provider_data) return;
335
+ const byLevel = mapByLevel(provider_data);
336
+ const { EUByLevel, EUByLevelPercents } = mapToAllEU(provider_data, byLevel);
337
+
338
+ const ecosystems = _.uniq(provider_data['Ecosystem_level2']);
339
+ const countries = _.uniq(provider_data['Country_name']);
340
+
341
+ const byArea = Object.fromEntries(ecosystems.map((l) => [l, {}]));
342
+ ecosystems.forEach((level) => {
343
+ countries.forEach((country) => {
344
+ const recs = byLevel[level][country];
345
+ const total = recs.reduce(
346
+ (acc, rec) => acc + parseInt(rec['Area (m2)']),
347
+ 0,
348
+ );
349
+ byArea[level][country] = total;
350
+ });
351
+ byArea[level]['EU'] = EUByLevel[level];
352
+ });
353
+
354
+ const tiles = ecosystems
355
+ .map((level, index) => {
356
+ if (focusEcosystem && level !== focusEcosystem) return null;
357
+ return {
358
+ layout: chartTileLayout(index, EUByLevelPercents[level]),
359
+ data: [
360
+ makeTrace(level, byArea[level], index, focusOn, { hoverTemplate }),
361
+ ],
362
+ title: mapLongLevelNameToShort(level),
363
+ };
364
+ })
365
+ .filter((c) => !!c);
366
+
367
+ return tiles;
368
+ }
369
+
370
+ // unused, old version
371
+ // export function makeSubplotsChart(provider_data) {
372
+ // if (!provider_data) return;
373
+ // const byLevel = mapByLevel(provider_data);
374
+ //
375
+ // const level2 = new Set(provider_data['Ecosystem_level2']);
376
+ // const countries = new Set(provider_data['Country_name']);
377
+ //
378
+ // const byArea = Object.fromEntries(Array.from(level2).map((l) => [l, {}]));
379
+ // level2.forEach((level) => {
380
+ // countries.forEach((country) => {
381
+ // const recs = byLevel[level][country];
382
+ // const total = recs.reduce(
383
+ // (acc, rec) => acc + parseInt(rec['Area (m2)']),
384
+ // 0,
385
+ // );
386
+ // byArea[level][country] = total;
387
+ // });
388
+ // });
389
+ //
390
+ // let layout = {
391
+ // height: 560,
392
+ // showlegend: false,
393
+ // margin: {
394
+ // l: 400,
395
+ // r: 0,
396
+ // t: 0,
397
+ // b: 0,
398
+ // },
399
+ // pad: 10,
400
+ // grid: {
401
+ // xside: 'bottom',
402
+ // yside: 'left',
403
+ // rows: Object.keys(byLevel).length,
404
+ // columns: 1,
405
+ // ygap: 0.2,
406
+ // roworder: 'bottom to top',
407
+ // yaxes: Object.keys(byLevel).map(
408
+ // (l, index) => `y${index > 0 ? index + 1 : ''}`,
409
+ // ),
410
+ // xaxes: Object.keys(byLevel).map(
411
+ // (l, index) => `x${index > 0 ? index + 1 : ''}`,
412
+ // ),
413
+ // pattern: 'independent',
414
+ // },
415
+ // ...axesFromTemplate(
416
+ // 'xaxis',
417
+ // Object.keys(byLevel).length,
418
+ // {
419
+ // // this axis is shared
420
+ // visible: true,
421
+ // ticks: '',
422
+ // showline: false,
423
+ // showgrid: false,
424
+ // zeroline: false,
425
+ // autorange: true,
426
+ // // fixedrange: true,
427
+ // showticklabels: false,
428
+ // },
429
+ // (index) => '',
430
+ // ),
431
+ // ...axesFromTemplate(
432
+ // 'yaxis',
433
+ // Object.keys(byLevel).length,
434
+ // {
435
+ // type: 'category',
436
+ // visible: true,
437
+ // ticks: '',
438
+ // showline: false,
439
+ // showgrid: true,
440
+ // gridwidth: 2,
441
+ // zeroline: false,
442
+ // gridcolor: '#000',
443
+ // showticklabels: false,
444
+ // },
445
+ // (index) => Object.keys(byLevel)[index],
446
+ // ),
447
+ // annotations: [...makeAnnotations(byLevel)],
448
+ // };
449
+ //
450
+ // const traces = Array.from(level2).map((level, index) =>
451
+ // makeTrace(level, byArea[level], index),
452
+ // );
453
+ //
454
+ // return { layout, data: traces };
455
+ // }
456
+ // export function makeAnnotations(byLabel) {
457
+ // const len = Object.keys(byLabel).length;
458
+ // return Object.keys(byLabel).map((label, index) => {
459
+ // return {
460
+ // xref: 'paper',
461
+ // yref: 'paper',
462
+ // x: -0.06,
463
+ // // y: 0,
464
+ // y: (1 / (len - 1)) * index,
465
+ // xanchor: 'right',
466
+ // // yanchor: 'bottom',
467
+ // text: label,
468
+ // showarrow: false,
469
+ // // ay: (10 * index) / 2,
470
+ // };
471
+ // });
472
+ // }
473
+ //
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { SidebarPortal } from '@plone/volto/components';
3
+ import InlineForm from '@plone/volto/components/manage/Form/InlineForm';
4
+ import schema from './schema';
5
+ import View from './View';
6
+
7
+ const Edit = (props) => (
8
+ <>
9
+ <View {...props} mode="edit" />
10
+
11
+ <SidebarPortal selected={props.selected}>
12
+ <InlineForm
13
+ schema={schema}
14
+ title={schema.title}
15
+ onChangeField={(id, value) => {
16
+ props.onChangeBlock(props.block, {
17
+ ...props.data,
18
+ [id]: value,
19
+ });
20
+ }}
21
+ formData={props.data}
22
+ />
23
+ </SidebarPortal>
24
+ </>
25
+ );
26
+
27
+ export default Edit;
@@ -0,0 +1,43 @@
1
+ import React, { useMemo } from 'react';
2
+ import { connect } from 'react-redux';
3
+ import { Menu } from 'semantic-ui-react';
4
+ import { UniversalLink } from '@plone/volto/components';
5
+ import { flattenToAppURL } from '@plone/volto/helpers';
6
+
7
+ import './styles.less';
8
+
9
+ const View = ({ history, data, navigation, ...props }) => {
10
+ const search = history?.location?.search || '';
11
+ const pathname = props.path || props.pathname;
12
+ const pages = data.pages || [];
13
+ const items = useMemo(() => {
14
+ return [
15
+ ...(navigation.filter(
16
+ (item) => flattenToAppURL(item.url) === flattenToAppURL(data.parent),
17
+ )[0]?.items || []),
18
+ ...pages,
19
+ ];
20
+ }, [navigation, pages, data.parent]);
21
+
22
+ return (
23
+ <Menu className="navigation-block">
24
+ {items.map((item) => (
25
+ <Menu.Item
26
+ key={item.url}
27
+ active={
28
+ item.url &&
29
+ pathname?.replace('/edit', '') === flattenToAppURL(item.url)
30
+ }
31
+ >
32
+ <UniversalLink href={`${item.url}${search}`}>
33
+ {item.title}
34
+ </UniversalLink>
35
+ </Menu.Item>
36
+ ))}
37
+ </Menu>
38
+ );
39
+ };
40
+
41
+ export default connect((state) => ({
42
+ navigation: state.navigation.items,
43
+ }))(View);
@@ -0,0 +1,22 @@
1
+ import imageSVG from '@plone/volto/icons/image.svg';
2
+ import NavigationBlockView from './View';
3
+ import NavigationBlockEdit from './Edit';
4
+
5
+ export default (config) => {
6
+ config.blocks.blocksConfig.navigationBlock = {
7
+ id: 'navigationBlock',
8
+ title: 'Navigation Block',
9
+ icon: imageSVG,
10
+ group: 'common',
11
+ view: NavigationBlockView,
12
+ edit: NavigationBlockEdit,
13
+ restricted: false,
14
+ mostUsed: false,
15
+ sidebarTab: 1,
16
+ security: {
17
+ addPermission: [],
18
+ view: [],
19
+ },
20
+ };
21
+ return config;
22
+ };
@@ -0,0 +1,43 @@
1
+ const pagesSchema = {
2
+ title: 'Page',
3
+ fieldsets: [
4
+ {
5
+ id: 'default',
6
+ title: 'Default',
7
+ fields: ['title', 'url'],
8
+ },
9
+ ],
10
+ properties: {
11
+ title: {
12
+ title: 'Title',
13
+ },
14
+ url: {
15
+ title: 'Url',
16
+ widget: 'url',
17
+ },
18
+ },
19
+ required: [],
20
+ };
21
+
22
+ export default {
23
+ title: 'Navigation block',
24
+ fieldsets: [
25
+ {
26
+ id: 'default',
27
+ title: 'Default',
28
+ fields: ['parent', 'pages'],
29
+ },
30
+ ],
31
+ properties: {
32
+ parent: {
33
+ title: 'Parent',
34
+ widget: 'url',
35
+ },
36
+ pages: {
37
+ title: 'Pages',
38
+ schema: pagesSchema,
39
+ widget: 'object_list',
40
+ },
41
+ },
42
+ required: [],
43
+ };
@@ -0,0 +1,42 @@
1
+ @import (multiple, reference, optional) '../../theme.config';
2
+
3
+ @type: extra;
4
+ @element: custom;
5
+
6
+ .ui.menu.navigation-block {
7
+ flex-wrap: wrap;
8
+ border: none;
9
+ box-shadow: unset;
10
+
11
+ .item {
12
+ padding: 0;
13
+ border: none;
14
+
15
+ &.active {
16
+ a {
17
+ color: @primaryColor;
18
+ // text-decoration: none;
19
+ // border-bottom: 1px solid @secondaryColor;
20
+ }
21
+ }
22
+
23
+ &:hover,
24
+ &.active:hover {
25
+ background-color: unset;
26
+ }
27
+
28
+ &::before {
29
+ content: none;
30
+ }
31
+
32
+ &:not(:last-child)::after {
33
+ top: 0%;
34
+ right: 0px;
35
+ width: 1px;
36
+ height: 80%;
37
+ margin: 0 0.3rem;
38
+ background: @secondaryColor;
39
+ content: '';
40
+ }
41
+ }
42
+ }