@kitconcept/volto-light-theme 8.0.0-alpha.27 → 8.0.0-alpha.29

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 (118) hide show
  1. package/.changelog.draft +26 -1
  2. package/CHANGELOG.md +34 -0
  3. package/locales/af/LC_MESSAGES/volto.po +645 -0
  4. package/locales/ar/LC_MESSAGES/volto.po +645 -0
  5. package/locales/bg/LC_MESSAGES/volto.po +645 -0
  6. package/locales/bn/LC_MESSAGES/volto.po +645 -0
  7. package/locales/ca/LC_MESSAGES/volto.po +645 -0
  8. package/locales/cs/LC_MESSAGES/volto.po +645 -0
  9. package/locales/cy/LC_MESSAGES/volto.po +645 -0
  10. package/locales/da/LC_MESSAGES/volto.po +645 -0
  11. package/locales/de/LC_MESSAGES/volto.po +58 -57
  12. package/locales/el/LC_MESSAGES/volto.po +645 -0
  13. package/locales/en_AU/LC_MESSAGES/volto.po +645 -0
  14. package/locales/en_GB/LC_MESSAGES/volto.po +645 -0
  15. package/locales/eo/LC_MESSAGES/volto.po +645 -0
  16. package/locales/es/LC_MESSAGES/volto.po +50 -50
  17. package/locales/et/LC_MESSAGES/volto.po +645 -0
  18. package/locales/eu/LC_MESSAGES/volto.po +7 -7
  19. package/locales/fa/LC_MESSAGES/volto.po +645 -0
  20. package/locales/fi/LC_MESSAGES/volto.po +645 -0
  21. package/locales/fr/LC_MESSAGES/volto.po +645 -0
  22. package/locales/fu/LC_MESSAGES/volto.po +645 -0
  23. package/locales/ga/LC_MESSAGES/volto.po +645 -0
  24. package/locales/gl/LC_MESSAGES/volto.po +645 -0
  25. package/locales/he/LC_MESSAGES/volto.po +645 -0
  26. package/locales/hi/LC_MESSAGES/volto.po +645 -0
  27. package/locales/hr/LC_MESSAGES/volto.po +645 -0
  28. package/locales/hu/LC_MESSAGES/volto.po +645 -0
  29. package/locales/hy/LC_MESSAGES/volto.po +645 -0
  30. package/locales/id/LC_MESSAGES/volto.po +645 -0
  31. package/locales/it/LC_MESSAGES/volto.po +645 -0
  32. package/locales/ja/LC_MESSAGES/volto.po +645 -0
  33. package/locales/ka/LC_MESSAGES/volto.po +645 -0
  34. package/locales/kn/LC_MESSAGES/volto.po +645 -0
  35. package/locales/ko/LC_MESSAGES/volto.po +645 -0
  36. package/locales/lt/LC_MESSAGES/volto.po +645 -0
  37. package/locales/lv/LC_MESSAGES/volto.po +645 -0
  38. package/locales/mi/LC_MESSAGES/volto.po +645 -0
  39. package/locales/mk_MK/LC_MESSAGES/volto.po +645 -0
  40. package/locales/ms/LC_MESSAGES/volto.po +645 -0
  41. package/locales/mt/LC_MESSAGES/volto.po +645 -0
  42. package/locales/my/LC_MESSAGES/volto.po +645 -0
  43. package/locales/nl/LC_MESSAGES/volto.po +645 -0
  44. package/locales/nl_BE/LC_MESSAGES/volto.po +645 -0
  45. package/locales/nn/LC_MESSAGES/volto.po +645 -0
  46. package/locales/no/LC_MESSAGES/volto.po +645 -0
  47. package/locales/pl/LC_MESSAGES/volto.po +645 -0
  48. package/locales/pt/LC_MESSAGES/volto.po +645 -0
  49. package/locales/rm/LC_MESSAGES/volto.po +645 -0
  50. package/locales/ro/LC_MESSAGES/volto.po +645 -0
  51. package/locales/ru/LC_MESSAGES/volto.po +645 -0
  52. package/locales/sk/LC_MESSAGES/volto.po +645 -0
  53. package/locales/sl/LC_MESSAGES/volto.po +645 -0
  54. package/locales/sm/LC_MESSAGES/volto.po +645 -0
  55. package/locales/sq/LC_MESSAGES/volto.po +645 -0
  56. package/locales/sr/LC_MESSAGES/volto.po +645 -0
  57. package/locales/sr_Cyrl/LC_MESSAGES/volto.po +645 -0
  58. package/locales/sr_Latn/LC_MESSAGES/volto.po +645 -0
  59. package/locales/sv/LC_MESSAGES/volto.po +645 -0
  60. package/locales/sw/LC_MESSAGES/volto.po +645 -0
  61. package/locales/ta/LC_MESSAGES/volto.po +645 -0
  62. package/locales/te/LC_MESSAGES/volto.po +645 -0
  63. package/locales/th/LC_MESSAGES/volto.po +645 -0
  64. package/locales/tl/LC_MESSAGES/volto.po +645 -0
  65. package/locales/to/LC_MESSAGES/volto.po +645 -0
  66. package/locales/tr/LC_MESSAGES/volto.po +645 -0
  67. package/locales/uk/LC_MESSAGES/volto.po +645 -0
  68. package/locales/vi/LC_MESSAGES/volto.po +645 -0
  69. package/locales/zh_CN/LC_MESSAGES/volto.po +645 -0
  70. package/locales/zh_HK/LC_MESSAGES/volto.po +645 -0
  71. package/locales/zh_TW/LC_MESSAGES/volto.po +645 -0
  72. package/package.json +5 -5
  73. package/src/components/Blocks/Button/schema.js +12 -0
  74. package/src/components/Blocks/Image/Edit.jsx +8 -32
  75. package/src/components/Blocks/Image/View.jsx +9 -26
  76. package/src/components/Blocks/Image/adapter.js +28 -14
  77. package/src/components/Blocks/Image/adapter.test.js +156 -0
  78. package/src/components/Blocks/Image/schema.js +21 -7
  79. package/src/components/Blocks/Listing/GridTemplate.jsx +1 -0
  80. package/src/components/Blocks/Listing/SummaryTemplate.jsx +1 -0
  81. package/src/components/Blocks/Maps/MapsSidebar.jsx +68 -0
  82. package/src/components/Blocks/Maps/View.jsx +37 -0
  83. package/src/components/Blocks/Maps/adapter.js +27 -0
  84. package/src/components/Blocks/Maps/adapter.test.js +63 -0
  85. package/src/components/Blocks/Maps/schema.js +42 -2
  86. package/src/components/Blocks/Separator/schema.js +12 -0
  87. package/src/components/Blocks/Teaser/DefaultBody.tsx +10 -1
  88. package/src/components/Blocks/Video/VideoSidebar.jsx +68 -0
  89. package/src/components/Blocks/Video/View.jsx +38 -0
  90. package/src/components/Blocks/Video/adapter.js +28 -0
  91. package/src/components/Blocks/Video/adapter.test.js +63 -0
  92. package/src/components/Blocks/Video/schema.js +42 -2
  93. package/src/components/Footer/Footer.tsx +2 -2
  94. package/src/components/Footer/slots/FollowUsLogoAndLinks.tsx +12 -23
  95. package/src/components/Theme/ImageView.jsx +8 -1
  96. package/src/components/Widgets/ObjectList.tsx +37 -27
  97. package/src/config/blocks.tsx +63 -0
  98. package/src/config/classExtenders.ts +11 -10
  99. package/src/config/settings.ts +6 -0
  100. package/src/customizations/volto/components/manage/Blocks/Maps/MapsSidebar.jsx +10 -0
  101. package/src/customizations/volto/components/manage/Blocks/Maps/View.jsx +10 -0
  102. package/src/customizations/volto/components/manage/Blocks/Video/VideoSidebar.jsx +10 -0
  103. package/src/customizations/volto/components/manage/Blocks/Video/View.jsx +10 -0
  104. package/src/index.ts +8 -0
  105. package/src/primitives/Card/Card.tsx +4 -1
  106. package/src/theme/_bgcolor-blocks-layout.scss +1 -1
  107. package/src/theme/_footer.scss +61 -51
  108. package/src/theme/_layout.scss +7 -62
  109. package/src/theme/_typo-custom.scss +1 -1
  110. package/src/theme/_variables.scss +21 -0
  111. package/src/theme/blocks/_grid.scss +1 -0
  112. package/src/theme/blocks/_highlight.scss +10 -7
  113. package/src/theme/blocks/_image.scss +96 -186
  114. package/src/theme/blocks/_listing.scss +5 -1
  115. package/src/theme/blocks/_maps.scss +60 -34
  116. package/src/transforms/to6.ts +5 -49
  117. package/src/transforms/to8.test.js +201 -0
  118. package/src/transforms/to8.ts +109 -0
