@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.
Files changed (94) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +30 -0
  3. package/cypress/support/commands.js +18 -0
  4. package/locales/ca/LC_MESSAGES/volto.po +41 -0
  5. package/locales/ca.json +1 -1
  6. package/locales/de/LC_MESSAGES/volto.po +41 -0
  7. package/locales/de.json +1 -1
  8. package/locales/en/LC_MESSAGES/volto.po +41 -0
  9. package/locales/en.json +1 -1
  10. package/locales/es/LC_MESSAGES/volto.po +41 -0
  11. package/locales/es.json +1 -1
  12. package/locales/eu/LC_MESSAGES/volto.po +41 -0
  13. package/locales/eu.json +1 -1
  14. package/locales/fi/LC_MESSAGES/volto.po +41 -0
  15. package/locales/fi.json +1 -1
  16. package/locales/fr/LC_MESSAGES/volto.po +41 -0
  17. package/locales/fr.json +1 -1
  18. package/locales/it/LC_MESSAGES/volto.po +41 -0
  19. package/locales/it.json +1 -1
  20. package/locales/ja/LC_MESSAGES/volto.po +41 -0
  21. package/locales/ja.json +1 -1
  22. package/locales/nl/LC_MESSAGES/volto.po +41 -0
  23. package/locales/nl.json +1 -1
  24. package/locales/pt/LC_MESSAGES/volto.po +41 -0
  25. package/locales/pt.json +1 -1
  26. package/locales/pt_BR/LC_MESSAGES/volto.po +41 -0
  27. package/locales/pt_BR.json +1 -1
  28. package/locales/ro/LC_MESSAGES/volto.po +41 -0
  29. package/locales/ro.json +1 -1
  30. package/locales/volto.pot +42 -1
  31. package/locales/zh_CN/LC_MESSAGES/volto.po +41 -0
  32. package/locales/zh_CN.json +1 -1
  33. package/news/4351.bugfix +1 -0
  34. package/news/4725.bugfix +1 -0
  35. package/news/4932.bugfix +1 -0
  36. package/news/4941.documentation +1 -0
  37. package/news/4951.breaking +1 -0
  38. package/news/4962.feature +1 -0
  39. package/news/4964.bugfix +1 -0
  40. package/package.json +1 -1
  41. package/packages/volto-slate/package.json +1 -1
  42. package/packages/volto-slate/src/blocks/Text/index.js +8 -0
  43. package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
  44. package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
  45. package/src/components/manage/Blocks/Container/Data.jsx +32 -0
  46. package/src/components/manage/Blocks/Container/Edit.jsx +174 -0
  47. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +120 -0
  48. package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
  49. package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
  50. package/src/components/manage/Blocks/Grid/Edit.jsx +33 -0
  51. package/src/components/manage/Blocks/Grid/View.jsx +43 -0
  52. package/src/components/manage/Blocks/Grid/adapter.js +14 -0
  53. package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
  54. package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
  55. package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
  56. package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
  57. package/src/components/manage/Blocks/Grid/schema.js +35 -0
  58. package/src/components/manage/Blocks/Grid/templates.js +47 -0
  59. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +2 -1
  60. package/src/components/manage/Blocks/Image/schema.js +11 -0
  61. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +3 -4
  62. package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
  63. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +20 -15
  64. package/src/components/manage/Blocks/Teaser/schema.js +5 -0
  65. package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
  66. package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
  67. package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
  68. package/src/components/manage/TemplateChooser/template.svg +10 -0
  69. package/src/components/manage/UniversalLink/UniversalLink.jsx +2 -6
  70. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +36 -0
  71. package/src/components/theme/Anontools/Anontools.jsx +3 -4
  72. package/src/components/theme/Component/Component.jsx +1 -1
  73. package/src/components/theme/Header/Header.jsx +2 -2
  74. package/src/components/theme/View/AlbumView.jsx +9 -1
  75. package/src/components/theme/View/EventView.jsx +6 -2
  76. package/src/components/theme/View/FileView.jsx +23 -18
  77. package/src/components/theme/View/ImageView.jsx +37 -32
  78. package/src/components/theme/View/LinkView.jsx +4 -1
  79. package/src/components/theme/View/ListingView.jsx +33 -27
  80. package/src/components/theme/View/RenderBlocks.jsx +56 -27
  81. package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
  82. package/src/components/theme/View/SummaryView.jsx +47 -38
  83. package/src/components/theme/View/TabularView.jsx +59 -53
  84. package/src/config/Blocks.jsx +44 -0
  85. package/src/helpers/Blocks/Blocks.js +26 -0
  86. package/src/helpers/Blocks/Blocks.test.js +21 -0
  87. package/src/helpers/Utils/Utils.js +25 -0
  88. package/src/helpers/index.js +2 -0
  89. package/src/hooks/index.js +0 -1
  90. package/src/icons/grid-block.svg +11 -0
  91. package/theme/themes/pastanaga/extras/grid.less +426 -0
  92. package/theme/themes/pastanaga/extras/main.less +1 -0
  93. package/src/hooks/content/useContent.js +0 -31
  94. 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,14 @@
1
+ export const GridBlockDataAdapter = ({
2
+ block,
3
+ data,
4
+ id,
5
+ onChangeBlock,
6
+ value,
7
+ }) => {
8
+ let dataSaved = {
9
+ ...data,
10
+ [id]: value,
11
+ };
12
+
13
+ onChangeBlock(block, dataSaved);
14
+ };
@@ -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
- querystring?.query?.length > 0 && querystringResults?.[id]
59
- ? querystringResults?.[id]?.items || []
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 =
@@ -24,7 +24,6 @@ const TeaserBody = (props) => {
24
24
  TeaserBody.propTypes = {
25
25
  data: PropTypes.objectOf(PropTypes.any).isRequired,
26
26
  isEditMode: PropTypes.bool,
27
- variation: PropTypes.string,
28
27
  };
29
28
 
30
29
  export default TeaserBody;
@@ -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 image = data.preview_image?.[0];
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
- const defaultImageSrc =
34
- href && flattenToAppURL(getTeaserImageURL({ href, image, align }));
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
- {(href.hasPreviewImage || href.image_field || image) && (
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
- {!isEmpty(placeholderProps) && snapshot.isDraggingOver && (
164
- <div
165
- style={{
166
- position: 'absolute',
167
- top: placeholderProps.clientY,
168
- left: placeholderProps.clientX,
169
- height: placeholderProps.clientHeight,
170
- background: '#eee',
171
- width: placeholderProps.clientWidth,
172
- borderRadius: '3px',
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>