@kitconcept/volto-light-theme 8.0.0-alpha.2 → 8.0.0-alpha.21

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 (101) hide show
  1. package/.changelog.draft +3 -4
  2. package/CHANGELOG.md +232 -0
  3. package/locales/de/LC_MESSAGES/volto.po +30 -115
  4. package/locales/en/LC_MESSAGES/volto.po +30 -115
  5. package/locales/es/LC_MESSAGES/volto.po +31 -116
  6. package/locales/eu/LC_MESSAGES/volto.po +58 -124
  7. package/locales/pt_BR/LC_MESSAGES/volto.po +38 -123
  8. package/locales/volto.pot +31 -116
  9. package/package.json +7 -4
  10. package/src/__mocks__/semantic-ui-react.ts +31 -0
  11. package/src/components/Blocks/Block/Edit.jsx +14 -6
  12. package/src/components/Blocks/Block/EditBlockWrapper.jsx +9 -3
  13. package/src/components/Blocks/Block/ErrorBoundary.test.tsx +55 -0
  14. package/src/components/Blocks/Block/ErrorBoundary.tsx +92 -0
  15. package/src/components/Blocks/Block/ErrorBoundaryMessage.tsx +66 -0
  16. package/src/components/Blocks/EventCalendar/Search/components/EventTemplate.tsx +1 -1
  17. package/src/components/Blocks/Image/Edit.jsx +1 -0
  18. package/src/components/Blocks/Listing/DefaultTemplate.jsx +12 -6
  19. package/src/components/Blocks/Listing/GridTemplate.jsx +16 -7
  20. package/src/components/Blocks/Listing/ListingBody.jsx +4 -1
  21. package/src/components/Blocks/Listing/SummaryTemplate.jsx +16 -7
  22. package/src/components/Blocks/Teaser/DefaultBody.tsx +25 -5
  23. package/src/components/Blocks/schema.ts +69 -0
  24. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +128 -0
  25. package/src/components/Breadcrumbs/Breadcrumbs.tsx +117 -0
  26. package/src/components/Caption/Caption.test.tsx +31 -0
  27. package/src/components/Caption/{Caption.jsx → Caption.tsx} +14 -21
  28. package/src/components/Footer/ColumnLinks.tsx +2 -2
  29. package/src/components/Footer/slots/Colophon.tsx +13 -1
  30. package/src/components/Footer/slots/CoreFooter.tsx +4 -2
  31. package/src/components/Header/Header.tsx +3 -3
  32. package/src/components/LanguageSelector/LanguageSelector.tsx +91 -0
  33. package/src/components/MobileNavigation/MobileNavigation.jsx +11 -9
  34. package/src/components/Navigation/Navigation.test.tsx +176 -0
  35. package/src/components/Navigation/{Navigation.jsx → Navigation.tsx} +77 -37
  36. package/src/components/StickyMenu/MobileCarouselArrowButton.tsx +81 -0
  37. package/src/components/StickyMenu/MobileStickyMenu.tsx +76 -0
  38. package/src/components/Summary/DefaultSummary.tsx +10 -3
  39. package/src/components/Summary/EventSummary.tsx +10 -3
  40. package/src/components/Summary/FileSummary.tsx +10 -3
  41. package/src/components/Summary/NewsItemSummary.tsx +10 -3
  42. package/src/components/Summary/PersonSummary.tsx +10 -3
  43. package/src/components/Summary/Summary.stories.tsx +46 -30
  44. package/src/components/Tags/Tags.test.tsx +71 -0
  45. package/src/components/Tags/{Tags.jsx → Tags.tsx} +9 -25
  46. package/src/components/Theme/EventView.jsx +4 -4
  47. package/src/components/Theme/NewsItemView.jsx +4 -4
  48. package/src/components/Theme/RenderBlocks.jsx +45 -37
  49. package/src/components/Theme/RenderBlocksV2.jsx +51 -20
  50. package/src/components/Widgets/ColorSwatch.stories.tsx +197 -0
  51. package/src/components/Widgets/ColorSwatch.test.tsx +188 -0
  52. package/src/components/Widgets/ColorSwatch.tsx +77 -39
  53. package/src/components/Widgets/SoftTextWidget.tsx +129 -0
  54. package/src/components/Widgets/SoftTextareaWidget.tsx +118 -0
  55. package/src/components/Widgets/ThemeColorSwatch.tsx +5 -9
  56. package/src/config/blocks.tsx +21 -29
  57. package/src/config/slots.ts +7 -0
  58. package/src/config/widgets.ts +5 -9
  59. package/src/customizations/volto/components/manage/DragDropList/DragDropList.jsx +263 -0
  60. package/src/customizations/volto/components/theme/LanguageSelector/LanguageSelector.tsx +10 -0
  61. package/src/helpers/styleDefinitions.test.tsx +30 -0
  62. package/src/helpers/styleDefinitions.ts +49 -0
  63. package/src/internalChecks.test.ts +94 -0
  64. package/src/primitives/Card/Card.stories.tsx +4 -1
  65. package/src/primitives/Card/Card.test.tsx +11 -33
  66. package/src/primitives/Card/Card.tsx +33 -43
  67. package/src/primitives/IconLinkList.tsx +53 -52
  68. package/src/theme/_bgcolor-blocks-layout.scss +43 -45
  69. package/src/theme/_content.scss +12 -13
  70. package/src/theme/_export_import.scss +94 -0
  71. package/src/theme/_footer.scss +64 -19
  72. package/src/theme/_header.scss +21 -4
  73. package/src/theme/_insets.scss +1 -1
  74. package/src/theme/_layout.scss +34 -15
  75. package/src/theme/_mobile-sticky-menu.scss +92 -0
  76. package/src/theme/_search-page.scss +249 -0
  77. package/src/theme/_typo-custom.scss +16 -5
  78. package/src/theme/_variables.scss +19 -4
  79. package/src/theme/_widgets.scss +15 -27
  80. package/src/theme/blocks/_accordion.scss +11 -4
  81. package/src/theme/blocks/_grid.scss +9 -77
  82. package/src/theme/blocks/_listing.scss +60 -126
  83. package/src/theme/blocks/_search.scss +3 -4
  84. package/src/theme/blocks/_table.scss +1 -0
  85. package/src/theme/blocks/_teaser.scss +7 -117
  86. package/src/theme/blocks/error-boundary.scss +11 -0
  87. package/src/theme/card.scss +107 -70
  88. package/src/theme/main.scss +5 -0
  89. package/src/theme/notfound.scss +27 -0
  90. package/src/theme/person.scss +28 -12
  91. package/src/theme/sticky-menu.scss +7 -5
  92. package/src/types.d.ts +1 -0
  93. package/vitest.config.mjs +4 -0
  94. package/razzle.extend.js +0 -38
  95. package/src/components/Blocks/schema.js +0 -44
  96. package/src/components/Breadcrumbs/Breadcrumbs.jsx +0 -118
  97. package/src/components/Widgets/AlignWidget.tsx +0 -84
  98. package/src/components/Widgets/BlockAlignment.tsx +0 -88
  99. package/src/components/Widgets/BlockWidth.tsx +0 -101
  100. package/src/components/Widgets/Buttons.tsx +0 -144
  101. package/src/components/Widgets/Size.tsx +0 -78
