@eeacms/volto-eea-website-policy 0.3.0 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,92 +4,89 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- #### [0.3.0](https://github.com/eea/volto-eea-website-policy/compare/0.2.6...0.3.0)
7
+ ### [0.3.3](https://github.com/eea/volto-eea-website-policy/compare/0.3.2...0.3.3) - 5 September 2022
8
8
 
9
- - change(package): bump release version [`1b00439`](https://github.com/eea/volto-eea-website-policy/commit/1b0043933442bb73c1042dce644a2c09e5f210f4)
10
- - change(item): enable theming for icon and text color and added tertiary option [`58c2d28`](https://github.com/eea/volto-eea-website-policy/commit/58c2d2864fe94c9e7808064b61d3b6c4e6fccbb1)
11
- - change(item): commented sizes too small for item icons and enabled tertiary theme [`0f02d7d`](https://github.com/eea/volto-eea-website-policy/commit/0f02d7d6204b068b78c3f977a561782a19b6f79e)
9
+ #### :hammer_and_wrench: Others
12
10
 
13
- #### [0.2.6](https://github.com/eea/volto-eea-website-policy/compare/0.2.5...0.2.6)
11
+ - override(ContentTypeLayout): Enable volto.blocks.editable.layout - refs #153858 [Alin Voinea - [`a07d3f5`](https://github.com/eea/volto-eea-website-policy/commit/a07d3f5f175925fdcd600e3c99620b4c0f42cb69)]
12
+ - override(ContentTypeLayout) - from @plone/volto 16 alpha 12 [Alin Voinea - [`777068d`](https://github.com/eea/volto-eea-website-policy/commit/777068d4b41d4c94863e2c34ea274f150ead7875)]
13
+ ### [0.3.2](https://github.com/eea/volto-eea-website-policy/compare/0.3.1...0.3.2) - 10 August 2022
14
14
 
15
- > 1 July 2022
15
+ #### :hammer_and_wrench: Others
16
16
 
17
- - Fix Cannot read properties of undefined (reading 'translations') [`8e7b8b8`](https://github.com/eea/volto-eea-website-policy/commit/8e7b8b8533b14f173d77783fe2d0b517c29c5937)
18
- - Customize Toolbar.jsx - copied unchanged from @plone/volto 16.0.0.alpha.11 [`b7add4f`](https://github.com/eea/volto-eea-website-policy/commit/b7add4faa8ef2dde7d77489fc8bc431cbb023314)
17
+ - Safely get data - refs #153018 [Alin Voinea - [`aefc689`](https://github.com/eea/volto-eea-website-policy/commit/aefc68979f6aad86ea02952f82e639dfe8c566f2)]
18
+ - Customize Blocks helper - copy unchanged from @plone/volto 16.alpha.12 [Alin Voinea - [`368ffa8`](https://github.com/eea/volto-eea-website-policy/commit/368ffa8c9ebfc54d364d7bf13b60f4016f1c90e8)]
19
+ ### [0.3.1](https://github.com/eea/volto-eea-website-policy/compare/0.3.0...0.3.1) - 1 August 2022
19
20
 
20
- #### [0.2.5](https://github.com/eea/volto-eea-website-policy/compare/0.2.4...0.2.5)
21
+ ### [0.3.0](https://github.com/eea/volto-eea-website-policy/compare/0.2.6...0.3.0) - 6 July 2022
21
22
 
22
- > 23 June 2022
23
+ #### :nail_care: Enhancements
23
24
 
24
- - Rounded Card variation and disable ImageCards block [`#11`](https://github.com/eea/volto-eea-website-policy/pull/11)
25
- - Rounded card via style wrapper [`6728684`](https://github.com/eea/volto-eea-website-policy/commit/67286844b00902aa4c6b316b50be35db95c531f0)
26
- - Revert "Rounded Card variation - refs #148723" [`0168a86`](https://github.com/eea/volto-eea-website-policy/commit/0168a86fa644a9b848e37c7f5054eb4308c36784)
27
- - Disable Image Cards block [`3ed8dc3`](https://github.com/eea/volto-eea-website-policy/commit/3ed8dc311cad8e30a8c918ecec7b21cc99260c50)
28
- - Rounded Card variation - refs #148723 [`4b46731`](https://github.com/eea/volto-eea-website-policy/commit/4b46731e25962bbb6e7159d0db8a9a642a95eb53)
25
+ - change(package): bump release version [David Ichim - [`1b00439`](https://github.com/eea/volto-eea-website-policy/commit/1b0043933442bb73c1042dce644a2c09e5f210f4)]
26
+ - change(item): enable theming for icon and text color and added tertiary option [David Ichim - [`58c2d28`](https://github.com/eea/volto-eea-website-policy/commit/58c2d2864fe94c9e7808064b61d3b6c4e6fccbb1)]
27
+ - change(item): commented sizes too small for item icons and enabled tertiary theme [David Ichim - [`0f02d7d`](https://github.com/eea/volto-eea-website-policy/commit/0f02d7d6204b068b78c3f977a561782a19b6f79e)]
29
28
 
30
- #### [0.2.4](https://github.com/eea/volto-eea-website-policy/compare/0.2.3...0.2.4)
29
+ ### [0.2.6](https://github.com/eea/volto-eea-website-policy/compare/0.2.5...0.2.6) - 1 July 2022
31
30
 
32
- > 13 June 2022
31
+ #### :hammer_and_wrench: Others
33
32
 
34
- - Release [`#10`](https://github.com/eea/volto-eea-website-policy/pull/10)
35
- - Fix listing block title: Listing (Content) [`cec2760`](https://github.com/eea/volto-eea-website-policy/commit/cec276082c3fc1b5486c996c3746a6958fb3a1a9)
36
- - Add theme to teaser stylewrapper [`60eeb28`](https://github.com/eea/volto-eea-website-policy/commit/60eeb28b7793708c59d0af73ffb09895e8f2c87d)
37
- - Fix className vs class warning [`fea1603`](https://github.com/eea/volto-eea-website-policy/commit/fea1603eefd628d57640a6b94ddbbb7edfb0d841)
38
- - Custom card view for teaser [`c8eded0`](https://github.com/eea/volto-eea-website-policy/commit/c8eded011b8317cce9f842526f5d86771f54797b)
33
+ - Fix Cannot read properties of undefined (reading 'translations') [Alin Voinea - [`8e7b8b8`](https://github.com/eea/volto-eea-website-policy/commit/8e7b8b8533b14f173d77783fe2d0b517c29c5937)]
34
+ - Customize Toolbar.jsx - copied unchanged from @plone/volto 16.0.0.alpha.11 [Alin Voinea - [`b7add4f`](https://github.com/eea/volto-eea-website-policy/commit/b7add4faa8ef2dde7d77489fc8bc431cbb023314)]
35
+ ### [0.2.5](https://github.com/eea/volto-eea-website-policy/compare/0.2.4...0.2.5) - 23 June 2022
39
36
 
40
- #### [0.2.3](https://github.com/eea/volto-eea-website-policy/compare/0.2.2...0.2.3)
37
+ #### :hammer_and_wrench: Others
41
38
 
42
- > 10 June 2022
39
+ - Rounded card via style wrapper [Alin Voinea - [`6728684`](https://github.com/eea/volto-eea-website-policy/commit/67286844b00902aa4c6b316b50be35db95c531f0)]
40
+ - Revert "Rounded Card variation - refs #148723" [Alin Voinea - [`0168a86`](https://github.com/eea/volto-eea-website-policy/commit/0168a86fa644a9b848e37c7f5054eb4308c36784)]
41
+ - Disable Image Cards block [Alin Voinea - [`3ed8dc3`](https://github.com/eea/volto-eea-website-policy/commit/3ed8dc311cad8e30a8c918ecec7b21cc99260c50)]
42
+ - Rounded Card variation - refs #148723 [Alin Voinea - [`4b46731`](https://github.com/eea/volto-eea-website-policy/commit/4b46731e25962bbb6e7159d0db8a9a642a95eb53)]
43
+ ### [0.2.4](https://github.com/eea/volto-eea-website-policy/compare/0.2.3...0.2.4) - 13 June 2022
43
44
 
44
- - Add Item block [`#9`](https://github.com/eea/volto-eea-website-policy/pull/9)
45
- - prettier fix [`c2a6d2d`](https://github.com/eea/volto-eea-website-policy/commit/c2a6d2d3b12ce771b9b4413c5a6db157ccd9d1fc)
46
- - Update index.js [`fb73895`](https://github.com/eea/volto-eea-website-policy/commit/fb7389509bc7e207942762c5e02a93d0168a6fba)
47
- - removed change from commit 719e3778 [`29e636b`](https://github.com/eea/volto-eea-website-policy/commit/29e636b34ab02992c67cc5960f4dc6b21b1b4e71)
48
- - added navDepth to level 4 in order to fill mega menu [`719e377`](https://github.com/eea/volto-eea-website-policy/commit/719e37785cfa2ed37568c82a34dc470b52483d04)
49
- - Rearrange Block Chooser [`9a5c118`](https://github.com/eea/volto-eea-website-policy/commit/9a5c1187ba21fa1898394a4b6cf6dd68bd68f258)
50
- - Move Item block un components folder [`9fb6ab2`](https://github.com/eea/volto-eea-website-policy/commit/9fb6ab2fb02cc5c1a7e25367de5ebe7109bd3dc4)
51
- - Set default sizes for image & icon in Item block [`af22ec7`](https://github.com/eea/volto-eea-website-policy/commit/af22ec78b2c6fd1d033dc1acfec5e64f8926cd77)
52
- - Add helpers functions [`353581c`](https://github.com/eea/volto-eea-website-policy/commit/353581cc6602b5e573eee93c74a67656203c0968)
53
- - Add Teaser block folder's structure [`9457dd4`](https://github.com/eea/volto-eea-website-policy/commit/9457dd4ffaa2631eceb46bfd1ec2d201e8b3d7a8)
45
+ #### :hammer_and_wrench: Others
54
46
 
55
- #### [0.2.2](https://github.com/eea/volto-eea-website-policy/compare/0.2.1...0.2.2)
47
+ - Fix listing block title: Listing (Content) [Alin Voinea - [`cec2760`](https://github.com/eea/volto-eea-website-policy/commit/cec276082c3fc1b5486c996c3746a6958fb3a1a9)]
48
+ - Add theme to teaser stylewrapper [Alin Voinea - [`60eeb28`](https://github.com/eea/volto-eea-website-policy/commit/60eeb28b7793708c59d0af73ffb09895e8f2c87d)]
49
+ - Fix className vs class warning [Alin Voinea - [`fea1603`](https://github.com/eea/volto-eea-website-policy/commit/fea1603eefd628d57640a6b94ddbbb7edfb0d841)]
50
+ - Custom card view for teaser [Alin Voinea - [`c8eded0`](https://github.com/eea/volto-eea-website-policy/commit/c8eded011b8317cce9f842526f5d86771f54797b)]
51
+ ### [0.2.3](https://github.com/eea/volto-eea-website-policy/compare/0.2.2...0.2.3) - 10 June 2022
56
52
 
57
- > 24 May 2022
53
+ #### :hammer_and_wrench: Others
58
54
 
59
- - Install callout and remove blockquote - refs #148688 [`#8`](https://github.com/eea/volto-eea-website-policy/pull/8)
55
+ - prettier fix [David Ichim - [`c2a6d2d`](https://github.com/eea/volto-eea-website-policy/commit/c2a6d2d3b12ce771b9b4413c5a6db157ccd9d1fc)]
56
+ - Update index.js [ichim-david - [`fb73895`](https://github.com/eea/volto-eea-website-policy/commit/fb7389509bc7e207942762c5e02a93d0168a6fba)]
57
+ - removed change from commit 719e3778 [ichim-david - [`29e636b`](https://github.com/eea/volto-eea-website-policy/commit/29e636b34ab02992c67cc5960f4dc6b21b1b4e71)]
58
+ - added navDepth to level 4 in order to fill mega menu [ichim-david - [`719e377`](https://github.com/eea/volto-eea-website-policy/commit/719e37785cfa2ed37568c82a34dc470b52483d04)]
59
+ - Rearrange Block Chooser [Alin Voinea - [`9a5c118`](https://github.com/eea/volto-eea-website-policy/commit/9a5c1187ba21fa1898394a4b6cf6dd68bd68f258)]
60
+ - Move Item block un components folder [Miu Razvan - [`9fb6ab2`](https://github.com/eea/volto-eea-website-policy/commit/9fb6ab2fb02cc5c1a7e25367de5ebe7109bd3dc4)]
61
+ - Set default sizes for image & icon in Item block [Miu Razvan - [`af22ec7`](https://github.com/eea/volto-eea-website-policy/commit/af22ec78b2c6fd1d033dc1acfec5e64f8926cd77)]
62
+ - Add helpers functions [Miu Razvan - [`353581c`](https://github.com/eea/volto-eea-website-policy/commit/353581cc6602b5e573eee93c74a67656203c0968)]
63
+ - Add Teaser block folder's structure [Alin Voinea - [`9457dd4`](https://github.com/eea/volto-eea-website-policy/commit/9457dd4ffaa2631eceb46bfd1ec2d201e8b3d7a8)]
64
+ ### [0.2.2](https://github.com/eea/volto-eea-website-policy/compare/0.2.1...0.2.2) - 24 May 2022
60
65
 
61
- #### [0.2.1](https://github.com/eea/volto-eea-website-policy/compare/0.2.0...0.2.1)
66
+ ### [0.2.1](https://github.com/eea/volto-eea-website-policy/compare/0.2.0...0.2.1) - 25 March 2022
62
67
 
63
- > 25 March 2022
68
+ ### [0.2.0](https://github.com/eea/volto-eea-website-policy/compare/0.1.1...0.2.0) - 18 March 2022
64
69
 
65
- - 137187 keycloak integration for plone6 (#3) [`#6`](https://github.com/eea/volto-eea-website-policy/pull/6)
66
- - 137187 keycloak integration for plone6 [`#3`](https://github.com/eea/volto-eea-website-policy/pull/3)
70
+ #### :hammer_and_wrench: Others
67
71
 
68
- #### [0.2.0](https://github.com/eea/volto-eea-website-policy/compare/0.1.1...0.2.0)
72
+ - Release 0.2.0 [Alin Voinea - [`4b72206`](https://github.com/eea/volto-eea-website-policy/commit/4b72206675ab13ed81567df590849613581d7f10)]
73
+ - Update index.js [Alexandru Ghica - [`0f5be15`](https://github.com/eea/volto-eea-website-policy/commit/0f5be1553b608f49c9a61bbc69a0f90219260a83)]
74
+ - Update index.js [Alexandru Ghica - [`03dd3b1`](https://github.com/eea/volto-eea-website-policy/commit/03dd3b162d3175df00048339bed4f9a7d097a896)]
75
+ - format index.js [Andrei Grigore - [`db4481a`](https://github.com/eea/volto-eea-website-policy/commit/db4481aeeba59a769272659f33278c902fc69bec)]
76
+ - Update index.js [Alexandru Ghica - [`dc09886`](https://github.com/eea/volto-eea-website-policy/commit/dc0988640a76a9914381b120f58bb61a9ab1eb39)]
77
+ - Added externalRoutes [Alexandru Ghica - [`b68135b`](https://github.com/eea/volto-eea-website-policy/commit/b68135b013fafa93a41ea602ea2fad30879abe0f)]
78
+ - Add Sonarqube tag using eea-website-frontend addons list [EEA Jenkins - [`575cca6`](https://github.com/eea/volto-eea-website-policy/commit/575cca674602ca2e1c2a41300c47ba594ff49aa0)]
79
+ ### [0.1.1](https://github.com/eea/volto-eea-website-policy/compare/0.1.0...0.1.1) - 4 March 2022
69
80
 
70
- > 18 March 2022
81
+ #### :hammer_and_wrench: Others
71
82
 
72
- - Release [`#5`](https://github.com/eea/volto-eea-website-policy/pull/5)
73
- - Enable multilingual - refs #146771 [`#4`](https://github.com/eea/volto-eea-website-policy/pull/4)
74
- - Release 0.2.0 [`4b72206`](https://github.com/eea/volto-eea-website-policy/commit/4b72206675ab13ed81567df590849613581d7f10)
75
- - Update index.js [`0f5be15`](https://github.com/eea/volto-eea-website-policy/commit/0f5be1553b608f49c9a61bbc69a0f90219260a83)
76
- - Update index.js [`03dd3b1`](https://github.com/eea/volto-eea-website-policy/commit/03dd3b162d3175df00048339bed4f9a7d097a896)
77
- - format index.js [`db4481a`](https://github.com/eea/volto-eea-website-policy/commit/db4481aeeba59a769272659f33278c902fc69bec)
78
- - Update index.js [`dc09886`](https://github.com/eea/volto-eea-website-policy/commit/dc0988640a76a9914381b120f58bb61a9ab1eb39)
79
- - Added externalRoutes [`b68135b`](https://github.com/eea/volto-eea-website-policy/commit/b68135b013fafa93a41ea602ea2fad30879abe0f)
83
+ - Enable description block by default [Alin Voinea - [`b3d6534`](https://github.com/eea/volto-eea-website-policy/commit/b3d6534a57f7288899c4f59b5cec553370363d8b)]
84
+ - Prepare for first release [Alin Voinea - [`69e36b1`](https://github.com/eea/volto-eea-website-policy/commit/69e36b1c82b75b0917ef3d433581c50cdcf6c341)]
85
+ - Enable working copy support [Alin Voinea - [`7c6269d`](https://github.com/eea/volto-eea-website-policy/commit/7c6269d75df915f39c15e8ed0893cc062601e7a7)]
86
+ - Enable multilingual [Alin Voinea - [`73210b9`](https://github.com/eea/volto-eea-website-policy/commit/73210b90ad968909da0c15583992f8f58e99e632)]
87
+ ### 0.1.0 - 4 March 2022
80
88
 
81
- #### [0.1.1](https://github.com/eea/volto-eea-website-policy/compare/0.1.0...0.1.1)
89
+ #### :hammer_and_wrench: Others
82
90
 
83
- > 4 March 2022
84
-
85
- - Release [`#1`](https://github.com/eea/volto-eea-website-policy/pull/1)
86
- - Enable description block by default [`b3d6534`](https://github.com/eea/volto-eea-website-policy/commit/b3d6534a57f7288899c4f59b5cec553370363d8b)
87
- - Prepare for first release [`69e36b1`](https://github.com/eea/volto-eea-website-policy/commit/69e36b1c82b75b0917ef3d433581c50cdcf6c341)
88
- - Enable working copy support [`7c6269d`](https://github.com/eea/volto-eea-website-policy/commit/7c6269d75df915f39c15e8ed0893cc062601e7a7)
89
- - Enable multilingual [`73210b9`](https://github.com/eea/volto-eea-website-policy/commit/73210b90ad968909da0c15583992f8f58e99e632)
90
-
91
- #### 0.1.0
92
-
93
- > 4 March 2022
94
-
95
- - Initial commit [`3800692`](https://github.com/eea/volto-eea-website-policy/commit/3800692f7a4de6460815bd6061facacb986cbef0)
91
+ - yarn bootstrap [Alin Voinea - [`9b4d197`](https://github.com/eea/volto-eea-website-policy/commit/9b4d197cbc7992566cb36524d190078d5ad21a44)]
92
+ - Initial commit [Alin Voinea - [`3800692`](https://github.com/eea/volto-eea-website-policy/commit/3800692f7a4de6460815bd6061facacb986cbef0)]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-policy",
3
- "version": "0.3.0",
3
+ "version": "0.3.3",
4
4
  "description": "@eeacms/volto-eea-website-policy: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { EditSchema } from './schema';
3
+ import { InlineForm, SidebarPortal } from '@plone/volto/components';
4
+ import ContextNavigationView from './ContextNavigationView';
5
+
6
+ const ContextNavigationFillView = (props) => {
7
+ const schema = EditSchema();
8
+ return (
9
+ <>
10
+ <h3>Context navigation</h3>
11
+ <ContextNavigationView {...props} />{' '}
12
+ <SidebarPortal selected={props.selected}>
13
+ {props.selected && (
14
+ <InlineForm
15
+ title={schema.title}
16
+ schema={schema}
17
+ formData={props.data}
18
+ onChangeField={(id, value) => {
19
+ props.onChangeBlock(props.block, {
20
+ ...props.data,
21
+ [id]: value,
22
+ });
23
+ }}
24
+ />
25
+ )}
26
+ </SidebarPortal>
27
+ </>
28
+ );
29
+ };
30
+
31
+ export default ContextNavigationFillView;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import ConnectedContextNavigation from '@plone/volto/components/theme/Navigation/ContextNavigation';
3
+ import { flattenToAppURL } from '@plone/volto/helpers';
4
+
5
+ const ContextNavigationView = ({ data = {} }) => {
6
+ const navProps = { ...data };
7
+ const root_path = data?.root_node?.[0]?.['@id'];
8
+ if (root_path) navProps['root_path'] = flattenToAppURL(root_path);
9
+
10
+ return (
11
+ <>
12
+ <ConnectedContextNavigation params={navProps} />
13
+ </>
14
+ );
15
+ };
16
+
17
+ export default ContextNavigationView;
@@ -0,0 +1,26 @@
1
+ import codeSVG from '@plone/volto/icons/code.svg';
2
+ import ContextNavigationEdit from './ContextNavigationEdit';
3
+ import ContextNavigationView from './ContextNavigationView';
4
+ import BlockSettingsSchema from '@plone/volto/components/manage/Blocks/Block/Schema';
5
+
6
+ export default (config) => {
7
+ config.blocks.blocksConfig.contextNavigation = {
8
+ id: 'contextNavigation',
9
+ title: 'Navigation',
10
+ icon: codeSVG,
11
+ group: 'site',
12
+ view: ContextNavigationView,
13
+ edit: ContextNavigationEdit,
14
+ schema: BlockSettingsSchema,
15
+ restricted: false,
16
+ mostUsed: true,
17
+ blockHasOwnFocusManagement: true,
18
+ sidebarTab: 1,
19
+ security: {
20
+ addPermission: [],
21
+ view: [],
22
+ },
23
+ };
24
+
25
+ return config;
26
+ };
@@ -0,0 +1,81 @@
1
+ export const EditSchema = () => {
2
+ return {
3
+ title: 'Navigation',
4
+ fieldsets: [
5
+ {
6
+ id: 'default',
7
+ title: 'Default',
8
+ fields: [
9
+ 'name',
10
+ 'root_node',
11
+ 'includeTop',
12
+ 'currentFolderOnly',
13
+ 'topLevel',
14
+ 'bottomLevel',
15
+ 'no_icons',
16
+ 'thumb_scale',
17
+ 'no_thumbs',
18
+ ],
19
+ },
20
+ ],
21
+ required: [],
22
+ properties: {
23
+ name: {
24
+ title: 'Title',
25
+ description: 'The title of the navigation tree',
26
+ },
27
+ root_node: {
28
+ title: 'Root node',
29
+ description:
30
+ 'You may search for and choose a folder to act as the root of the navigation tree. Leave blank to use the Plone site root.',
31
+ widget: 'object_browser',
32
+ // TODO: these don't work. Why?
33
+ mode: 'link',
34
+ selectedItemAttrs: ['Title', 'Description'],
35
+ },
36
+ includeTop: {
37
+ title: 'Include top node',
38
+ description:
39
+ "Whether or not to show the top, or 'root', node in the navigation tree. This is affected by the 'Start level' setting.",
40
+ type: 'boolean',
41
+ },
42
+ currentFolderOnly: {
43
+ title: 'Only show the contents of the current folder',
44
+ description:
45
+ 'If selected, the navigation tree will only show the current folder and its children at all times.',
46
+ type: 'boolean',
47
+ },
48
+
49
+ topLevel: {
50
+ title: 'Start level',
51
+ description:
52
+ 'An integer value that specifies the number of folder levels below the site root that must be exceeded before the navigation tree will display. 0 means that the navigation tree should be displayed everywhere including pages in the root of the site. 1 means the tree only shows up inside folders located in the root and downwards, never showing at the top level.',
53
+ type: 'number',
54
+ default: 1,
55
+ },
56
+ bottomLevel: {
57
+ title: 'Navigation tree depth',
58
+ description:
59
+ 'How many folders should be included before the navigation tree stops. 0 means no limit. 1 only includes the root folder.',
60
+ type: 'number',
61
+ default: 0,
62
+ },
63
+ no_icons: {
64
+ title: 'Supress icons',
65
+ description:
66
+ 'If enabled, the portlet will not show document type icons.',
67
+ type: 'boolean',
68
+ },
69
+ thumb_scale: {
70
+ title: 'Override thumb scale',
71
+ description:
72
+ "Enter a valid scale name (see 'Image Handling' control panel) to override (e.g. icon, tile, thumb, mini, preview, ... ). Leave empty to use default (see 'Site' control panel).",
73
+ },
74
+ no_thumbs: {
75
+ title: 'Supress thumbs',
76
+ type: 'boolean',
77
+ description: 'If enabled, the portlet will not show thumbs.',
78
+ },
79
+ },
80
+ };
81
+ };
@@ -0,0 +1,520 @@
1
+ /**
2
+ * Content Type component.
3
+ * @module components/manage/Controlpanels/ContentTypeLayout
4
+ */
5
+
6
+ import React, { Component } from 'react';
7
+ import PropTypes from 'prop-types';
8
+ import { connect } from 'react-redux';
9
+ import { compose } from 'redux';
10
+ import { Link } from 'react-router-dom';
11
+ import {
12
+ getParentUrl,
13
+ hasBlocksData,
14
+ getBlocksFieldname,
15
+ getBlocksLayoutFieldname,
16
+ } from '@plone/volto/helpers';
17
+ import { Portal } from 'react-portal';
18
+ import { Button, Segment } from 'semantic-ui-react';
19
+ import { toast } from 'react-toastify';
20
+ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
21
+ import { nth, join } from 'lodash';
22
+ import {
23
+ Error,
24
+ Form,
25
+ Icon,
26
+ Toolbar,
27
+ Sidebar,
28
+ Toast,
29
+ } from '@plone/volto/components';
30
+ import {
31
+ getSchema,
32
+ updateSchema,
33
+ getControlpanel,
34
+ updateControlpanel,
35
+ } from '@plone/volto/actions';
36
+
37
+ import saveSVG from '@plone/volto/icons/save.svg';
38
+ import clearSVG from '@plone/volto/icons/clear.svg';
39
+ import backSVG from '@plone/volto/icons/back.svg';
40
+
41
+ const messages = defineMessages({
42
+ changesSaved: {
43
+ id: 'Changes saved.',
44
+ defaultMessage: 'Changes saved.',
45
+ },
46
+ back: {
47
+ id: 'Back',
48
+ defaultMessage: 'Back',
49
+ },
50
+ save: {
51
+ id: 'Save',
52
+ defaultMessage: 'Save',
53
+ },
54
+ cancel: {
55
+ id: 'Cancel',
56
+ defaultMessage: 'Cancel',
57
+ },
58
+ info: {
59
+ id: 'Info',
60
+ defaultMessage: 'Info',
61
+ },
62
+ enable: {
63
+ id: 'Enable editable Blocks',
64
+ defaultMessage: 'Enable editable Blocks',
65
+ },
66
+ });
67
+
68
+ /**
69
+ * ContentTypeLayout class.
70
+ * @class ContentTypeLayout
71
+ * @extends Component
72
+ */
73
+ class ContentTypeLayout extends Component {
74
+ /**
75
+ * Property types.
76
+ * @property {Object} propTypes Property types.
77
+ * @static
78
+ */
79
+ static propTypes = {
80
+ updateControlpanel: PropTypes.func.isRequired,
81
+ getControlpanel: PropTypes.func.isRequired,
82
+ getSchema: PropTypes.func.isRequired,
83
+ updateSchema: PropTypes.func.isRequired,
84
+ id: PropTypes.string.isRequired,
85
+ parent: PropTypes.string.isRequired,
86
+ pathname: PropTypes.string.isRequired,
87
+ schemaRequest: PropTypes.objectOf(PropTypes.any).isRequired,
88
+ cpanelRequest: PropTypes.objectOf(PropTypes.any).isRequired,
89
+ schema: PropTypes.objectOf(PropTypes.any),
90
+ controlpanel: PropTypes.shape({
91
+ '@id': PropTypes.string,
92
+ data: PropTypes.object,
93
+ schema: PropTypes.object,
94
+ title: PropTypes.string,
95
+ }),
96
+ };
97
+
98
+ /**
99
+ * Default properties.
100
+ * @property {Object} defaultProps Default properties.
101
+ * @static
102
+ */
103
+ static defaultProps = {
104
+ schema: {},
105
+ controlpanel: null,
106
+ };
107
+
108
+ /**
109
+ * Constructor
110
+ * @method constructor
111
+ * @param {Object} props Component properties
112
+ * @constructs ContentTypeLayout
113
+ */
114
+ constructor(props) {
115
+ super(props);
116
+
117
+ this.state = {
118
+ visual: false,
119
+ content: null,
120
+ readOnlyBehavior: null,
121
+ error: null,
122
+ isClient: false,
123
+ };
124
+
125
+ this.onCancel = this.onCancel.bind(this);
126
+ this.onSubmit = this.onSubmit.bind(this);
127
+ this.onEnableBlocks = this.onEnableBlocks.bind(this);
128
+ this.onEnableBlocksBehavior = this.onEnableBlocksBehavior.bind(this);
129
+ this.onDisableBlocksBehavior = this.onDisableBlocksBehavior.bind(this);
130
+ this.form = React.createRef();
131
+ }
132
+
133
+ /**
134
+ * Component did mount
135
+ * @method componentDidMount
136
+ * @returns {undefined}
137
+ */
138
+ componentDidMount() {
139
+ this.props.getControlpanel(join([this.props.parent, this.props.id], '/'));
140
+ this.props.getSchema(this.props.id);
141
+ this.setState({ isClient: true });
142
+ }
143
+
144
+ /**
145
+ * Component will receive props
146
+ * @method componentWillReceiveProps
147
+ * @param {Object} nextProps Next properties
148
+ * @returns {undefined}
149
+ */
150
+ UNSAFE_componentWillReceiveProps(nextProps) {
151
+ // Control Panel GET
152
+ if (
153
+ this.props.cpanelRequest.get.loading &&
154
+ nextProps.cpanelRequest.get.error
155
+ ) {
156
+ this.setState({
157
+ error: nextProps.cpanelRequest.get.error,
158
+ });
159
+ }
160
+
161
+ // Schema GET
162
+ if (this.props.schemaRequest.loading && nextProps.schemaRequest.loaded) {
163
+ const properties = nextProps.schema?.properties || {};
164
+ const content = {};
165
+ for (const key in properties) {
166
+ const value = properties[key].default;
167
+ if (value) {
168
+ content[key] = value;
169
+ }
170
+ }
171
+
172
+ if (hasBlocksData(properties)) {
173
+ this.setState({
174
+ visual: true,
175
+ });
176
+
177
+ const blocksFieldName = getBlocksFieldname(properties);
178
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
179
+ content[blocksFieldName] = properties[blocksFieldName]?.default || {};
180
+ content[blocksLayoutFieldname] = properties[blocksLayoutFieldname]
181
+ ?.default || { items: [] };
182
+
183
+ const blocksBehavior = properties[blocksFieldName]?.behavior || '';
184
+ this.setState({
185
+ readOnlyBehavior: !blocksBehavior.includes('generated')
186
+ ? blocksBehavior
187
+ : '',
188
+ });
189
+ } else {
190
+ this.setState({
191
+ visual: false,
192
+ readOnlyBehavior: '',
193
+ });
194
+ }
195
+
196
+ this.setState({
197
+ content: content,
198
+ });
199
+ }
200
+
201
+ // Schema updated
202
+ if (
203
+ this.props.schemaRequest.update.loading &&
204
+ nextProps.schemaRequest.update.loaded
205
+ ) {
206
+ this.props.getSchema(this.props.id);
207
+ toast.info(
208
+ <Toast
209
+ info
210
+ title={this.props.intl.formatMessage(messages.info)}
211
+ content={this.props.intl.formatMessage(messages.changesSaved)}
212
+ />,
213
+ );
214
+ }
215
+
216
+ // Blocks behavior disabled
217
+ if (
218
+ this.props.cpanelRequest.update.loading &&
219
+ nextProps.cpanelRequest.update.loaded
220
+ ) {
221
+ this.onEnableBlocks();
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Submit handler
227
+ * @method onSubmit
228
+ * @param {object} data Form data.
229
+ * @returns {undefined}
230
+ */
231
+ onSubmit(data) {
232
+ const schema = { properties: {} };
233
+ Object.keys(data)
234
+ .filter((k) => data[k])
235
+ .forEach((k) => (schema.properties[k] = { default: data[k] }));
236
+ this.props.updateSchema(this.props.id, schema);
237
+ }
238
+
239
+ /**
240
+ * Cancel handler
241
+ * @method onCancel
242
+ * @returns {undefined}
243
+ */
244
+ onCancel() {
245
+ const url = getParentUrl(this.props.pathname);
246
+ this.props.history.push(getParentUrl(url));
247
+ }
248
+
249
+ /**
250
+ * Enable blocks handler
251
+ * @method onEnableBlocks
252
+ * @returns {undefined}
253
+ */
254
+ onEnableBlocks() {
255
+ const { properties = {} } = this.props.schema;
256
+ const blocksFieldName = getBlocksFieldname(properties);
257
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
258
+ const schema = {
259
+ fieldsets: [
260
+ {
261
+ id: 'layout',
262
+ title: 'Layout',
263
+ fields: ['blocks', 'blocks_layout'],
264
+ },
265
+ ],
266
+ properties: {
267
+ blocks: {
268
+ title: 'Blocks',
269
+ type: 'dict',
270
+ widget: 'json',
271
+ factory: 'JSONField',
272
+ default: properties[blocksFieldName]?.default || {},
273
+ },
274
+ blocks_layout: {
275
+ title: 'Blocks Layout',
276
+ type: 'dict',
277
+ widget: 'json',
278
+ factory: 'JSONField',
279
+ default: properties[blocksLayoutFieldname]?.default || { items: [] },
280
+ },
281
+ },
282
+ };
283
+ this.props.updateSchema(this.props.id, schema);
284
+ }
285
+
286
+ /**
287
+ * Disable Blocks behavior handler
288
+ * @method onDisableBlocksBehavior
289
+ * @returns {undefined}
290
+ */
291
+ onDisableBlocksBehavior() {
292
+ this.props.updateControlpanel(this.props.controlpanel['@id'], {
293
+ [this.state.readOnlyBehavior]: false,
294
+ 'volto.blocks.editable.layout': true,
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Enable Blocks behavior handler
300
+ * @method onEnableBlocksBehavior
301
+ * @returns {undefined}
302
+ */
303
+ onEnableBlocksBehavior() {
304
+ this.props.updateControlpanel(this.props.controlpanel['@id'], {
305
+ 'volto.blocks.editable.layout': true,
306
+ });
307
+ }
308
+
309
+ /**
310
+ * Render method.
311
+ * @method render
312
+ * @returns {string} Markup for the component.
313
+ */
314
+ render() {
315
+ // Error
316
+ if (this.state.error) {
317
+ return <Error error={this.state.error} />;
318
+ }
319
+
320
+ if (!this.state.visual) {
321
+ // Still loading
322
+ if (!this.state.content) {
323
+ return <div />;
324
+ }
325
+
326
+ // Blocks are not enabled
327
+ return (
328
+ <>
329
+ <Segment
330
+ placeholder
331
+ id="page-controlpanel-layout"
332
+ className="ui container center aligned"
333
+ >
334
+ <div>
335
+ <FormattedMessage
336
+ id="Can not edit Layout for <strong>{type}</strong> content-type as it doesn't have support for <strong>Volto Blocks</strong> enabled"
337
+ defaultMessage="Can not edit Layout for <strong>{type}</strong> content-type as it doesn't have support for <strong>Volto Blocks</strong> enabled"
338
+ values={{
339
+ strong: (...chunks) => <strong>{chunks}</strong>,
340
+ type: this.props?.controlpanel?.title || this.props.id,
341
+ }}
342
+ />
343
+ </div>
344
+ <div className="ui divider"></div>
345
+ <Button
346
+ primary
347
+ onClick={this.onEnableBlocksBehavior}
348
+ content={this.props.intl.formatMessage(messages.enable)}
349
+ />
350
+ </Segment>
351
+ <Portal
352
+ node={this.state.isClient && document.getElementById('toolbar')}
353
+ >
354
+ <Toolbar
355
+ pathname={this.props.pathname}
356
+ hideDefaultViewButtons
357
+ inner={
358
+ <>
359
+ <Link className="item" to="#" onClick={() => this.onCancel()}>
360
+ <Icon
361
+ name={backSVG}
362
+ size="30px"
363
+ className="contents circled"
364
+ title={this.props.intl.formatMessage(messages.back)}
365
+ />
366
+ </Link>
367
+ </>
368
+ }
369
+ />
370
+ </Portal>
371
+ </>
372
+ );
373
+ }
374
+
375
+ if (this.state.readOnlyBehavior) {
376
+ return (
377
+ <>
378
+ <Segment
379
+ placeholder
380
+ id="page-controlpanel-layout"
381
+ className="ui container center aligned"
382
+ >
383
+ <div>
384
+ <FormattedMessage
385
+ id="Can not edit Layout for <strong>{type}</strong> content-type as the <strong>Blocks behavior</strong> is enabled and <strong>read-only</strong>"
386
+ defaultMessage="Can not edit Layout for <strong>{type}</strong> content-type as the <strong>Blocks behavior</strong> is enabled and <strong>read-only</strong>"
387
+ values={{
388
+ strong: (...chunks) => <strong>{chunks}</strong>,
389
+ type: this.props?.controlpanel?.title || this.props.id,
390
+ }}
391
+ />
392
+ </div>
393
+ <div className="ui divider"></div>
394
+ <Button
395
+ primary
396
+ onClick={this.onDisableBlocksBehavior}
397
+ content={this.props.intl.formatMessage(messages.enable)}
398
+ />
399
+ </Segment>
400
+ <Portal
401
+ node={this.state.isClient && document.getElementById('toolbar')}
402
+ >
403
+ <Toolbar
404
+ pathname={this.props.pathname}
405
+ hideDefaultViewButtons
406
+ inner={
407
+ <>
408
+ <Link className="item" to="#" onClick={() => this.onCancel()}>
409
+ <Icon
410
+ name={backSVG}
411
+ size="30px"
412
+ className="contents circled"
413
+ title={this.props.intl.formatMessage(messages.back)}
414
+ />
415
+ </Link>
416
+ </>
417
+ }
418
+ />
419
+ </Portal>
420
+ </>
421
+ );
422
+ }
423
+
424
+ // Render layout editor
425
+ const blocksFieldName = getBlocksFieldname(
426
+ this.props.schema?.properties || {},
427
+ );
428
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(
429
+ this.props.schema?.properties || {},
430
+ );
431
+ return (
432
+ <div id="page-controlpanel-layout">
433
+ <Form
434
+ isAdminForm
435
+ ref={this.form}
436
+ schema={{
437
+ fieldsets: [
438
+ {
439
+ id: 'layout',
440
+ title: 'Layout',
441
+ fields: [blocksFieldName, blocksLayoutFieldname],
442
+ },
443
+ ],
444
+ properties: {
445
+ ...this.props.schema.properties[blocksFieldName],
446
+ ...this.props.schema.properties[blocksLayoutFieldname],
447
+ },
448
+ required: [],
449
+ }}
450
+ formData={this.state.content}
451
+ onSubmit={this.onSubmit}
452
+ onCancel={this.onCancel}
453
+ pathname={this.props.pathname}
454
+ visual={this.state.visual}
455
+ hideActions
456
+ />
457
+ <Portal
458
+ node={this.state.isClient && document.getElementById('sidebar')}
459
+ >
460
+ <Sidebar settingsTab={true} documentTab={false} />
461
+ </Portal>
462
+ <Portal
463
+ node={this.state.isClient && document.getElementById('toolbar')}
464
+ >
465
+ <Toolbar
466
+ pathname={this.props.pathname}
467
+ hideDefaultViewButtons
468
+ inner={
469
+ <>
470
+ <Button
471
+ id="toolbar-save"
472
+ className="save"
473
+ aria-label={this.props.intl.formatMessage(messages.save)}
474
+ onClick={() => this.form.current.onSubmit()}
475
+ disabled={this.props.schemaRequest.update.loading}
476
+ loading={this.props.schemaRequest.update.loading}
477
+ >
478
+ <Icon
479
+ name={saveSVG}
480
+ className="circled"
481
+ size="30px"
482
+ title={this.props.intl.formatMessage(messages.save)}
483
+ />
484
+ </Button>
485
+ <Button
486
+ className="cancel"
487
+ aria-label={this.props.intl.formatMessage(messages.cancel)}
488
+ onClick={() => this.onCancel()}
489
+ >
490
+ <Icon
491
+ name={clearSVG}
492
+ className="circled"
493
+ size="30px"
494
+ title={this.props.intl.formatMessage(messages.cancel)}
495
+ />
496
+ </Button>
497
+ </>
498
+ }
499
+ />
500
+ </Portal>
501
+ </div>
502
+ );
503
+ }
504
+ }
505
+
506
+ export default compose(
507
+ injectIntl,
508
+ connect(
509
+ (state, props) => ({
510
+ schema: state.schema.schema,
511
+ schemaRequest: state.schema,
512
+ cpanelRequest: state.controlpanels,
513
+ controlpanel: state.controlpanels.controlpanel,
514
+ pathname: props.location.pathname,
515
+ id: nth(props.location.pathname.split('/'), -2),
516
+ parent: nth(props.location.pathname.split('/'), -3),
517
+ }),
518
+ { getSchema, updateSchema, getControlpanel, updateControlpanel },
519
+ ),
520
+ )(ContentTypeLayout);
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Blocks helper.
3
+ * @module helpers/Blocks
4
+ */
5
+
6
+ import { omit, without, endsWith, find, isObject, keys, toPairs } from 'lodash';
7
+ import move from 'lodash-move';
8
+ import { v4 as uuid } from 'uuid';
9
+ import config from '@plone/volto/registry';
10
+ import { applySchemaEnhancer } from '@plone/volto/helpers';
11
+
12
+ /**
13
+ * Get blocks field.
14
+ * @function getBlocksFieldname
15
+ * @param {Object} props Properties.
16
+ * @return {string} Field name of the blocks
17
+ */
18
+ export function getBlocksFieldname(props) {
19
+ return (
20
+ find(
21
+ keys(props),
22
+ (key) => key !== 'volto.blocks' && endsWith(key, 'blocks'),
23
+ ) || null
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Get blocks layout field.
29
+ * @function getBlocksLayoutFieldname
30
+ * @param {Object} props Properties.
31
+ * @return {string} Field name of the blocks layout
32
+ */
33
+ export function getBlocksLayoutFieldname(props) {
34
+ return (
35
+ find(
36
+ keys(props),
37
+ (key) => key !== 'volto.blocks' && endsWith(key, 'blocks_layout'),
38
+ ) || null
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Has blocks data.
44
+ * @function hasBlocksData
45
+ * @param {Object} props Properties.
46
+ * @return {boolean} True if it has blocks data.
47
+ */
48
+ export function hasBlocksData(props) {
49
+ return (
50
+ find(
51
+ keys(props),
52
+ (key) => key !== 'volto.blocks' && endsWith(key, 'blocks'),
53
+ ) !== undefined
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Pluggable method to test if a block has a set value (any non-empty value)
59
+ * @function blockHasValue
60
+ * @param {Object} data Block data
61
+ * @return {boolean} True if block has a non-empty value
62
+ */
63
+ export function blockHasValue(data) {
64
+ const { blocks } = config;
65
+ const blockType = data['@type'];
66
+ const check = blocks.blocksConfig[blockType]?.blockHasValue;
67
+ if (!check) {
68
+ return true;
69
+ }
70
+ return check(data);
71
+ }
72
+
73
+ /**
74
+ * Get block pairs of [id, block] from content properties
75
+ * @function getBlocks
76
+ * @param {Object} properties
77
+ * @return {Array} a list of block [id, value] pairs, in order from layout
78
+ */
79
+ export const getBlocks = (properties) => {
80
+ const blocksFieldName = getBlocksFieldname(properties);
81
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
82
+ return (
83
+ properties[blocksLayoutFieldname]?.items?.map((n) => [
84
+ n,
85
+ properties[blocksFieldName][n],
86
+ ]) || []
87
+ );
88
+ };
89
+
90
+ /**
91
+ * Move block to different location index within blocks_layout
92
+ * @function moveBlock
93
+ * @param {Object} formData Form data
94
+ * @param {number} source index within form blocks_layout items
95
+ * @param {number} destination index within form blocks_layout items
96
+ * @return {Object} New form data
97
+ */
98
+ export function moveBlock(formData, source, destination) {
99
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
100
+ return {
101
+ ...formData,
102
+ [blocksLayoutFieldname]: {
103
+ items: move(formData[blocksLayoutFieldname].items, source, destination),
104
+ },
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Delete block by id
110
+ * @function deleteBlock
111
+ * @param {Object} formData Form data
112
+ * @param {string} blockId Block uid
113
+ * @return {Object} New form data
114
+ */
115
+ export function deleteBlock(formData, blockId) {
116
+ const blocksFieldname = getBlocksFieldname(formData);
117
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
118
+
119
+ let newFormData = {
120
+ ...formData,
121
+ [blocksLayoutFieldname]: {
122
+ items: without(formData[blocksLayoutFieldname].items, blockId),
123
+ },
124
+ [blocksFieldname]: omit(formData[blocksFieldname], [blockId]),
125
+ };
126
+
127
+ if (newFormData[blocksLayoutFieldname].items.length === 0) {
128
+ newFormData = addBlock(newFormData, config.settings.defaultBlockType, 0);
129
+ }
130
+
131
+ return newFormData;
132
+ }
133
+
134
+ /**
135
+ * Add block
136
+ * @function addBlock
137
+ * @param {Object} formData Form data
138
+ * @param {string} type Block type
139
+ * @param {number} index Destination index
140
+ * @return {Array} New block id, New form data
141
+ */
142
+ export function addBlock(formData, type, index) {
143
+ const { settings } = config;
144
+ const id = uuid();
145
+ const idTrailingBlock = uuid();
146
+ const blocksFieldname = getBlocksFieldname(formData);
147
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
148
+ const totalItems = formData[blocksLayoutFieldname].items.length;
149
+ const insert = index === -1 ? totalItems : index;
150
+
151
+ return [
152
+ id,
153
+ {
154
+ ...formData,
155
+ [blocksLayoutFieldname]: {
156
+ items: [
157
+ ...formData[blocksLayoutFieldname].items.slice(0, insert),
158
+ id,
159
+ ...(type !== settings.defaultBlockType ? [idTrailingBlock] : []),
160
+ ...formData[blocksLayoutFieldname].items.slice(insert),
161
+ ],
162
+ },
163
+ [blocksFieldname]: {
164
+ ...formData[blocksFieldname],
165
+ [id]: {
166
+ '@type': type,
167
+ },
168
+ ...(type !== settings.defaultBlockType && {
169
+ [idTrailingBlock]: {
170
+ '@type': settings.defaultBlockType,
171
+ },
172
+ }),
173
+ },
174
+ selected: id,
175
+ },
176
+ ];
177
+ }
178
+
179
+ /**
180
+ * Mutate block
181
+ * @function mutateBlock
182
+ * @param {Object} formData Form data
183
+ * @param {string} id Block uid to mutate
184
+ * @param {number} value Block's new value
185
+ * @return {Object} New form data
186
+ */
187
+ export function mutateBlock(formData, id, value) {
188
+ const { settings } = config;
189
+ const blocksFieldname = getBlocksFieldname(formData);
190
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
191
+ const index = formData[blocksLayoutFieldname].items.indexOf(id) + 1;
192
+
193
+ // Test if block at index is already a placeholder (trailing) block
194
+ const trailId = formData[blocksLayoutFieldname].items[index];
195
+ if (trailId) {
196
+ const block = formData[blocksFieldname][trailId];
197
+ if (!blockHasValue(block)) {
198
+ return {
199
+ ...formData,
200
+ [blocksFieldname]: {
201
+ ...formData[blocksFieldname],
202
+ [id]: value || null,
203
+ },
204
+ };
205
+ }
206
+ }
207
+
208
+ const idTrailingBlock = uuid();
209
+ return {
210
+ ...formData,
211
+ [blocksFieldname]: {
212
+ ...formData[blocksFieldname],
213
+ [id]: value || null,
214
+ [idTrailingBlock]: {
215
+ '@type': settings.defaultBlockType,
216
+ },
217
+ },
218
+ [blocksLayoutFieldname]: {
219
+ items: [
220
+ ...formData[blocksLayoutFieldname].items.slice(0, index),
221
+ idTrailingBlock,
222
+ ...formData[blocksLayoutFieldname].items.slice(index),
223
+ ],
224
+ },
225
+ };
226
+ }
227
+
228
+ /**
229
+ * Insert new block before another block
230
+ * @function insertBlock
231
+ * @param {Object} formData Form data
232
+ * @param {string} id Insert new block before the block with this id
233
+ * @param {number} value New block's value
234
+ * @return {Array} New block id, New form data
235
+ */
236
+ export function insertBlock(formData, id, value, current = {}) {
237
+ const blocksFieldname = getBlocksFieldname(formData);
238
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
239
+ const index = formData[blocksLayoutFieldname].items.indexOf(id);
240
+
241
+ const newBlockId = uuid();
242
+ return [
243
+ newBlockId,
244
+ {
245
+ ...formData,
246
+ [blocksFieldname]: {
247
+ ...formData[blocksFieldname],
248
+ [newBlockId]: value || null,
249
+ [id]: {
250
+ ...formData[blocksFieldname][id],
251
+ ...current,
252
+ },
253
+ },
254
+ [blocksLayoutFieldname]: {
255
+ items: [
256
+ ...formData[blocksLayoutFieldname].items.slice(0, index),
257
+ newBlockId,
258
+ ...formData[blocksLayoutFieldname].items.slice(index),
259
+ ],
260
+ },
261
+ },
262
+ ];
263
+ }
264
+
265
+ /**
266
+ * Change block
267
+ * @function changeBlock
268
+ * @param {Object} formData Form data
269
+ * @param {string} id Block uid to change
270
+ * @param {number} value Block's new value
271
+ * @return {Object} New form data
272
+ */
273
+ export function changeBlock(formData, id, value) {
274
+ const blocksFieldname = getBlocksFieldname(formData);
275
+ return {
276
+ ...formData,
277
+ [blocksFieldname]: {
278
+ ...formData[blocksFieldname],
279
+ [id]: value || null,
280
+ },
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Get the next block UID within form
286
+ * @function nextBlockId
287
+ * @param {Object} formData Form data
288
+ * @param {string} currentBlock Block uid
289
+ * @return {string} Next block uid
290
+ */
291
+ export function nextBlockId(formData, currentBlock) {
292
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
293
+ const currentIndex = formData[blocksLayoutFieldname].items.indexOf(
294
+ currentBlock,
295
+ );
296
+
297
+ if (currentIndex === formData[blocksLayoutFieldname].items.length - 1) {
298
+ // We are already at the bottom block don't do anything
299
+ return null;
300
+ }
301
+
302
+ const newIndex = currentIndex + 1;
303
+ return formData[blocksLayoutFieldname].items[newIndex];
304
+ }
305
+
306
+ /**
307
+ * Get the previous block UID within form
308
+ * @function previousBlockId
309
+ * @param {Object} formData Form data
310
+ * @param {string} currentBlock Block uid
311
+ * @return {string} Previous block uid
312
+ */
313
+ export function previousBlockId(formData, currentBlock) {
314
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
315
+ const currentIndex = formData[blocksLayoutFieldname].items.indexOf(
316
+ currentBlock,
317
+ );
318
+
319
+ if (currentIndex === 0) {
320
+ // We are already at the top block don't do anything
321
+ return null;
322
+ }
323
+ const newindex = currentIndex - 1;
324
+ return formData[blocksLayoutFieldname].items[newindex];
325
+ }
326
+
327
+ /**
328
+ * Generate empty block form
329
+ * @function emptyBlocksForm
330
+ * @param {Object} formData Form data
331
+ * @return {Object} Empty blocks form with one defaultBlockType block
332
+ */
333
+ export function emptyBlocksForm() {
334
+ const { settings } = config;
335
+ const id = uuid();
336
+ return {
337
+ blocks: {
338
+ [id]: {
339
+ '@type': settings.defaultBlockType,
340
+ },
341
+ },
342
+ blocks_layout: { items: [id] },
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Recursively discover blocks in data and call the provided callback
348
+ * @function visitBlocks
349
+ * @param {Object} content A content data structure (an object with blocks and blocks_layout)
350
+ * @param {Function} callback A function to call on each discovered block
351
+ */
352
+ export function visitBlocks(content, callback) {
353
+ const queue = getBlocks(content);
354
+ while (queue.length > 0) {
355
+ const [id, blockdata] = queue.shift();
356
+ callback([id, blockdata]);
357
+
358
+ // assumes that a block value is like: {blocks, blocks_layout} or
359
+ // { data: {blocks, blocks_layout}}
360
+ if (Object.keys(blockdata || {}).indexOf('blocks') > -1) {
361
+ queue.push(...getBlocks(blockdata));
362
+ }
363
+ if (Object.keys(blockdata?.data || {}).indexOf('blocks') > -1) {
364
+ queue.push(...getBlocks(blockdata.data));
365
+ }
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Initializes data with the default values coming from schema
371
+ */
372
+ export function applySchemaDefaults({ data = {}, schema }) {
373
+ const derivedData = {
374
+ ...Object.keys(schema.properties).reduce((accumulator, currentField) => {
375
+ return schema.properties[currentField].default
376
+ ? {
377
+ ...accumulator,
378
+ [currentField]: schema.properties[currentField].default,
379
+ }
380
+ : accumulator;
381
+ }, {}),
382
+ ...data,
383
+ };
384
+ return derivedData;
385
+ }
386
+
387
+ /**
388
+ * Apply the block's default (as defined in schema) to the block data.
389
+ *
390
+ * @function applyBlockDefaults
391
+ * @param {Object} params An object with data, intl and anything else
392
+ * @return {Object} Derived data, with the defaults extracted from the schema
393
+ */
394
+ export function applyBlockDefaults({ data = {}, intl, ...rest }, blocksConfig) {
395
+ const block_type = data['@type'];
396
+ const { blockSchema } =
397
+ (blocksConfig || config.blocks.blocksConfig)[block_type] || {};
398
+ if (!blockSchema) return data;
399
+
400
+ let schema =
401
+ typeof blockSchema === 'function'
402
+ ? blockSchema({ data, intl, ...rest })
403
+ : blockSchema;
404
+ schema = applySchemaEnhancer({ schema, formData: data, intl });
405
+
406
+ return applySchemaDefaults({ data, schema });
407
+ }
408
+
409
+ export const buildStyleClassNamesFromData = (styles) => {
410
+ // styles has the form
411
+ // const styles = {
412
+ // color: 'red',
413
+ // backgroundColor: '#AABBCC',
414
+ // }
415
+ // Returns: ['has--color--red', 'has--backgroundColor--AABBCC']
416
+ let styleArray = [];
417
+ const pairedStyles = toPairs(styles);
418
+ pairedStyles.forEach((item) => {
419
+ if (isObject(item[1])) {
420
+ const flattenedNestedStyles = toPairs(item[1]).map((nested) => [
421
+ item[0],
422
+ ...nested,
423
+ ]);
424
+ flattenedNestedStyles.forEach((sub) => styleArray.push(sub));
425
+ } else {
426
+ styleArray.push(item);
427
+ }
428
+ });
429
+ return styleArray.map((item) => {
430
+ const classname = item.map((item) => {
431
+ const str_item = item ? item.toString() : '';
432
+ return str_item && str_item.startsWith('#')
433
+ ? str_item.replace('#', '')
434
+ : str_item;
435
+ });
436
+ return `has--${classname[0]}--${classname[1]}${
437
+ classname[2] ? `--${classname[2]}` : ''
438
+ }`;
439
+ });
440
+ };
package/src/index.js CHANGED
@@ -2,6 +2,7 @@ import { runtimeConfig } from '@plone/volto/runtime_config';
2
2
  import installCallout from 'volto-slate/editor/plugins/Callout';
3
3
  import installItemBlock from '@eeacms/volto-eea-website-policy/components/Blocks/Item';
4
4
  import customizeTeaserBlock from '@eeacms/volto-eea-website-policy/components/Blocks/Teaser';
5
+ import installContextNavigationBlock from '@eeacms/volto-eea-website-policy/components/Blocks/ContextNavigation';
5
6
 
6
7
  const applyConfig = (config) => {
7
8
  // if (process.env.NODE_ENV === 'production') {
@@ -99,10 +100,11 @@ const applyConfig = (config) => {
99
100
  }
100
101
 
101
102
  // Custom blocks
102
- config = [installItemBlock, customizeTeaserBlock].reduce(
103
- (acc, apply) => apply(acc),
104
- config,
105
- );
103
+ config = [
104
+ installItemBlock,
105
+ installContextNavigationBlock,
106
+ customizeTeaserBlock,
107
+ ].reduce((acc, apply) => apply(acc), config);
106
108
  config.blocks.blocksConfig.item.mostUsed = true;
107
109
 
108
110
  // Disable some blocks