@eeacms/volto-n2k 1.0.23 → 1.0.25

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 (21) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -1
  3. package/src/components/manage/Blocks/CddaShape/View.jsx +2 -2
  4. package/src/components/manage/Blocks/ContactBlock/styles.less +1 -1
  5. package/src/components/manage/Blocks/ExploreHabitats/View.jsx +2 -2
  6. package/src/components/manage/Blocks/ExploreSites/View.jsx +2 -2
  7. package/src/components/manage/Blocks/ExploreSpecies/View.jsx +2 -2
  8. package/src/components/manage/Blocks/HabitatDistribution/View.jsx +2 -2
  9. package/src/components/manage/Blocks/HabitatProtectedSites/View.jsx +2 -2
  10. package/src/components/manage/Blocks/HabitatsBanner/View.jsx +122 -53
  11. package/src/components/manage/Blocks/HabitatsBanner/style.less +64 -19
  12. package/src/components/manage/Blocks/SiteBanner/style.less +8 -0
  13. package/src/components/manage/Blocks/SiteShape/View.jsx +1 -1
  14. package/src/components/manage/Blocks/SiteSpeciesList/style.less +1 -1
  15. package/src/components/manage/Blocks/SpeciesBanner/View.jsx +160 -100
  16. package/src/components/manage/Blocks/SpeciesBanner/style.less +161 -72
  17. package/src/components/manage/Blocks/SpeciesDistribution/View.jsx +2 -2
  18. package/src/components/manage/Blocks/SpeciesProtectedSites/View.jsx +2 -2
  19. package/src/customizations/@eeacms/volto-group-block/components/manage/Blocks/Group/EditBlockWrapper.jsx +9 -2
  20. /package/src/{components/manage/Blocks/HabitatsBanner/chevron-left-square-fill-svgrepo-com.svg → icons/arrow-left.svg} +0 -0
  21. /package/src/{components/manage/Blocks/HabitatsBanner/chevron-right-square-fill-svgrepo-com.svg → icons/arrow-right.svg} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ 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.25](https://github.com/eea/volto-n2k/compare/1.0.24...1.0.25) - 26 April 2023
8
+
9
+ #### :rocket: New Features
10
+
11
+ - feat(Group/EditBlockWrapper.jsx): add support for custom styles in group block editing mode [Miu Razvan - [`97daa39`](https://github.com/eea/volto-n2k/commit/97daa39e8b3bb7a8c04b357706bb79126c2faecf)]
12
+
13
+ ### [1.0.24](https://github.com/eea/volto-n2k/compare/1.0.23...1.0.24) - 26 April 2023
14
+
15
+ #### :house: Internal changes
16
+
17
+ - style: fix margin-bottom position in habitat-banner-details and species-banner-details [Miu Razvan - [`4e78d69`](https://github.com/eea/volto-n2k/commit/4e78d6927829f18a91f6b409ee670b01d538dba6)]
18
+
7
19
  ### [1.0.23](https://github.com/eea/volto-n2k/compare/1.0.22...1.0.23) - 12 April 2023
8
20
 
9
21
  #### :house: Internal changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-n2k",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "volto-n2k: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -107,8 +107,8 @@ const View = (props) => {
107
107
  />
108
108
  </Map>
109
109
  </div>
110
- <Container className="map-info-notice">
111
- <Message>
110
+ <Container>
111
+ <Message className="map-info-notice shadow-none">
112
112
  <p>
113
113
  The designations employed and the presentation of material on this
114
114
  map do not imply the expression of any opinion whatsoever on the
@@ -2,7 +2,7 @@
2
2
  margin-left: 1rem;
3
3
 
4
4
  &.no-data:not(.edit) {
5
- margin-top: -1.5rem;
5
+ margin-top: -1em;
6
6
  margin-left: 0;
7
7
  }
8
8
 
@@ -165,8 +165,8 @@ const View = (props) => {
165
165
  />
166
166
  </Map>
167
167
  </div>
168
- <Container className="map-info-notice">
169
- <Message style={{ color: '#005248' }}>
168
+ <Container>
169
+ <Message className="map-info-notice" style={{ color: '#005248' }}>
170
170
  <p>
171
171
  The designations employed and the presentation of material on this
172
172
  map do not imply the expression of any opinion whatsoever on the
@@ -160,8 +160,8 @@ const View = (props) => {
160
160
  />
161
161
  </Map>
162
162
  </div>
163
- <Container className="map-info-notice">
164
- <Message style={{ color: '#005248' }}>
163
+ <Container>
164
+ <Message className="map-info-notice" style={{ color: '#005248' }}>
165
165
  <p>
166
166
  The designations employed and the presentation of material on this
167
167
  map do not imply the expression of any opinion whatsoever on the
@@ -163,8 +163,8 @@ const View = (props) => {
163
163
  />
164
164
  </Map>
165
165
  </div>
166
- <Container className="map-info-notice">
167
- <Message style={{ color: '#005248' }}>
166
+ <Container>
167
+ <Message className="map-info-notice" style={{ color: '#005248' }}>
168
168
  <p>
169
169
  The designations employed and the presentation of material on this
170
170
  map do not imply the expression of any opinion whatsoever on the
@@ -95,8 +95,8 @@ const View = (props) => {
95
95
  <Interactions pointer={false} select={false} />
96
96
  </Map>
97
97
  </div>
98
- <Container className="map-info-notice">
99
- <Message>
98
+ <Container>
99
+ <Message className="map-info-notice">
100
100
  <p>
101
101
  The designations employed and the presentation of material on this
102
102
  map do not imply the expression of any opinion whatsoever on the
@@ -95,8 +95,8 @@ const View = (props) => {
95
95
  <Interactions pointer={false} select={false} />
96
96
  </Map>
97
97
  </div>
98
- <Container className="map-info-notice">
99
- <Message>
98
+ <Container>
99
+ <Message className="map-info-notice">
100
100
  <p>
101
101
  The designations employed and the presentation of material on this
102
102
  map do not imply the expression of any opinion whatsoever on the
@@ -1,16 +1,20 @@
1
- import React, { useState } from 'react';
2
- import loadable from '@loadable/component';
1
+ import React, { useRef, useState } from 'react';
3
2
  import { compose } from 'redux';
3
+ import cx from 'classnames';
4
+ import loadable from '@loadable/component';
5
+ import { Icon } from '@plone/volto/components';
4
6
  import { VisibilitySensor } from '@eeacms/volto-datablocks/components';
5
7
  import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs';
6
- import arrowLeft from './chevron-left-square-fill-svgrepo-com.svg';
7
- import arrowRight from './chevron-right-square-fill-svgrepo-com.svg';
8
+ import arrowLeft from '@eeacms/volto-n2k/icons/arrow-left.svg';
9
+ import arrowRight from '@eeacms/volto-n2k/icons/arrow-right.svg';
8
10
  import './style.less';
9
11
 
10
12
  const SwiperLoader = loadable.lib(() => import('swiper'));
11
13
  const SwiperReactLoader = loadable.lib(() => import('swiper/react'));
12
14
 
13
15
  const _View = (props) => {
16
+ const swiperEl = useRef();
17
+ const previewEl = useRef();
14
18
  const [activeSlide, setActiveSlide] = useState(0);
15
19
  const { providers = [] } = props.data;
16
20
  const habitat = props.providers_data?.[providers[0]?.provider_url] || {};
@@ -27,13 +31,16 @@ const _View = (props) => {
27
31
  } = habitat;
28
32
 
29
33
  const pictures = habitat_pictures?.['WebURL'] || [];
30
- const picture_names = habitat_pictures?.['filename'] || [];
31
- const copyright = habitat_pictures?.['attribution_copyright'] || [];
34
+ const pictures_length = pictures?.length;
35
+ // const picture_names = habitat_pictures?.['filename'] || [];
36
+ // const copyright = habitat_pictures?.['attribution_copyright'] || [];
32
37
 
33
38
  if (!code_2000 && props.mode === 'edit') {
34
39
  return 'Habitat banner block (code_2000 undefined)';
35
40
  }
36
41
  if (!code_2000) return null;
42
+ if (activeSlide) {
43
+ }
37
44
 
38
45
  return (
39
46
  <div className="habitat-banner-details">
@@ -52,62 +59,124 @@ const _View = (props) => {
52
59
  </>
53
60
  )} */}
54
61
  </div>
55
- {pictures?.length > 0 && (
56
- <div className="carousel">
62
+ {pictures_length > 0 && (
63
+ <div
64
+ className={cx('carousel', {
65
+ 'one-slide': pictures_length === 1,
66
+ 'two-slides': pictures_length === 2,
67
+ 'three-slides': pictures_length > 2,
68
+ })}
69
+ >
57
70
  <div className="arrows">
58
- <div className="swiper-button image-swiper-button-prev">
59
- <img
60
- className="icon icon-left"
61
- src={arrowLeft}
62
- alt="left arrow"
71
+ <button
72
+ className="swiper-button image-swiper-button-prev"
73
+ onClick={() => {
74
+ swiperEl.current.slidePrev();
75
+ if (previewEl.current?.[0]) {
76
+ previewEl.current[0].slidePrev();
77
+ }
78
+ if (previewEl.current?.[1]) {
79
+ previewEl.current[1].slidePrev();
80
+ }
81
+ setActiveSlide(swiperEl.current.realIndex);
82
+ }}
83
+ >
84
+ <Icon
85
+ className="icon-left"
86
+ color="#000"
87
+ name={arrowLeft}
88
+ size="32px"
63
89
  />
64
- {/* <Icon className="icon-left" name={arrowSVG} size="24px" /> */}
65
- </div>
66
- <div className="swiper-button image-swiper-button-next">
67
- <img
68
- className="icon icon-right"
69
- src={arrowRight}
70
- alt="right arrow"
90
+ </button>
91
+ <button
92
+ className="swiper-button image-swiper-button-next"
93
+ onClick={() => {
94
+ swiperEl.current.slideNext();
95
+ if (previewEl.current?.[0]) {
96
+ previewEl.current[0].slideNext();
97
+ }
98
+ if (previewEl.current?.[1]) {
99
+ previewEl.current[1].slideNext();
100
+ }
101
+ setActiveSlide(swiperEl.current.realIndex);
102
+ }}
103
+ >
104
+ <Icon
105
+ className="icon-right"
106
+ color="#000"
107
+ name={arrowRight}
108
+ size="32px"
71
109
  />
72
- {/* <Icon className="icon-right" name={arrowSVG} size="24px" /> */}
73
- </div>
74
- <p>{copyright[activeSlide]}</p>
110
+ </button>
111
+ {/* <p title={`${source[activeSlide]} - ${license[activeSlide]}`}>
112
+ {source[activeSlide]} - {license[activeSlide]}
113
+ </p> */}
75
114
  </div>
76
115
  <SwiperLoader>
77
- {({ Navigation, Pagination }) => {
116
+ {() => {
78
117
  return (
79
118
  <SwiperReactLoader>
80
119
  {({ Swiper, SwiperSlide }) => {
81
120
  return (
82
- <Swiper
83
- modules={[Navigation, Pagination]}
84
- navigation={{
85
- prevEl: '.image-swiper-button-prev',
86
- nextEl: '.image-swiper-button-next',
87
- }}
88
- slidesPerView={3}
89
- spaceBetween={0}
90
- loop={true}
91
- breakpoints={{
92
- 320: {
93
- slidesPerView: 1,
94
- spaceBetween: 0,
95
- },
96
- 1200: {
97
- slidesPerView: 3,
98
- spaceBetween: 0,
99
- },
100
- }}
101
- onSlideChange={(swiper) => {
102
- setActiveSlide(swiper.activeIndex);
103
- }}
104
- >
105
- {pictures.map((source, index) => (
106
- <SwiperSlide>
107
- <img src={source} alt={picture_names[index]} />
108
- </SwiperSlide>
109
- ))}
110
- </Swiper>
121
+ <>
122
+ <Swiper
123
+ loop={true}
124
+ initialSlide={0}
125
+ slidesPerView={1}
126
+ spaceBetween={0}
127
+ onBeforeInit={(swiper) => {
128
+ swiperEl.current = swiper;
129
+ }}
130
+ >
131
+ {pictures.map((source, index) => (
132
+ <SwiperSlide>
133
+ <img src={source} alt={pictures[index]} />
134
+ </SwiperSlide>
135
+ ))}
136
+ </Swiper>
137
+ {pictures_length > 1 && (
138
+ <Swiper
139
+ className="preview preview-one"
140
+ loop={true}
141
+ initialSlide={1}
142
+ slidesPerView={1}
143
+ spaceBetween={0}
144
+ onBeforeInit={(swiper) => {
145
+ if (!previewEl.current) {
146
+ previewEl.current = [];
147
+ }
148
+ previewEl.current[0] = swiper;
149
+ }}
150
+ >
151
+ {pictures.map((source, index) => (
152
+ <SwiperSlide>
153
+ <img src={source} alt={pictures[index]} />
154
+ </SwiperSlide>
155
+ ))}
156
+ </Swiper>
157
+ )}
158
+ {pictures_length > 2 && (
159
+ <Swiper
160
+ className="preview preview-two"
161
+ loop={true}
162
+ initialSlide={2}
163
+ slidesPerView={1}
164
+ spaceBetween={0}
165
+ onBeforeInit={(swiper) => {
166
+ if (!previewEl.current) {
167
+ previewEl.current = [];
168
+ }
169
+ previewEl.current[1] = swiper;
170
+ }}
171
+ >
172
+ {pictures.map((source, index) => (
173
+ <SwiperSlide>
174
+ <img src={source} alt={pictures[index]} />
175
+ </SwiperSlide>
176
+ ))}
177
+ </Swiper>
178
+ )}
179
+ </>
111
180
  );
112
181
  }}
113
182
  </SwiperReactLoader>
@@ -4,8 +4,6 @@
4
4
  @import (multiple, reference, optional) '../../theme.config';
5
5
 
6
6
  @import 'swiper/swiper.less';
7
- @import 'swiper/modules/navigation/navigation.less';
8
- @import 'swiper/modules/pagination/pagination.less';
9
7
 
10
8
  @{view} {
11
9
  .habitat-banner-details {
@@ -44,12 +42,14 @@
44
42
  }
45
43
 
46
44
  div#view .habitat-banner-details .ui.container > * {
45
+ margin-top: 0;
47
46
  margin-bottom: 0;
48
47
  }
49
48
 
50
49
  .habitat-banner-details {
51
50
  position: relative;
52
51
  padding: 0;
52
+ margin-bottom: 1rem;
53
53
  color: #fff !important;
54
54
  font-family: 'RajdhaniB', 'Helvetica Neue', Arial, Helvetica, sans-serif;
55
55
 
@@ -68,10 +68,10 @@ div#view .habitat-banner-details .ui.container > * {
68
68
  .habitat-metadata {
69
69
  display: flex;
70
70
  width: 50%;
71
+ height: 320px !important;
71
72
  flex: 0 0 50%;
72
73
  flex-direction: column;
73
74
  justify-content: center;
74
- margin: 4rem 0;
75
75
  word-wrap: break-word;
76
76
 
77
77
  .name {
@@ -120,10 +120,21 @@ div#view .habitat-banner-details .ui.container > * {
120
120
  font-size: 18px;
121
121
  font-weight: 600;
122
122
  line-height: 18px;
123
+
124
+ a {
125
+ color: #fff;
126
+
127
+ &:hover {
128
+ color: #fff;
129
+ }
130
+ }
123
131
  }
124
132
  }
125
133
 
126
134
  .carousel {
135
+ display: flex;
136
+ height: 320px;
137
+
127
138
  p {
128
139
  font-family: 'RajdhaniB', 'Helvetica Neue', Arial, Helvetica, sans-serif;
129
140
  }
@@ -132,25 +143,72 @@ div#view .habitat-banner-details .ui.container > * {
132
143
  width: 100%;
133
144
  }
134
145
 
146
+ &.one-slide .swiper {
147
+ width: 100%;
148
+ }
149
+
150
+ &.two-slides .swiper {
151
+ width: 50%;
152
+
153
+ @media @mobile {
154
+ width: 100%;
155
+
156
+ &.preview.preview-one {
157
+ display: none;
158
+ }
159
+ }
160
+ }
161
+
162
+ &.three-slides .swiper {
163
+ width: 33.33%;
164
+
165
+ @media @tablet {
166
+ width: 50%;
167
+
168
+ &.preview.preview-two {
169
+ display: none;
170
+ }
171
+ }
172
+
173
+ @media @mobile {
174
+ width: 100%;
175
+
176
+ &.preview.preview-two,
177
+ &.preview.preview-one {
178
+ display: none;
179
+ }
180
+ }
181
+ }
182
+
135
183
  .swiper {
184
+ &.preview .swiper-slide {
185
+ filter: grayscale(100%);
186
+ }
187
+
136
188
  img {
137
189
  display: block;
138
- // max-height: 300px;
139
190
  width: 100%;
140
- height: 300px;
191
+ height: 100%;
141
192
  object-fit: cover;
142
193
  }
143
194
  }
144
195
 
145
196
  .arrows {
146
197
  position: absolute;
147
- z-index: 1;
198
+ z-index: 2;
148
199
  bottom: 0;
149
200
  left: 0;
150
201
  display: flex;
151
202
  width: 100%;
152
203
  align-items: center;
153
204
 
205
+ button {
206
+ padding: 0;
207
+ border: none;
208
+ margin: 0;
209
+ background-color: transparent;
210
+ }
211
+
154
212
  .icon {
155
213
  display: block;
156
214
  width: 32px;
@@ -170,19 +228,6 @@ div#view .habitat-banner-details .ui.container > * {
170
228
  }
171
229
  }
172
230
 
173
- .swiper {
174
- z-index: 0;
175
- width: 100%;
176
-
177
- .swiper-slide:not(.swiper-slide-active) {
178
- filter: grayscale(100%);
179
- }
180
-
181
- .swiper-slide {
182
- transition: filter 0.5s;
183
- }
184
- }
185
-
186
231
  @media @mobile {
187
232
  flex-flow: column;
188
233
 
@@ -2,6 +2,14 @@
2
2
  margin-top: 0;
3
3
  }
4
4
 
5
+ .subsite.subsite-natura2000 .content-area {
6
+ .site-banner {
7
+ h2 {
8
+ color: #fff !important;
9
+ }
10
+ }
11
+ }
12
+
5
13
  .site-banner {
6
14
  padding: 1.5em 0;
7
15
  background-color: #00a390;
@@ -105,7 +105,7 @@ const View = (props) => {
105
105
  />
106
106
  </Map>
107
107
  </div>
108
- <Message className="map-info-notice">
108
+ <Message className="map-info-notice shadow-none">
109
109
  <Container>
110
110
  <p>
111
111
  The designations employed and the presentation of material on this
@@ -5,7 +5,7 @@ div#view .site-species-list .species-list .ui.container > * {
5
5
  }
6
6
 
7
7
  .site-species-list {
8
- margin-top: -1.5rem;
8
+ margin-top: -1em;
9
9
 
10
10
  .species-filters {
11
11
  .ui.container {
@@ -1,17 +1,19 @@
1
1
  /* eslint-disable react/jsx-no-target-blank */
2
- import React from 'react';
3
- import { Container } from 'semantic-ui-react';
4
- import loadable from '@loadable/component';
5
- import { photoPlaceholders } from '@eeacms/volto-n2k/helpers';
2
+ import React, { useRef, useState } from 'react';
6
3
  import cx from 'classnames';
4
+ import loadable from '@loadable/component';
5
+ import { Icon } from '@plone/volto/components';
6
+ import arrowLeft from '@eeacms/volto-n2k/icons/arrow-left.svg';
7
+ import arrowRight from '@eeacms/volto-n2k/icons/arrow-right.svg';
7
8
  import './style.less';
8
- import 'slick-carousel/slick/slick.css';
9
- import 'slick-carousel/slick/slick-theme.css';
10
9
 
11
- const Slider = loadable(() => import('react-slick'));
10
+ const SwiperLoader = loadable.lib(() => import('swiper'));
11
+ const SwiperReactLoader = loadable.lib(() => import('swiper/react'));
12
12
 
13
13
  const View = (props) => {
14
- const slider = React.useRef(null);
14
+ const swiperEl = useRef();
15
+ const previewEl = useRef();
16
+ const [activeSlide, setActiveSlide] = useState(0);
15
17
  const provider_data = props.provider_data || {};
16
18
  const {
17
19
  author = [],
@@ -23,112 +25,170 @@ const View = (props) => {
23
25
  picture_url = [],
24
26
  scientific_name = [],
25
27
  source = [],
26
- source_url = [],
27
- species_group_name = [],
28
+ // source_url = [],
29
+ // species_group_name = [],
28
30
  } = provider_data;
29
31
 
30
- const sliderSettings = {
31
- adaptiveHeight: true,
32
- autoplay: true,
33
- autoplaySpeed: 5000,
34
- arrows: true,
35
- cssEase: 'ease',
36
- dots: false,
37
- speed: 500,
38
- initialSlide: 0,
39
- lazyLoad: 'ondemand',
40
- swipe: true,
41
- slidesToShow: 1,
42
- slidesToScroll: 1,
43
- touchMove: true,
44
- };
32
+ const pictures_length = picture_url?.length;
45
33
 
46
34
  if (!id_eunis[0]) return '';
47
35
  return (
48
- <div className="species-banner-details full-width">
49
- <Container>
50
- <div className="species-details">
51
- <div className="species-metadata">
52
- <h2 className="name">
53
- {common_name[0] ? common_name[0] + ' - ' : ''}{' '}
54
- <span style={{ fontStyle: 'italic', textTransform: 'none' }}>
55
- {scientific_name[0]}
56
- </span>
57
- </h2>
58
- {author[0] && (
59
- <p
60
- className="info radjhan-bold"
61
- style={{ marginBottom: '0.5rem' }}
36
+ <div className="species-banner-details">
37
+ <div className="species-details">
38
+ <div className="species-metadata">
39
+ <h2 className="name">
40
+ {common_name[0] ? common_name[0] + ' - ' : ''}{' '}
41
+ <span style={{ fontStyle: 'italic', textTransform: 'none' }}>
42
+ {scientific_name[0]}
43
+ </span>
44
+ </h2>
45
+ {author[0] && (
46
+ <p className="info radjhan-bold" style={{ marginBottom: '0.5rem' }}>
47
+ {author[0]}
48
+ </p>
49
+ )}
50
+ {code_2000[0] && (
51
+ <p className="info">
52
+ Natura 2000 species code&nbsp;&nbsp;&nbsp;{code_2000[0]}
53
+ <br />
54
+ <br />
55
+ <a
56
+ href={`http://images.google.com/images?q=${scientific_name[0]}`}
57
+ target="_blank"
62
58
  >
63
- {author[0]}
64
- </p>
65
- )}
66
- {code_2000[0] && (
67
- <p className="info">
68
- Natura 2000 species code&nbsp;&nbsp;&nbsp;{code_2000[0]}
69
- </p>
70
- )}
71
- {/* <br />
72
- {number_sites[0] && (
73
- <h3 style={{ marginBottom: '0.5rem' }}>{number_sites[0]}</h3>
74
- )}
75
- <h4 className="radjhan-normal">
76
- NATURA 2000 SITES PROTECTING THIS SPECIES
77
- </h4> */}
78
- </div>
59
+ Find image on the web
60
+ </a>
61
+ </p>
62
+ )}
63
+ </div>
64
+
65
+ {pictures_length > 0 && (
79
66
  <div
80
- className={cx('species-pictures', {
81
- 'with-slider': picture_url.length > 0 && picture_url[0],
67
+ className={cx('carousel', {
68
+ 'one-slide': pictures_length === 1,
69
+ 'two-slides': pictures_length === 2,
70
+ 'three-slides': pictures_length > 2,
82
71
  })}
83
72
  >
84
- {picture_url.length > 0 && picture_url[0] ? (
85
- <Slider {...sliderSettings} ref={slider}>
86
- {picture_url.map((picture, index) => (
87
- <div key={`${picture}_${index}`} className="picture-wrapper">
88
- <img src={picture} alt={common_name[0]} />
89
- <div className="source">
90
- <a
91
- href={source_url[index]}
92
- title={'Picture source'}
93
- target="_blank"
94
- rel="noreferrer"
95
- >
96
- {source[index]}
97
- {license[index] ? (
98
- <>
99
- ,<br />
100
- {license[index]}
101
- </>
102
- ) : (
103
- ''
104
- )}
105
- </a>
106
- </div>
107
- </div>
108
- ))}
109
- </Slider>
110
- ) : (
111
- <div className="picture-wrapper">
112
- <img
113
- src={
114
- photoPlaceholders[species_group_name[0]] ||
115
- photoPlaceholders.default
73
+ <div className="arrows">
74
+ <button
75
+ className="swiper-button image-swiper-button-prev"
76
+ onClick={() => {
77
+ swiperEl.current.slidePrev();
78
+ if (previewEl.current?.[0]) {
79
+ previewEl.current[0].slidePrev();
116
80
  }
117
- alt={species_group_name[0]}
81
+ if (previewEl.current?.[1]) {
82
+ previewEl.current[1].slidePrev();
83
+ }
84
+ setActiveSlide(swiperEl.current.realIndex);
85
+ }}
86
+ >
87
+ <Icon
88
+ className="icon-left"
89
+ color="#000"
90
+ name={arrowLeft}
91
+ size="32px"
118
92
  />
119
- </div>
120
- )}
121
- <div className="find-image">
122
- <a
123
- href={`http://images.google.com/images?q=${scientific_name[0]}`}
124
- target="_blank"
93
+ </button>
94
+ <button
95
+ className="swiper-button image-swiper-button-next"
96
+ onClick={() => {
97
+ swiperEl.current.slideNext();
98
+ if (previewEl.current?.[0]) {
99
+ previewEl.current[0].slideNext();
100
+ }
101
+ if (previewEl.current?.[1]) {
102
+ previewEl.current[1].slideNext();
103
+ }
104
+ setActiveSlide(swiperEl.current.realIndex);
105
+ }}
125
106
  >
126
- Find image on the web
127
- </a>
107
+ <Icon
108
+ className="icon-right"
109
+ color="#000"
110
+ name={arrowRight}
111
+ size="32px"
112
+ />
113
+ </button>
114
+ <p title={`${source[activeSlide]} - ${license[activeSlide]}`}>
115
+ {source[activeSlide]} - {license[activeSlide]}
116
+ </p>
128
117
  </div>
118
+ <SwiperLoader>
119
+ {() => {
120
+ return (
121
+ <SwiperReactLoader>
122
+ {({ Swiper, SwiperSlide }) => {
123
+ return (
124
+ <>
125
+ <Swiper
126
+ loop={true}
127
+ initialSlide={0}
128
+ slidesPerView={1}
129
+ spaceBetween={0}
130
+ onBeforeInit={(swiper) => {
131
+ swiperEl.current = swiper;
132
+ }}
133
+ >
134
+ {picture_url.map((source, index) => (
135
+ <SwiperSlide>
136
+ <img src={source} alt={picture_url[index]} />
137
+ </SwiperSlide>
138
+ ))}
139
+ </Swiper>
140
+ {pictures_length > 1 && (
141
+ <Swiper
142
+ className="preview preview-one"
143
+ loop={true}
144
+ initialSlide={1}
145
+ slidesPerView={1}
146
+ spaceBetween={0}
147
+ onBeforeInit={(swiper) => {
148
+ if (!previewEl.current) {
149
+ previewEl.current = [];
150
+ }
151
+ previewEl.current[0] = swiper;
152
+ }}
153
+ >
154
+ {picture_url.map((source, index) => (
155
+ <SwiperSlide>
156
+ <img src={source} alt={picture_url[index]} />
157
+ </SwiperSlide>
158
+ ))}
159
+ </Swiper>
160
+ )}
161
+ {pictures_length > 2 && (
162
+ <Swiper
163
+ className="preview preview-two"
164
+ loop={true}
165
+ initialSlide={2}
166
+ slidesPerView={1}
167
+ spaceBetween={0}
168
+ onBeforeInit={(swiper) => {
169
+ if (!previewEl.current) {
170
+ previewEl.current = [];
171
+ }
172
+ previewEl.current[1] = swiper;
173
+ }}
174
+ >
175
+ {picture_url.map((source, index) => (
176
+ <SwiperSlide>
177
+ <img src={source} alt={picture_url[index]} />
178
+ </SwiperSlide>
179
+ ))}
180
+ </Swiper>
181
+ )}
182
+ </>
183
+ );
184
+ }}
185
+ </SwiperReactLoader>
186
+ );
187
+ }}
188
+ </SwiperLoader>
129
189
  </div>
130
- </div>
131
- </Container>
190
+ )}
191
+ </div>
132
192
  </div>
133
193
  );
134
194
  };
@@ -1,31 +1,85 @@
1
+ @type: extra;
2
+ @element: custom;
3
+
4
+ @import (multiple, reference, optional) '../../theme.config';
5
+
6
+ @import 'swiper/swiper.less';
7
+
8
+ @{view} {
9
+ .species-banner-details {
10
+ &::before {
11
+ .full-width();
12
+ position: absolute;
13
+ z-index: -1;
14
+ }
15
+
16
+ .carousel {
17
+ .half-width(960px);
18
+
19
+ @media @mobile {
20
+ width: calc(@pageFullWidth - 2rem) !important;
21
+ }
22
+ }
23
+ }
24
+ }
25
+
26
+ @{edit} {
27
+ .species-banner-details {
28
+ &::before {
29
+ .full-width-edit();
30
+ position: absolute;
31
+ z-index: -1;
32
+ }
33
+
34
+ .carousel {
35
+ .half-width-edit(960px);
36
+
37
+ @media @mobile {
38
+ width: calc(@pageFullWidthEdit - 2rem) !important;
39
+ }
40
+ }
41
+ }
42
+ }
43
+
1
44
  div#view .species-banner-details .ui.container > * {
2
45
  margin-top: 0;
3
46
  margin-bottom: 0;
4
47
  }
5
48
 
6
49
  .species-banner-details {
7
- padding: 1.5em 0;
8
- background-color: #00a390;
50
+ position: relative;
51
+ padding: 0;
52
+ margin-bottom: 1rem;
9
53
  color: #fff !important;
10
54
  font-family: 'RajdhaniB', 'Helvetica Neue', Arial, Helvetica, sans-serif;
11
55
 
56
+ &::before {
57
+ display: block;
58
+ height: 100%;
59
+ background-color: #00a390;
60
+ content: '';
61
+ }
62
+
12
63
  .species-details {
13
64
  display: flex;
14
- flex-flow: row;
15
65
  align-items: center;
16
66
  justify-content: space-between;
17
- gap: 2rem;
18
-
19
- @media only screen and (max-width: 800px) {
20
- flex-flow: column;
21
- }
22
67
 
23
68
  .species-metadata {
69
+ display: flex;
70
+ width: 50%;
71
+ height: 320px !important;
72
+ flex: 0 0 50%;
73
+ flex-direction: column;
74
+ justify-content: center;
75
+ word-wrap: break-word;
76
+
24
77
  .name {
25
78
  margin-top: 0 !important;
26
79
  margin-bottom: 0 !important;
27
80
  color: #fff !important;
28
81
  font-family: inherit;
82
+
29
83
  font-family: RajdhaniB, 'Helvetica Neue', Arial, Helvetica, sans-serif;
30
84
  font-size: 54px;
31
85
  line-height: 54px;
@@ -56,96 +110,131 @@ div#view .species-banner-details .ui.container > * {
56
110
  text-transform: uppercase;
57
111
  }
58
112
 
59
- .info {
60
- margin-bottom: 1rem;
113
+ .info,
114
+ .info > * {
61
115
  font-family: 'RajdhaniR', 'Helvetica Neue', Arial, Helvetica, sans-serif;
62
116
  font-size: 18px;
63
117
  font-weight: 600;
64
118
  line-height: 18px;
119
+
120
+ a {
121
+ color: #fff;
122
+
123
+ &:hover {
124
+ color: #fff;
125
+ }
126
+ }
65
127
  }
66
128
  }
67
129
 
68
- .slick-slider {
69
- .slick-arrow {
70
- display: block !important;
130
+ .carousel {
131
+ display: flex;
132
+ height: 320px;
133
+
134
+ p {
135
+ font-family: 'RajdhaniB', 'Helvetica Neue', Arial, Helvetica, sans-serif;
71
136
  }
72
- }
73
137
 
74
- .species-pictures {
75
- width: 400px;
76
- height: 400px;
77
- margin-bottom: 1rem;
78
-
79
- .picture-wrapper {
80
- position: relative;
81
- display: flex !important;
82
- overflow: hidden;
83
- width: 400px;
84
- height: 400px;
85
- flex-flow: column;
86
- align-items: center;
87
- justify-content: center;
88
- background-color: #000;
89
- border-radius: 10px;
138
+ img {
139
+ width: 100%;
140
+ }
90
141
 
91
- img {
92
- width: auto;
93
- max-width: 400px;
94
- height: auto;
95
- max-height: 400px;
96
- filter: drop-shadow(-2px 2px 3px rgba(0, 0, 0, 0.4));
142
+ &.one-slide .swiper {
143
+ width: 100%;
144
+ }
145
+
146
+ &.two-slides .swiper {
147
+ width: 50%;
148
+
149
+ @media @mobile {
150
+ width: 100%;
151
+
152
+ &.preview.preview-one {
153
+ display: none;
154
+ }
97
155
  }
156
+ }
98
157
 
99
- .source {
100
- position: absolute;
101
- bottom: 0;
102
- left: 0;
158
+ &.three-slides .swiper {
159
+ width: 33.33%;
160
+
161
+ @media @tablet {
162
+ width: 50%;
163
+
164
+ &.preview.preview-two {
165
+ display: none;
166
+ }
167
+ }
168
+
169
+ @media @mobile {
103
170
  width: 100%;
104
- padding: 0.5rem 1rem;
105
- background-color: rgba(0, 0, 0, 0.7);
106
- border-bottom-left-radius: 3px;
107
- border-bottom-right-radius: 3px;
108
-
109
- a {
110
- margin-top: 1rem;
111
- color: #fff !important;
112
-
113
- &:hover {
114
- color: #fff !important;
115
- text-decoration: underline;
116
- }
171
+
172
+ &.preview.preview-two,
173
+ &.preview.preview-one {
174
+ display: none;
117
175
  }
118
176
  }
119
177
  }
120
178
 
121
- &:not(.with-slider) {
122
- display: flex;
123
- flex-flow: column;
124
- justify-content: center;
179
+ .swiper {
180
+ &.preview .swiper-slide {
181
+ filter: grayscale(100%);
182
+ }
125
183
 
126
- .picture-wrapper {
127
- height: fit-content;
184
+ img {
185
+ display: block;
186
+ width: 100%;
187
+ height: 100%;
188
+ object-fit: cover;
128
189
  }
129
190
  }
130
191
 
131
- .find-image {
132
- padding: 0.25rem 1rem;
133
- text-align: left;
192
+ .arrows {
193
+ position: absolute;
194
+ z-index: 2;
195
+ bottom: 0;
196
+ left: 0;
197
+ display: flex;
198
+ width: 100%;
199
+ align-items: center;
134
200
 
135
- a {
136
- color: #fff;
201
+ button {
202
+ padding: 0;
203
+ border: none;
204
+ margin: 0;
205
+ background-color: transparent;
206
+ }
207
+
208
+ .icon {
209
+ display: block;
210
+ width: 32px;
211
+ background-color: #fff;
212
+ cursor: pointer;
213
+ }
214
+
215
+ p {
216
+ overflow: hidden;
217
+ width: calc(100% - 64px);
218
+ padding: 0.25rem 0.25rem 0.25rem 1rem;
219
+ margin: 0;
220
+ backdrop-filter: brightness(0.5);
221
+ text-overflow: ellipsis;
222
+ white-space: nowrap;
137
223
  }
138
224
  }
139
225
  }
140
- }
141
226
 
142
- .slick-slide:not(.slick-active) {
143
- opacity: 0;
144
- transition: opacity 0.6s;
145
- }
227
+ @media @mobile {
228
+ flex-flow: column;
146
229
 
147
- .slick-slide {
148
- opacity: 1;
149
- transition: opacity 0.4s;
230
+ .species-metadata {
231
+ width: 100%;
232
+ margin-top: 2rem;
233
+ }
234
+
235
+ .carousel {
236
+ margin: 0 1rem 1rem 1rem;
237
+ }
238
+ }
150
239
  }
151
240
  }
@@ -100,8 +100,8 @@ const View = (props) => {
100
100
  <Interactions pointer={false} select={false} />
101
101
  </Map>
102
102
  </div>
103
- <Container className="map-info-notice">
104
- <Message>
103
+ <Container>
104
+ <Message className="map-info-notice">
105
105
  <p>
106
106
  The designations employed and the presentation of material on this
107
107
  map do not imply the expression of any opinion whatsoever on the
@@ -96,8 +96,8 @@ const View = (props) => {
96
96
  <Interactions pointer={false} select={false} />
97
97
  </Map>
98
98
  </div>
99
- <Container className="map-info-notice">
100
- <Message>
99
+ <Container>
100
+ <Message className="map-info-notice">
101
101
  <p>
102
102
  The designations employed and the presentation of material on this
103
103
  map do not imply the expression of any opinion whatsoever on the
@@ -1,6 +1,9 @@
1
1
  import React from 'react';
2
2
  import { Icon, BlockChooser } from '@plone/volto/components';
3
- import { blockHasValue } from '@plone/volto/helpers';
3
+ import {
4
+ blockHasValue,
5
+ buildStyleClassNamesFromData,
6
+ } from '@plone/volto/helpers';
4
7
  import config from '@plone/volto/registry';
5
8
  import { Button } from 'semantic-ui-react';
6
9
  import includes from 'lodash/includes';
@@ -86,6 +89,8 @@ class EditBlockWrapper extends React.Component {
86
89
  ? data.required
87
90
  : includes(config.blocks.requiredBlocks, type);
88
91
 
92
+ const styles = buildStyleClassNamesFromData(data.styles);
93
+
89
94
  // Get editing instructions from block settings or props
90
95
  let instructions = data?.instructions?.data || data?.instructions;
91
96
  if (!instructions || instructions === '<p><br/></p>') {
@@ -97,7 +102,9 @@ class EditBlockWrapper extends React.Component {
97
102
  <div
98
103
  ref={draginfo?.innerRef}
99
104
  {...(selected ? draginfo?.draggableProps : null)}
100
- className={`block-editor-${data['@type']}`}
105
+ className={cx(`block-editor-${data['@type']}`, styles, {
106
+ [data.align]: data.align,
107
+ })}
101
108
  >
102
109
  {(!selected || !visible || disabled) && (
103
110
  <div