@@ -1,3 +1,4 @@
1
+ import { useContext } from 'react';
1
2
  import { useSelector } from 'react-redux';
2
3
  import type { GetSiteResponse } from '@plone/types';
3
4
  import { isInternalURL } from '@plone/volto/helpers/Url/Url';
@@ -5,6 +6,7 @@ import DefaultSummary from '@kitconcept/volto-light-theme/components/Summary/Def
5
6
  import type { SummaryComponentType } from '@kitconcept/volto-light-theme/components/Summary/DefaultSummary';
6
7
  import Card from '../../../primitives/Card/Card';
7
8
  import config from '@plone/volto/registry';
9
+ import { GridContext } from '@plone/volto/components/manage/Blocks/Grid/context';
8
10
 
9
11
  type FormState = {
10
12
  site: { data: GetSiteResponse };
@@ -15,7 +17,13 @@ const TeaserDefaultTemplate = (props) => {
15
17
  (state) => state.site?.data,
16
18
  );
17
19
  const hideProfileLinks = site?.['kitconcept.disable_profile_links'];
18
- const { data, isEditMode } = props;
20
+ const { data, isEditMode, isContainer } = props;
21
+ const columns = useContext(GridContext);
22
+ const sizes = config.blocks.blocksConfig.teaser?.getSizes?.({
23
+ data,
24
+ inGrid: isContainer,
25
+ columns,
26
+ });
19
27
  const href = data.href?.[0] || {};
20
28
  const image = data.preview_image?.[0];
21
29
  const url = data.preview_image?.[0]?.['@id'];
@@ -50,6 +58,7 @@ const TeaserDefaultTemplate = (props) => {
50
58
  item={!data.overwrite ? href : { ...href, ...localOverrides }}
51
59
  image={data.overwrite ? image : undefined}
52
60
  imageComponent={Image}
61
+ sizes={sizes}
53
62
  />
54
63
  <Card.Summary>
55
64
  <Summary
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import { VideoBlockSchema } from '@plone/volto/components/manage/Blocks/Video/schema';
3
+ import { Segment } from 'semantic-ui-react';
4
+ import { defineMessages, useIntl } from 'react-intl';
5
+ import Icon from '@plone/volto/components/theme/Icon/Icon';
6
+ import { BlockDataForm } from '@plone/volto/components/manage/Form';
7
+ import videoSVG from '@plone/volto/icons/videocamera.svg';
8
+
9
+ const messages = defineMessages({
10
+ Video: {
11
+ id: 'Video',
12
+ defaultMessage: 'Video',
13
+ },
14
+ NoVideo: {
15
+ id: 'No Video selected',
16
+ defaultMessage: 'No Video selected',
17
+ },
18
+ });
19
+
20
+ const VideoSidebar = (props) => {
21
+ const {
22
+ blocksConfig,
23
+ blocksErrors,
24
+ data,
25
+ block,
26
+ onChangeBlock,
27
+ navRoot,
28
+ contentType,
29
+ } = props;
30
+ const intl = useIntl();
31
+ const schema = VideoBlockSchema({ ...props, intl });
32
+ const dataAdapter = blocksConfig[data['@type']].dataAdapter;
33
+
34
+ return (
35
+ <>
36
+ {!data.url ? (
37
+ <Segment className="sidebar-metadata-container" secondary>
38
+ {intl.formatMessage(messages.NoVideo)}
39
+ <Icon name={videoSVG} size="100px" color="#b8c6c8" />
40
+ </Segment>
41
+ ) : (
42
+ <BlockDataForm
43
+ schema={schema}
44
+ title={intl.formatMessage(messages.Video)}
45
+ // START CUSTOMIZATION
46
+ onChangeField={(id, value) => {
47
+ dataAdapter({
48
+ block,
49
+ data,
50
+ id,
51
+ onChangeBlock,
52
+ value,
53
+ });
54
+ }}
55
+ // END CUSTOMIZATION
56
+ onChangeBlock={onChangeBlock}
57
+ formData={data}
58
+ block={block}
59
+ navRoot={navRoot}
60
+ contentType={contentType}
61
+ errors={blocksErrors}
62
+ />
63
+ )}
64
+ </>
65
+ );
66
+ };
67
+
68
+ export default VideoSidebar;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * View video block.
3
+ * @module components/manage/Blocks/Video/View
4
+ */
5
+
6
+ import React from 'react';
7
+ import PropTypes from 'prop-types';
8
+ import Body from '@plone/volto/components/manage/Blocks/Video/Body';
9
+ import { withBlockExtensions } from '@plone/volto/helpers/Extensions';
10
+ import cx from 'classnames';
11
+
12
+ /**
13
+ * View video block class.
14
+ * @class View
15
+ * @extends Component
16
+ */
17
+ const View = (props) => {
18
+ const { data, className, style } = props;
19
+
20
+ return (
21
+ // START CUSTOMIZATION
22
+ <div className={cx('block video', className)} style={style}>
23
+ {/* // END CUSTOMIZATION */}
24
+ <Body data={data} />
25
+ </div>
26
+ );
27
+ };
28
+
29
+ /**
30
+ * Property types.
31
+ * @property {Object} propTypes Property types.
32
+ * @static
33
+ */
34
+ View.propTypes = {
35
+ data: PropTypes.objectOf(PropTypes.any).isRequired,
36
+ };
37
+
38
+ export default withBlockExtensions(View);
@@ -0,0 +1,28 @@
1
+ export const VideoBlockDataAdapter = ({
2
+ block,
3
+ data,
4
+ id,
5
+ item,
6
+ onChangeBlock,
7
+ value,
8
+ }) => {
9
+ let dataSaved = {
10
+ ...data,
11
+ [id]: value,
12
+ };
13
+
14
+ const align = dataSaved.styles?.['align:noprefix'];
15
+ const isFloating = align === 'left' || align === 'right';
16
+
17
+ if (isFloating) {
18
+ dataSaved = {
19
+ ...dataSaved,
20
+ styles: {
21
+ ...dataSaved.styles,
22
+ 'blockWidth:noprefix': 'default',
23
+ },
24
+ };
25
+ }
26
+
27
+ onChangeBlock(block, dataSaved);
28
+ };
@@ -0,0 +1,63 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { VideoBlockDataAdapter } from './adapter';
3
+
4
+ const alignLeft = { 'align:noprefix': 'left' };
5
+ const alignCenter = { 'align:noprefix': 'center' };
6
+
7
+ describe('VideoBlockDataAdapter', () => {
8
+ it('applies the default width for a floating video', () => {
9
+ const onChangeBlock = vi.fn();
10
+
11
+ VideoBlockDataAdapter({
12
+ block: 'block-1',
13
+ data: { '@type': 'video', styles: { ...alignLeft } },
14
+ id: 'url',
15
+ onChangeBlock,
16
+ value: 'https://example.com/video',
17
+ });
18
+
19
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
20
+ '@type': 'video',
21
+ url: 'https://example.com/video',
22
+ styles: {
23
+ ...alignLeft,
24
+ 'blockWidth:noprefix': 'default',
25
+ },
26
+ });
27
+ });
28
+
29
+ it('leaves the styles untouched for a non-floating video', () => {
30
+ const onChangeBlock = vi.fn();
31
+
32
+ VideoBlockDataAdapter({
33
+ block: 'block-1',
34
+ data: { '@type': 'video', styles: { ...alignCenter } },
35
+ id: 'url',
36
+ onChangeBlock,
37
+ value: 'https://example.com/video',
38
+ });
39
+
40
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
41
+ '@type': 'video',
42
+ url: 'https://example.com/video',
43
+ styles: { ...alignCenter },
44
+ });
45
+ });
46
+
47
+ it('leaves the styles untouched when no alignment is set', () => {
48
+ const onChangeBlock = vi.fn();
49
+
50
+ VideoBlockDataAdapter({
51
+ block: 'block-1',
52
+ data: { '@type': 'video' },
53
+ id: 'url',
54
+ onChangeBlock,
55
+ value: 'https://example.com/video',
56
+ });
57
+
58
+ expect(onChangeBlock).toHaveBeenCalledWith('block-1', {
59
+ '@type': 'video',
60
+ url: 'https://example.com/video',
61
+ });
62
+ });
63
+ });
@@ -1,5 +1,45 @@
1
+ import config from '@plone/volto/registry';
2
+ import { defineMessages } from 'react-intl';
3
+ import { addStyling } from '@plone/volto/helpers/Extensions/withBlockSchemaEnhancer';
4
+
5
+ const messages = defineMessages({
6
+ BlockWidth: {
7
+ id: 'Block Width',
8
+ defaultMessage: 'Block Width',
9
+ },
10
+ Alignment: {
11
+ id: 'Alignment',
12
+ defaultMessage: 'Alignment',
13
+ },
14
+ });
1
15
  export const videoBlockSchemaEnhancer = ({ formData, schema, intl }) => {
2
- schema.properties.align.default = 'wide';
3
- schema.properties.align.actions = ['left', 'right', 'center', 'wide', 'full'];
16
+ addStyling({ schema, intl });
17
+
18
+ schema.fieldsets[0].fields = schema.fieldsets[0].fields.filter(
19
+ (f) => f !== 'align',
20
+ );
21
+
22
+ const align = formData.styles?.['align:noprefix'];
23
+ const isFloating = align === 'left' || align === 'right';
24
+
25
+ schema.properties.styles.schema.fieldsets[0].fields = [
26
+ 'align:noprefix',
27
+ 'blockWidth:noprefix',
28
+ ...schema.properties.styles.schema.fieldsets[0].fields,
29
+ ];
30
+ schema.properties.styles.schema.properties['align:noprefix'] = {
31
+ widget: 'blockAlignment',
32
+ title: intl.formatMessage(messages.Alignment),
33
+ default: 'center',
34
+ actions: config.blocks.alignments.map((alignment) => alignment.name),
35
+ };
36
+ schema.properties.styles.schema.properties['blockWidth:noprefix'] = {
37
+ widget: 'blockWidth',
38
+ title: intl.formatMessage(messages.BlockWidth),
39
+ default: 'default',
40
+ actions: config.blocks.widths.map((width) => width.name),
41
+ };
42
+ schema.properties.styles.schema.properties['blockWidth:noprefix'].disabled =
43
+ isFloating;
4
44
  return schema;
5
45
  };
