@eeacms/volto-eea-website-theme 1.34.0 → 2.0.1
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/.eslintrc.js +7 -2
- package/CHANGELOG.md +53 -1
- package/docker-compose.yml +1 -1
- package/jest-addon.config.js +3 -0
- package/package.json +2 -1
- package/src/components/manage/Blocks/LayoutSettings/index.js +3 -1
- package/src/components/manage/Blocks/Title/index.js +3 -1
- package/src/components/manage/Blocks/Title/schema.js +3 -1
- package/src/components/theme/Banner/View.jsx +12 -5
- package/src/components/theme/DraftBackground/DraftBackground.jsx +30 -21
- package/src/components/theme/DraftBackground/DraftBackground.test.jsx +85 -0
- package/src/config.js +2 -0
- package/src/customizations/@plone/volto-slate/blocks/Text/TextBlockView.jsx +32 -0
- package/src/customizations/@plone/volto-slate/editor/render.jsx +75 -0
- package/src/customizations/@plone/volto-slate/elementEditor/utils.js +76 -75
- package/src/customizations/volto/components/manage/Blocks/Grid/Edit.jsx +70 -0
- package/src/customizations/volto/components/manage/Blocks/Grid/View.jsx +61 -0
- package/src/customizations/volto/components/manage/Blocks/Grid/readme.md +1 -0
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.jsx +82 -23
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.test.jsx +10 -3
- package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +110 -111
- package/src/customizations/volto/components/manage/Blocks/Image/schema.js +17 -2
- package/src/customizations/volto/components/manage/Blocks/LeadImage/Edit.jsx +35 -14
- package/src/customizations/volto/components/manage/Blocks/LeadImage/View.jsx +65 -79
- package/src/customizations/volto/components/manage/Display/Display.jsx +306 -0
- package/src/customizations/volto/components/manage/Display/Readme.md +1 -0
- package/src/customizations/volto/components/manage/Sidebar/SidebarPopup copy.jsx +82 -0
- package/src/customizations/volto/components/manage/Toolbar/More.jsx +541 -0
- package/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx +3 -1
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +24 -14
- package/src/customizations/volto/components/manage/Widgets/README.md +1 -0
- package/src/customizations/volto/components/manage/Workflow/README.txt +1 -0
- package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +324 -0
- package/src/customizations/volto/components/manage/Workflow/Workflow.test.jsx +81 -0
- package/src/customizations/volto/components/theme/Comments/Comments.jsx +1 -2
- package/src/customizations/volto/components/theme/ContactForm/ContactForm.jsx +1 -1
- package/src/customizations/volto/components/theme/EventDetails/EventDetails.jsx +1 -0
- package/src/index.js +21 -16
- package/src/middleware/ok.js +4 -2
- package/src/middleware/voltoCustom.js +4 -2
- package/src/slate.js +10 -8
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/README.txt +0 -1
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/StyleMenu.jsx +0 -157
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/utils.js +0 -168
- package/src/customizations/volto/components/manage/Add/Add.jsx +0 -498
- package/src/customizations/volto/components/manage/Add/readme.md +0 -1
- package/src/customizations/volto/components/manage/Contents/ContentsPropertiesModal.jsx +0 -232
- package/src/customizations/volto/components/manage/Form/Form.jsx +0 -810
- package/src/customizations/volto/components/manage/Form/Form.test.jsx +0 -1124
- package/src/customizations/volto/components/manage/Form/ModalForm.jsx +0 -326
- package/src/customizations/volto/components/manage/Sharing/Sharing.jsx +0 -528
- package/src/customizations/volto/components/manage/Sharing/Sharing.test.jsx +0 -72
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.test.jsx +0 -193
- package/src/customizations/volto/components/theme/AppExtras/AppExtras.jsx +0 -27
@@ -37,6 +37,10 @@ const messages = defineMessages({
|
|
37
37
|
id: 'Alt text hint link text',
|
38
38
|
defaultMessage: 'Describe the purpose of the image.',
|
39
39
|
},
|
40
|
+
linkSettings: {
|
41
|
+
id: 'Link settings',
|
42
|
+
defaultMessage: 'Link settings',
|
43
|
+
},
|
40
44
|
});
|
41
45
|
|
42
46
|
export function ImageSchema({ formData, intl }) {
|
@@ -61,7 +65,7 @@ export function ImageSchema({ formData, intl }) {
|
|
61
65
|
? [
|
62
66
|
{
|
63
67
|
id: 'link_settings',
|
64
|
-
title:
|
68
|
+
title: intl.formatMessage(messages.linkSettings),
|
65
69
|
fields: ['href', 'openLinkInNewTab'],
|
66
70
|
},
|
67
71
|
]
|
@@ -80,7 +84,7 @@ export function ImageSchema({ formData, intl }) {
|
|
80
84
|
href="https://www.w3.org/WAI/tutorials/images/decision-tree/"
|
81
85
|
title={intl.formatMessage(messages.openLinkInNewTab)}
|
82
86
|
target="_blank"
|
83
|
-
rel="noopener"
|
87
|
+
rel="noopener noreferrer"
|
84
88
|
>
|
85
89
|
{intl.formatMessage(messages.AltTextHintLinkText)}
|
86
90
|
</a>{' '}
|
@@ -136,3 +140,14 @@ export function ImageSchema({ formData, intl }) {
|
|
136
140
|
required: [],
|
137
141
|
};
|
138
142
|
}
|
143
|
+
|
144
|
+
export const gridImageDisableSizeAndPositionHandlersSchema = ({
|
145
|
+
schema,
|
146
|
+
formData,
|
147
|
+
intl,
|
148
|
+
}) => {
|
149
|
+
schema.fieldsets[0].fields = schema.fieldsets[0].fields.filter(
|
150
|
+
(item) => !['align', 'size'].includes(item),
|
151
|
+
);
|
152
|
+
return schema;
|
153
|
+
};
|
@@ -10,10 +10,11 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|
10
10
|
import cx from 'classnames';
|
11
11
|
import { Message } from 'semantic-ui-react';
|
12
12
|
import { isEqual } from 'lodash';
|
13
|
+
|
13
14
|
import { Copyright } from '@eeacms/volto-eea-design-system/ui';
|
14
15
|
import { Icon } from 'semantic-ui-react';
|
15
16
|
import { LeadImageSidebar, SidebarPortal } from '@plone/volto/components';
|
16
|
-
import
|
17
|
+
import config from '@plone/volto/registry';
|
17
18
|
|
18
19
|
import imageBlockSVG from '@plone/volto/components/manage/Blocks/Image/block-image.svg';
|
19
20
|
|
@@ -80,11 +81,18 @@ class Edit extends Component {
|
|
80
81
|
* @returns {string} Markup for the component.
|
81
82
|
*/
|
82
83
|
render() {
|
84
|
+
const Image = config.getComponent({ name: 'Image' }).component;
|
83
85
|
const { data, properties } = this.props;
|
86
|
+
const { copyright, copyrightIcon, copyrightPosition } = data;
|
84
87
|
const placeholder =
|
85
88
|
this.props.data.placeholder ||
|
86
89
|
this.props.intl.formatMessage(messages.ImageBlockInputPlaceholder);
|
87
|
-
|
90
|
+
|
91
|
+
const hasImage = !!properties.image;
|
92
|
+
const hasImageData = hasImage && !!properties.image.data;
|
93
|
+
const className = cx('responsive', { 'full-image': data.align === 'full' });
|
94
|
+
const altText = data.image_caption || properties.image_caption || '';
|
95
|
+
|
88
96
|
return (
|
89
97
|
<div
|
90
98
|
className={cx(
|
@@ -100,7 +108,7 @@ class Edit extends Component {
|
|
100
108
|
`image-block-container ${data?.align ? data?.align : ''}`,
|
101
109
|
)}
|
102
110
|
>
|
103
|
-
{!
|
111
|
+
{!hasImage && (
|
104
112
|
<Message>
|
105
113
|
<center>
|
106
114
|
<img src={imageBlockSVG} alt="" />
|
@@ -108,19 +116,17 @@ class Edit extends Component {
|
|
108
116
|
</center>
|
109
117
|
</Message>
|
110
118
|
)}
|
111
|
-
{
|
119
|
+
{hasImage && hasImageData && (
|
112
120
|
<div className="image-block">
|
113
121
|
<img
|
114
|
-
className={
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
}
|
123
|
-
alt={data.image_caption || ''}
|
122
|
+
className={(className, data?.styles?.objectPosition)}
|
123
|
+
src={`data:${properties.image['content-type']};base64,${properties.image.data}`}
|
124
|
+
width={properties.image.width}
|
125
|
+
height={properties.image.height}
|
126
|
+
alt={altText}
|
127
|
+
style={{
|
128
|
+
aspectRatio: `${properties.image.width}/${properties.image.height}`,
|
129
|
+
}}
|
124
130
|
/>
|
125
131
|
<div className="copyright-wrapper">
|
126
132
|
{copyright ? (
|
@@ -136,6 +142,21 @@ class Edit extends Component {
|
|
136
142
|
</div>
|
137
143
|
</div>
|
138
144
|
)}
|
145
|
+
{hasImage && !hasImageData && (
|
146
|
+
<Image
|
147
|
+
className={className}
|
148
|
+
item={properties}
|
149
|
+
imageField="image"
|
150
|
+
sizes={(() => {
|
151
|
+
if (data.align === 'full' || data.align === 'center')
|
152
|
+
return '100vw';
|
153
|
+
if (data.align === 'left' || data.align === 'right')
|
154
|
+
return '50vw';
|
155
|
+
return undefined;
|
156
|
+
})()}
|
157
|
+
alt={altText}
|
158
|
+
/>
|
159
|
+
)}
|
139
160
|
</div>
|
140
161
|
<SidebarPortal selected={this.props.selected}>
|
141
162
|
<LeadImageSidebar {...this.props} />
|
@@ -7,101 +7,87 @@ import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
8
8
|
import { UniversalLink } from '@plone/volto/components';
|
9
9
|
import cx from 'classnames';
|
10
|
+
import config from '@plone/volto/registry';
|
10
11
|
import { Copyright } from '@eeacms/volto-eea-design-system/ui';
|
11
12
|
import { Icon } from 'semantic-ui-react';
|
12
|
-
import { flattenToAppURL } from '@plone/volto/helpers';
|
13
13
|
|
14
14
|
/**
|
15
15
|
* View image block class.
|
16
16
|
* @class View
|
17
17
|
* @extends Component
|
18
18
|
*/
|
19
|
-
const View = (
|
20
|
-
const {
|
21
|
-
const
|
22
|
-
|
23
|
-
// const [hovering, setHovering] = React.useState(false);
|
24
|
-
const [viewLoaded, setViewLoaded] = React.useState(false);
|
25
|
-
|
26
|
-
React.useEffect(() => {
|
27
|
-
setViewLoaded(true);
|
28
|
-
}, []);
|
19
|
+
const View = ({ data, properties }) => {
|
20
|
+
const { copyright, copyrightIcon, copyrightPosition } = data;
|
21
|
+
const Image = config.getComponent({ name: 'Image' }).component;
|
29
22
|
|
30
23
|
return (
|
31
24
|
<>
|
32
|
-
|
33
|
-
|
25
|
+
<p
|
26
|
+
className={cx(
|
27
|
+
'block image align',
|
28
|
+
{
|
29
|
+
center: !Boolean(data.align),
|
30
|
+
},
|
31
|
+
data.align,
|
32
|
+
)}
|
33
|
+
>
|
34
|
+
<div
|
34
35
|
className={cx(
|
35
|
-
|
36
|
-
{
|
37
|
-
center: !Boolean(data.align),
|
38
|
-
},
|
39
|
-
data.align,
|
36
|
+
`image-block-container ${data?.align ? data?.align : ''}`,
|
40
37
|
)}
|
41
38
|
>
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
{/* 'copyright-hover-container',*/}
|
75
|
-
{/* !hovering ? 'hiddenStructure' : '',*/}
|
76
|
-
{/* )}*/}
|
77
|
-
{/*>*/}
|
78
|
-
<Copyright.Text>{copyright}</Copyright.Text>
|
79
|
-
{/*</div>*/}
|
80
|
-
</Copyright>
|
81
|
-
) : (
|
82
|
-
''
|
83
|
-
)}
|
84
|
-
</div>
|
39
|
+
{properties.image && (
|
40
|
+
<>
|
41
|
+
{(() => {
|
42
|
+
const image = (
|
43
|
+
<div className="image-block">
|
44
|
+
<Image
|
45
|
+
className={cx(
|
46
|
+
{ 'full-width': data.align === 'full' },
|
47
|
+
data?.styles?.objectPosition,
|
48
|
+
)}
|
49
|
+
item={properties}
|
50
|
+
imageField="image"
|
51
|
+
sizes={config.blocks.blocksConfig.leadimage.getSizes(
|
52
|
+
data,
|
53
|
+
)}
|
54
|
+
alt={properties.image_caption || ''}
|
55
|
+
responsive={true}
|
56
|
+
/>
|
57
|
+
<div
|
58
|
+
className={`copyright-wrapper ${
|
59
|
+
copyrightPosition ? copyrightPosition : 'left'
|
60
|
+
}`}
|
61
|
+
>
|
62
|
+
{copyright ? (
|
63
|
+
<Copyright copyrightPosition={copyrightPosition}>
|
64
|
+
<Copyright.Icon>
|
65
|
+
<Icon className={copyrightIcon} />
|
66
|
+
</Copyright.Icon>
|
67
|
+
</Copyright>
|
68
|
+
) : (
|
69
|
+
''
|
70
|
+
)}
|
85
71
|
</div>
|
72
|
+
</div>
|
73
|
+
);
|
74
|
+
if (data.href) {
|
75
|
+
return (
|
76
|
+
<UniversalLink
|
77
|
+
href={data.href}
|
78
|
+
openLinkInNewTab={data.openLinkInNewTab}
|
79
|
+
>
|
80
|
+
{image}
|
81
|
+
</UniversalLink>
|
86
82
|
);
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
);
|
96
|
-
} else {
|
97
|
-
return image;
|
98
|
-
}
|
99
|
-
})()}
|
100
|
-
</>
|
101
|
-
)}
|
102
|
-
</div>
|
103
|
-
</p>
|
104
|
-
)}
|
83
|
+
} else {
|
84
|
+
return image;
|
85
|
+
}
|
86
|
+
})()}
|
87
|
+
</>
|
88
|
+
)}
|
89
|
+
</div>
|
90
|
+
</p>
|
105
91
|
</>
|
106
92
|
);
|
107
93
|
};
|
@@ -0,0 +1,306 @@
|
|
1
|
+
import React, { Component, Fragment } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { connect } from 'react-redux';
|
4
|
+
import { compose } from 'redux';
|
5
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
6
|
+
|
7
|
+
import jwtDecode from 'jwt-decode';
|
8
|
+
import {
|
9
|
+
getSchema,
|
10
|
+
getUser,
|
11
|
+
updateContent,
|
12
|
+
getContent,
|
13
|
+
} from '@plone/volto/actions';
|
14
|
+
import { getLayoutFieldname } from '@plone/volto/helpers';
|
15
|
+
import { FormFieldWrapper, Icon } from '@plone/volto/components';
|
16
|
+
import { defineMessages, injectIntl } from 'react-intl';
|
17
|
+
import config from '@plone/volto/registry';
|
18
|
+
|
19
|
+
import downSVG from '@plone/volto/icons/down-key.svg';
|
20
|
+
import upSVG from '@plone/volto/icons/up-key.svg';
|
21
|
+
import checkSVG from '@plone/volto/icons/check.svg';
|
22
|
+
|
23
|
+
const messages = defineMessages({
|
24
|
+
Viewmode: {
|
25
|
+
id: 'Viewmode',
|
26
|
+
defaultMessage: 'View',
|
27
|
+
},
|
28
|
+
});
|
29
|
+
|
30
|
+
const Option = injectLazyLibs('reactSelect')((props) => {
|
31
|
+
const { Option } = props.reactSelect.components;
|
32
|
+
return (
|
33
|
+
<Option {...props}>
|
34
|
+
<div>{props.label}</div>
|
35
|
+
{props.isFocused && !props.isSelected && (
|
36
|
+
<Icon name={checkSVG} size="18px" color="#b8c6c8" />
|
37
|
+
)}
|
38
|
+
{props.isSelected && <Icon name={checkSVG} size="18px" color="#007bc1" />}
|
39
|
+
</Option>
|
40
|
+
);
|
41
|
+
});
|
42
|
+
|
43
|
+
const DropdownIndicator = injectLazyLibs('reactSelect')((props) => {
|
44
|
+
const { DropdownIndicator } = props.reactSelect.components;
|
45
|
+
return (
|
46
|
+
<DropdownIndicator {...props}>
|
47
|
+
{props.selectProps.menuIsOpen ? (
|
48
|
+
<Icon name={upSVG} size="24px" color="#007bc1" />
|
49
|
+
) : (
|
50
|
+
<Icon name={downSVG} size="24px" color="#007bc1" />
|
51
|
+
)}
|
52
|
+
</DropdownIndicator>
|
53
|
+
);
|
54
|
+
});
|
55
|
+
|
56
|
+
const selectTheme = (theme) => ({
|
57
|
+
...theme,
|
58
|
+
borderRadius: 0,
|
59
|
+
colors: {
|
60
|
+
...theme.colors,
|
61
|
+
primary25: 'hotpink',
|
62
|
+
primary: '#b8c6c8',
|
63
|
+
},
|
64
|
+
});
|
65
|
+
|
66
|
+
const customSelectStyles = {
|
67
|
+
control: (styles, state) => ({
|
68
|
+
...styles,
|
69
|
+
border: 'none',
|
70
|
+
borderBottom: '2px solid #b8c6c8',
|
71
|
+
boxShadow: 'none',
|
72
|
+
borderBottomStyle: state.menuIsOpen ? 'dotted' : 'solid',
|
73
|
+
}),
|
74
|
+
menu: (styles, state) => ({
|
75
|
+
...styles,
|
76
|
+
top: null,
|
77
|
+
marginTop: 0,
|
78
|
+
boxShadow: 'none',
|
79
|
+
borderBottom: '2px solid #b8c6c8',
|
80
|
+
}),
|
81
|
+
menuList: (styles, state) => ({
|
82
|
+
...styles,
|
83
|
+
maxHeight: '400px',
|
84
|
+
}),
|
85
|
+
indicatorSeparator: (styles) => ({
|
86
|
+
...styles,
|
87
|
+
width: null,
|
88
|
+
}),
|
89
|
+
valueContainer: (styles) => ({
|
90
|
+
...styles,
|
91
|
+
padding: 0,
|
92
|
+
}),
|
93
|
+
option: (styles, state) => ({
|
94
|
+
...styles,
|
95
|
+
backgroundColor: null,
|
96
|
+
minHeight: '50px',
|
97
|
+
display: 'flex',
|
98
|
+
justifyContent: 'space-between',
|
99
|
+
alignItems: 'center',
|
100
|
+
padding: '12px 12px',
|
101
|
+
color: state.isSelected
|
102
|
+
? '#007bc1'
|
103
|
+
: state.isFocused
|
104
|
+
? '#4a4a4a'
|
105
|
+
: 'inherit',
|
106
|
+
':active': {
|
107
|
+
backgroundColor: null,
|
108
|
+
},
|
109
|
+
span: {
|
110
|
+
flex: '0 0 auto',
|
111
|
+
},
|
112
|
+
svg: {
|
113
|
+
flex: '0 0 auto',
|
114
|
+
},
|
115
|
+
}),
|
116
|
+
};
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Display container class.
|
120
|
+
* @class Display
|
121
|
+
* @extends Component
|
122
|
+
*/
|
123
|
+
class DisplaySelect extends Component {
|
124
|
+
/**
|
125
|
+
* Property types.
|
126
|
+
* @property {Object} propTypes Property types.
|
127
|
+
* @static
|
128
|
+
*/
|
129
|
+
static propTypes = {
|
130
|
+
getSchema: PropTypes.func.isRequired,
|
131
|
+
updateContent: PropTypes.func.isRequired,
|
132
|
+
getContent: PropTypes.func.isRequired,
|
133
|
+
loaded: PropTypes.bool.isRequired,
|
134
|
+
pathname: PropTypes.string.isRequired,
|
135
|
+
layouts: PropTypes.arrayOf(PropTypes.string),
|
136
|
+
layout: PropTypes.string,
|
137
|
+
type: PropTypes.string.isRequired,
|
138
|
+
};
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Default properties
|
142
|
+
* @property {Object} defaultProps Default properties.
|
143
|
+
* @static
|
144
|
+
*/
|
145
|
+
static defaultProps = {
|
146
|
+
layouts: [],
|
147
|
+
layout: '',
|
148
|
+
rolesWhoCanChangeLayout: [],
|
149
|
+
};
|
150
|
+
|
151
|
+
state = {
|
152
|
+
hasMatchingRole: false,
|
153
|
+
selectedOption: {
|
154
|
+
value: this.props.layout,
|
155
|
+
label: config.views.layoutViewsNamesMapping?.[this.props.layout]
|
156
|
+
? this.props.intl.formatMessage({
|
157
|
+
id: config.views.layoutViewsNamesMapping?.[this.props.layout],
|
158
|
+
defaultMessage:
|
159
|
+
config.views.layoutViewsNamesMapping?.[this.props.layout],
|
160
|
+
})
|
161
|
+
: this.props.layout,
|
162
|
+
},
|
163
|
+
};
|
164
|
+
|
165
|
+
componentDidMount() {
|
166
|
+
this.props.getSchema(this.props.type);
|
167
|
+
}
|
168
|
+
|
169
|
+
UNSAFE_componentWillMount() {
|
170
|
+
if (!this.props.rolesWhoCanChangeLayout.length) {
|
171
|
+
this.props.rolesWhoCanChangeLayout.push(
|
172
|
+
...(config?.settings?.eea?.rolesWhoCanChangeLayout || []),
|
173
|
+
);
|
174
|
+
}
|
175
|
+
if (!this.props.layouts.length) {
|
176
|
+
this.props.getSchema(this.props.type);
|
177
|
+
}
|
178
|
+
if (Object.keys(this.props.user).length === 0) {
|
179
|
+
this.props.getUser(this.props.userId);
|
180
|
+
} else {
|
181
|
+
const hasMatchingRole = this.props.user.roles.some((role) =>
|
182
|
+
this.props.rolesWhoCanChangeLayout.includes(role),
|
183
|
+
);
|
184
|
+
if (hasMatchingRole !== this.state.hasMatchingRole) {
|
185
|
+
this.setState({ hasMatchingRole });
|
186
|
+
}
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
/**
|
191
|
+
* Component will receive props
|
192
|
+
* @method componentWillReceiveProps
|
193
|
+
* @param {Object} nextProps Next properties
|
194
|
+
* @returns {undefined}
|
195
|
+
*/
|
196
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
197
|
+
if (nextProps.pathname !== this.props.pathname) {
|
198
|
+
this.props.getSchema(nextProps.type);
|
199
|
+
}
|
200
|
+
if (!this.props.loaded && nextProps.loaded) {
|
201
|
+
this.props.getContent(nextProps.pathname);
|
202
|
+
}
|
203
|
+
|
204
|
+
if (Object.keys(nextProps.user).length !== 0) {
|
205
|
+
const hasMatchingRole = nextProps.user.roles.some((role) =>
|
206
|
+
this.props.rolesWhoCanChangeLayout.includes(role),
|
207
|
+
);
|
208
|
+
if (hasMatchingRole !== this.state.hasMatchingRole) {
|
209
|
+
this.setState({ hasMatchingRole });
|
210
|
+
}
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
/**
|
215
|
+
* On set layout handler
|
216
|
+
* @method setLayout
|
217
|
+
* @param {Object} event Event object
|
218
|
+
* @returns {undefined}
|
219
|
+
*/
|
220
|
+
setLayout = (selectedOption) => {
|
221
|
+
this.props.updateContent(this.props.pathname, {
|
222
|
+
layout: selectedOption.value,
|
223
|
+
});
|
224
|
+
this.setState({ selectedOption });
|
225
|
+
};
|
226
|
+
|
227
|
+
selectValue = (option) => (
|
228
|
+
<Fragment>
|
229
|
+
<span className="Select-value-label">{option.label}</span>
|
230
|
+
</Fragment>
|
231
|
+
);
|
232
|
+
|
233
|
+
optionRenderer = (option) => (
|
234
|
+
<Fragment>
|
235
|
+
<span style={{ marginRight: 'auto' }}>{option.label}</span>
|
236
|
+
<Icon name={checkSVG} size="24px" />
|
237
|
+
</Fragment>
|
238
|
+
);
|
239
|
+
|
240
|
+
render() {
|
241
|
+
if (!this.state.hasMatchingRole) {
|
242
|
+
return null;
|
243
|
+
}
|
244
|
+
const { selectedOption } = this.state;
|
245
|
+
const Select = this.props.reactSelect.default;
|
246
|
+
const layoutsNames = config.views.layoutViewsNamesMapping;
|
247
|
+
const layoutOptions = this.props.layouts
|
248
|
+
.filter(
|
249
|
+
(layout) =>
|
250
|
+
Object.keys(config.views.contentTypesViews).includes(layout) ||
|
251
|
+
Object.keys(config.views.layoutViews).includes(layout),
|
252
|
+
)
|
253
|
+
.map((item) => ({
|
254
|
+
value: item,
|
255
|
+
label:
|
256
|
+
this.props.intl.formatMessage({
|
257
|
+
id: layoutsNames[item],
|
258
|
+
defaultMessage: layoutsNames[item],
|
259
|
+
}) || item,
|
260
|
+
}));
|
261
|
+
|
262
|
+
return layoutOptions?.length > 1 ? (
|
263
|
+
<FormFieldWrapper
|
264
|
+
id="display-select"
|
265
|
+
title={this.props.intl.formatMessage(messages.Viewmode)}
|
266
|
+
{...this.props}
|
267
|
+
>
|
268
|
+
<Select
|
269
|
+
name="display-select"
|
270
|
+
className="react-select-container"
|
271
|
+
classNamePrefix="react-select"
|
272
|
+
options={layoutOptions}
|
273
|
+
styles={customSelectStyles}
|
274
|
+
theme={selectTheme}
|
275
|
+
components={{ DropdownIndicator, Option }}
|
276
|
+
onChange={this.setLayout}
|
277
|
+
defaultValue={selectedOption}
|
278
|
+
isSearchable={false}
|
279
|
+
/>
|
280
|
+
</FormFieldWrapper>
|
281
|
+
) : null;
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
export default compose(
|
286
|
+
injectIntl,
|
287
|
+
injectLazyLibs('reactSelect'),
|
288
|
+
connect(
|
289
|
+
(state) => ({
|
290
|
+
loaded: state.content.update.loaded,
|
291
|
+
layouts: state.schema.schema ? state.schema.schema.layouts : [],
|
292
|
+
layout: state.content.data
|
293
|
+
? state.content.data[getLayoutFieldname(state.content.data)]
|
294
|
+
: '',
|
295
|
+
layout_fieldname: state.content.data
|
296
|
+
? getLayoutFieldname(state.content.data)
|
297
|
+
: '',
|
298
|
+
type: state.content.data ? state.content.data['@type'] : '',
|
299
|
+
user: state.users.user,
|
300
|
+
userId: state.userSession.token
|
301
|
+
? jwtDecode(state.userSession.token).sub
|
302
|
+
: '',
|
303
|
+
}),
|
304
|
+
{ getSchema, getUser, updateContent, getContent },
|
305
|
+
),
|
306
|
+
)(DisplaySelect);
|
@@ -0,0 +1 @@
|
|
1
|
+
Added customization to condition the display of the layout options based on the user's roles.
|
@@ -0,0 +1,82 @@
|
|
1
|
+
// Check this https://github.com/plone/volto/pull/5520
|
2
|
+
import React from 'react';
|
3
|
+
import { Portal } from 'react-portal';
|
4
|
+
import { CSSTransition } from 'react-transition-group';
|
5
|
+
import PropTypes from 'prop-types';
|
6
|
+
import { doesNodeContainClick } from 'semantic-ui-react/dist/commonjs/lib';
|
7
|
+
|
8
|
+
const DEFAULT_TIMEOUT = 500;
|
9
|
+
|
10
|
+
const SidebarPopup = (props) => {
|
11
|
+
const { children, open, onClose, overlay } = props;
|
12
|
+
|
13
|
+
const asideElement = React.useRef();
|
14
|
+
|
15
|
+
const handleClickOutside = (e) => {
|
16
|
+
if (asideElement && doesNodeContainClick(asideElement.current, e)) return;
|
17
|
+
onClose();
|
18
|
+
};
|
19
|
+
|
20
|
+
React.useEffect(() => {
|
21
|
+
document.addEventListener('mousedown', handleClickOutside, false);
|
22
|
+
return () => {
|
23
|
+
document.removeEventListener('mousedown', handleClickOutside, false);
|
24
|
+
};
|
25
|
+
});
|
26
|
+
|
27
|
+
return (
|
28
|
+
<>
|
29
|
+
{overlay && (
|
30
|
+
<CSSTransition
|
31
|
+
in={open}
|
32
|
+
timeout={DEFAULT_TIMEOUT}
|
33
|
+
classNames="overlay-container"
|
34
|
+
unmountOnExit
|
35
|
+
>
|
36
|
+
<Portal node={document?.body}>
|
37
|
+
<div className="overlay-container"></div>
|
38
|
+
</Portal>
|
39
|
+
</CSSTransition>
|
40
|
+
)}
|
41
|
+
<CSSTransition
|
42
|
+
in={open}
|
43
|
+
timeout={DEFAULT_TIMEOUT}
|
44
|
+
classNames="sidebar-container"
|
45
|
+
unmountOnExit
|
46
|
+
>
|
47
|
+
<Portal>
|
48
|
+
<aside
|
49
|
+
id="test"
|
50
|
+
role="presentation"
|
51
|
+
onClick={(e) => {
|
52
|
+
e.stopPropagation();
|
53
|
+
}}
|
54
|
+
onKeyDown={(e) => {
|
55
|
+
e.stopPropagation();
|
56
|
+
}}
|
57
|
+
ref={asideElement}
|
58
|
+
key="sidebarpopup"
|
59
|
+
className="sidebar-container"
|
60
|
+
style={{ overflowY: 'auto' }}
|
61
|
+
>
|
62
|
+
{children}
|
63
|
+
</aside>
|
64
|
+
</Portal>
|
65
|
+
</CSSTransition>
|
66
|
+
</>
|
67
|
+
);
|
68
|
+
};
|
69
|
+
|
70
|
+
SidebarPopup.propTypes = {
|
71
|
+
open: PropTypes.bool,
|
72
|
+
onClose: PropTypes.func,
|
73
|
+
overlay: PropTypes.bool,
|
74
|
+
};
|
75
|
+
|
76
|
+
SidebarPopup.defaultProps = {
|
77
|
+
open: false,
|
78
|
+
onClose: () => {},
|
79
|
+
overlay: false,
|
80
|
+
};
|
81
|
+
|
82
|
+
export default SidebarPopup;
|