@eeacms/volto-eea-website-theme 1.9.3 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ /* eslint-disable jsx-a11y/mouse-events-have-key-events */
1
2
  /**
2
3
  * View image block.
3
4
  * @module components/manage/Blocks/Image/View
@@ -11,91 +12,133 @@ import cx from 'classnames';
11
12
  import { withBlockExtensions } from '@plone/volto/helpers';
12
13
  import { flattenToAppURL, isInternalURL } from '@plone/volto/helpers';
13
14
  import { Copyright } from '@eeacms/volto-eea-design-system/ui';
14
- import './style.less';
15
+
15
16
  /**
16
17
  * View image block class.
17
18
  * @class View
18
19
  * @extends Component
19
20
  */
20
- export const View = ({ data, detached }) => {
21
+ export const View = (props) => {
22
+ const { data, detached } = props;
21
23
  const href = data?.href?.[0]?.['@id'] || '';
22
24
  const { copyright, copyrightIcon, copyrightPosition } = data;
25
+ // const [hovering, setHovering] = React.useState(false);
26
+ const [viewLoaded, setViewLoaded] = React.useState(false);
27
+
28
+ const showCopyright = data?.size === 'l' || !data.size;
29
+
30
+ React.useEffect(() => {
31
+ setViewLoaded(true);
32
+ }, []);
33
+
23
34
  return (
24
- <p
25
- className={cx(
26
- 'block image align',
27
- {
28
- center: !Boolean(data.align),
29
- detached,
30
- },
31
- data.align,
32
- )}
33
- >
34
- {data.url && (
35
- <>
36
- {(() => {
37
- const image = (
38
- <div className="image-block">
39
- <img
40
- className={cx({
41
- 'full-width': data.align === 'full',
42
- large: data.size === 'l',
43
- medium: data.size === 'm',
44
- small: data.size === 's',
45
- })}
46
- src={
47
- isInternalURL(data.url)
48
- ? // Backwards compat in the case that the block is storing the full server URL
49
- (() => {
50
- if (data.size === 'l')
51
- return `${flattenToAppURL(
52
- data.url,
53
- )}/@@images/image`;
54
- if (data.size === 'm')
55
- return `${flattenToAppURL(
56
- data.url,
57
- )}/@@images/image/preview`;
58
- if (data.size === 's')
59
- return `${flattenToAppURL(
60
- data.url,
61
- )}/@@images/image/mini`;
62
- return `${flattenToAppURL(data.url)}/@@images/image`;
63
- })()
64
- : data.url
35
+ <>
36
+ {viewLoaded && (
37
+ <div
38
+ className={cx(
39
+ 'block image align',
40
+ {
41
+ center: !Boolean(data.align),
42
+ detached,
43
+ },
44
+ data.align,
45
+ )}
46
+ >
47
+ <div
48
+ className={cx(
49
+ 'image-block-container',
50
+ {
51
+ large: data.size === 'l',
52
+ medium: data.size === 'm',
53
+ small: data.size === 's',
54
+ },
55
+ data?.align ? data?.align : '',
56
+ )}
57
+ >
58
+ {data.url && (
59
+ <>
60
+ {(() => {
61
+ const image = (
62
+ <>
63
+ <img
64
+ className={cx({
65
+ 'full-width': data.align === 'full',
66
+ })}
67
+ src={
68
+ isInternalURL(data.url)
69
+ ? // Backwards compat in the case that the block is storing the full server URL
70
+ (() => {
71
+ if (data.align === 'full')
72
+ return `${flattenToAppURL(
73
+ data.url,
74
+ )}/@@images/image/huge`;
75
+ if (data.size === 'l')
76
+ return `${flattenToAppURL(
77
+ data.url,
78
+ )}/@@images/image/great`;
79
+ if (data.size === 'm')
80
+ return `${flattenToAppURL(
81
+ data.url,
82
+ )}/@@images/image/preview`;
83
+ if (data.size === 's')
84
+ return `${flattenToAppURL(
85
+ data.url,
86
+ )}/@@images/image/mini`;
87
+ return `${flattenToAppURL(
88
+ data.url,
89
+ )}/@@images/image/great`;
90
+ })()
91
+ : data.url
92
+ }
93
+ alt={data.alt || ''}
94
+ loading="lazy"
95
+ />
96
+ <div
97
+ // onMouseEnter={() => setHovering(true)}
98
+ // onMouseLeave={() => setHovering(false)}
99
+ className={`copyright-wrapper ${
100
+ copyrightPosition ? copyrightPosition : 'left'
101
+ }`}
102
+ >
103
+ {copyright && showCopyright ? (
104
+ <Copyright copyrightPosition={copyrightPosition}>
105
+ <Copyright.Icon>
106
+ <Icon className={copyrightIcon} />
107
+ </Copyright.Icon>
108
+ {/*<div*/}
109
+ {/* className={cx(*/}
110
+ {/* 'copyright-hover-container',*/}
111
+ {/* !hovering ? 'hiddenStructure' : '',*/}
112
+ {/* )}*/}
113
+ {/*>*/}
114
+ <Copyright.Text>{copyright}</Copyright.Text>
115
+ {/*</div>*/}
116
+ </Copyright>
117
+ ) : (
118
+ ''
119
+ )}
120
+ </div>
121
+ </>
122
+ );
123
+ if (href) {
124
+ return (
125
+ <UniversalLink
126
+ href={href}
127
+ openLinkInNewTab={data.openLinkInNewTab}
128
+ >
129
+ {image}
130
+ </UniversalLink>
131
+ );
132
+ } else {
133
+ return image;
65
134
  }
66
- alt={data.alt || ''}
67
- loading="lazy"
68
- />
69
- <div className="copyright-image">
70
- {copyright ? (
71
- <Copyright copyrightPosition={copyrightPosition}>
72
- <Copyright.Icon>
73
- <Icon className={copyrightIcon} />
74
- </Copyright.Icon>
75
- <Copyright.Text>{copyright}</Copyright.Text>
76
- </Copyright>
77
- ) : (
78
- ''
79
- )}
80
- </div>
81
- </div>
82
- );
83
- if (href) {
84
- return (
85
- <UniversalLink
86
- href={href}
87
- openLinkInNewTab={data.openLinkInNewTab}
88
- >
89
- {image}
90
- </UniversalLink>
91
- );
92
- } else {
93
- return image;
94
- }
95
- })()}
96
- </>
135
+ })()}
136
+ </>
137
+ )}
138
+ </div>
139
+ </div>
97
140
  )}
98
- </p>
141
+ </>
99
142
  );
100
143
  };
101
144
 
@@ -108,4 +151,4 @@ View.propTypes = {
108
151
  data: PropTypes.objectOf(PropTypes.any).isRequired,
109
152
  };
110
153
 
111
- export default withBlockExtensions(View);
154
+ export default withBlockExtensions(React.memo(View));
@@ -47,12 +47,17 @@ export function ImageSchema({ formData, intl }) {
47
47
  title: 'Default',
48
48
  fields: [...(formData.url ? ['url', 'alt', 'align', 'size'] : [])],
49
49
  },
50
- {
51
- id: 'copyright',
52
- title: 'Copyright',
53
- fields: ['copyright', 'copyrightIcon', 'copyrightPosition'],
54
- },
55
- ...(formData.url
50
+
51
+ ...(!formData?.size || formData?.size === 'l'
52
+ ? [
53
+ {
54
+ id: 'copyright',
55
+ title: 'Copyright',
56
+ fields: ['copyright', 'copyrightIcon', 'copyrightPosition'],
57
+ },
58
+ ]
59
+ : []),
60
+ ...(formData?.url
56
61
  ? [
57
62
  {
58
63
  id: 'link_settings',
@@ -11,7 +11,6 @@ import cx from 'classnames';
11
11
  import { Message } from 'semantic-ui-react';
12
12
  import { isEqual } from 'lodash';
13
13
  import { Copyright } from '@eeacms/volto-eea-design-system/ui';
14
- import './style.less';
15
14
  import { Icon } from 'semantic-ui-react';
16
15
  import { LeadImageSidebar, SidebarPortal } from '@plone/volto/components';
17
16
  import { flattenToAppURL } from '@plone/volto/helpers';
@@ -97,39 +96,43 @@ class Edit extends Component {
97
96
  data.align,
98
97
  )}
99
98
  >
100
- {!properties.image && (
101
- <Message>
102
- <center>
103
- <img src={imageBlockSVG} alt="" />
104
- <div className="message-text">{placeholder}</div>
105
- </center>
106
- </Message>
107
- )}
108
- {properties.image && (
109
- <div className="image-block">
110
- <img
111
- className={cx({ 'full-width': data.align === 'full' })}
112
- src={
113
- properties.image.data
114
- ? `data:${properties.image['content-type']};base64,${properties.image.data}`
115
- : flattenToAppURL(properties.image.download)
116
- }
117
- alt={data.image_caption || ''}
118
- />
119
- <div className="copyright-image">
120
- {copyright ? (
121
- <Copyright copyrightPosition={copyrightPosition}>
122
- <Copyright.Icon>
123
- <Icon className={copyrightIcon} />
124
- </Copyright.Icon>
125
- <Copyright.Text>{copyright}</Copyright.Text>
126
- </Copyright>
127
- ) : (
128
- ''
129
- )}
99
+ <div
100
+ className={`image-block-container ${data?.align ? data?.align : ''}`}
101
+ >
102
+ {!properties.image && (
103
+ <Message>
104
+ <center>
105
+ <img src={imageBlockSVG} alt="" />
106
+ <div className="message-text">{placeholder}</div>
107
+ </center>
108
+ </Message>
109
+ )}
110
+ {properties.image && (
111
+ <div className="image-block">
112
+ <img
113
+ className={cx({ 'full-width': data.align === 'full' })}
114
+ src={
115
+ properties.image.data
116
+ ? `data:${properties.image['content-type']};base64,${properties.image.data}`
117
+ : flattenToAppURL(properties.image.download)
118
+ }
119
+ alt={data.image_caption || ''}
120
+ />
121
+ <div className="copyright-wrapper">
122
+ {copyright ? (
123
+ <Copyright copyrightPosition={copyrightPosition}>
124
+ <Copyright.Icon>
125
+ <Icon className={copyrightIcon} />
126
+ </Copyright.Icon>
127
+ <Copyright.Text>{copyright}</Copyright.Text>
128
+ </Copyright>
129
+ ) : (
130
+ ''
131
+ )}
132
+ </div>
130
133
  </div>
131
- </div>
132
- )}
134
+ )}
135
+ </div>
133
136
  <SidebarPortal selected={this.props.selected}>
134
137
  <LeadImageSidebar {...this.props} />
135
138
  </SidebarPortal>
@@ -1,80 +1,16 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Form } from 'semantic-ui-react';
4
- import { Accordion, Grid, Segment } from 'semantic-ui-react';
5
- import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
6
- import { CheckboxWidget, Icon, TextWidget } from '@plone/volto/components';
3
+ import { Segment } from 'semantic-ui-react';
4
+ import { FormattedMessage, injectIntl } from 'react-intl';
5
+ import { Icon, BlockDataForm } from '@plone/volto/components';
7
6
  import { flattenToAppURL } from '@plone/volto/helpers';
8
- import AlignBlock from '@plone/volto/components/manage/Sidebar/AlignBlock';
9
- import AlignChooser from './AlignChooser';
10
-
7
+ import { LeadImageSchema } from './schema';
11
8
  import imageSVG from '@plone/volto/icons/image.svg';
12
- import clearSVG from '@plone/volto/icons/clear.svg';
13
- import upSVG from '@plone/volto/icons/up-key.svg';
14
- import downSVG from '@plone/volto/icons/down-key.svg';
15
- import navTreeSVG from '@plone/volto/icons/nav.svg';
16
-
17
- const messages = defineMessages({
18
- Image: {
19
- id: 'Image',
20
- defaultMessage: 'Image',
21
- },
22
- Origin: {
23
- id: 'Origin',
24
- defaultMessage: 'Origin',
25
- },
26
- AltText: {
27
- id: 'Alt text',
28
- defaultMessage: 'Alt text',
29
- },
30
- Copyright: {
31
- id: 'Text',
32
- defaultMessage: 'Text',
33
- },
34
- CopyrightIcon: {
35
- id: 'Icon',
36
- defaultMessage: 'Icon',
37
- },
38
- Align: {
39
- id: 'Alignment',
40
- defaultMessage: 'Alignment',
41
- },
42
- LinkTo: {
43
- id: 'Link to',
44
- defaultMessage: 'Link to',
45
- },
46
- openLinkInNewTab: {
47
- id: 'Open in a new tab',
48
- defaultMessage: 'Open in a new tab',
49
- },
50
- NoImageSelected: {
51
- id: 'No image set in image content field',
52
- defaultMessage: 'No image set in image content field',
53
- },
54
- externalURL: {
55
- id: 'External URL',
56
- defaultMessage: 'External URL',
57
- },
58
- });
59
9
 
60
- const LeadImageSidebar = ({
61
- properties,
62
- data,
63
- block,
64
- onChangeBlock,
65
- openObjectBrowser,
66
- required = false,
67
- onChangeField,
68
- intl,
69
- }) => {
70
- const [activeAccIndex, setActiveAccIndex] = useState(0);
10
+ const LeadImageSidebar = ({ properties, data, block, onChangeBlock, intl }) => {
71
11
  const defaultValueCopyrightIcon = 'ri-copyright-line';
72
12
  const defaultValueCopyrightPosition = 'left';
73
- function handleAccClick(e, titleProps) {
74
- const { index } = titleProps;
75
- const newIndex = activeAccIndex === index ? -1 : index;
76
- setActiveAccIndex(newIndex);
77
- }
13
+ const schema = LeadImageSchema({ formData: properties, intl });
78
14
 
79
15
  useEffect(() => {
80
16
  if (data.copyrightIcon === '' || data.copyrightIcon === undefined)
@@ -121,164 +57,19 @@ const LeadImageSidebar = ({
121
57
  alt={properties.image_caption || ''}
122
58
  />
123
59
  </Segment>
124
- <Segment className="form sidebar-image-data">
125
- <TextWidget
126
- id="alt"
127
- title={intl.formatMessage(messages.AltText)}
128
- required={false}
129
- value={properties.image_caption}
130
- icon={properties.image_caption ? clearSVG : null}
131
- iconAction={() => onChangeField('image_caption', '')}
132
- onChange={(name, value) => {
133
- onChangeField('image_caption', value);
134
- }}
135
- />
136
- <Form.Field inline required={required}>
137
- <Grid>
138
- <Grid.Row>
139
- <Grid.Column width="4">
140
- <div className="wrapper">
141
- <label htmlFor="field-align">
142
- <FormattedMessage
143
- id="Alignment"
144
- defaultMessage="Alignment"
145
- />
146
- </label>
147
- </div>
148
- </Grid.Column>
149
- <Grid.Column width="8" className="align-tools">
150
- <AlignBlock
151
- align={data.align}
152
- onChangeBlock={onChangeBlock}
153
- data={data}
154
- block={block}
155
- />
156
- </Grid.Column>
157
- </Grid.Row>
158
- </Grid>
159
- </Form.Field>
160
- </Segment>
161
- <Accordion fluid styled className="form">
162
- <Accordion.Title
163
- active={activeAccIndex === 0}
164
- index={0}
165
- onClick={handleAccClick}
166
- >
167
- Link Settings
168
- {activeAccIndex === 0 ? (
169
- <Icon name={upSVG} size="20px" />
170
- ) : (
171
- <Icon name={downSVG} size="20px" />
172
- )}
173
- </Accordion.Title>
174
- <Accordion.Content active={activeAccIndex === 0}>
175
- <TextWidget
176
- id="link"
177
- title={intl.formatMessage(messages.LinkTo)}
178
- required={false}
179
- value={flattenToAppURL(data.href)}
180
- icon={data.href ? clearSVG : navTreeSVG}
181
- iconAction={
182
- data.href
183
- ? () => {
184
- onChangeBlock(block, {
185
- ...data,
186
- href: '',
187
- });
188
- }
189
- : () => openObjectBrowser({ mode: 'link' })
190
- }
191
- onChange={(name, value) => {
192
- onChangeBlock(block, {
193
- ...data,
194
- href: flattenToAppURL(value),
195
- });
196
- }}
197
- />
198
- <CheckboxWidget
199
- id="openLinkInNewTab"
200
- title={intl.formatMessage(messages.openLinkInNewTab)}
201
- value={data.openLinkInNewTab ? data.openLinkInNewTab : false}
202
- onChange={(name, value) => {
203
- onChangeBlock(block, {
204
- ...data,
205
- openLinkInNewTab: value,
206
- });
207
- }}
208
- />
209
- </Accordion.Content>
210
- </Accordion>
211
- <Accordion fluid styled className="form">
212
- <Accordion.Title
213
- active={activeAccIndex === 1}
214
- index={1}
215
- onClick={handleAccClick}
216
- >
217
- Copyright
218
- {activeAccIndex === 1 ? (
219
- <Icon name={upSVG} size="20px" />
220
- ) : (
221
- <Icon name={downSVG} size="20px" />
222
- )}
223
- </Accordion.Title>
224
- <Accordion.Content active={activeAccIndex === 1}>
225
- <Segment className="form">
226
- <TextWidget
227
- id="copyright"
228
- title={intl.formatMessage(messages.Copyright)}
229
- required={false}
230
- value={data.copyright}
231
- icon={data.copyright ? clearSVG : null}
232
- iconAction={() => onChangeField('copyright', '')}
233
- onChange={(name, value) => {
234
- onChangeBlock(block, {
235
- ...data,
236
- copyright: value,
237
- });
238
- }}
239
- />
240
- <TextWidget
241
- id="copyrightIcon"
242
- title={intl.formatMessage(messages.CopyrightIcon)}
243
- required={false}
244
- value={data.copyrightIcon}
245
- icon={data.copyrightIcon ? clearSVG : null}
246
- iconAction={() => onChangeField('copyrightIcon', '')}
247
- onChange={(name, value) => {
248
- onChangeBlock(block, {
249
- ...data,
250
- copyrightIcon: value,
251
- });
252
- }}
253
- />
254
- <Form.Field inline required={required}>
255
- <Grid>
256
- <Grid.Row>
257
- <Grid.Column width="4">
258
- <div className="wrapper">
259
- <label htmlFor="field-align">
260
- <FormattedMessage
261
- id="Alignment"
262
- defaultMessage="Alignment"
263
- />
264
- </label>
265
- </div>
266
- </Grid.Column>
267
- <Grid.Column width="8" className="align-tools">
268
- <AlignChooser
269
- align={data.copyrightPosition}
270
- actions={['left', 'right']}
271
- onChangeBlock={onChangeBlock}
272
- block={block}
273
- data={data}
274
- />
275
- </Grid.Column>
276
- </Grid.Row>
277
- </Grid>
278
- </Form.Field>
279
- </Segment>
280
- </Accordion.Content>
281
- </Accordion>
60
+ <BlockDataForm
61
+ schema={schema}
62
+ title={schema.title}
63
+ onChangeField={(id, value) => {
64
+ onChangeBlock(block, {
65
+ ...data,
66
+ [id]: value,
67
+ });
68
+ }}
69
+ onChangeBlock={onChangeBlock}
70
+ formData={data}
71
+ block={block}
72
+ />
282
73
  </>
283
74
  )}
284
75
  </Segment.Group>