@kitconcept/volto-light-theme 8.0.0-alpha.28 → 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 (116) hide show
  1. package/.changelog.draft +23 -3
  2. package/CHANGELOG.md +27 -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 +6 -6
  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/config/blocks.tsx +63 -0
  97. package/src/config/classExtenders.ts +11 -10
  98. package/src/config/settings.ts +6 -0
  99. package/src/customizations/volto/components/manage/Blocks/Maps/MapsSidebar.jsx +10 -0
  100. package/src/customizations/volto/components/manage/Blocks/Maps/View.jsx +10 -0
  101. package/src/customizations/volto/components/manage/Blocks/Video/VideoSidebar.jsx +10 -0
  102. package/src/customizations/volto/components/manage/Blocks/Video/View.jsx +10 -0
  103. package/src/index.ts +8 -0
  104. package/src/primitives/Card/Card.tsx +4 -1
  105. package/src/theme/_footer.scss +61 -51
  106. package/src/theme/_layout.scss +7 -62
  107. package/src/theme/_typo-custom.scss +1 -1
  108. package/src/theme/_variables.scss +21 -0
  109. package/src/theme/blocks/_grid.scss +1 -0
  110. package/src/theme/blocks/_highlight.scss +10 -7
  111. package/src/theme/blocks/_image.scss +96 -186
  112. package/src/theme/blocks/_listing.scss +5 -1
  113. package/src/theme/blocks/_maps.scss +60 -34
  114. package/src/transforms/to6.ts +5 -49
  115. package/src/transforms/to8.test.js +201 -0
  116. 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}
@@ -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
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OVERRIDE MapsSidebar.jsx
3
+ * REASON: Route field changes through dataAdapter to enforce default block width when floating.
4
+ * DATE: 2026-04-16
5
+ * DEVELOPER: @danalvrz
6
+ */
7
+
8
+ import MapsSidebar from '../../../../../../components/Blocks/Maps/MapsSidebar';
9
+
10
+ export default MapsSidebar;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OVERRIDE View.jsx
3
+ * REASON: Use align:noprefix and blockWidth:noprefix CSS custom properties instead of align class.
4
+ * DATE: 2026-04-16
5
+ * DEVELOPER: @danalvrz
6
+ */
7
+
8
+ import MapsView from '../../../../../../components/Blocks/Maps/View';
9
+
10
+ export default MapsView;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OVERRIDE VideoSidebar.jsx
3
+ * REASON: Route field changes through dataAdapter to enforce default block width when floating.
4
+ * DATE: 2026-04-16
5
+ * DEVELOPER: @danalvrz
6
+ */
7
+
8
+ import VideoSidebar from '../../../../../../components/Blocks/Video/VideoSidebar';
9
+
10
+ export default VideoSidebar;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OVERRIDE View.jsx
3
+ * REASON: Use align:noprefix and blockWidth:noprefix CSS custom properties instead of align class.
4
+ * DATE: 2026-04-16
5
+ * DEVELOPER: @danalvrz
6
+ */
7
+
8
+ import VideoView from '../../../../../../components/Blocks/Video/View';
9
+
10
+ export default VideoView;
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import { Container } from '@plone/components';
6
6
  import EventView from './components/Theme/EventView';
7
7
 
8
8
  import { migrateToVLT6ColorAndWidthModel } from './transforms/to6';
9
+ import { migrateToVLT8FloatingBlocks } from './transforms/to8';
9
10
 
10
11
  import installSettings from './config/settings';
11
12
  import installBlocks from './config/blocks';
@@ -98,6 +99,13 @@ const applyConfig = (config: ConfigType) => {
98
99
  method: migrateToVLT6ColorAndWidthModel,
99
100
  });
100
101
 
102
+ config.registerUtility({
103
+ name: 'migrateToVLT8FloatingBlocks',
104
+ type: 'transform',
105
+ dependencies: { reducer: 'content' },
106
+ method: migrateToVLT8FloatingBlocks,
107
+ });
108
+
101
109
  config.views.contentTypesViews.Event = EventView;
102
110
 
103
111
  config.addonReducers = {
@@ -94,10 +94,12 @@ type CardImageProps = {
94
94
  imageComponent?: React.ComponentType<any>;
95
95
  children?: React.ReactNode;
96
96
  showPlaceholderImage?: boolean;
97
+ sizes?: string;
97
98
  };
98
99
 
99
100
  const CardImage = (props: CardImageProps) => {
100
- const { src, item, image, imageComponent, showPlaceholderImage } = props;
101
+ const { src, item, image, imageComponent, showPlaceholderImage, sizes } =
102
+ props;
101
103
  const Image = imageComponent || DefaultImage;
102
104
 
103
105
  return (
@@ -115,6 +117,7 @@ const CardImage = (props: CardImageProps) => {
115
117
  alt=""
116
118
  loading="lazy"
117
119
  responsive={true}
120
+ sizes={sizes}
118
121
  />
119
122
  )
120
123
  ) : (