@@ -0,0 +1,118 @@
1
+ import { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Label, TextArea } from 'semantic-ui-react';
4
+ import { injectIntl } from 'react-intl';
5
+ import FormFieldWrapper from '@plone/volto/components/manage/Widgets/FormFieldWrapper';
6
+ /**
7
+ * TextareaWidget, a widget for multiple lines text
8
+ *
9
+ * To use it, in schema properties, declare a field like:
10
+ *
11
+ * ```jsx
12
+ * {
13
+ * title: "Text",
14
+ * widget: 'textarea',
15
+ * }
16
+ * ```
17
+ */
18
+ const SoftTextareaWidget = (props) => {
19
+ const {
20
+ id,
21
+ maxLength,
22
+ value,
23
+ onChange,
24
+ placeholder,
25
+ isDisabled,
26
+ softMaxLength,
27
+ } = props;
28
+ const [lengthError, setlengthError] = useState('');
29
+ const [softLengthWarning, setSoftLengthWarning] = useState('');
30
+ const onhandleChange = (id, value) => {
31
+ if (maxLength && value?.length) {
32
+ let remlength = maxLength - value.length;
33
+ if (remlength < 0) {
34
+ setlengthError(
35
+ `You have exceeded word limit by ${Math.abs(remlength)}`,
36
+ );
37
+ } else {
38
+ setlengthError('');
39
+ }
40
+ }
41
+ //START CUSTOMIZATION
42
+ if (softMaxLength && value?.length) {
43
+ let remaining = softMaxLength - value.length;
44
+ if (remaining < 0) {
45
+ setSoftLengthWarning(
46
+ `You have exceeded the recommended limit by ${Math.abs(remaining)}`,
47
+ );
48
+ } else {
49
+ setSoftLengthWarning('');
50
+ }
51
+ }
52
+ //END CUSTOMIZATION
53
+ onChange(id, value);
54
+ };
55
+ return (
56
+ <FormFieldWrapper {...props} className="textarea">
57
+ <TextArea
58
+ id={`field-${id}`}
59
+ name={id}
60
+ value={value || ''}
61
+ disabled={isDisabled}
62
+ placeholder={placeholder}
63
+ onChange={({ target }) =>
64
+ onhandleChange(id, target.value === '' ? undefined : target.value)
65
+ }
66
+ />
67
+ {/* START CUSTOMIZATION */}
68
+ {softLengthWarning.length > 0 && (
69
+ <Label key={softLengthWarning} basic color="yellow" pointing>
70
+ {softLengthWarning}
71
+ </Label>
72
+ )}
73
+ {/* END CUSTOMIZATION */}
74
+ {lengthError.length > 0 && (
75
+ <Label key={lengthError} basic color="red" pointing>
76
+ {lengthError}
77
+ </Label>
78
+ )}
79
+ </FormFieldWrapper>
80
+ );
81
+ };
82
+ /**
83
+ * Property types.
84
+ * @property {Object} propTypes Property types.
85
+ * @static
86
+ */
87
+ SoftTextareaWidget.propTypes = {
88
+ id: PropTypes.string.isRequired,
89
+ title: PropTypes.string.isRequired,
90
+ description: PropTypes.string,
91
+ maxLength: PropTypes.number,
92
+ softMaxLength: PropTypes.number,
93
+ required: PropTypes.bool,
94
+ error: PropTypes.arrayOf(PropTypes.string),
95
+ value: PropTypes.string,
96
+ onChange: PropTypes.func,
97
+ onEdit: PropTypes.func,
98
+ onDelete: PropTypes.func,
99
+ wrapped: PropTypes.bool,
100
+ placeholder: PropTypes.string,
101
+ };
102
+ /**
103
+ * Default properties.
104
+ * @property {Object} defaultProps Default properties.
105
+ * @static
106
+ */
107
+ SoftTextareaWidget.defaultProps = {
108
+ description: null,
109
+ maxLength: null,
110
+ softMaxLength: null,
111
+ required: false,
112
+ error: [],
113
+ value: null,
114
+ onChange: null,
115
+ onEdit: null,
116
+ onDelete: null,
117
+ };
118
+ export default injectIntl(SoftTextareaWidget);
@@ -2,16 +2,12 @@ import ColorSwatchWidget from './ColorSwatch';
2
2
  import config from '@plone/volto/registry';
