@eeacms/volto-n2k 1.0.17 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,14 +4,27 @@ 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
+ ### [1.0.19](https://github.com/eea/volto-n2k/compare/1.0.18...1.0.19) - 6 March 2023
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Add connected link list [Miu Razvan - [`633f1de`](https://github.com/eea/volto-n2k/commit/633f1ded7a208f42f1c25dee271c9671c53c01c3)]
12
+ ### [1.0.18](https://github.com/eea/volto-n2k/compare/1.0.17...1.0.18) - 25 February 2023
13
+
14
+ #### :rocket: New Features
15
+
16
+ - feat: conditional rendering for section group [Miu Razvan - [`6afd9a5`](https://github.com/eea/volto-n2k/commit/6afd9a536a520c51ad457127aa02f07fd135010e)]
17
+
18
+ #### :hammer_and_wrench: Others
19
+
20
+ - update [Miu Razvan - [`ab24f16`](https://github.com/eea/volto-n2k/commit/ab24f1624eb541d3ab492aa934c923f6cbdd245c)]
21
+ - uopdate label on habitats page [Claudia Ifrim - [`aa708e2`](https://github.com/eea/volto-n2k/commit/aa708e24b8f456407192cf1b889d893fd7b92f0e)]
7
22
  ### [1.0.17](https://github.com/eea/volto-n2k/compare/1.0.16...1.0.17) - 24 February 2023
8
23
 
9
24
  #### :hammer_and_wrench: Others
10
25
 
11
26
  - eslint fix [Miu Razvan - [`417aa35`](https://github.com/eea/volto-n2k/commit/417aa3588b32920a3e378fad8dd72de6d0f02ca3)]
12
27
  - species distribution map for birds group [Miu Razvan - [`7712d28`](https://github.com/eea/volto-n2k/commit/7712d28c0c5a79ffd33d4fc8d1d8e73af55937a2)]
13
- - update [Miu Razvan - [`6ce1f47`](https://github.com/eea/volto-n2k/commit/6ce1f4776686307f867a481b07d5bddd34e10961)]
14
- - update [Miu Razvan - [`d017393`](https://github.com/eea/volto-n2k/commit/d017393b02d3628a6a72c9f6fccf59b34e517933)]
15
28
  ### [1.0.16](https://github.com/eea/volto-n2k/compare/1.0.15...1.0.16) - 22 February 2023
16
29
 
17
30
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-n2k",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "volto-n2k: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -24,11 +24,11 @@
24
24
  "url": "git@github.com:eea/volto-n2k.git"
25
25
  },
26
26
  "dependencies": {
27
+ "@eeacms/volto-bise": "*",
27
28
  "@eeacms/volto-datablocks": "*",
28
- "@eeacms/volto-resize-helper": "*",
29
29
  "@eeacms/volto-openlayers-map": "*",
30
+ "@eeacms/volto-resize-helper": "*",
30
31
  "@eeacms/volto-tabs-block": "*",
31
- "@eeacms/volto-bise": "*",
32
32
  "d3": "^7.6.1",
33
33
  "d3-shape": "^3.1.0",
34
34
  "react-lazy-load-image-component": "1.5.1",
@@ -38,8 +38,8 @@
38
38
  "slick-carousel": "1.8.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@plone/scripts": "*",
42
41
  "@cypress/code-coverage": "^3.10.0",
42
+ "@plone/scripts": "*",
43
43
  "babel-plugin-transform-class-properties": "^6.24.1",
44
44
  "md5": "^2.3.0"
45
45
  },
@@ -0,0 +1,31 @@
1
+ /* eslint-disable react/jsx-no-target-blank */
2
+ import React from 'react';
3
+ import { FormattedValue } from '@eeacms/volto-datablocks/Utils';
4
+ import './style.less';
5
+
6
+ const View = (props) => {
7
+ const { data = {} } = props;
8
+ const provider_data = props.provider_data || {};
9
+ const columns = provider_data[Object.keys(provider_data)?.[0]]?.length || 0;
10
+
11
+ return (
12
+ <div className="connected-list">
13
+ {props.mode === 'edit' ? <p>Connected link list</p> : ''}
14
+ <p>
15
+ {Array(Math.max(0, columns))
16
+ .fill()
17
+ .map((_, column) => (
18
+ <FormattedValue
19
+ textTemplate={data.textTemplate}
20
+ linkTemplate={data.linkTemplate}
21
+ value={provider_data[data.value]?.[column]}
22
+ linkValue={provider_data[data.linkValue]?.[column]}
23
+ link={true}
24
+ />
25
+ ))}
26
+ </p>
27
+ </div>
28
+ );
29
+ };
30
+
31
+ export default View;
@@ -0,0 +1,17 @@
1
+ import ConnectedLinkList from './View';
2
+ import getSchema from './schema';
3
+
4
+ export default (config) => {
5
+ config.blocks.blocksConfig.custom_connected_block = {
6
+ ...config.blocks.blocksConfig.custom_connected_block,
7
+ blocks: {
8
+ ...config.blocks.blocksConfig.custom_connected_block.blocks,
9
+ connected_link_list: {
10
+ view: ConnectedLinkList,
11
+ getSchema: getSchema,
12
+ title: 'Connected link list',
13
+ },
14
+ },
15
+ };
16
+ return config;
17
+ };
@@ -0,0 +1,39 @@
1
+ const getSchema = (props) => {
2
+ const data = props.provider_data || {};
3
+ const choices = Object.keys(data).map((key) => [key, key]);
4
+
5
+ return {
6
+ title: 'Connected link list',
7
+
8
+ fieldsets: [
9
+ {
10
+ id: 'default',
11
+ title: 'Default',
12
+ fields: ['value', 'linkValue', 'textTemplate', 'linkTemplate'],
13
+ },
14
+ ],
15
+ properties: {
16
+ value: {
17
+ title: 'Value',
18
+ type: 'array',
19
+ choices,
20
+ },
21
+ linkValue: {
22
+ title: 'Link value',
23
+ type: 'array',
24
+ choices,
25
+ },
26
+ textTemplate: {
27
+ title: 'Text template',
28
+ description: 'Add suffix/prefix to text. Use {} for value placeholder',
29
+ },
30
+ linkTemplate: {
31
+ title: 'Link template',
32
+ description: 'Add suffix/prefix to link. Use {} for value placeholder',
33
+ },
34
+ },
35
+ required: [],
36
+ };
37
+ };
38
+
39
+ export default getSchema;
@@ -21,7 +21,7 @@ const View = (props) => {
21
21
  <div className="habitat-metadata">
22
22
  <h2 className="name">{scientific_name[0]}</h2>
23
23
  <p className="info">
24
- ANNEX I habitat code&nbsp;&nbsp;&nbsp;{code_2000[0]}
24
+ Habitats Directive Annex I code&nbsp;&nbsp;&nbsp;{code_2000[0]}
25
25
  </p>
26
26
  <br />
27
27
  {/* {number_sites[0] && (
@@ -120,7 +120,8 @@ div#view .species-banner-details .ui.container > * {
120
120
 
121
121
  &:not(.with-slider) {
122
122
  display: flex;
123
- align-items: center;
123
+ flex-flow: column;
124
+ justify-content: center;
124
125
 
125
126
  .picture-wrapper {
126
127
  height: fit-content;
@@ -417,8 +417,7 @@ class Navigation extends Component {
417
417
  )}
418
418
  </Dropdown>
419
419
  ) : (
420
- <Link
421
- to={flatUrl === '' ? '/' : flatUrl}
420
+ <div
422
421
  key={flatUrl}
423
422
  className={
424
423
  this.isActive(flatUrl)
@@ -426,8 +425,10 @@ class Navigation extends Component {
426
425
  : 'item firstLevel'
427
426
  }
428
427
  >
429
- {item.title}
430
- </Link>
428
+ <Link to={flatUrl === '' ? '/' : flatUrl}>
429
+ {item.title}
430
+ </Link>
431
+ </div>
431
432
  );
432
433
  })}
433
434
  </>
@@ -0,0 +1,266 @@
1
+ import React, { useState } from 'react';
2
+ import { isEmpty } from 'lodash';
3
+ import {
4
+ BlocksForm,
5
+ SidebarPortal,
6
+ Icon,
7
+ BlockDataForm,
8
+ } from '@plone/volto/components';
9
+ import { emptyBlocksForm } from '@plone/volto/helpers';
10
+ import delightedSVG from '@plone/volto/icons/delighted.svg';
11
+ import dissatisfiedSVG from '@plone/volto/icons/dissatisfied.svg';
12
+ import PropTypes from 'prop-types';
13
+ import { Button, Segment } from 'semantic-ui-react';
14
+ import EditBlockWrapper from './EditBlockWrapper';
15
+ import EditSchema from './EditSchema';
16
+ import helpSVG from '@plone/volto/icons/help.svg';
17
+ import cx from 'classnames';
18
+ import './editor.less';
19
+
20
+ const Edit = (props) => {
21
+ const {
22
+ block,
23
+ data,
24
+ onChangeBlock,
25
+ onChangeField,
26
+ pathname,
27
+ selected,
28
+ manage,
29
+ formDescription,
30
+ } = props;
31
+
32
+ const metadata = props.metadata || props.properties;
33
+ const data_blocks = data?.data?.blocks;
34
+ const properties = isEmpty(data_blocks) ? emptyBlocksForm() : data.data;
35
+
36
+ const [selectedBlock, setSelectedBlock] = useState(
37
+ properties.blocks_layout.items[0],
38
+ );
39
+
40
+ React.useEffect(() => {
41
+ if (
42
+ isEmpty(data_blocks) &&
43
+ properties.blocks_layout.items[0] !== selectedBlock
44
+ ) {
45
+ setSelectedBlock(properties.blocks_layout.items[0]);
46
+ onChangeBlock(block, {
47
+ ...data,
48
+ data: properties,
49
+ });
50
+ }
51
+ }, [onChangeBlock, properties, selectedBlock, block, data, data_blocks]);
52
+
53
+ const blockState = {};
54
+ let charCount = 0;
55
+
56
+ /**
57
+ * Count the number of characters that are anything except using Regex
58
+ * @param {string} paragraph
59
+ * @returns
60
+ */
61
+ const countCharsWithoutSpaces = (paragraph) => {
62
+ const regex = /[^\s\\]/g;
63
+
64
+ return (paragraph.match(regex) || []).length;
65
+ };
66
+
67
+ /**
68
+ * Count the number of characters
69
+ * @param {string} paragraph
70
+ * @returns
71
+ */
72
+ const countCharsWithSpaces = (paragraph) => {
73
+ return paragraph?.length || 0;
74
+ };
75
+
76
+ /**
77
+ * Recursively look for any block that contains text or plaintext
78
+ * @param {Object} blocksObject
79
+ * @returns
80
+ */
81
+ const countTextInBlocks = (blocksObject) => {
82
+ let groupCharCount = 0;
83
+ if (!props.data.maxChars) {
84
+ return groupCharCount;
85
+ }
86
+
87
+ Object.keys(blocksObject).forEach((blockId) => {
88
+ const foundText = blocksObject[blockId]?.plaintext
89
+ ? blocksObject[blockId]?.plaintext
90
+ : blocksObject[blockId]?.text?.blocks[0]?.text
91
+ ? blocksObject[blockId].text.blocks[0].text
92
+ : blocksObject[blockId]?.data?.blocks
93
+ ? countTextInBlocks(blocksObject[blockId]?.data?.blocks)
94
+ : blocksObject[blockId]?.blocks
95
+ ? countTextInBlocks(blocksObject[blockId]?.blocks)
96
+ : '';
97
+ const resultText =
98
+ typeof foundText === 'string' || foundText instanceof String
99
+ ? foundText
100
+ : '';
101
+
102
+ groupCharCount += props.data.ignoreSpaces
103
+ ? countCharsWithoutSpaces(resultText)
104
+ : countCharsWithSpaces(resultText);
105
+ });
106
+
107
+ return groupCharCount;
108
+ };
109
+
110
+ const showCharCounter = () => {
111
+ if (data_blocks) {
112
+ charCount = countTextInBlocks(data_blocks);
113
+ }
114
+ };
115
+ showCharCounter();
116
+
117
+ const counterClass =
118
+ charCount < Math.ceil(props.data.maxChars / 1.05)
119
+ ? 'info'
120
+ : charCount < props.data.maxChars
121
+ ? 'warning'
122
+ : 'danger';
123
+
124
+ const counterComponent = props.data.maxChars ? (
125
+ <p
126
+ className={cx('counter', counterClass)}
127
+ onClick={() => {
128
+ setSelectedBlock();
129
+ props.setSidebarTab(1);
130
+ }}
131
+ aria-hidden="true"
132
+ >
133
+ {props.data.maxChars - charCount < 0 ? (
134
+ <>
135
+ <span>{`${
136
+ charCount - props.data.maxChars
137
+ } characters over the limit`}</span>
138
+ <Icon name={dissatisfiedSVG} size="24px" />
139
+ </>
140
+ ) : (
141
+ <>
142
+ <span>{`${
143
+ props.data.maxChars - charCount
144
+ } characters remaining out of ${props.data.maxChars}`}</span>
145
+ <Icon name={delightedSVG} size="24px" />
146
+ </>
147
+ )}
148
+ </p>
149
+ ) : null;
150
+
151
+ // Get editing instructions from block settings or props
152
+ let instructions = data?.instructions?.data || data?.instructions;
153
+ if (!instructions || instructions === '<p><br/></p>') {
154
+ instructions = formDescription;
155
+ }
156
+
157
+ return (
158
+ <fieldset className="section-block">
159
+ <legend
160
+ onClick={() => {
161
+ setSelectedBlock();
162
+ props.setSidebarTab(1);
163
+ }}
164
+ aria-hidden="true"
165
+ >
166
+ {data.title || 'Section'}
167
+ </legend>
168
+ <BlocksForm
169
+ metadata={metadata}
170
+ properties={properties}
171
+ manage={manage}
172
+ selectedBlock={selected ? selectedBlock : null}
173
+ allowedBlocks={data.allowedBlocks}
174
+ title={data.placeholder}
175
+ description={instructions}
176
+ onSelectBlock={(id) => {
177
+ setSelectedBlock(id);
178
+ }}
179
+ onChangeFormData={(newFormData) => {
180
+ onChangeBlock(block, {
181
+ ...data,
182
+ data: newFormData,
183
+ });
184
+ }}
185
+ onChangeField={(id, value) => {
186
+ if (['blocks', 'blocks_layout'].indexOf(id) > -1) {
187
+ blockState[id] = value;
188
+ onChangeBlock(block, {
189
+ ...data,
190
+ data: {
191
+ ...data.data,
192
+ ...blockState,
193
+ },
194
+ });
195
+ } else {
196
+ onChangeField(id, value);
197
+ }
198
+ }}
199
+ pathname={pathname}
200
+ >
201
+ {({ draginfo }, editBlock, blockProps) => (
202
+ <EditBlockWrapper
203
+ draginfo={draginfo}
204
+ blockProps={blockProps}
205
+ disabled={data.disableInnerButtons}
206
+ extraControls={
207
+ <>
208
+ {instructions && (
209
+ <>
210
+ <Button
211
+ icon
212
+ basic
213
+ title="Section help"
214
+ onClick={() => {
215
+ setSelectedBlock();
216
+ const tab = manage ? 0 : 1;
217
+ props.setSidebarTab(tab);
218
+ }}
219
+ >
220
+ <Icon name={helpSVG} className="" size="19px" />
221
+ </Button>
222
+ </>
223
+ )}
224
+ </>
225
+ }
226
+ >
227
+ {editBlock}
228
+ </EditBlockWrapper>
229
+ )}
230
+ </BlocksForm>
231
+
232
+ {counterComponent}
233
+ <SidebarPortal selected={selected && !selectedBlock}>
234
+ {instructions && (
235
+ <Segment attached>
236
+ <div dangerouslySetInnerHTML={{ __html: instructions }} />
237
+ </Segment>
238
+ )}
239
+ {!data?.readOnlySettings && (
240
+ <BlockDataForm
241
+ schema={EditSchema}
242
+ title="Section (Group) settings"
243
+ formData={data}
244
+ onChangeField={(id, value) => {
245
+ props.onChangeBlock(props.block, {
246
+ ...props.data,
247
+ [id]: value,
248
+ });
249
+ }}
250
+ />
251
+ )}
252
+ </SidebarPortal>
253
+ </fieldset>
254
+ );
255
+ };
256
+
257
+ Edit.propTypes = {
258
+ block: PropTypes.string.isRequired,
259
+ data: PropTypes.object.isRequired,
260
+ onChangeBlock: PropTypes.func.isRequired,
261
+ pathname: PropTypes.string.isRequired,
262
+ selected: PropTypes.bool.isRequired,
263
+ manage: PropTypes.bool.isRequired,
264
+ };
265
+
266
+ export default Edit;
@@ -0,0 +1,188 @@
1
+ import React from 'react';
2
+ import { Icon, BlockChooser } from '@plone/volto/components';
3
+ import { blockHasValue } from '@plone/volto/helpers';
4
+ import config from '@plone/volto/registry';
5
+ import { Button } from 'semantic-ui-react';
6
+ import includes from 'lodash/includes';
7
+ import isBoolean from 'lodash/isBoolean';
8
+ import { defineMessages, injectIntl } from 'react-intl';
9
+ import { doesNodeContainClick } from 'semantic-ui-react/dist/commonjs/lib';
10
+ import cx from 'classnames';
11
+
12
+ import dragSVG from '@plone/volto/icons/drag.svg';
13
+ import addSVG from '@plone/volto/icons/circle-plus.svg';
14
+ import trashSVG from '@plone/volto/icons/delete.svg';
15
+
16
+ const messages = defineMessages({
17
+ unknownBlock: {
18
+ id: 'Unknown Block',
19
+ defaultMessage: 'Unknown Block {block}',
20
+ },
21
+ delete: {
22
+ id: 'delete',
23
+ defaultMessage: 'delete',
24
+ },
25
+ });
26
+
27
+ class EditBlockWrapper extends React.Component {
28
+ constructor(props) {
29
+ super(props);
30
+ this.state = {
31
+ addNewBlockOpened: false,
32
+ };
33
+ }
34
+
35
+ componentDidMount() {
36
+ document.addEventListener('mousedown', this.handleClickOutside, false);
37
+ }
38
+
39
+ componentWillUnmount() {
40
+ document.removeEventListener('mousedown', this.handleClickOutside);
41
+ }
42
+
43
+ handleClickOutside = (e) => {
44
+ if (
45
+ this.blockNode.current &&
46
+ doesNodeContainClick(this.blockNode.current, e)
47
+ )
48
+ return;
49
+
50
+ if (this.state.addNewBlockOpened) {
51
+ this.setState({
52
+ addNewBlockOpened: false,
53
+ });
54
+ return true;
55
+ }
56
+ };
57
+
58
+ blockNode = React.createRef();
59
+
60
+ render() {
61
+ const {
62
+ intl,
63
+ blockProps,
64
+ draginfo,
65
+ extraControls,
66
+ disabled,
67
+ children,
68
+ } = this.props;
69
+
70
+ const {
71
+ allowedBlocks,
72
+ block,
73
+ data,
74
+ onSelectBlock,
75
+ onDeleteBlock,
76
+ onMutateBlock,
77
+ onInsertBlock,
78
+ selected,
79
+ } = blockProps;
80
+ const type = data['@type'];
81
+ const { disableNewBlocks } = data;
82
+ const dragVisible = !data.fixed;
83
+ const visible = selected;
84
+
85
+ const required = isBoolean(data.required)
86
+ ? data.required
87
+ : includes(config.blocks.requiredBlocks, type);
88
+
89
+ // Get editing instructions from block settings or props
90
+ let instructions = data?.instructions?.data || data?.instructions;
91
+ if (!instructions || instructions === '<p><br/></p>') {
92
+ instructions = '';
93
+ }
94
+
95
+ return (
96
+ <div ref={this.blockNode}>
97
+ <div
98
+ ref={draginfo?.innerRef}
99
+ {...(selected ? draginfo?.draggableProps : null)}
100
+ className={`block-editor-${data['@type']}`}
101
+ >
102
+ {(!selected || !visible || disabled) && (
103
+ <div
104
+ style={{
105
+ display: 'none',
106
+ // keep react-beautiful-dnd happy
107
+ }}
108
+ {...draginfo.dragHandleProps}
109
+ ></div>
110
+ )}
111
+ {visible && (
112
+ <div className="block-toolbar">
113
+ {instructions ? extraControls : ''}
114
+
115
+ {!disabled && (
116
+ <>
117
+ <div
118
+ style={{
119
+ display: dragVisible ? 'inline-block' : 'none',
120
+ }}
121
+ {...draginfo.dragHandleProps}
122
+ className="drag handle wrapper-group-block"
123
+ >
124
+ <Button icon basic title="Drag and drop">
125
+ <Icon name={dragSVG} size="19px" />
126
+ </Button>
127
+ </div>
128
+
129
+ {!disableNewBlocks && !blockHasValue(data) && (
130
+ <Button
131
+ icon
132
+ basic
133
+ title="Add block"
134
+ onClick={() => {
135
+ this.setState({
136
+ addNewBlockOpened: !this.state.addNewBlockOpened,
137
+ });
138
+ }}
139
+ className="group-block-add-button"
140
+ >
141
+ <Icon name={addSVG} className="" size="19px" />
142
+ </Button>
143
+ )}
144
+ {!required && (
145
+ <Button
146
+ icon
147
+ basic
148
+ title="Remove block"
149
+ onClick={() => onDeleteBlock(block)}
150
+ className="delete-button-group-block"
151
+ aria-label={intl.formatMessage(messages.delete)}
152
+ >
153
+ <Icon name={trashSVG} size="19px" />
154
+ </Button>
155
+ )}
156
+ {this.state.addNewBlockOpened && (
157
+ <BlockChooser
158
+ onMutateBlock={(id, value) => {
159
+ onMutateBlock(id, value);
160
+ this.setState({ addNewBlockOpened: false });
161
+ }}
162
+ onInsertBlock={(id, value) => {
163
+ onSelectBlock(onInsertBlock(id, value));
164
+ this.setState({ addNewBlockOpened: false });
165
+ }}
166
+ currentBlock={block}
167
+ allowedBlocks={allowedBlocks}
168
+ />
169
+ )}
170
+ </>
171
+ )}
172
+ </div>
173
+ )}
174
+
175
+ <div
176
+ className={cx('ui drag block wrapper inner', type, {
177
+ multiSelected: this.props.multiSelected,
178
+ })}
179
+ >
180
+ {children}
181
+ </div>
182
+ </div>
183
+ </div>
184
+ );
185
+ }
186
+ }
187
+
188
+ export default injectIntl(EditBlockWrapper);
@@ -0,0 +1,41 @@
1
+ const Schema = {
2
+ title: 'Section block',
3
+ fieldsets: [
4
+ {
5
+ id: 'default',
6
+ title: 'Default',
7
+ fields: ['title', 'as', 'condition'],
8
+ },
9
+ ],
10
+ properties: {
11
+ title: {
12
+ title: 'Title',
13
+ description: 'Section friendly name',
14
+ type: 'string',
15
+ },
16
+ as: {
17
+ title: 'HTML5 element',
18
+ description: 'Select HTML5 element to be used for this block',
19
+ type: 'string',
20
+ factory: 'Choice',
21
+ default: 'div',
22
+ choices: [
23
+ ['div', 'div'],
24
+ ['section', 'section'],
25
+ ['article', 'article'],
26
+ ['aside', 'aside'],
27
+ ['details', 'details'],
28
+ ],
29
+ },
30
+ condition: {
31
+ title: 'Condition',
32
+ choices: [
33
+ ['bird', 'Is bird'],
34
+ ['species', 'Is not bird'],
35
+ ],
36
+ },
37
+ },
38
+ required: [],
39
+ };
40
+
41
+ export default Schema;
@@ -0,0 +1,116 @@
1
+ const Schema = {
2
+ title: 'Section (Group) settings',
3
+ fieldsets: [
4
+ {
5
+ id: 'default',
6
+ title: 'Default',
7
+ fields: [
8
+ 'title',
9
+ 'placeholder',
10
+ 'instructions',
11
+ 'allowedBlocks',
12
+ 'as',
13
+ 'maxChars',
14
+ 'ignoreSpaces',
15
+ 'readOnlySettings',
16
+ 'disableInnerButtons',
17
+ 'required',
18
+ 'fixed',
19
+ 'fixedLayout',
20
+ 'disableNewBlocks',
21
+ 'readOnly',
22
+ ],
23
+ },
24
+ ],
25
+ properties: {
26
+ title: {
27
+ title: 'Title',
28
+ description: 'Section friendly name',
29
+ type: 'string',
30
+ },
31
+ allowedBlocks: {
32
+ title: 'Allowed blocks',
33
+ description: 'Allow only the following blocks types',
34
+ type: 'array',
35
+ items: {
36
+ choices: [],
37
+ },
38
+ },
39
+ placeholder: {
40
+ title: 'Helper text',
41
+ description:
42
+ 'A short hint that describes the expected value within this block',
43
+ type: 'string',
44
+ },
45
+ instructions: {
46
+ title: 'Instructions',
47
+ description: 'Detailed expected value within this block',
48
+ type: 'string',
49
+ widget: 'richtext',
50
+ },
51
+ as: {
52
+ title: 'HTML5 element',
53
+ description: 'Select HTML5 element to be used for this block',
54
+ type: 'string',
55
+ factory: 'Choice',
56
+ default: 'div',
57
+ choices: [
58
+ ['div', 'div'],
59
+ ['section', 'section'],
60
+ ['article', 'article'],
61
+ ['aside', 'aside'],
62
+ ['details', 'details'],
63
+ ],
64
+ },
65
+ maxChars: {
66
+ title: 'Maximum Characters',
67
+ description: 'The maximum number of characters.',
68
+ type: 'integer',
69
+ factory: 'Integer',
70
+ },
71
+ ignoreSpaces: {
72
+ title: 'Ignore spaces',
73
+ description: 'Ignore spaces while calculating maximum characters',
74
+ type: 'boolean',
75
+ },
76
+ required: {
77
+ title: 'Required',
78
+ description: "Don't allow deletion of this block",
79
+ type: 'boolean',
80
+ },
81
+ fixed: {
82
+ title: 'Fixed position',
83
+ description: 'Disable drag & drop on this block',
84
+ type: 'boolean',
85
+ },
86
+ fixedLayout: {
87
+ title: 'Fixed layout',
88
+ description:
89
+ 'Fixed layout, New blocks created by Editor within this block will be ignored',
90
+ type: 'boolean',
91
+ },
92
+ disableNewBlocks: {
93
+ title: 'Disable new blocks',
94
+ description: 'Disable creation of new blocks after this block',
95
+ type: 'boolean',
96
+ },
97
+ readOnly: {
98
+ title: 'Read-only',
99
+ description: 'Disable editing on this block',
100
+ type: 'boolean',
101
+ },
102
+ readOnlySettings: {
103
+ title: 'Read-only settings',
104
+ description: 'Disable editing on section block settings',
105
+ type: 'boolean',
106
+ },
107
+ disableInnerButtons: {
108
+ title: 'Disable inner buttons',
109
+ description: 'Hide all block related buttons within this block',
110
+ type: 'boolean',
111
+ },
112
+ },
113
+ required: [],
114
+ };
115
+
116
+ export default Schema;
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { RenderBlocks } from '@plone/volto/components';
3
+ import config from '@plone/volto/registry';
4
+
5
+ const View = (props) => {
6
+ const { data } = props;
7
+ const condition = data.condition;
8
+ const metadata = props.metadata || props.properties;
9
+ const CustomTag = `${data.as || 'div'}`;
10
+ const customId = data?.title
11
+ ?.toLowerCase()
12
+ ?.replace(/[^a-zA-Z-\s]/gi, '')
13
+ ?.trim()
14
+ ?.replace(/\s+/gi, '-');
15
+
16
+ const ConditionalRendering =
17
+ config.blocks.blocksConfig.group.conditions?.[condition] || null;
18
+
19
+ if (ConditionalRendering) {
20
+ return (
21
+ <ConditionalRendering>
22
+ <CustomTag id={customId}>
23
+ <RenderBlocks
24
+ {...props}
25
+ metadata={metadata}
26
+ content={data?.data || {}}
27
+ />
28
+ </CustomTag>
29
+ </ConditionalRendering>
30
+ );
31
+ }
32
+
33
+ return (
34
+ <CustomTag id={customId}>
35
+ <RenderBlocks {...props} metadata={metadata} content={data?.data || {}} />
36
+ </CustomTag>
37
+ );
38
+ };
39
+
40
+ export default View;
@@ -0,0 +1,99 @@
1
+ @type: 'extra';
2
+ @element: 'custom';
3
+
4
+ @import (multiple, reference, optional) '../../theme.config';
5
+
6
+ @borderColor: rgba(120, 192, 215, 0.75);
7
+
8
+ .block-editor-group {
9
+ [data-rbd-draggable-context-id] {
10
+ margin-bottom: 1rem;
11
+ }
12
+
13
+ .block-add-button {
14
+ display: none !important;
15
+ }
16
+
17
+ .block.group.selected::before,
18
+ .block.group:hover::before {
19
+ border-style: dashed;
20
+ }
21
+
22
+ fieldset {
23
+ border: none;
24
+
25
+ legend {
26
+ position: absolute;
27
+ z-index: 3;
28
+ top: -1.3em;
29
+ left: 0;
30
+ width: fit-content;
31
+ padding: 0 1rem;
32
+ margin-right: auto;
33
+ margin-left: auto;
34
+ background-color: @pageBackground;
35
+ color: @borderColor;
36
+ cursor: pointer;
37
+ text-align: center;
38
+ }
39
+ }
40
+
41
+ .section-block {
42
+ padding-top: 1rem;
43
+ padding-bottom: 0.1rem;
44
+ margin: 0;
45
+ }
46
+
47
+ .counter {
48
+ display: grid;
49
+ align-items: center;
50
+ font-size: 85%;
51
+ grid-gap: 0.5em;
52
+ grid-template-columns: 98% auto;
53
+ text-align: end;
54
+
55
+ &.info {
56
+ color: #ccc;
57
+ }
58
+
59
+ &.danger {
60
+ color: crimson;
61
+ }
62
+
63
+ &.warning {
64
+ color: darkorange;
65
+ }
66
+ }
67
+
68
+ .blocks-form {
69
+ margin-top: 0.5rem;
70
+ }
71
+
72
+ .blocks-chooser {
73
+ right: 0;
74
+ left: auto;
75
+ margin-top: 3rem;
76
+ }
77
+
78
+ .block-toolbar {
79
+ position: absolute;
80
+ z-index: 3;
81
+ right: -9px;
82
+ display: flex;
83
+ border: none;
84
+ border: 1px solid @borderColor;
85
+ border-bottom: 1px solid @pageBackground;
86
+ margin-top: -45px;
87
+ background-color: @pageBackground;
88
+ border-top-left-radius: 1rem;
89
+ border-top-right-radius: 1rem;
90
+
91
+ .ui.basic.button {
92
+ padding: 8px 5px;
93
+ }
94
+
95
+ .ui.basic.button:hover {
96
+ background: transparent !important;
97
+ }
98
+ }
99
+ }
package/src/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable no-extend-native */
2
2
  import React from 'react';
3
3
  import loadable from '@loadable/component';
4
+ import { compose } from 'redux';
5
+ import { connectToProviderData } from '@eeacms/volto-datablocks/hocs';
4
6
 
5
7
  import { hashlink, localStorage } from './reducers';
6
8
 
@@ -12,6 +14,7 @@ import installBubbleChart from './components/manage/Blocks/BubbleChart';
12
14
  import installCarouselHorizontal from './components/manage/Blocks/CarouselHorizontal';
13
15
  import installCddaShape from './components/manage/Blocks/CddaShape';
14
16
  import installConnectedList from './components/manage/Blocks/List';
17
+ import installConnectedLinkList from './components/manage/Blocks/ConnectedLinkList';
15
18
  import installContactBlock from './components/manage/Blocks/ContactBlock';
16
19
  import installExplodedPiesChart from './components/manage/Blocks/ExplodedPiesChart';
17
20
  import installExploreHabitats from './components/manage/Blocks/ExploreHabitats';
@@ -125,6 +128,38 @@ const applyConfig = (config) => {
125
128
  ],
126
129
  };
127
130
 
131
+ config.blocks.blocksConfig.group = {
132
+ ...(config.blocks.blocksConfig.group || {}),
133
+ conditions: {
134
+ bird: compose(
135
+ connectToProviderData((props) => ({
136
+ provider_url: '/data/natura-2000-species',
137
+ })),
138
+ )(({ children, provider_data }) => {
139
+ if (
140
+ provider_data?.species_group_name?.[0] &&
141
+ provider_data?.species_group_name?.[0] === 'Birds'
142
+ ) {
143
+ return children;
144
+ }
145
+ return null;
146
+ }),
147
+ species: compose(
148
+ connectToProviderData((props) => ({
149
+ provider_url: '/data/natura-2000-species',
150
+ })),
151
+ )(({ children, provider_data }) => {
152
+ if (
153
+ provider_data?.species_group_name?.[0] &&
154
+ provider_data?.species_group_name?.[0] !== 'Birds'
155
+ ) {
156
+ return children;
157
+ }
158
+ return null;
159
+ }),
160
+ },
161
+ };
162
+
128
163
  config.settings.slate.elements[LINK] = LinkElement;
129
164
 
130
165
  config.settings.loadables = {
@@ -139,6 +174,7 @@ const applyConfig = (config) => {
139
174
  installCarouselHorizontal,
140
175
  installCddaShape,
141
176
  installConnectedList,
177
+ installConnectedLinkList,
142
178
  installContactBlock,
143
179
  installExplodedPiesChart,
144
180
  installExploreHabitats,