@plone/volto 17.0.0-alpha.15 → 17.0.0-alpha.16
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 +8 -0
- package/cypress/support/commands.js +18 -0
- package/locales/ca/LC_MESSAGES/volto.po +41 -0
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +41 -0
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +41 -0
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +41 -0
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +41 -0
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +41 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +41 -0
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +41 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +41 -0
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +41 -0
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +41 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +41 -0
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +41 -0
- package/locales/ro.json +1 -1
- package/locales/volto.pot +42 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +41 -0
- package/locales/zh_CN.json +1 -1
- package/package.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/blocks/Text/index.js +8 -0
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
- package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
- package/src/components/manage/Blocks/Container/Data.jsx +32 -0
- package/src/components/manage/Blocks/Container/Edit.jsx +174 -0
- package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +120 -0
- package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
- package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
- package/src/components/manage/Blocks/Grid/Edit.jsx +33 -0
- package/src/components/manage/Blocks/Grid/View.jsx +42 -0
- package/src/components/manage/Blocks/Grid/adapter.js +14 -0
- package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
- package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
- package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
- package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
- package/src/components/manage/Blocks/Grid/schema.js +35 -0
- package/src/components/manage/Blocks/Grid/templates.js +47 -0
- package/src/components/manage/Blocks/Image/ImageSidebar.jsx +2 -1
- package/src/components/manage/Blocks/Image/schema.js +11 -0
- package/src/components/manage/Blocks/Teaser/schema.js +5 -0
- package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
- package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
- package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
- package/src/components/manage/TemplateChooser/template.svg +10 -0
- package/src/components/theme/Component/Component.jsx +1 -1
- package/src/components/theme/View/RenderBlocks.jsx +56 -27
- package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
- package/src/config/Blocks.jsx +44 -0
- package/src/helpers/Blocks/Blocks.js +26 -0
- package/src/helpers/Blocks/Blocks.test.js +21 -0
- package/src/helpers/Utils/Utils.js +25 -0
- package/src/helpers/index.js +2 -0
- package/src/icons/grid-block.svg +11 -0
- package/theme/themes/pastanaga/extras/grid.less +426 -0
- package/theme/themes/pastanaga/extras/main.less +1 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Icon } from '@plone/volto/components';
|
|
2
|
+
import { Button } from 'semantic-ui-react';
|
|
3
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
4
|
+
import NewBlockAddButton from './NewBlockAddButton';
|
|
5
|
+
import cx from 'classnames';
|
|
6
|
+
import { isInteractiveElement } from '@plone/volto/helpers';
|
|
7
|
+
|
|
8
|
+
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
9
|
+
|
|
10
|
+
const messages = defineMessages({
|
|
11
|
+
delete: {
|
|
12
|
+
id: 'Remove element {index}',
|
|
13
|
+
defaultMessage: 'Remove element {index}',
|
|
14
|
+
},
|
|
15
|
+
reset: {
|
|
16
|
+
id: 'Reset element {index}',
|
|
17
|
+
defaultMessage: 'Reset element {index}',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const EditBlockWrapper = (props) => {
|
|
22
|
+
const intl = useIntl();
|
|
23
|
+
|
|
24
|
+
const { blockProps, draginfo, children } = props;
|
|
25
|
+
const {
|
|
26
|
+
block,
|
|
27
|
+
selected,
|
|
28
|
+
type,
|
|
29
|
+
blocksConfig,
|
|
30
|
+
onChangeBlock,
|
|
31
|
+
onDeleteBlock,
|
|
32
|
+
onSelectBlock,
|
|
33
|
+
data,
|
|
34
|
+
index,
|
|
35
|
+
} = blockProps;
|
|
36
|
+
|
|
37
|
+
function onResetBlock() {
|
|
38
|
+
onChangeBlock(block, { '@type': 'empty' });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
ref={draginfo.innerRef}
|
|
44
|
+
{...draginfo.draggableProps}
|
|
45
|
+
{...draginfo.dragHandleProps}
|
|
46
|
+
className={cx(`block-editor-${data['@type']} contained`, { selected })}
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
role="presentation"
|
|
50
|
+
className="cell-wrapper"
|
|
51
|
+
onClick={(e) => {
|
|
52
|
+
onSelectBlock(block);
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{type !== 'empty' ? (
|
|
56
|
+
<Button
|
|
57
|
+
aria-label={intl.formatMessage(messages.reset, {
|
|
58
|
+
index,
|
|
59
|
+
})}
|
|
60
|
+
basic
|
|
61
|
+
icon
|
|
62
|
+
onClick={(e) => onResetBlock(block, {})}
|
|
63
|
+
className="remove-block-button"
|
|
64
|
+
>
|
|
65
|
+
<Icon name={clearSVG} className="circled" size="24px" />
|
|
66
|
+
</Button>
|
|
67
|
+
) : (
|
|
68
|
+
<Button
|
|
69
|
+
basic
|
|
70
|
+
icon
|
|
71
|
+
className="remove-block-button"
|
|
72
|
+
onClick={(e) => onDeleteBlock(block, true)}
|
|
73
|
+
aria-label={intl.formatMessage(messages.delete, {
|
|
74
|
+
index,
|
|
75
|
+
})}
|
|
76
|
+
>
|
|
77
|
+
<Icon
|
|
78
|
+
name={clearSVG}
|
|
79
|
+
className="circled"
|
|
80
|
+
size="24px"
|
|
81
|
+
color="#e40166"
|
|
82
|
+
/>
|
|
83
|
+
</Button>
|
|
84
|
+
)}
|
|
85
|
+
{type && type !== 'empty' ? (
|
|
86
|
+
<div className={cx('ui drag block inner', type)}>{children}</div>
|
|
87
|
+
) : (
|
|
88
|
+
<div
|
|
89
|
+
className={cx('gridBlock-empty-placeholder', {
|
|
90
|
+
selected: selected,
|
|
91
|
+
})}
|
|
92
|
+
role="presentation"
|
|
93
|
+
onClick={(e) => {
|
|
94
|
+
onSelectBlock(block);
|
|
95
|
+
// If the click is in the button, then
|
|
96
|
+
if (isInteractiveElement(e.target)) {
|
|
97
|
+
e.stopPropagation();
|
|
98
|
+
}
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
<p>Add a new block</p>
|
|
102
|
+
<NewBlockAddButton
|
|
103
|
+
block={block}
|
|
104
|
+
index={index}
|
|
105
|
+
blocksConfig={blocksConfig}
|
|
106
|
+
onMutateBlock={(block, value) =>
|
|
107
|
+
onChangeBlock(block, {
|
|
108
|
+
...data,
|
|
109
|
+
...value,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export default EditBlockWrapper;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button, Ref } from 'semantic-ui-react';
|
|
3
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
4
|
+
import { BlockChooser, Icon } from '@plone/volto/components';
|
|
5
|
+
import { useDetectClickOutside } from '@plone/volto/helpers';
|
|
6
|
+
import addSVG from '@plone/volto/icons/add.svg';
|
|
7
|
+
import { usePopper } from 'react-popper';
|
|
8
|
+
import { Portal } from 'react-portal';
|
|
9
|
+
|
|
10
|
+
const messages = defineMessages({
|
|
11
|
+
addBlock: {
|
|
12
|
+
id: 'Add block in position {index}',
|
|
13
|
+
defaultMessage: 'Add block in position {index}',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const NewBlockAddButton = (props) => {
|
|
18
|
+
const intl = useIntl();
|
|
19
|
+
const { blocksConfig, block, index, onMutateBlock } = props;
|
|
20
|
+
const [isOpenMenu, setOpenMenu] = React.useState(false);
|
|
21
|
+
|
|
22
|
+
const blockChooserRef = useDetectClickOutside({
|
|
23
|
+
onTriggered: () => setOpenMenu(false),
|
|
24
|
+
triggerKeys: ['Escape'],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const [referenceElement, setReferenceElement] = React.useState(null);
|
|
28
|
+
const [popperElement, setPopperElement] = React.useState(null);
|
|
29
|
+
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
|
30
|
+
placement: 'bottom',
|
|
31
|
+
modifiers: [
|
|
32
|
+
{
|
|
33
|
+
name: 'offset',
|
|
34
|
+
options: {
|
|
35
|
+
offset: [0, -30],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'flip',
|
|
40
|
+
options: {
|
|
41
|
+
fallbackPlacements: ['right', 'top-start'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<Ref innerRef={setReferenceElement}>
|
|
50
|
+
<Button
|
|
51
|
+
basic
|
|
52
|
+
icon
|
|
53
|
+
onClick={() => setOpenMenu(true)}
|
|
54
|
+
className="add-block-button"
|
|
55
|
+
aria-label={intl.formatMessage(messages.addBlock, {
|
|
56
|
+
index,
|
|
57
|
+
})}
|
|
58
|
+
>
|
|
59
|
+
<Icon name={addSVG} className="circled" size="24px" />
|
|
60
|
+
</Button>
|
|
61
|
+
</Ref>
|
|
62
|
+
{isOpenMenu ? (
|
|
63
|
+
<Portal node={document.getElementById('body')}>
|
|
64
|
+
<div
|
|
65
|
+
ref={setPopperElement}
|
|
66
|
+
style={styles.popper}
|
|
67
|
+
{...attributes.popper}
|
|
68
|
+
className="container-chooser-wrapper"
|
|
69
|
+
>
|
|
70
|
+
<BlockChooser
|
|
71
|
+
onMutateBlock={onMutateBlock}
|
|
72
|
+
currentBlock={block}
|
|
73
|
+
showRestricted
|
|
74
|
+
blocksConfig={blocksConfig}
|
|
75
|
+
ref={blockChooserRef}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
</Portal>
|
|
79
|
+
) : null}
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default NewBlockAddButton;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
2
|
+
import { Button } from 'semantic-ui-react';
|
|
3
|
+
import { Icon } from '@plone/volto/components';
|
|
4
|
+
|
|
5
|
+
import addSVG from '@plone/volto/icons/add.svg';
|
|
6
|
+
import configSVG from '@plone/volto/icons/configuration.svg';
|
|
7
|
+
|
|
8
|
+
const messages = defineMessages({
|
|
9
|
+
addBlock: {
|
|
10
|
+
id: 'Add element to container',
|
|
11
|
+
defaultMessage: 'Add element to container',
|
|
12
|
+
},
|
|
13
|
+
blockSettings: {
|
|
14
|
+
id: 'Container settings',
|
|
15
|
+
defaultMessage: 'Container settings',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const SimpleContainerToolbar = (props) => {
|
|
20
|
+
const { data, onAddNewBlock, maxLength, setSelectedBlock } = props;
|
|
21
|
+
const intl = useIntl();
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="toolbar">
|
|
25
|
+
<Button.Group>
|
|
26
|
+
<Button
|
|
27
|
+
aria-label={intl.formatMessage(messages.addBlock)}
|
|
28
|
+
icon
|
|
29
|
+
basic
|
|
30
|
+
disabled={data?.blocks_layout?.items?.length >= maxLength}
|
|
31
|
+
onClick={(e) => onAddNewBlock()}
|
|
32
|
+
>
|
|
33
|
+
<Icon name={addSVG} size="24px" />
|
|
34
|
+
</Button>
|
|
35
|
+
</Button.Group>
|
|
36
|
+
<Button.Group>
|
|
37
|
+
<Button
|
|
38
|
+
aria-label={intl.formatMessage(messages.blockSettings)}
|
|
39
|
+
icon
|
|
40
|
+
basic
|
|
41
|
+
onClick={(e) => {
|
|
42
|
+
e.stopPropagation();
|
|
43
|
+
setSelectedBlock();
|
|
44
|
+
props.setSidebarTab(1);
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
<Icon name={configSVG} size="24px" />
|
|
48
|
+
</Button>
|
|
49
|
+
</Button.Group>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default SimpleContainerToolbar;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import ContainerEdit from '../Container/Edit';
|
|
4
|
+
|
|
5
|
+
const GridBlockEdit = (props) => {
|
|
6
|
+
const { data } = props;
|
|
7
|
+
|
|
8
|
+
const columnsLength = data?.blocks_layout?.items?.length || 0;
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
className={cx({
|
|
13
|
+
one: columnsLength === 1,
|
|
14
|
+
two: columnsLength === 2,
|
|
15
|
+
three: columnsLength === 3,
|
|
16
|
+
four: columnsLength >= 4,
|
|
17
|
+
'grid-items': true,
|
|
18
|
+
})}
|
|
19
|
+
>
|
|
20
|
+
<ContainerEdit {...props} direction="horizontal" />
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
GridBlockEdit.propTypes = {
|
|
26
|
+
block: PropTypes.string.isRequired,
|
|
27
|
+
onChangeBlock: PropTypes.func.isRequired,
|
|
28
|
+
pathname: PropTypes.string.isRequired,
|
|
29
|
+
selected: PropTypes.bool.isRequired,
|
|
30
|
+
manage: PropTypes.bool.isRequired,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default GridBlockEdit;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Grid } from 'semantic-ui-react';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import { RenderBlocks } from '@plone/volto/components';
|
|
4
|
+
import { withBlockExtensions } from '@plone/volto/helpers';
|
|
5
|
+
import config from '@plone/volto/registry';
|
|
6
|
+
|
|
7
|
+
const GridBlockView = (props) => {
|
|
8
|
+
const { data, path, className } = props;
|
|
9
|
+
const metadata = props.metadata || props.properties;
|
|
10
|
+
const columns = data.blocks_layout.items;
|
|
11
|
+
const blocksConfig =
|
|
12
|
+
config.blocks.blocksConfig[data['@type']].blocksConfig ||
|
|
13
|
+
props.blocksConfig;
|
|
14
|
+
const location = {
|
|
15
|
+
pathname: path,
|
|
16
|
+
};
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
className={cx('block', data['@type'], className, {
|
|
20
|
+
one: columns?.length === 1,
|
|
21
|
+
two: columns?.length === 2,
|
|
22
|
+
three: columns?.length === 3,
|
|
23
|
+
four: columns?.length === 4,
|
|
24
|
+
})}
|
|
25
|
+
>
|
|
26
|
+
{data.headline && <h2 className="headline">{data.headline}</h2>}
|
|
27
|
+
|
|
28
|
+
<Grid stackable stretched columns={columns.length}>
|
|
29
|
+
<RenderBlocks
|
|
30
|
+
{...props}
|
|
31
|
+
blockWrapperTag={Grid.Column}
|
|
32
|
+
metadata={metadata}
|
|
33
|
+
content={data}
|
|
34
|
+
location={location}
|
|
35
|
+
blocksConfig={blocksConfig}
|
|
36
|
+
/>
|
|
37
|
+
</Grid>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default withBlockExtensions(GridBlockView);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<rect width="96" height="96" fill="#9FD1E5" rx="3"/>
|
|
4
|
+
<rect width="67" height="53" fill="#FFF" opacity=".9" transform="translate(15 22)"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<rect width="96" height="96" fill="#9FD1E5" rx="3"/>
|
|
4
|
+
<g fill="#FFF" opacity=".9" transform="translate(9 22)">
|
|
5
|
+
<rect width="37" height="53"/>
|
|
6
|
+
<rect width="37" height="53" x="42"/>
|
|
7
|
+
</g>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<rect width="96" height="96" fill="#9FD1E5" rx="3"/>
|
|
4
|
+
<g fill="#FFF" opacity=".9" transform="translate(6 22)">
|
|
5
|
+
<rect width="25" height="53" x="58"/>
|
|
6
|
+
<rect width="25" height="53"/>
|
|
7
|
+
<rect width="25" height="53" x="29"/>
|
|
8
|
+
</g>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<rect width="96" height="96" fill="#9FD1E5" rx="3"/>
|
|
4
|
+
<g fill="#FFF" opacity=".9" transform="translate(8 22)">
|
|
5
|
+
<rect width="18" height="53" x="42"/>
|
|
6
|
+
<rect width="18" height="53" x="63"/>
|
|
7
|
+
<rect width="18" height="53"/>
|
|
8
|
+
<rect width="18" height="53" x="21"/>
|
|
9
|
+
</g>
|
|
10
|
+
</g>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl';
|
|
2
|
+
|
|
3
|
+
const messages = defineMessages({
|
|
4
|
+
headline: {
|
|
5
|
+
id: 'Headline',
|
|
6
|
+
defaultMessage: 'Headline',
|
|
7
|
+
},
|
|
8
|
+
grid: {
|
|
9
|
+
id: 'Grid',
|
|
10
|
+
defaultMessage: 'Grid',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const GridBlockSchema = (props) => {
|
|
15
|
+
const { intl } = props;
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
title: intl.formatMessage(messages.grid),
|
|
19
|
+
block: 'grid',
|
|
20
|
+
fieldsets: [
|
|
21
|
+
{
|
|
22
|
+
id: 'default',
|
|
23
|
+
title: 'Default',
|
|
24
|
+
fields: ['headline'],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
|
|
28
|
+
properties: {
|
|
29
|
+
headline: {
|
|
30
|
+
title: intl.formatMessage(messages.headline),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: [],
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl';
|
|
2
|
+
import { blocksFormGenerator } from '@plone/volto/helpers';
|
|
3
|
+
|
|
4
|
+
import gridTemplate1 from './grid-1.svg';
|
|
5
|
+
import gridTemplate2 from './grid-2.svg';
|
|
6
|
+
import gridTemplate3 from './grid-3.svg';
|
|
7
|
+
import gridTemplate4 from './grid-4.svg';
|
|
8
|
+
|
|
9
|
+
const messages = defineMessages({
|
|
10
|
+
column: {
|
|
11
|
+
id: 'column',
|
|
12
|
+
defaultMessage: 'column',
|
|
13
|
+
},
|
|
14
|
+
columns: {
|
|
15
|
+
id: 'columns',
|
|
16
|
+
defaultMessage: 'columns',
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const templates = (type) => (intl) => [
|
|
21
|
+
{
|
|
22
|
+
image: gridTemplate1,
|
|
23
|
+
id: 'gridtemplateone',
|
|
24
|
+
title: `1 ${intl.formatMessage(messages.column)}`,
|
|
25
|
+
blocksData: blocksFormGenerator(1, type),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
image: gridTemplate2,
|
|
29
|
+
id: 'gridtemplatetwo',
|
|
30
|
+
title: `2 ${intl.formatMessage(messages.columns)}`,
|
|
31
|
+
blocksData: blocksFormGenerator(2, type),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
image: gridTemplate3,
|
|
35
|
+
id: 'gridtemplatethree',
|
|
36
|
+
title: `3 ${intl.formatMessage(messages.columns)}`,
|
|
37
|
+
blocksData: blocksFormGenerator(3, type),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
image: gridTemplate4,
|
|
41
|
+
id: 'gridtemplatefour',
|
|
42
|
+
title: `4 ${intl.formatMessage(messages.columns)}`,
|
|
43
|
+
blocksData: blocksFormGenerator(4, type),
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
export default templates;
|
|
@@ -8,7 +8,7 @@ import { ImageSchema } from './schema';
|
|
|
8
8
|
import imageSVG from '@plone/volto/icons/image.svg';
|
|
9
9
|
|
|
10
10
|
const ImageSidebar = (props) => {
|
|
11
|
-
const { data, block, onChangeBlock } = props;
|
|
11
|
+
const { blocksConfig, data, block, onChangeBlock } = props;
|
|
12
12
|
const intl = useIntl();
|
|
13
13
|
const schema = ImageSchema({ formData: data, intl });
|
|
14
14
|
return (
|
|
@@ -55,6 +55,7 @@ const ImageSidebar = (props) => {
|
|
|
55
55
|
onChangeBlock={onChangeBlock}
|
|
56
56
|
formData={data}
|
|
57
57
|
block={block}
|
|
58
|
+
blocksConfig={blocksConfig}
|
|
58
59
|
/>
|
|
59
60
|
</>
|
|
60
61
|
);
|
|
@@ -101,3 +101,14 @@ export function ImageSchema({ formData, intl }) {
|
|
|
101
101
|
required: [],
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
export const gridImageDisableSizeAndPositionHandlersSchema = ({
|
|
106
|
+
schema,
|
|
107
|
+
formData,
|
|
108
|
+
intl,
|
|
109
|
+
}) => {
|
|
110
|
+
schema.fieldsets[0].fields = schema.fieldsets[0].fields.filter(
|
|
111
|
+
(item) => !['align', 'size'].includes(item),
|
|
112
|
+
);
|
|
113
|
+
return schema;
|
|
114
|
+
};
|
|
@@ -101,3 +101,8 @@ export const TeaserSchema = ({ intl }) => {
|
|
|
101
101
|
|
|
102
102
|
return schema;
|
|
103
103
|
};
|
|
104
|
+
|
|
105
|
+
export const gridTeaserDisableStylingSchema = ({ schema, formData, intl }) => {
|
|
106
|
+
schema.fieldsets = schema.fieldsets.filter((item) => item.id !== 'styling');
|
|
107
|
+
return schema;
|
|
108
|
+
};
|
|
@@ -53,6 +53,7 @@ const DragDropList = (props) => {
|
|
|
53
53
|
const {
|
|
54
54
|
childList,
|
|
55
55
|
children,
|
|
56
|
+
direction = 'vertical',
|
|
56
57
|
onMoveItem,
|
|
57
58
|
as = 'div',
|
|
58
59
|
style,
|
|
@@ -128,6 +129,7 @@ const DragDropList = (props) => {
|
|
|
128
129
|
>
|
|
129
130
|
<Droppable
|
|
130
131
|
droppableId={uid}
|
|
132
|
+
direction={direction}
|
|
131
133
|
renderClone={(provided, snapshot, rubric) => {
|
|
132
134
|
const index = rubric.source.index;
|
|
133
135
|
return children({
|
|
@@ -160,19 +162,22 @@ const DragDropList = (props) => {
|
|
|
160
162
|
</Draggable>
|
|
161
163
|
))}
|
|
162
164
|
{provided.placeholder}
|
|
163
|
-
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
165
|
+
{/* TODO: Fix the ghost problem if horizontal dnd is present */}
|
|
166
|
+
{direction !== 'horizontal' &&
|
|
167
|
+
!isEmpty(placeholderProps) &&
|
|
168
|
+
snapshot.isDraggingOver && (
|
|
169
|
+
<div
|
|
170
|
+
style={{
|
|
171
|
+
position: 'absolute',
|
|
172
|
+
top: placeholderProps.clientY,
|
|
173
|
+
left: placeholderProps.clientX,
|
|
174
|
+
height: placeholderProps.clientHeight,
|
|
175
|
+
background: '#eee',
|
|
176
|
+
width: placeholderProps.clientWidth,
|
|
177
|
+
borderRadius: '3px',
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
)}
|
|
176
181
|
</AsDomComponent>
|
|
177
182
|
)}
|
|
178
183
|
</Droppable>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import { Button, Grid, Message } from 'semantic-ui-react';
|
|
4
|
+
|
|
5
|
+
const TemplateChooser = ({ templates, onSelectTemplate }) => {
|
|
6
|
+
const intl = useIntl();
|
|
7
|
+
return (
|
|
8
|
+
<div className="template-chooser">
|
|
9
|
+
<Message>
|
|
10
|
+
<Grid columns={templates(intl).length}>
|
|
11
|
+
{templates(intl).map((template, index) => (
|
|
12
|
+
<Grid.Column key={template.id}>
|
|
13
|
+
<Button
|
|
14
|
+
className="template-chooser-item"
|
|
15
|
+
onClick={() => onSelectTemplate(index)}
|
|
16
|
+
>
|
|
17
|
+
<img src={template.image} alt="" />
|
|
18
|
+
<div className="template-chooser-title">
|
|
19
|
+
{intl.formatMessage({
|
|
20
|
+
id: template.id,
|
|
21
|
+
defaultMessage: template.title,
|
|
22
|
+
})}
|
|
23
|
+
</div>
|
|
24
|
+
</Button>
|
|
25
|
+
</Grid.Column>
|
|
26
|
+
))}
|
|
27
|
+
</Grid>
|
|
28
|
+
</Message>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
TemplateChooser.propTypes = {
|
|
34
|
+
templates: PropTypes.func.isRequired,
|
|
35
|
+
onSelectTemplate: PropTypes.func.isRequired,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default TemplateChooser;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import { Provider } from 'react-intl-redux';
|
|
5
|
+
import TemplateChooser from './TemplateChooser';
|
|
6
|
+
import templateSVG from './template.svg';
|
|
7
|
+
|
|
8
|
+
const mockStore = configureStore();
|
|
9
|
+
|
|
10
|
+
test('renders a TemplateChooser component', () => {
|
|
11
|
+
const store = mockStore({
|
|
12
|
+
intl: {
|
|
13
|
+
locale: 'en',
|
|
14
|
+
messages: { templateid: 'Template default translation' },
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const component = renderer.create(
|
|
19
|
+
<Provider store={store}>
|
|
20
|
+
<TemplateChooser
|
|
21
|
+
templates={() => [
|
|
22
|
+
{
|
|
23
|
+
image: templateSVG,
|
|
24
|
+
id: 'templateid',
|
|
25
|
+
defaultMessage: 'Template default translation',
|
|
26
|
+
},
|
|
27
|
+
]}
|
|
28
|
+
onSelectTemplate={() => {}}
|
|
29
|
+
/>
|
|
30
|
+
</Provider>,
|
|
31
|
+
);
|
|
32
|
+
const json = component.toJSON();
|
|
33
|
+
expect(json).toMatchSnapshot();
|
|
34
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<rect width="96" height="96" fill="#9FD1E5" rx="3"/>
|
|
4
|
+
<g fill="#FFF" opacity=".9" transform="translate(9 21)">
|
|
5
|
+
<rect width="78" height="29"/>
|
|
6
|
+
<rect width="36" height="8" y="33"/>
|
|
7
|
+
<rect width="77" height="10" y="45"/>
|
|
8
|
+
</g>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import registry from '@plone/volto/registry';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* A component that can
|
|
4
|
+
* A component that can automatically look up its implementation from the
|
|
5
5
|
* registry based on the provided component `componentName`
|
|
6
6
|
*/
|
|
7
7
|
const Component = ({ componentName, dependencies, ...rest }) => {
|