@eeacms/volto-eea-website-theme 1.9.2 → 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.
- package/CHANGELOG.md +42 -11
- package/package.json +4 -3
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.jsx +143 -124
- package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +119 -76
- package/src/customizations/volto/components/manage/Blocks/Image/schema.js +11 -6
- package/src/customizations/volto/components/manage/Blocks/LeadImage/Edit.jsx +36 -33
- package/src/customizations/volto/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +20 -229
- package/src/customizations/volto/components/manage/Blocks/LeadImage/View.jsx +81 -51
- package/src/customizations/volto/components/manage/Blocks/LeadImage/schema.js +105 -0
- package/src/hocs/withDeviceSize.jsx +45 -0
- package/src/index.js +4 -1
- package/src/middleware/ok.js +15 -0
- package/src/customizations/volto/components/manage/Blocks/Image/style.less +0 -12
- package/src/customizations/volto/components/manage/Blocks/LeadImage/style.less +0 -12
@@ -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
|
-
|
15
|
+
|
15
16
|
/**
|
16
17
|
* View image block class.
|
17
18
|
* @class View
|
18
19
|
* @extends Component
|
19
20
|
*/
|
20
|
-
export const View = (
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
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, {
|
1
|
+
import React, { useEffect } from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
|
-
import {
|
4
|
-
import {
|
5
|
-
import {
|
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
|
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
|
-
|
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
|
-
<
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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>
|