3
3
  import type { ColorSwatchProps } from './ColorSwatch';
4
4
 
5
- const ThemeColorSwatch = (props: ColorSwatchProps) => {
6
- const colors: ColorSwatchProps['colors'] = config.blocks.themes;
5
+ const ThemeColorSwatch = (
6
+ props: Omit<ColorSwatchProps, 'themes' | 'colors'>,
7
+ ) => {
8
+ const themes: ColorSwatchProps['themes'] = config.blocks.themes;
7
9
 
8
- const defaultValue = colors.find(
9
- (color) => color.name === config.settings.defaultBackgroundColor,
10
- )?.style;
11
-
12
- return (
13
- <ColorSwatchWidget {...props} default={defaultValue} colors={colors} />
14
- );
10
+ return <ColorSwatchWidget {...props} themes={themes} />;
15
11
  };
16
12
 
17
13
  export default ThemeColorSwatch;
@@ -4,8 +4,12 @@ import type { StyleDefinition } from '@plone/types';
4
4
  import cloneDeep from 'lodash/cloneDeep';
5
5
 
6
6
  import { composeSchema } from '@plone/volto/helpers/Extensions';
7
- import { findStyleByName } from '@plone/volto/helpers/Blocks/Blocks';
8
7
  import { defaultStylingSchema } from '../components/Blocks/schema';
8
+ import {
9
+ blockThemesEnhancer,
10
+ styleDefinitionsEnhancer,
11
+ } from '../helpers/styleDefinitions';
12
+
9
13
  import {
10
14
  gridTeaserDisableStylingSchema,
11
15
  teaserSchemaEnhancer,
@@ -40,7 +44,6 @@ import { tocBlockSchemaEnhancer } from '../components/Blocks/Toc/schema';
40
44
  import { mapsBlockSchemaEnhancer } from '../components/Blocks/Maps/schema';
41
45
  import { sliderBlockSchemaEnhancer } from '../components/Blocks/Slider/schema';
42
46
  import EventMetadataView from '../components/Blocks/EventMetadata/View';
43
- import isEmpty from 'lodash/isEmpty';
44
47
 
45
48
  import SearchBlockViewEvent from '../components/Blocks/EventCalendar/Search/SearchBlockView';
46
49
  import SearchBlockEditEvent from '../components/Blocks/EventCalendar/Search/SearchBlockEdit';
@@ -51,6 +54,7 @@ declare module '@plone/types' {
51
54
  export interface BlocksConfigData {
52
55
  introduction: BlockConfigBase;
53
56
  heading: BlockConfigBase;
57
+ banner: BlockConfigBase;
54
58
  __button: BlockConfigBase;
55
59
  separator: BlockConfigBase;
56
60
  slider: BlockConfigBase;
@@ -62,6 +66,7 @@ declare module '@plone/types' {
62
66
  }
63
67
  export interface BlockConfigBase {
64
68
  themes?: StyleDefinition[];
69
+ defaultTheme: string;
65
70
  allowedBlocks?: string[];
66
71
  allowed_headline_tags?: string[][];
67
72
  unwantedButtons?: string[];
@@ -142,38 +147,18 @@ export default function install(config: ConfigType) {
142
147
  },
143
148
  ];
144
149
 
145
- function blockThemesEnhancer({ data, container }) {
146
- if (!data['@type']) return {};
147
- const blockConfig = config.blocks.blocksConfig[data['@type']];
148
- if (!blockConfig) return {};
149
- const blockStyleDefinitions =
150
- // We look up for the blockThemes in the block's config, then in the global config
151
- // We keep `colors` for BBB, but `themes` should be used
152
- blockConfig.themes || blockConfig.colors || config.blocks.themes || [];
153
-
154
- if (
155
- !isEmpty(container) &&
156
- container.theme &&
157
- (!data.theme || data.theme === 'default')
158
- ) {
159
- return findStyleByName(blockStyleDefinitions, container.theme);
160
- }
161
- if (data.theme) {
162
- return data.theme
163
- ? findStyleByName(blockStyleDefinitions, data.theme)
164
- : {};
165
- } else {
166
- // No theme, return default color
167
- return findStyleByName(config.blocks.themes, 'default');
168
- }
169
- }
170
-
171
150
  config.registerUtility({
172
151
  name: 'blockThemesEnhancer',
173
152
  type: 'styleWrapperStyleObjectEnhancer',
174
153
  method: blockThemesEnhancer,
175
154
  });
176
155
 
156
+ config.registerUtility({
157
+ name: 'styleDefinitionsEnhancer',
158
+ type: 'styleWrapperStyleObjectEnhancer',
159
+ method: styleDefinitionsEnhancer,
160
+ });
161
+
177
162
  // No required blocks except eventMetadata
178
163
  config.blocks.requiredBlocks = [
179
164
  ...config.blocks.requiredBlocks,
@@ -314,6 +299,10 @@ export default function install(config: ConfigType) {
314
299
  ),
315
300
  );
316
301
 
302
+ config.blocks.blocksConfig.banner = {
303
+ ...config.blocks.blocksConfig.banner,
304
+ schemaEnhancer: defaultStylingSchema,
305
+ };
317
306
  config.blocks.blocksConfig.introduction = {
318
307
  ...config.blocks.blocksConfig.introduction,
319
308
  unwantedButtons: ['heading-three', 'blockquote'],
@@ -416,7 +405,10 @@ export default function install(config: ConfigType) {
416
405
  };
417
406
 
418
407
  // Slider Block
419
- config.blocks.blocksConfig.slider.schemaEnhancer = sliderBlockSchemaEnhancer;
408
+ config.blocks.blocksConfig.slider = {
409
+ ...config.blocks.blocksConfig.slider,
410
+ schemaEnhancer: sliderBlockSchemaEnhancer,
411
+ };
420
412
 
421
413
  return config;
422
414
  }
@@ -5,6 +5,7 @@ import FollowUsLogoAndLinks from '../components/Footer/slots/FollowUsLogoAndLink
5
5
  import Colophon from '../components/Footer/slots/Colophon';
6
6
  import CoreFooter from '../components/Footer/slots/CoreFooter';
7
7
  import StickyMenu from '../components/StickyMenu/StickyMenu';
8
+ import MobileStickyMenu from '../components/StickyMenu/MobileStickyMenu';
8
9
  import Anontools from '../components/Anontools/Anontools';
9
10
  import type { Content } from '@plone/types';
10
11
 
@@ -57,5 +58,11 @@ export default function install(config: ConfigType) {
57
58
  component: Colophon,
58
59
  });
59
60
 
61
+ config.registerSlotComponent({
62
+ name: 'MobileStickyMenu',
63
+ slot: 'preFooter',
64
+ component: MobileStickyMenu,
65
+ });
66
+
60
67
  return config;
61
68
  }
@@ -1,8 +1,5 @@
1
1
  import type { ConfigType } from '@plone/registry';
2
- import BlockWidth from '../components/Widgets/BlockWidth';
3
- import BlockAlignment from '../components/Widgets/BlockAlignment';
4
2
  import ColorSwatch from '../components/Widgets/ColorSwatch';
5
- import Size from '../components/Widgets/Size';
6
3
  import ColorPicker from '../components/Widgets/ColorPicker';
7
4
  import ThemeColorSwatch from '../components/Widgets/ThemeColorSwatch';
8
5
  // import BlocksObject from '../components/Widgets/BlocksObject';
@@ -12,7 +9,8 @@ import { footerLogosSchema } from '../components/Widgets/schema/footerLogosSchem
12
9
  import { footerLinksSchema } from '../components/Widgets/schema/footerLinksSchema';
13
10
  import { iconLinkListSchema } from '../components/Widgets/schema/iconLinkListSchema';
14
11
  import ModalJSONEditor from '../components/Widgets/ModalJSONEditor';
15
- import AlignWidget from '../components/Widgets/AlignWidget';
12
+ import SoftTextWidget from '../components/Widgets/SoftTextWidget';
13
+ import SoftTextareaWidget from '../components/Widgets/SoftTextareaWidget';
16
14
 
17
15
  export default function install(config: ConfigType) {
18
16
  // Color picker widget override - use our own non-semanticUI widget
@@ -20,21 +18,19 @@ export default function install(config: ConfigType) {
20
18
  // `color_picker` is a terrible name for this widget, it should be `colorSwatch`
21
19
  // ToDo: Rename it in Volto 19
22
20
  config.widgets.widget.color_picker = ColorSwatch;
21
+ config.widgets.widget.colorSwatch = ColorSwatch;
23
22
 
24
23
  // ObjectList widget override - use our own non-semanticUI widget
25
24
  // it uses also dnd-kit for drag and drop
26
25
  config.widgets.widget.object_list = ObjectList;
27
26
 
28
- config.widgets.widget.align = AlignWidget;
29
-
30
27
  // Force Preview image link widget to the image widget
31
28
  config.widgets.id.preview_image_link = config.widgets.widget.image;
32
29
 
33
- config.widgets.widget.blockWidth = BlockWidth;
34
- config.widgets.widget.blockAlignment = BlockAlignment;
35
30
  config.widgets.widget.colorPicker = ColorPicker;
36
- config.widgets.widget.size = Size;
37
31
  config.widgets.widget.themeColorSwatch = ThemeColorSwatch;
32
+ config.widgets.widget.softTextWidget = SoftTextWidget;
33
+ config.widgets.widget.softTextareaWidget = SoftTextareaWidget;
38
34
 
39
35
  config.widgets.widget.modalJSONEditor = ModalJSONEditor;
40
36
 
@@ -0,0 +1,263 @@
1
+ /**
2
+ * OVERRIDE: DragDropList.jsx
3
+ * REASON: Adding BlockModelv3 guards and customizations.
4
+ * Not moved to core yet, and as it's temporary, we keep it here.
5
+ * DATE: 2025-12-01
6
+ * DEVELOPER: @sneridagh
7
+ */
8
+
9
+ import React, { useRef } from 'react';
10
+ import isEmpty from 'lodash/isEmpty';
11
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
12
+ import { v4 as uuid } from 'uuid';
13
+ import config from '@plone/volto/registry';
14
+
15
+ const getVerticalPlaceholder = (
16
+ draggedDOM,
17
+ sourceIndex,
18
+ destinationIndex,
19
+ parentDOM,
20
+ ) => {
21
+ // Because of the margin rendering rules, there is no easy
22
+ // way to calculate the offset of the placeholder.
23
+ //
24
+ // (Note that this is the reason we cannot use the solutions
25
+ // published on the net, because they assume that we are in control
26
+ // of the content and there are no additional margins involved.)
27
+ //
28
+ // To get a placeholder that looks good in all cases, we
29
+ // fill up the space between the previous and the next element.
30
+ const childrenArray = [...parentDOM.children];
31
+ // Remove the source element
32
+ childrenArray.splice(sourceIndex, 1);
33
+ // Also remove the placeholder that the library always inserts at the end
34
+ childrenArray.splice(-1, 1);
35
+ const parentRect = parentDOM.getBoundingClientRect();
36
+ const prevNode = childrenArray[destinationIndex - 1];
37
+ const nextNode = childrenArray[destinationIndex];
38
+ let top, bottom;
39
+ if (prevNode) {
40
+ const prevRect = prevNode.getBoundingClientRect();
41
+ top = prevRect.top + prevRect.height - parentRect.top;
42
+ } else {
43
+ top = 0;
44
+ }
45
+ if (nextNode) {
46
+ const nextRect = nextNode.getBoundingClientRect();
47
+ bottom = nextRect.top - parentRect.top;
48
+ } else {
49
+ bottom =
50
+ parentRect.bottom +
51
+ draggedDOM.getBoundingClientRect().height -
52
+ parentRect.top;
53
+ }
54
+ return {
55
+ clientY: top,
56
+ clientHeight: bottom - top,
57
+ clientX: parseFloat(window.getComputedStyle(parentDOM).paddingLeft),
58
+ clientWidth: draggedDOM.clientWidth,
59
+ };
60
+ };
61
+
62
+ const getHorizontalPlaceholder = (draggedDOM, destinationIndex, parentDOM) => {
63
+ // The next element will be the one with a gray placeholder behind it.
64
+ //
65
+ // If the element is the last (doesn't have a next element), the whole draggable
66
+ // area will be highlighted
67
+
68
+ const parentRect = parentDOM.getBoundingClientRect();
69
+ let height;
70
+
71
+ height = parentRect.bottom - parentRect.top;
72
+
73
+ let clientWidth = draggedDOM.clientWidth;
74
+
75
+ let innerDroppablePlaceholderMarginLeft =
76
+ parentDOM.children[destinationIndex].getBoundingClientRect().x;
77
+ if (parentDOM.children.length - 1 === destinationIndex) {
78
+ innerDroppablePlaceholderMarginLeft = parentRect.x;
79
+ clientWidth = parentDOM.getBoundingClientRect().width;
80
+ }
81
+ return {
82
+ clientY: 0,
83
+ clientHeight: height,
84
+ clientX: parseFloat(innerDroppablePlaceholderMarginLeft - parentRect.x),
85
+ clientWidth: clientWidth,
86
+ };
87
+ };
88
+
89
+ const DragDropList = (props) => {
90
+ const {
91
+ childList,
92
+ children,
93
+ direction = 'vertical',
94
+ onMoveItem,
95
+ as = 'div',
96
+ style,
97
+ forwardedAriaLabelledBy,
98
+ reactBeautifulDnd,
99
+ } = props; //renderChild
100
+ const { DragDropContext, Draggable, Droppable } = reactBeautifulDnd;
101
+ const [placeholderProps, setPlaceholderProps] = React.useState({});
102
+ const [uid] = React.useState(uuid());
103
+ // queueing timed action
104
+ const timer = useRef(null);
105
+ const parentRef = useRef(null);
106
+ const onDragStart = React.useCallback(
107
+ (event) => {
108
+ clearTimeout(timer.current);
109
+ const queryAttr = 'data-rbd-draggable-id';
110
+ const domQuery = `[${queryAttr}='${event.draggableId}']`;
111
+ const draggedDOM = document.querySelector(domQuery);
112
+ if (!draggedDOM) {
113
+ return;
114
+ }
115
+ const sourceIndex = event.source.index;
116
+ setPlaceholderProps(
117
+ direction === 'horizontal'
118
+ ? getHorizontalPlaceholder(draggedDOM, sourceIndex, parentRef.current)
119
+ : getVerticalPlaceholder(
120
+ draggedDOM,
121
+ sourceIndex,
122
+ sourceIndex,
123
+ parentRef.current,
124
+ ),
125
+ );
126
+ },
127
+ [direction],
128
+ );
129
+
130
+ const onDragEnd = React.useCallback(
131
+ (result) => {
132
+ clearTimeout(timer.current);
133
+ onMoveItem(result);
134
+ setPlaceholderProps({});
135
+ },
136
+ [onMoveItem],
137
+ );
138
+
139
+ const onDragUpdate = React.useCallback(
140
+ (update) => {
141
+ clearTimeout(timer.current);
142
+ setPlaceholderProps({});
143
+ if (!update.destination) {
144
+ return;
145
+ }
146
+ const draggableId = update.draggableId;
147
+ const queryAttr = 'data-rbd-draggable-id';
148
+ const domQuery = `[${queryAttr}='${draggableId}']`;
149
+ const draggedDOM = document.querySelector(domQuery);
150
+ if (!draggedDOM) {
151
+ return;
152
+ }
153
+ const sourceIndex = update.source.index;
154
+ const destinationIndex = update.destination.index;
155
+ // Wait until the animations have finished, to make it look good.
156
+ timer.current = setTimeout(() => {
157
+ setPlaceholderProps(
158
+ direction === 'horizontal'
159
+ ? getHorizontalPlaceholder(
160
+ draggedDOM,
161
+ destinationIndex,
162
+ parentRef.current,
163
+ )
164
+ : getVerticalPlaceholder(
165
+ draggedDOM,
166
+ sourceIndex,
167
+ destinationIndex,
168
+ parentRef.current,
169
+ ),
170
+ );
171
+ }, 250);
172
+ },
173
+ [direction],
174
+ );
175
+
176
+ const AsDomComponent = as;
177
+ return (
178
+ <DragDropContext
179
+ onDragStart={onDragStart}
180
+ onDragUpdate={onDragUpdate}
181
+ onDragEnd={onDragEnd}
182
+ >
183
+ <Droppable
184
+ droppableId={uid}
185
+ direction={direction}
186
+ renderClone={(provided, snapshot, rubric) => {
187
+ const index = rubric.source.index;
188
+ return children({
189
+ child: childList[index][1],
190
+ childId: childList[index][0],
191
+ index,
192
+ draginfo: provided,
193
+ });
194
+ }}
195
+ >
196
+ {(provided, snapshot) => (
197
+ <AsDomComponent
198
+ ref={(el) => {
199
+ provided.innerRef(el);
200
+ parentRef.current = el;
201
+ }}
202
+ {...provided.droppableProps}
203
+ style={{ ...style, position: 'relative' }}
204
+ aria-labelledby={forwardedAriaLabelledBy}
205
+ >
206
+ {childList
207
+ .filter(([id, child]) => id && child) // beware numbers!
208
+ .map(([childId, child], index) => {
209
+ const dragDisabled =
210
+ config.blocks.blocksConfig?.[child?.['@type']]?.blockModel ===
211
+ 3;
212
+ if (!dragDisabled) {
213
+ return (
214
+ <Draggable
215
+ draggableId={childId.toString()}
216
+ index={index}
217
+ key={childId}
218
+ isDragDisabled={!!dragDisabled}
219
+ style={{
220
+ userSelect: 'none',
221
+ }}
222
+ >
223
+ {(draginfo) =>
224
+ children({ child, childId, index, draginfo })
225
+ }
226
+ </Draggable>
227
+ );
228
+ } else {
229
+ return (
230
+ <React.Fragment key={childId}>
231
+ {children({
232
+ child,
233
+ childId,
234
+ index,
235
+ draginfo: { draggableProps: {} },
236
+ })}
237
+ </React.Fragment>
238
+ );
239
+ }
240
+ })}
241
+ {provided.placeholder}
242
+ {!isEmpty(placeholderProps) && snapshot.isDraggingOver && (
243
+ <div
244
+ style={{
245
+ position: 'absolute',
246
+ top: placeholderProps.clientY,
247
+ left: placeholderProps.clientX,
248
+ height: placeholderProps.clientHeight,
249
+ background: '#eee',
250
+ width: placeholderProps.clientWidth,
251
+ borderRadius: '3px',
252
+ ...(direction === 'horizontal' && { zIndex: -1 }),
253
+ }}
254
+ />
255
+ )}
256
+ </AsDomComponent>
257
+ )}
258
+ </Droppable>
259
+ </DragDropContext>
260
+ );
261
+ };
262
+
263
+ export default injectLazyLibs(['reactBeautifulDnd'])(DragDropList);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OVERRIDE LanguageSelector.tsx
3
+ * REASON: Show only two letters of the native language name.
4
+ * SemanticUI-free located at the components folder.
5
+ * To override it, override the @kitconcept/volto-light-theme one instead of
6
+ * this one.
7
+ */
8
+ import LanguageSelector from '../../../../../components/LanguageSelector/LanguageSelector';
9
+
10
+ export default LanguageSelector;
@@ -0,0 +1,30 @@
1
+ import { styleDefinitionsEnhancer } from './styleDefinitions';
2
+ import config from '@plone/volto/registry';
3
+
4
+ describe('styleDefinitionsEnhancer', () => {
5
+ it('should enhance style definitions correctly', () => {
6
+ const data = {
7
+ styles: {
8
+ backgroundColor: 'red',
9
+ textColor: 'blue',
10
+ },
11
+ };
12
+
13
+ config.registerUtility({
14
+ type: 'styleFieldDefinition',
15
+ name: 'backgroundColor',
16
+ method: () => {
17
+ return [
18
+ { name: 'red', label: 'Red', style: { '--bg-color': 'red' } },
19
+ { name: 'green', label: 'Green', style: { '--bg-color': 'green' } },
20
+ ];
21
+ },
22
+ });
23
+
24
+ const container = {};
25
+
26
+ const result = styleDefinitionsEnhancer({ data, container });
27
+
28
+ expect(result).toEqual({ '--bg-color': 'red' });
29
+ });
30
+ });
@@ -0,0 +1,49 @@
1
+ import config from '@plone/volto/registry';
2
+ import { findStyleByName } from '@plone/volto/helpers/Blocks/Blocks';
3
+ import isEmpty from 'lodash/isEmpty';
4
+
5
+ export function blockThemesEnhancer({ data, container }) {
6
+ if (!data['@type']) return {};
7
+ const blockConfig = config.blocks.blocksConfig[data['@type']];
8
+ if (!blockConfig) return {};
9
+ const blockStyleDefinitions =
10
+ // We look up for the blockThemes in the block's config, then in the global config
11
+ // We keep `colors` for BBB, but `themes` should be used
12
+ blockConfig.themes || blockConfig.colors || config.blocks.themes || [];
13
+
14
+ if (
15
+ !isEmpty(container) &&
16
+ container.theme &&
17
+ (!data.theme || data.theme === 'default')
18
+ ) {
19
+ return findStyleByName(blockStyleDefinitions, container.theme);
20
+ }
21
+ if (data.theme) {
22
+ return data.theme ? findStyleByName(blockStyleDefinitions, data.theme) : {};
23
+ } else {
24
+ // No theme, return default color
25
+ return findStyleByName(config.blocks.themes, 'default');
26
+ }
27
+ }
28
+
29
+ export function styleDefinitionsEnhancer({ data, container }) {
30
+ let resultantStyles = {};
31
+ Object.keys(data.styles || {}).forEach((fieldName) => {
32
+ const styleFieldEnhancer = config.getUtility({
33
+ type: 'styleFieldDefinition',
34
+ name: fieldName,
35
+ });
36
+
37
+ if (styleFieldEnhancer.method) {
38
+ resultantStyles = {
39
+ ...resultantStyles,
40
+ ...findStyleByName(
41
+ styleFieldEnhancer.method({ data, container }),
42
+ data.styles[fieldName],
43
+ ),
44
+ };
45
+ }
46
+ });
47
+
48
+ return resultantStyles;
49
+ }