@@ -20,9 +20,9 @@ const Footer = () => {
20
20
 
21
21
  return (
22
22
  <footer id="footer">
23
- <div className="pre-footer">
23
+ <Container className="pre-footer">
24
24
  <SlotRenderer name="preFooter" content={content} />
25
- </div>
25
+ </Container>
26
26
  <SlotRenderer name="footer" content={content} />
27
27
  <Container className="post-footer">
28
28
  <SlotRenderer name="postFooter" content={content} />
@@ -1,5 +1,4 @@
1
1
  import { FormattedMessage } from 'react-intl';
2
- import cx from 'classnames';
3
2
  import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
4
3
  import ConditionalLink from '@plone/volto/components/manage/ConditionalLink/ConditionalLink';
5
4
  import { Container } from '@plone/components';
@@ -34,27 +33,12 @@ const FollowUsPostFooterLogoAndLinks = ({ content }: { content: Content }) => {
34
33
  : flattenToAppURL(post_footer_logo?.download);
35
34
 
36
35
  return content ? (
37
- <Container
38
- className={cx('default follow-us-links-and-logo', {
39
- 'no-logo': !post_footer_logo?.data && !post_footer_logo?.download,
40
- })}
41
- >
42
- <div className="followus-and-links">
43
- {social_links?.length > 0 && (
44
- <div className="follow-us">
45
- <span>
46
- <FormattedMessage id="Follow us:" defaultMessage="Follow us:" />
47
- </span>
48
- <SlotRenderer name="followUs" content={content} />
49
- </div>
50
- )}
51
- {footer_links?.length > 0 && (
52
- <div className="footer-links">
53
- <SlotRenderer name="footerLinks" content={content} />
54
- <LinkList links={footer_links} />
55
- </div>
56
- )}
57
- </div>
36
+ <Container className="default follow-us-links-and-logo">
37
+ {social_links?.length > 0 && (
38
+ <div className="follow-us">
39
+ <SlotRenderer name="followUs" content={content} />
40
+ </div>
41
+ )}
58
42
  {post_footer_logo?.data || post_footer_logo?.download ? (
59
43
  <div className="footer-logo">
60
44
  <span>
@@ -63,7 +47,6 @@ const FollowUsPostFooterLogoAndLinks = ({ content }: { content: Content }) => {
63
47
  defaultMessage="Sponsored by:"
64
48
  />
65
49
  </span>
66
- {/* @ts-ignore */}
67
50
  <ConditionalLink
68
51
  condition={content?.post_footer_logo_link}
69
52
  to={content?.post_footer_logo_link}
@@ -73,6 +56,12 @@ const FollowUsPostFooterLogoAndLinks = ({ content }: { content: Content }) => {
73
56
  </ConditionalLink>
74
57
  </div>
75
58
  ) : null}
59
+ {footer_links?.length > 0 && (
60
+ <div className="footer-links">
61
+ <SlotRenderer name="footerLinks" content={content} />
62
+ <LinkList links={footer_links} />
63
+ </div>
64
+ )}
76
65
  </Container>
77
66
  ) : null;
78
67
  };
@@ -23,6 +23,7 @@ const ImageView = ({ content }) => {
23
23
  const Image = config.getComponent('Image').component;
24
24
  const Container =
25
25
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
26
+ const width = config.settings.layout.defaultContainerWidth;
26
27
 
27
28
  return (
28
29
  <Container id="page-document" className="view-wrapper image-view">
@@ -30,7 +31,13 @@ const ImageView = ({ content }) => {
30
31
  <h1 className="documentFirstHeading">{content.title}</h1>
31
32
  {content?.image?.download && (
32
33
  <figure>
33
- <Image item={content} imageField="image" alt="" responsive={true} />
34
+ <Image
35
+ item={content}
36
+ imageField="image"
37
+ alt=""
38
+ responsive={true}
39
+ sizes={`(max-width: ${width}px) 100vw, ${width}px`}
40
+ />
34
41
  <Caption
35
42
  title={content.title}
36
43
  description={content.description}
@@ -16,10 +16,11 @@ import deleteSVG from '@plone/volto/icons/delete.svg';
16
16
  import addSVG from '@plone/volto/icons/add.svg';
17
17
  import dragSVG from '@plone/volto/icons/drag.svg';
18
18
  import { v4 as uuid } from 'uuid';
19
- import type { BlockConfigBase, Content, JSONSchema } from '@plone/types';
19
+ import type { Content, JSONSchema } from '@plone/types';
20
20
  import type { IntlShape } from 'react-intl';
21
21
  import config from '@plone/volto/registry';
22
22
  import isEmpty from 'lodash/isEmpty';
23
+ import { type DragEndEvent } from '@dnd-kit/core';
23
24
 
24
25
  const messages = defineMessages({
25
26
  labelRemoveItem: {
@@ -114,11 +115,22 @@ export type ObjectListWidgetProps = {
114
115
  */
115
116
  schemaEnhancer?: (args: {
116
117
  schema: JSONSchema & { addMessage: string };
117
- formData: BlockConfigBase;
118
+ formData: object;
118
119
  intl: IntlShape;
119
- navRoot: Content;
120
- contentType: string;
120
+ navRoot?: Content;
121
+ contentType?: string;
121
122
  }) => JSONSchema;
123
+ /**
124
+ * Another optional function to enhance the schema.
125
+ * (Deprecated API with fewer supported arguments.
126
+ * This is here for backwards-compatibility
127
+ * with the ObjectListWidget in Volto.)
128
+ */
129
+ schemaExtender?: (
130
+ schema: JSONSchema,
131
+ formData: object,
132
+ intl: IntlShape,
133
+ ) => JSONSchema;
122
134
  };
123
135
 
124
136
  const EMPTY_SCHEMA = {
@@ -141,14 +153,16 @@ const ObjectListWidget = (props: ObjectListWidgetProps) => {
141
153
  value = [],
142
154
  onChange,
143
155
  schemaEnhancer,
156
+ schemaExtender,
144
157
  schemaName,
145
158
  } = props;
146
159
 
147
160
  const schema =
148
- config.getUtility({
149
- type: 'schema',
150
- name: schemaName,
151
- }).method ||
161
+ (schemaName &&
162
+ config.getUtility({
163
+ type: 'schema',
164
+ name: schemaName,
165
+ }).method) ||
152
166
  props.schema ||
153
167
  EMPTY_SCHEMA;
154
168
 
@@ -170,7 +184,7 @@ const ObjectListWidget = (props: ObjectListWidgetProps) => {
170
184
 
171
185
  const intl = useIntl();
172
186
 
173
- function handleChangeActiveObject(index) {
187
+ function handleChangeActiveObject(index: number) {
174
188
  const newIndex = activeObject === index ? -1 : index;
175
189
 
176
190
  setActiveObject(newIndex);
@@ -178,13 +192,22 @@ const ObjectListWidget = (props: ObjectListWidgetProps) => {
178
192
 
179
193
  const objectSchema =
180
194
  typeof schema === 'function' ? schema({ ...props, activeObject }) : schema;
195
+ const getEnhancedSchema = (data: object) => {
196
+ const enhancedSchema = schemaEnhancer
197
+ ? schemaEnhancer({ schema: objectSchema, formData: data, intl })
198
+ : objectSchema;
199
+ const extendedSchema = schemaExtender
200
+ ? schemaExtender(objectSchema, data, intl)
201
+ : enhancedSchema;
202
+ return extendedSchema;
203
+ };
181
204
 
182
- function handleDragEnd(event) {
205
+ function handleDragEnd(event: DragEndEvent) {
183
206
  const { active, over } = event;
184
207
 
185
- if (active.id !== over.id) {
208
+ if (active.id !== over?.id) {
186
209
  const source = value.findIndex((item) => item['@id'] === active.id);
187
- const destination = value.findIndex((item) => item['@id'] === over.id);
210
+ const destination = value.findIndex((item) => item['@id'] === over?.id);
188
211
 
189
212
  const newValue = reorderArray(value, source, destination);
190
213
  onChange(id, newValue);
@@ -205,13 +228,9 @@ const ObjectListWidget = (props: ObjectListWidgetProps) => {
205
228
  '@id': uuid(),
206
229
  };
207
230
 
208
- const objSchema = schemaEnhancer
209
- ? // @ts-ignore - TODO Make sure this continues to have sense
210
- schemaEnhancer({ schema: objectSchema, formData: data, intl })
211
- : objectSchema;
212
231
  const dataWithDefaults = applySchemaDefaults({
213
232
  data,
214
- schema: objSchema,
233
+ schema: getEnhancedSchema(data),
215
234
  intl,
216
235
  });
217
236
 
@@ -318,16 +337,7 @@ const ObjectListWidget = (props: ObjectListWidgetProps) => {
318
337
  id={`${uid}`}
319
338
  key={`olw-${uid}`}
320
339
  block={block}
321
- schema={
322
- schemaEnhancer
323
- ? // @ts-ignore - TODO Make sure this continues to have sense
324
- schemaEnhancer({
325
- schema: objectSchema,
326
- formData: item,
327
- intl,
328
- })
329
- : objectSchema
330
- }
340
+ schema={getEnhancedSchema(item)}
331
341
  value={item}
332
342
  onChange={(fieldId: string, fieldValue: any) => {
333
343
  const newvalue = value.map((v, i) =>
@@ -30,6 +30,8 @@ import {
30
30
  standAloneImageBlockSchemaEnhancer,
31
31
  } from '../components/Blocks/Image/schema';
32
32
  import { ImageBlockDataAdapter } from '../components/Blocks/Image/adapter';
33
+ import { VideoBlockDataAdapter } from '../components/Blocks/Video/adapter';
34
+ import { MapsBlockDataAdapter } from '../components/Blocks/Maps/adapter';
33
35
 
34
36
  import { AccordionSchemaEnhancer } from '../components/Blocks/Accordion/schema';
35
37
 
@@ -66,6 +68,7 @@ declare module '@plone/types' {
66
68
  }
67
69
  export interface BlockConfigBase {
68
70
  themes?: StyleDefinition[];
71
+ alignments?: StyleDefinition[];
69
72
  defaultTheme: string;
70
73
  allowedBlocks?: string[];
71
74
  allowed_headline_tags?: string[][];
@@ -81,6 +84,10 @@ declare module '@plone/types' {
81
84
  export interface SettingsConfig {
82
85
  blockModel?: number;
83
86
  }
87
+
88
+ export interface BlocksConfig {
89
+ alignments: StyleDefinition[];
90
+ }
84
91
  }
85
92
 
86
93
  export default function install(config: ConfigType) {
@@ -147,6 +154,31 @@ export default function install(config: ConfigType) {
147
154
  },
148
155
  ];
149
156
 
157
+ // Default block alignments
158
+ config.blocks.alignments = [
159
+ {
160
+ style: {
161
+ '--block-alignment': 'var(--align-left)',
162
+ },
163
+ name: 'left',
164
+ label: 'Left',
165
+ },
166
+ {
167
+ style: {
168
+ '--block-alignment': 'var(--align-center)',
169
+ },
170
+ name: 'center',
171
+ label: 'Center',
172
+ },
173
+ {
174
+ style: {
175
+ '--block-alignment': 'var(--align-right)',
176
+ },
177
+ name: 'right',
178
+ label: 'Right',
179
+ },
180
+ ];
181
+
150
182
  config.registerUtility({
151
183
  name: 'blockThemesEnhancer',
152
184
  type: 'styleWrapperStyleObjectEnhancer',
@@ -159,6 +191,20 @@ export default function install(config: ConfigType) {
159
191
  method: styleDefinitionsEnhancer,
160
192
  });
161
193
 
194
+ config.registerUtility({
195
+ name: 'align:noprefix',
196
+ type: 'styleFieldDefinition',
197
+ method: ({ data }: { data: { '@type'?: string } }) =>
198
+ config.blocks.blocksConfig?.[data?.['@type'] ?? '']?.alignments ||
199
+ config.blocks.alignments,
200
+ });
201
+
202
+ config.registerUtility({
203
+ name: 'blockWidth:noprefix',
204
+ type: 'styleFieldDefinition',
205
+ method: () => config.blocks.widths,
206
+ });
207
+
162
208
  // No required blocks except eventMetadata
163
209
  config.blocks.requiredBlocks = [
164
210
  ...config.blocks.requiredBlocks,
@@ -323,15 +369,25 @@ export default function install(config: ConfigType) {
323
369
  category: 'title',
324
370
  };
325
371
 
372
+ const getTeaserSizes = ({ inGrid = false, columns = 1 } = {}) => {
373
+ const { defaultContainerWidth, tabletBreakpoint } = config.settings.layout;
374
+ const desktopWidth = Math.floor(
375
+ defaultContainerWidth / (inGrid ? columns : 2),
376
+ );
377
+ return `(max-width: ${tabletBreakpoint}px) 100vw, ${desktopWidth}px`;
378
+ };
379
+
326
380
  config.blocks.blocksConfig.teaser = {
327
381
  ...config.blocks.blocksConfig.teaser,
328
382
  group: 'teasers',
329
383
  imageScale: 'larger',
384
+ getSizes: getTeaserSizes,
330
385
  schemaEnhancer: composeSchema(defaultStylingSchema, teaserSchemaEnhancer),
331
386
  };
332
387
 
333
388
  config.blocks.blocksConfig.video = {
334
389
  ...config.blocks.blocksConfig.video,
390
+ dataAdapter: VideoBlockDataAdapter,
335
391
  schemaEnhancer: composeSchema(
336
392
  defaultStylingSchema,
337
393
  videoBlockSchemaEnhancer,
@@ -339,6 +395,7 @@ export default function install(config: ConfigType) {
339
395
  };
340
396
  config.blocks.blocksConfig.maps = {
341
397
  ...config.blocks.blocksConfig.maps,
398
+ dataAdapter: MapsBlockDataAdapter,
342
399
  schemaEnhancer: composeSchema(
343
400
  defaultStylingSchema,
344
401
  mapsBlockSchemaEnhancer,
@@ -410,5 +467,11 @@ export default function install(config: ConfigType) {
410
467
  schemaEnhancer: sliderBlockSchemaEnhancer,
411
468
  };
412
469
 
470
+ // Highlight Block
471
+ config.blocks.blocksConfig.highlight = {
472
+ ...config.blocks.blocksConfig.highlight,
473
+ schemaEnhancer: defaultStylingSchema,
474
+ };
475
+
413
476
  return config;
414
477
  }
@@ -1,6 +1,5 @@
1
1
  import type { ConfigType } from '@plone/registry';
2
2
  import { getPreviousNextBlock } from '@plone/volto/helpers/Blocks/Blocks';
3
- import { getCurrentStyleByName } from '../helpers/helpers';
4
3
 
5
4
  export default function install(config: ConfigType) {
6
5
  // Register custom StyleWrapper ClassNames
@@ -71,15 +70,17 @@ export default function install(config: ConfigType) {
71
70
  config.settings.styleClassNameExtenders.push(
72
71
  ({ data, classNames }: { data: any; classNames: Array<string> }) => {
73
72
  const currentBlockWidth =
74
- getCurrentStyleByName(
75
- config.blocks.widths,
76
- 'blockWidth:noprefix',
77
- data,
78
- ) || 'default';
79
- if (currentBlockWidth) {
80
- return [...classNames, `has--block-width--${currentBlockWidth}`];
81
- }
82
- return classNames;
73
+ data?.styles?.['blockWidth:noprefix'] || 'default';
74
+ return [...classNames, `has--block-width--${currentBlockWidth}`];
75
+ },
76
+ );
77
+
78
+ // Blocks alignment convenience classes injection
79
+ config.settings.styleClassNameExtenders.push(
80
+ ({ data, classNames }: { data: any; classNames: Array<string> }) => {
81
+ const currentBlockAlignment =
82
+ data?.styles?.['align:noprefix'] || 'center';
83
+ return [...classNames, `has--block-alignment--${currentBlockAlignment}`];
83
84
  },
84
85
  );
85
86
 
@@ -28,6 +28,12 @@ export default function install(config: ConfigType) {
28
28
  config.settings.contentMetadataTagsImageField = 'preview_image';
29
29
  config.settings.querystringSearchGet = true;
30
30
 
31
+ config.settings.layout = {
32
+ ...config.settings.layout,
33
+ defaultContainerWidth: 940,
34
+ tabletBreakpoint: 768,
35
+ };
36
+
31
37
  config.settings.slidingSearchAnimation = true;
32
38
  config.settings.openExternalLinkInNewTab = true;
33
39