@eeacms/volto-n2k 1.2.2 → 1.2.3

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,12 +4,31 @@ 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.2.3](https://github.com/eea/volto-n2k/compare/1.2.2...1.2.3) - 26 March 2026
8
+
9
+ #### :rocket: New Features
10
+
11
+ - feat(tilesImages): add Data connected Images variation [nileshgulia1 - [`1c92f16`](https://github.com/eea/volto-n2k/commit/1c92f167e1960b548ecbae908ab25ec0ce9042bf)]
12
+ - feat(): add red habitat banner listing variation for habitat banner refs#301670 [nileshgulia1 - [`2fa0b3a`](https://github.com/eea/volto-n2k/commit/2fa0b3ac0f585c022eb30e5e0c638903dc6b4135)]
13
+
14
+ #### :bug: Bug Fixes
15
+
16
+ - fix: images rendering [nileshgulia1 - [`d1e22b0`](https://github.com/eea/volto-n2k/commit/d1e22b007f0f42c0913b2f8359bfe672ac253651)]
17
+ - fix: remove unused imports [nileshgulia1 - [`df7de96`](https://github.com/eea/volto-n2k/commit/df7de96eecbf40ad3b095628172b391223393ce2)]
18
+
19
+ #### :house: Internal changes
20
+
21
+
22
+ #### :hammer_and_wrench: Others
23
+
24
+ - remove: habitats variation for Habitats Banner [nileshgulia1 - [`d160a38`](https://github.com/eea/volto-n2k/commit/d160a3806d3b68507d15c77054f764a2f3ce1962)]
25
+ - move arrows to left [nileshgulia1 - [`781c07d`](https://github.com/eea/volto-n2k/commit/781c07d384ea0e6060df124bb0412c724d651e40)]
26
+ - eslint . [nileshgulia1 - [`516da36`](https://github.com/eea/volto-n2k/commit/516da36ce3a95ee2f8cc9b3e630ddc2875034745)]
7
27
  ### [1.2.2](https://github.com/eea/volto-n2k/compare/1.2.1...1.2.2) - 23 February 2026
8
28
 
9
29
  #### :house: Internal changes
10
30
 
11
31
  - style: Automated code fix [eea-jenkins - [`dd7eb96`](https://github.com/eea/volto-n2k/commit/dd7eb969dbf212e8cc0e49d76dff0cedfcc26e53)]
12
- - chore: [JENKINSFILE] use sonarqube branches [EEA Jenkins - [`0e3a176`](https://github.com/eea/volto-n2k/commit/0e3a176b5013e874fd769549c7c43dacdfee07d2)]
13
32
 
14
33
  #### :hammer_and_wrench: Others
15
34
 
@@ -167,7 +186,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
167
186
 
168
187
  #### :house: Internal changes
169
188
 
170
- - chore: [JENKINS] Refactor automated testing [valentinab25 - [`e4f9fc8`](https://github.com/eea/volto-n2k/commit/e4f9fc800b753b97cca7c685bc47dd48804276f0)]
171
189
  - chore: husky, lint-staged use fixed versions [valentinab25 - [`647d621`](https://github.com/eea/volto-n2k/commit/647d621d52400c50f5bc629b3ddc4f2121f14ffa)]
172
190
  - chore:volto 16 in tests, update docs, fix stylelint overrides [valentinab25 - [`60d94b6`](https://github.com/eea/volto-n2k/commit/60d94b61ff8826abc57af9616793a1a247f585b5)]
173
191
 
@@ -182,11 +200,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
182
200
  - try to use volto-spotlight theme in jenkins [Miu Razvan - [`ec0b952`](https://github.com/eea/volto-n2k/commit/ec0b9525b967f907922ae660bd9f520119cd80c1)]
183
201
  - remove waitForResourceToLoad [ana-oprea - [`eaded9b`](https://github.com/eea/volto-n2k/commit/eaded9b9ecd1ca2eb78aa8dd65838c063d95db85)]
184
202
  - update image size from 300 to 800 px [Claudia Ifrim - [`8d9270a`](https://github.com/eea/volto-n2k/commit/8d9270a09506278ceaea25e8a72abdf5b22fcc78)]
185
- - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`350f8a6`](https://github.com/eea/volto-n2k/commit/350f8a6928aafbf168472b572f8041a79f71f577)]
186
- - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`99b3a84`](https://github.com/eea/volto-n2k/commit/99b3a84a266710fadf7d430117359b741cadede8)]
187
- - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`050ac98`](https://github.com/eea/volto-n2k/commit/050ac982ec39e9a1b4885b665cdc118fca7b1283)]
188
- - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`3546040`](https://github.com/eea/volto-n2k/commit/35460409817f496fc752ab34e37d6d33a069b2e9)]
189
- - test: [JENKINS] Improve cypress time [valentinab25 - [`06a4ab1`](https://github.com/eea/volto-n2k/commit/06a4ab151799b8cdf4a316dbcb0c323e0f568e30)]
190
203
  - test: EN locales, pre-commit fix, feature PRs checks Refs #257193 [valentinab25 - [`4850d6a`](https://github.com/eea/volto-n2k/commit/4850d6a65349fe49dbe9244c30592ae38c9aeaba)]
191
204
  - test: Update Makefile and docker-compose to align it with Jenkinsfile [valentinab25 - [`2c91856`](https://github.com/eea/volto-n2k/commit/2c9185688a2fe6b36e5eeb2963f6c8676e3c5628)]
192
205
  ### [1.0.33](https://github.com/eea/volto-n2k/compare/1.0.32...1.0.33) - 3 July 2023
@@ -443,7 +456,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
443
456
  - Update [razvanMiu - [`42ab50e`](https://github.com/eea/volto-n2k/commit/42ab50e6681a47251d15ac06b836c6f8c27157fa)]
444
457
  - Added bubble chart [razvanMiu - [`89fba5e`](https://github.com/eea/volto-n2k/commit/89fba5e5db41aa955ba2d0ed58c9f0042c461504)]
445
458
  - Group species by id_eunis in species list [razvanMiu - [`543c957`](https://github.com/eea/volto-n2k/commit/543c9573b53acd8eecc9afbb6ae7beeddba53966)]
446
- - Add Sonarqube tag using frontend addons list [EEA Jenkins - [`2ca3e4c`](https://github.com/eea/volto-n2k/commit/2ca3e4c092211f92339791d71db81f5b4ca2d562)]
447
459
  - update maps - species and habitats distribution maps [Claudia Ifrim - [`ad12eb1`](https://github.com/eea/volto-n2k/commit/ad12eb141907d6f28444f1eafc3f687735d2a68d)]
448
460
  - update format for common name and author [Claudia Ifrim - [`26b7a2f`](https://github.com/eea/volto-n2k/commit/26b7a2f3d20c7701b57fb24f39fc7dfeceec3ce8)]
449
461
  - update format for species name (common / scientific) [Claudia Ifrim - [`abdedcf`](https://github.com/eea/volto-n2k/commit/abdedcfe645665031dedf050fd2c5cdde8c14a50)]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-n2k",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "volto-n2k: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -1,208 +1,16 @@
1
- import { VisibilitySensor } from '@eeacms/volto-datablocks/components';
2
- import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs';
3
- import { replaceQueryParam, priorityLabels } from '@eeacms/volto-n2k/helpers';
4
- import arrowLeft from '@eeacms/volto-n2k/icons/arrow-left.svg';
5
- import arrowRight from '@eeacms/volto-n2k/icons/arrow-right.svg';
6
- import loadable from '@loadable/component';
7
- import { Icon } from '@plone/volto/components';
8
- import { flattenToAppURL } from '@plone/volto/helpers';
9
- import cx from 'classnames';
10
- import { useCallback, useMemo, useRef, useState } from 'react';
11
- import { compose } from 'redux';
1
+ import React from 'react';
2
+ import { withBlockExtensions } from '@plone/volto/helpers/Extensions';
3
+ import config from '@plone/volto/registry';
12
4
 
13
- import 'swiper/css';
14
- import './style.less';
5
+ function View(props) {
6
+ const { mode = 'view', variation } = props;
15
7
 
16
- const SwiperLoader = loadable.lib(() => import('swiper'));
17
- const SwiperReactLoader = loadable.lib(() => import('swiper/react'));
8
+ const variations =
9
+ config.blocks?.blocksConfig['habitats_banner']?.variations || [];
10
+ const defaultVariation = variations.filter((item) => item.isDefault)?.[0];
11
+ const Template = variation?.template ?? defaultVariation?.template ?? null;
18
12
 
19
- const getSource = (source) => {
20
- let parsedSource = replaceQueryParam(source, 'x', 800);
21
- parsedSource = replaceQueryParam(parsedSource, 'y', 800);
22
-
23
- return parsedSource;
24
- };
25
-
26
- const ViewComponent = (props) => {
27
- const swiperEl = useRef();
28
- const previewEl = useRef();
29
- const [activeSlide, setActiveSlide] = useState(0);
30
-
31
- const habitat_provider = useMemo(
32
- () => flattenToAppURL(props.data.habitat_provider),
33
- [props.data.habitat_provider],
34
- );
35
- const habitat_pictures_provider = useMemo(
36
- () => flattenToAppURL(props.data.habitat_pictures_provider),
37
- [props.data.habitat_pictures_provider],
38
- );
39
-
40
- const habitat = useMemo(
41
- () => props.providers_data?.[habitat_provider] || {},
42
- [props.providers_data, habitat_provider],
43
- );
44
- const habitat_pictures = useMemo(
45
- () => props.providers_data?.[habitat_pictures_provider] || {},
46
- [props.providers_data, habitat_pictures_provider],
47
- );
48
-
49
- const {
50
- code_2000 = [],
51
- scientific_name = [],
52
- habitat_prioriy = [],
53
- } = habitat;
54
- const { attribution_copyright = [] } = habitat_pictures;
55
-
56
- const priorityLabel = useMemo(() => {
57
- const priority = habitat_prioriy[0];
58
-
59
- return priorityLabels.habitat_prioriy[priority] ?? '';
60
- }, [habitat_prioriy]);
61
-
62
- const pictures = useMemo(
63
- () => habitat_pictures?.['WebURL'] || [],
64
- [habitat_pictures],
65
- );
66
- const pictures_length = useMemo(
67
- () => pictures.filter((picture) => !!picture)?.length,
68
- [pictures],
69
- );
70
-
71
- const handleSliderPrevious = useCallback(() => {
72
- swiperEl.current.slidePrev();
73
- if (previewEl.current?.[0]) {
74
- previewEl.current[0].slidePrev();
75
- }
76
- if (previewEl.current?.[1]) {
77
- previewEl.current[1].slidePrev();
78
- }
79
- setActiveSlide(swiperEl.current.realIndex);
80
- }, []);
81
-
82
- const handleSliderNext = useCallback(() => {
83
- swiperEl.current.slideNext();
84
- if (previewEl.current?.[0]) {
85
- previewEl.current[0].slideNext();
86
- }
87
- if (previewEl.current?.[1]) {
88
- previewEl.current[1].slideNext();
89
- }
90
- setActiveSlide(swiperEl.current.realIndex);
91
- }, []);
92
-
93
- if (!habitat_provider && props.mode === 'edit') {
94
- return 'Habitat banner block, habitat provider undefined';
95
- }
96
- if (!habitat_pictures_provider && props.mode === 'edit') {
97
- return 'Habitat banner block, habitat pictures provider undefined';
98
- }
99
-
100
- return (
101
- <div className="habitat-banner-details">
102
- <div className="habitat-details">
103
- <div className="habitat-metadata">
104
- <h2 className="name">{scientific_name[0]}</h2>
105
- <p className="info">
106
- Habitats Directive Annex I code&nbsp;&nbsp;&nbsp;{code_2000[0]}
107
- </p>
108
- <p className="info" style={{ marginTop: '1rem' }}>
109
- {priorityLabel}
110
- </p>
111
- </div>
112
- {pictures_length > 0 && (
113
- <div className={cx('carousel one-slide')}>
114
- <div
115
- className={cx('arrows', { 'arrows-hidden': pictures_length < 2 })}
116
- >
117
- {pictures_length > 1 && (
118
- <>
119
- <button
120
- className="swiper-button image-swiper-button-prev"
121
- onClick={handleSliderPrevious}
122
- >
123
- <Icon
124
- className="icon-left"
125
- color="#000"
126
- name={arrowLeft}
127
- size="32px"
128
- />
129
- </button>
130
- <button
131
- className="swiper-button image-swiper-button-next"
132
- onClick={handleSliderNext}
133
- >
134
- <Icon
135
- className="icon-right"
136
- color="#000"
137
- name={arrowRight}
138
- size="32px"
139
- />
140
- </button>
141
- </>
142
- )}
143
- {!!attribution_copyright[activeSlide] && (
144
- <p title={attribution_copyright[activeSlide]}>
145
- {attribution_copyright[activeSlide]}
146
- </p>
147
- )}
148
- </div>
149
- {__CLIENT__ && (
150
- <SwiperLoader>
151
- {() => {
152
- return (
153
- <SwiperReactLoader>
154
- {({ Swiper, SwiperSlide }) => {
155
- return (
156
- <>
157
- <Swiper
158
- loop={true}
159
- allowTouchMove={false}
160
- initialSlide={0}
161
- slidesPerView={1}
162
- spaceBetween={0}
163
- onBeforeInit={(swiper) => {
164
- swiperEl.current = swiper;
165
- }}
166
- >
167
- {pictures.map((source, index) => (
168
- <SwiperSlide key={source}>
169
- <img
170
- src={getSource(source)}
171
- alt={pictures[index]}
172
- />
173
- </SwiperSlide>
174
- ))}
175
- </Swiper>
176
- </>
177
- );
178
- }}
179
- </SwiperReactLoader>
180
- );
181
- }}
182
- </SwiperLoader>
183
- )}
184
- </div>
185
- )}
186
- </div>
187
- </div>
188
- );
189
- };
190
-
191
- const ViewCompose = compose(
192
- connectToMultipleProviders((props) => ({
193
- providers: [
194
- {
195
- provider_url: props.data?.habitat_provider,
196
- },
197
- { provider_url: props.data?.habitat_pictures_provider },
198
- ],
199
- })),
200
- )(ViewComponent);
201
-
202
- export default function View(props) {
203
- return (
204
- <VisibilitySensor Placeholder={() => <div>loading....&nbsp;</div>}>
205
- <ViewCompose {...props} />
206
- </VisibilitySensor>
207
- );
13
+ return <Template {...props} mode={mode} />;
208
14
  }
15
+
16
+ export default withBlockExtensions(View);
@@ -1,6 +1,7 @@
1
1
  import worldSVG from '@plone/volto/icons/world.svg';
2
2
  import Edit from './Edit';
3
3
  import View from './View';
4
+ import DefaultVariation from './variations/Default';
4
5
 
5
6
  export default function applyConfig(config) {
6
7
  config.blocks.blocksConfig.habitats_banner = {
@@ -19,6 +20,14 @@ export default function applyConfig(config) {
19
20
  view: [],
20
21
  },
21
22
  blockHasOwnFocusManagement: false,
23
+ variations: [
24
+ {
25
+ id: 'default',
26
+ title: 'Default',
27
+ isDefault: true,
28
+ template: DefaultVariation,
29
+ },
30
+ ],
22
31
  };
23
32
  return config;
24
33
  }
@@ -0,0 +1,208 @@
1
+ import { VisibilitySensor } from '@eeacms/volto-datablocks/components';
2
+ import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs';
3
+ import { replaceQueryParam, priorityLabels } from '@eeacms/volto-n2k/helpers';
4
+ import arrowLeft from '@eeacms/volto-n2k/icons/arrow-left.svg';
5
+ import arrowRight from '@eeacms/volto-n2k/icons/arrow-right.svg';
6
+ import loadable from '@loadable/component';
7
+ import { Icon } from '@plone/volto/components';
8
+ import { flattenToAppURL } from '@plone/volto/helpers';
9
+ import cx from 'classnames';
10
+ import { useCallback, useMemo, useRef, useState } from 'react';
11
+ import { compose } from 'redux';
12
+
13
+ import 'swiper/css';
14
+ import './style.less';
15
+
16
+ const SwiperLoader = loadable.lib(() => import('swiper'));
17
+ const SwiperReactLoader = loadable.lib(() => import('swiper/react'));
18
+
19
+ const getSource = (source) => {
20
+ let parsedSource = replaceQueryParam(source, 'x', 800);
21
+ parsedSource = replaceQueryParam(parsedSource, 'y', 800);
22
+
23
+ return parsedSource;
24
+ };
25
+
26
+ const Default = (props) => {
27
+ const swiperEl = useRef();
28
+ const previewEl = useRef();
29
+ const [activeSlide, setActiveSlide] = useState(0);
30
+
31
+ const habitat_provider = useMemo(
32
+ () => flattenToAppURL(props.data.habitat_provider),
33
+ [props.data.habitat_provider],
34
+ );
35
+ const habitat_pictures_provider = useMemo(
36
+ () => flattenToAppURL(props.data.habitat_pictures_provider),
37
+ [props.data.habitat_pictures_provider],
38
+ );
39
+
40
+ const habitat = useMemo(
41
+ () => props.providers_data?.[habitat_provider] || {},
42
+ [props.providers_data, habitat_provider],
43
+ );
44
+ const habitat_pictures = useMemo(
45
+ () => props.providers_data?.[habitat_pictures_provider] || {},
46
+ [props.providers_data, habitat_pictures_provider],
47
+ );
48
+
49
+ const {
50
+ code_2000 = [],
51
+ scientific_name = [],
52
+ habitat_prioriy = [],
53
+ } = habitat;
54
+ const { attribution_copyright = [] } = habitat_pictures;
55
+
56
+ const priorityLabel = useMemo(() => {
57
+ const priority = habitat_prioriy[0];
58
+
59
+ return priorityLabels.habitat_prioriy[priority] ?? '';
60
+ }, [habitat_prioriy]);
61
+
62
+ const pictures = useMemo(
63
+ () => habitat_pictures?.['WebURL'] || [],
64
+ [habitat_pictures],
65
+ );
66
+ const pictures_length = useMemo(
67
+ () => pictures.filter((picture) => !!picture)?.length,
68
+ [pictures],
69
+ );
70
+
71
+ const handleSliderPrevious = useCallback(() => {
72
+ swiperEl.current.slidePrev();
73
+ if (previewEl.current?.[0]) {
74
+ previewEl.current[0].slidePrev();
75
+ }
76
+ if (previewEl.current?.[1]) {
77
+ previewEl.current[1].slidePrev();
78
+ }
79
+ setActiveSlide(swiperEl.current.realIndex);
80
+ }, []);
81
+
82
+ const handleSliderNext = useCallback(() => {
83
+ swiperEl.current.slideNext();
84
+ if (previewEl.current?.[0]) {
85
+ previewEl.current[0].slideNext();
86
+ }
87
+ if (previewEl.current?.[1]) {
88
+ previewEl.current[1].slideNext();
89
+ }
90
+ setActiveSlide(swiperEl.current.realIndex);
91
+ }, []);
92
+
93
+ if (!habitat_provider && props.mode === 'edit') {
94
+ return 'Habitat banner block, habitat provider undefined';
95
+ }
96
+ if (!habitat_pictures_provider && props.mode === 'edit') {
97
+ return 'Habitat banner block, habitat pictures provider undefined';
98
+ }
99
+
100
+ return (
101
+ <div className="habitat-banner-details">
102
+ <div className="habitat-details">
103
+ <div className="habitat-metadata">
104
+ <h2 className="name">{scientific_name[0]}</h2>
105
+ <p className="info">
106
+ Habitats Directive Annex I code&nbsp;&nbsp;&nbsp;{code_2000[0]}
107
+ </p>
108
+ <p className="info" style={{ marginTop: '1rem' }}>
109
+ {priorityLabel}
110
+ </p>
111
+ </div>
112
+ {pictures_length > 0 && (
113
+ <div className={cx('carousel one-slide')}>
114
+ <div
115
+ className={cx('arrows', { 'arrows-hidden': pictures_length < 2 })}
116
+ >
117
+ {pictures_length > 1 && (
118
+ <>
119
+ <button
120
+ className="swiper-button image-swiper-button-prev"
121
+ onClick={handleSliderPrevious}
122
+ >
123
+ <Icon
124
+ className="icon-left"
125
+ color="#000"
126
+ name={arrowLeft}
127
+ size="32px"
128
+ />
129
+ </button>
130
+ <button
131
+ className="swiper-button image-swiper-button-next"
132
+ onClick={handleSliderNext}
133
+ >
134
+ <Icon
135
+ className="icon-right"
136
+ color="#000"
137
+ name={arrowRight}
138
+ size="32px"
139
+ />
140
+ </button>
141
+ </>
142
+ )}
143
+ {!!attribution_copyright[activeSlide] && (
144
+ <p title={attribution_copyright[activeSlide]}>
145
+ {attribution_copyright[activeSlide]}
146
+ </p>
147
+ )}
148
+ </div>
149
+ {__CLIENT__ && (
150
+ <SwiperLoader>
151
+ {() => {
152
+ return (
153
+ <SwiperReactLoader>
154
+ {({ Swiper, SwiperSlide }) => {
155
+ return (
156
+ <>
157
+ <Swiper
158
+ loop={true}
159
+ allowTouchMove={false}
160
+ initialSlide={0}
161
+ slidesPerView={1}
162
+ spaceBetween={0}
163
+ onBeforeInit={(swiper) => {
164
+ swiperEl.current = swiper;
165
+ }}
166
+ >
167
+ {pictures.map((source, index) => (
168
+ <SwiperSlide key={source}>
169
+ <img
170
+ src={getSource(source)}
171
+ alt={pictures[index]}
172
+ />
173
+ </SwiperSlide>
174
+ ))}
175
+ </Swiper>
176
+ </>
177
+ );
178
+ }}
179
+ </SwiperReactLoader>
180
+ );
181
+ }}
182
+ </SwiperLoader>
183
+ )}
184
+ </div>
185
+ )}
186
+ </div>
187
+ </div>
188
+ );
189
+ };
190
+
191
+ const ViewCompose = compose(
192
+ connectToMultipleProviders((props) => ({
193
+ providers: [
194
+ {
195
+ provider_url: props.data?.habitat_provider,
196
+ },
197
+ { provider_url: props.data?.habitat_pictures_provider },
198
+ ],
199
+ })),
200
+ )(Default);
201
+
202
+ export default function View(props) {
203
+ return (
204
+ <VisibilitySensor Placeholder={() => <div>loading....&nbsp;</div>}>
205
+ <ViewCompose {...props} />
206
+ </VisibilitySensor>
207
+ );
208
+ }
@@ -1,5 +1,6 @@
1
1
  import TilesImagesEdit from './Edit';
2
2
  import ImageGallery from './variations/ImageGallery/ImageGallery';
3
+ import DataConnectedImageGallery from './variations/DataConnectedImageGallery/ImageGallery';
3
4
  import DefaultView from './variations/Default/Default';
4
5
  import TilesImagesView from './View';
5
6
  import worldSVG from '@plone/volto/icons/world.svg';
@@ -33,7 +34,53 @@ export default function applyConfig(config) {
33
34
  title: 'ImageGallery',
34
35
  template: ImageGallery,
35
36
  },
37
+ {
38
+ id: 'dataConnectedImageGallery',
39
+ isDefault: false,
40
+ title: 'Data Connected ImageGallery',
41
+ template: DataConnectedImageGallery,
42
+ schemaEnhancer: ({ schema, intl, formData }) => {
43
+ schema.fieldsets = schema.fieldsets.map((fieldset) =>
44
+ fieldset.id === 'default'
45
+ ? {
46
+ ...fieldset,
47
+ fields: fieldset.fields.filter((field) => field !== 'theme'),
48
+ }
49
+ : fieldset,
50
+ );
51
+ schema.fieldsets.push({
52
+ id: 'data_query',
53
+ title: 'Data query',
54
+ fields: [
55
+ 'has_data_query_by_context',
56
+ 'has_data_query_by_provider',
57
+ 'data_query',
58
+ ],
59
+ });
60
+ schema.properties.images.widget = 'url';
61
+ schema.properties.has_data_query_by_context = {
62
+ title: 'Has data_query by context',
63
+ type: 'boolean',
64
+ description:
65
+ 'This flag will denote whether or not the connector will be filtered by data_query applied on the page',
66
+ defaultValue: true,
67
+ };
68
+ schema.properties.has_data_query_by_provider = {
69
+ title: 'Has data_query by provider',
70
+ type: 'boolean',
71
+ description:
72
+ 'This flag will denote whether or not the connector will be filtered by data_query applied on the connector itself',
73
+ defaultValue: true,
74
+ };
75
+ schema.properties.data_query = {
76
+ title: 'Data query',
77
+ widget: 'data_query',
78
+ };
79
+ return schema;
80
+ },
81
+ },
36
82
  ],
37
83
  };
84
+
38
85
  return config;
39
86
  }
@@ -0,0 +1,172 @@
1
+ import { VisibilitySensor } from '@eeacms/volto-datablocks/components';
2
+ import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs';
3
+ import { replaceQueryParam } from '@eeacms/volto-n2k/helpers';
4
+ import arrowLeft from '@eeacms/volto-n2k/icons/arrow-left.svg';
5
+ import arrowRight from '@eeacms/volto-n2k/icons/arrow-right.svg';
6
+ import loadable from '@loadable/component';
7
+ import { Icon } from '@plone/volto/components';
8
+ import { flattenToAppURL } from '@plone/volto/helpers';
9
+ import cx from 'classnames';
10
+ import { useCallback, useMemo, useRef, useState } from 'react';
11
+ import { compose } from 'redux';
12
+
13
+ import 'swiper/css';
14
+ import './style.less';
15
+
16
+ const SwiperLoader = loadable.lib(() => import('swiper'));
17
+ const SwiperReactLoader = loadable.lib(() => import('swiper/react'));
18
+
19
+ const getSource = (source) => {
20
+ let parsedSource = replaceQueryParam(source, 'x', 800);
21
+ parsedSource = replaceQueryParam(parsedSource, 'y', 800);
22
+
23
+ return parsedSource;
24
+ };
25
+
26
+ const RedHabitats = (props) => {
27
+ const swiperEl = useRef();
28
+ const previewEl = useRef();
29
+ const [activeSlide, setActiveSlide] = useState(0);
30
+
31
+ const habitat_pictures_provider = useMemo(
32
+ () => flattenToAppURL(props.data.images),
33
+ [props.data.images],
34
+ );
35
+
36
+ const habitat_pictures = useMemo(
37
+ () => props.providers_data?.[habitat_pictures_provider] || {},
38
+ [props.providers_data, habitat_pictures_provider],
39
+ );
40
+
41
+ const { attribution_copyright = [] } = habitat_pictures;
42
+
43
+ const pictures = useMemo(
44
+ () => habitat_pictures?.['WebURL'] || [],
45
+ [habitat_pictures],
46
+ );
47
+ const pictures_length = useMemo(
48
+ () => pictures.filter((picture) => !!picture)?.length,
49
+ [pictures],
50
+ );
51
+
52
+ const handleSliderPrevious = useCallback(() => {
53
+ swiperEl.current.slidePrev();
54
+ if (previewEl.current?.[0]) {
55
+ previewEl.current[0].slidePrev();
56
+ }
57
+ if (previewEl.current?.[1]) {
58
+ previewEl.current[1].slidePrev();
59
+ }
60
+ setActiveSlide(swiperEl.current.realIndex);
61
+ }, []);
62
+
63
+ const handleSliderNext = useCallback(() => {
64
+ swiperEl.current.slideNext();
65
+ if (previewEl.current?.[0]) {
66
+ previewEl.current[0].slideNext();
67
+ }
68
+ if (previewEl.current?.[1]) {
69
+ previewEl.current[1].slideNext();
70
+ }
71
+ setActiveSlide(swiperEl.current.realIndex);
72
+ }, []);
73
+
74
+ if (!habitat_pictures_provider && props.mode === 'edit') {
75
+ return ' habitat pictures provider undefined';
76
+ }
77
+
78
+ return (
79
+ <div className="habitat-banner-details">
80
+ <div className="habitat-details">
81
+ {pictures_length > 0 && (
82
+ <div className={cx('carousel one-slide')}>
83
+ <div
84
+ className={cx('arrows', { 'arrows-hidden': pictures_length < 2 })}
85
+ >
86
+ {pictures_length > 1 && (
87
+ <>
88
+ <button
89
+ className="swiper-button image-swiper-button-prev"
90
+ onClick={handleSliderPrevious}
91
+ >
92
+ <Icon
93
+ className="icon-left"
94
+ color="#000"
95
+ name={arrowLeft}
96
+ size="32px"
97
+ />
98
+ </button>
99
+ <button
100
+ className="swiper-button image-swiper-button-next"
101
+ onClick={handleSliderNext}
102
+ >
103
+ <Icon
104
+ className="icon-right"
105
+ color="#000"
106
+ name={arrowRight}
107
+ size="32px"
108
+ />
109
+ </button>
110
+ </>
111
+ )}
112
+ {!!attribution_copyright[activeSlide] && (
113
+ <p title={attribution_copyright[activeSlide]}>
114
+ {attribution_copyright[activeSlide]}
115
+ </p>
116
+ )}
117
+ </div>
118
+ {__CLIENT__ && (
119
+ <SwiperLoader>
120
+ {() => {
121
+ return (
122
+ <SwiperReactLoader>
123
+ {({ Swiper, SwiperSlide }) => {
124
+ return (
125
+ <>
126
+ <Swiper
127
+ loop={true}
128
+ allowTouchMove={false}
129
+ initialSlide={0}
130
+ slidesPerView={1}
131
+ spaceBetween={0}
132
+ onBeforeInit={(swiper) => {
133
+ swiperEl.current = swiper;
134
+ }}
135
+ >
136
+ {pictures.map((source, index) => (
137
+ <SwiperSlide key={source}>
138
+ <img
139
+ src={getSource(source)}
140
+ alt={pictures[index]}
141
+ />
142
+ </SwiperSlide>
143
+ ))}
144
+ </Swiper>
145
+ </>
146
+ );
147
+ }}
148
+ </SwiperReactLoader>
149
+ );
150
+ }}
151
+ </SwiperLoader>
152
+ )}
153
+ </div>
154
+ )}
155
+ </div>
156
+ </div>
157
+ );
158
+ };
159
+
160
+ const ViewCompose = compose(
161
+ connectToMultipleProviders((props) => ({
162
+ providers: [{ provider_url: props.data?.images }],
163
+ })),
164
+ )(RedHabitats);
165
+
166
+ export default function View(props) {
167
+ return (
168
+ <VisibilitySensor Placeholder={() => <div>loading....&nbsp;</div>}>
169
+ <ViewCompose {...props} />
170
+ </VisibilitySensor>
171
+ );
172
+ }
@@ -0,0 +1,248 @@
1
+ @type: extra;
2
+ @element: custom;
3
+
4
+ @import (multiple, reference, optional) '../../theme.config';
5
+
6
+ @{view} {
7
+ .habitat-banner-details {
8
+ &::before {
9
+ .full-width();
10
+ position: absolute;
11
+ z-index: -1;
12
+ }
13
+
14
+ .carousel {
15
+ .half-width(960px);
16
+
17
+ @media @mobile {
18
+ width: calc(@pageFullWidth - 2rem) !important;
19
+ }
20
+ }
21
+ }
22
+ }
23
+
24
+ @{edit} {
25
+ .habitat-banner-details {
26
+ &::before {
27
+ .full-width-edit();
28
+ position: absolute;
29
+ z-index: -1;
30
+ }
31
+
32
+ .carousel {
33
+ .half-width-edit(960px);
34
+
35
+ @media @mobile {
36
+ width: calc(@pageFullWidthEdit - 2rem) !important;
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ div#view .habitat-banner-details .ui.container > * {
43
+ margin-top: 0;
44
+ margin-bottom: 0;
45
+ }
46
+
47
+ .habitat-banner-details {
48
+ position: relative;
49
+ padding: 0;
50
+ margin-bottom: 1rem;
51
+ color: #fff !important;
52
+ font-family: 'RajdhaniB', 'Helvetica Neue', Arial, Helvetica, sans-serif;
53
+
54
+ &::before {
55
+ display: block;
56
+ height: 100%;
57
+ background-color: transparent;
58
+ content: '';
59
+ }
60
+
61
+ .habitat-details {
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: space-between;
65
+
66
+ .habitat-metadata {
67
+ display: flex;
68
+ width: 50%;
69
+ height: 320px !important;
70
+ flex: 0 0 50%;
71
+ flex-direction: column;
72
+ justify-content: center;
73
+ word-wrap: break-word;
74
+
75
+ .name {
76
+ margin-top: 0 !important;
77
+ margin-bottom: 0 !important;
78
+ color: #fff !important;
79
+ font-family: inherit;
80
+
81
+ font-family: RajdhaniB, 'Helvetica Neue', Arial, Helvetica, sans-serif;
82
+ font-size: 40px;
83
+ line-height: 40px;
84
+ text-transform: uppercase;
85
+
86
+ > * {
87
+ font-family: RajdhaniB, 'Helvetica Neue', Arial, Helvetica, sans-serif;
88
+ }
89
+ }
90
+
91
+ .info {
92
+ margin-bottom: 0;
93
+ }
94
+
95
+ h3 {
96
+ margin-top: 0 !important;
97
+ margin-bottom: 0 !important;
98
+ color: #fff !important;
99
+ font-family: inherit;
100
+ font-size: 32px;
101
+ line-height: 32px;
102
+ text-transform: uppercase;
103
+ }
104
+
105
+ h4 {
106
+ margin-top: 0 !important;
107
+ margin-bottom: 0 !important;
108
+ color: #fff !important;
109
+ font-family: inherit;
110
+ font-size: 32px;
111
+ line-height: 32px;
112
+ text-transform: uppercase;
113
+ }
114
+
115
+ .info,
116
+ .info > * {
117
+ font-family: 'RajdhaniR', 'Helvetica Neue', Arial, Helvetica, sans-serif;
118
+ font-size: 18px;
119
+ font-weight: 600;
120
+ line-height: 18px;
121
+
122
+ a {
123
+ color: #fff;
124
+
125
+ &:hover {
126
+ color: #fff;
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ .carousel {
133
+ display: flex;
134
+ height: 320px;
135
+
136
+ p {
137
+ font-family: 'RajdhaniB', 'Helvetica Neue', Arial, Helvetica, sans-serif;
138
+ }
139
+
140
+ img {
141
+ width: 100%;
142
+ }
143
+
144
+ &.one-slide .swiper {
145
+ width: 100%;
146
+ }
147
+
148
+ &.two-slides .swiper {
149
+ width: 50%;
150
+
151
+ @media @mobile {
152
+ width: 100%;
153
+
154
+ &.preview.preview-one {
155
+ display: none;
156
+ }
157
+ }
158
+ }
159
+
160
+ &.three-slides .swiper {
161
+ width: 33.33%;
162
+
163
+ @media @tablet {
164
+ width: 50%;
165
+
166
+ &.preview.preview-two {
167
+ display: none;
168
+ }
169
+ }
170
+
171
+ @media @mobile {
172
+ width: 100%;
173
+
174
+ &.preview.preview-two,
175
+ &.preview.preview-one {
176
+ display: none;
177
+ }
178
+ }
179
+ }
180
+
181
+ .swiper {
182
+ &.preview .swiper-slide {
183
+ filter: grayscale(100%);
184
+ }
185
+
186
+ img {
187
+ display: block;
188
+ width: 100%;
189
+ height: 100%;
190
+ object-fit: contain;
191
+ }
192
+ }
193
+
194
+ .arrows {
195
+ position: absolute;
196
+ z-index: 2;
197
+ bottom: 0;
198
+ left: 0;
199
+ display: flex;
200
+ width: 100%;
201
+ align-items: center;
202
+
203
+ button {
204
+ padding: 0;
205
+ border: none;
206
+ margin: 0;
207
+ background-color: transparent;
208
+ }
209
+
210
+ .icon {
211
+ display: block;
212
+ width: 32px;
213
+ background-color: #fff;
214
+ cursor: pointer;
215
+ }
216
+
217
+ p {
218
+ overflow: hidden;
219
+ width: 100%;
220
+ padding: 0.25rem 0.25rem 0.25rem 1rem;
221
+ margin: 0;
222
+ backdrop-filter: brightness(0.5);
223
+ text-overflow: ellipsis;
224
+ white-space: nowrap;
225
+ }
226
+
227
+ &:not(.arrows-hidden) {
228
+ p {
229
+ width: calc(100% - 64px);
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ @media @mobile {
236
+ flex-flow: column;
237
+
238
+ .habitat-metadata {
239
+ width: 100%;
240
+ margin-top: 2rem;
241
+ }
242
+
243
+ .carousel {
244
+ margin: 0 1rem 1rem 1rem;
245
+ }
246
+ }
247
+ }
248
+ }
@@ -12,42 +12,43 @@ const DefaultView = (props) => {
12
12
  return (
13
13
  <div className={cx('tiles-images', mode, data.theme || 'light')}>
14
14
  {mode === 'edit' && !images.length ? <p>Tiles images block</p> : ''}
15
- {images.map((image) => {
16
- const { copyright } = image;
17
- return (
18
- <p
19
- key={`tile-${image.title}`}
20
- className={cx('p-image', {
21
- 'with-border': data.hasBorder ?? true,
22
- 'rounded-border': data.rounded ?? true,
23
- })}
24
- >
25
- <UniversalLink href={image.link || '#'} title={image.title}>
26
- <>
27
- <img
28
- src={`${image.image}/@@images/image/mini`}
29
- alt={image.title}
30
- style={
31
- data.size
32
- ? { width: `${data.size}px`, height: `${data.size}px` }
33
- : {}
34
- }
35
- />
36
- <div className={`copyright-wrapper ${'left'}`}>
37
- {copyright ? (
38
- <Copyright copyrightPosition={'left'}>
39
- <Copyright.Icon>@</Copyright.Icon>
40
- <Copyright.Text>{copyright}</Copyright.Text>
41
- </Copyright>
42
- ) : (
43
- ''
44
- )}
45
- </div>
46
- </>
47
- </UniversalLink>
48
- </p>
49
- );
50
- })}
15
+ {Array.isArray(images) &&
16
+ images.map((image) => {
17
+ const { copyright } = image;
18
+ return (
19
+ <p
20
+ key={`tile-${image.title}`}
21
+ className={cx('p-image', {
22
+ 'with-border': data.hasBorder ?? true,
23
+ 'rounded-border': data.rounded ?? true,
24
+ })}
25
+ >
26
+ <UniversalLink href={image.link || '#'} title={image.title}>
27
+ <>
28
+ <img
29
+ src={`${image.image}/@@images/image/mini`}
30
+ alt={image.title}
31
+ style={
32
+ data.size
33
+ ? { width: `${data.size}px`, height: `${data.size}px` }
34
+ : {}
35
+ }
36
+ />
37
+ <div className={`copyright-wrapper ${'left'}`}>
38
+ {copyright ? (
39
+ <Copyright copyrightPosition={'left'}>
40
+ <Copyright.Icon>@</Copyright.Icon>
41
+ <Copyright.Text>{copyright}</Copyright.Text>
42
+ </Copyright>
43
+ ) : (
44
+ ''
45
+ )}
46
+ </div>
47
+ </>
48
+ </UniversalLink>
49
+ </p>
50
+ );
51
+ })}
51
52
  </div>
52
53
  );
53
54
  };