@plone/volto 17.0.0-alpha.15 → 17.0.0-alpha.17
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/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +30 -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/news/4351.bugfix +1 -0
- package/news/4725.bugfix +1 -0
- package/news/4932.bugfix +1 -0
- package/news/4941.documentation +1 -0
- package/news/4951.breaking +1 -0
- package/news/4962.feature +1 -0
- package/news/4964.bugfix +1 -0
- 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 +43 -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/Listing/withQuerystringResults.jsx +3 -4
- package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
- package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +20 -15
- 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/manage/UniversalLink/UniversalLink.jsx +2 -6
- package/src/components/manage/UniversalLink/UniversalLink.test.jsx +36 -0
- package/src/components/theme/Anontools/Anontools.jsx +3 -4
- package/src/components/theme/Component/Component.jsx +1 -1
- package/src/components/theme/Header/Header.jsx +2 -2
- package/src/components/theme/View/AlbumView.jsx +9 -1
- package/src/components/theme/View/EventView.jsx +6 -2
- package/src/components/theme/View/FileView.jsx +23 -18
- package/src/components/theme/View/ImageView.jsx +37 -32
- package/src/components/theme/View/LinkView.jsx +4 -1
- package/src/components/theme/View/ListingView.jsx +33 -27
- package/src/components/theme/View/RenderBlocks.jsx +56 -27
- package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
- package/src/components/theme/View/SummaryView.jsx +47 -38
- package/src/components/theme/View/TabularView.jsx +59 -53
- 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/hooks/index.js +0 -1
- 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
- package/src/hooks/content/useContent.js +0 -31
- package/src/hooks/userSession/useToken.js +0 -5
|
@@ -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,43 @@
|
|
|
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
|
+
isContainer
|
|
37
|
+
/>
|
|
38
|
+
</Grid>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
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
|
+
};
|
|
@@ -54,10 +54,9 @@ export default function withQuerystringResults(WrappedComponent) {
|
|
|
54
54
|
const hasQuery = querystring?.query?.length > 0;
|
|
55
55
|
const hasLoaded = hasQuery ? querystringResults?.[id]?.loaded : true;
|
|
56
56
|
|
|
57
|
-
const listingItems =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
: folderItems;
|
|
57
|
+
const listingItems = hasQuery
|
|
58
|
+
? querystringResults?.[id]?.items || []
|
|
59
|
+
: folderItems;
|
|
61
60
|
|
|
62
61
|
const showAsFolderListing = !hasQuery && content?.items_total > b_size;
|
|
63
62
|
const showAsQueryListing =
|
|
@@ -18,20 +18,31 @@ const messages = defineMessages({
|
|
|
18
18
|
},
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
const DefaultImage = (props) => <img {...props} alt={props.alt || ''} />;
|
|
22
|
-
|
|
23
21
|
const TeaserDefaultTemplate = (props) => {
|
|
24
22
|
const { className, data, isEditMode } = props;
|
|
25
23
|
const intl = useIntl();
|
|
26
24
|
const href = data.href?.[0];
|
|
27
|
-
const
|
|
25
|
+
const imageOverride = data.preview_image?.[0];
|
|
28
26
|
const align = data?.styles?.align;
|
|
29
27
|
|
|
30
|
-
const hasImageComponent = config.getComponent('Image').component;
|
|
31
|
-
const Image = config.getComponent('Image').component || DefaultImage;
|
|
32
28
|
const { openExternalLinkInNewTab } = config.settings;
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
|
|
30
|
+
let renderedImage = null;
|
|
31
|
+
if (href && (imageOverride || href.hasPreviewImage || href.image_field)) {
|
|
32
|
+
let Image = config.getComponent('Image').component;
|
|
33
|
+
if (Image) {
|
|
34
|
+
// custom image component expects item summary as src
|
|
35
|
+
renderedImage = (
|
|
36
|
+
<Image src={imageOverride || href} alt="" loading="lazy" />
|
|
37
|
+
);
|
|
38
|
+
} else {
|
|
39
|
+
// default img expects string src
|
|
40
|
+
const src = flattenToAppURL(
|
|
41
|
+
getTeaserImageURL({ href, imageOverride, align }),
|
|
42
|
+
);
|
|
43
|
+
renderedImage = <img src={src} alt="" loading="lazy" />;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
35
46
|
|
|
36
47
|
return (
|
|
37
48
|
<div className={cx('block teaser', className)}>
|
|
@@ -57,14 +68,8 @@ const TeaserDefaultTemplate = (props) => {
|
|
|
57
68
|
}
|
|
58
69
|
>
|
|
59
70
|
<div className="teaser-item default">
|
|
60
|
-
{
|
|
61
|
-
<div className="image-wrapper">
|
|
62
|
-
<Image
|
|
63
|
-
src={hasImageComponent ? href : defaultImageSrc}
|
|
64
|
-
alt=""
|
|
65
|
-
loading="lazy"
|
|
66
|
-
/>
|
|
67
|
-
</div>
|
|
71
|
+
{renderedImage && (
|
|
72
|
+
<div className="image-wrapper">{renderedImage}</div>
|
|
68
73
|
)}
|
|
69
74
|
<div className="content">
|
|
70
75
|
{data?.head_title && (
|
|
@@ -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>
|