@eeacms/volto-eea-website-theme 1.34.0 → 2.0.1

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 (54) hide show
  1. package/.eslintrc.js +7 -2
  2. package/CHANGELOG.md +53 -1
  3. package/docker-compose.yml +1 -1
  4. package/jest-addon.config.js +3 -0
  5. package/package.json +2 -1
  6. package/src/components/manage/Blocks/LayoutSettings/index.js +3 -1
  7. package/src/components/manage/Blocks/Title/index.js +3 -1
  8. package/src/components/manage/Blocks/Title/schema.js +3 -1
  9. package/src/components/theme/Banner/View.jsx +12 -5
  10. package/src/components/theme/DraftBackground/DraftBackground.jsx +30 -21
  11. package/src/components/theme/DraftBackground/DraftBackground.test.jsx +85 -0
  12. package/src/config.js +2 -0
  13. package/src/customizations/@plone/volto-slate/blocks/Text/TextBlockView.jsx +32 -0
  14. package/src/customizations/@plone/volto-slate/editor/render.jsx +75 -0
  15. package/src/customizations/@plone/volto-slate/elementEditor/utils.js +76 -75
  16. package/src/customizations/volto/components/manage/Blocks/Grid/Edit.jsx +70 -0
  17. package/src/customizations/volto/components/manage/Blocks/Grid/View.jsx +61 -0
  18. package/src/customizations/volto/components/manage/Blocks/Grid/readme.md +1 -0
  19. package/src/customizations/volto/components/manage/Blocks/Image/Edit.jsx +82 -23
  20. package/src/customizations/volto/components/manage/Blocks/Image/Edit.test.jsx +10 -3
  21. package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +110 -111
  22. package/src/customizations/volto/components/manage/Blocks/Image/schema.js +17 -2
  23. package/src/customizations/volto/components/manage/Blocks/LeadImage/Edit.jsx +35 -14
  24. package/src/customizations/volto/components/manage/Blocks/LeadImage/View.jsx +65 -79
  25. package/src/customizations/volto/components/manage/Display/Display.jsx +306 -0
  26. package/src/customizations/volto/components/manage/Display/Readme.md +1 -0
  27. package/src/customizations/volto/components/manage/Sidebar/SidebarPopup copy.jsx +82 -0
  28. package/src/customizations/volto/components/manage/Toolbar/More.jsx +541 -0
  29. package/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx +3 -1
  30. package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +24 -14
  31. package/src/customizations/volto/components/manage/Widgets/README.md +1 -0
  32. package/src/customizations/volto/components/manage/Workflow/README.txt +1 -0
  33. package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +324 -0
  34. package/src/customizations/volto/components/manage/Workflow/Workflow.test.jsx +81 -0
  35. package/src/customizations/volto/components/theme/Comments/Comments.jsx +1 -2
  36. package/src/customizations/volto/components/theme/ContactForm/ContactForm.jsx +1 -1
  37. package/src/customizations/volto/components/theme/EventDetails/EventDetails.jsx +1 -0
  38. package/src/index.js +21 -16
  39. package/src/middleware/ok.js +4 -2
  40. package/src/middleware/voltoCustom.js +4 -2
  41. package/src/slate.js +10 -8
  42. package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/README.txt +0 -1
  43. package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/StyleMenu.jsx +0 -157
  44. package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/utils.js +0 -168
  45. package/src/customizations/volto/components/manage/Add/Add.jsx +0 -498
  46. package/src/customizations/volto/components/manage/Add/readme.md +0 -1
  47. package/src/customizations/volto/components/manage/Contents/ContentsPropertiesModal.jsx +0 -232
  48. package/src/customizations/volto/components/manage/Form/Form.jsx +0 -810
  49. package/src/customizations/volto/components/manage/Form/Form.test.jsx +0 -1124
  50. package/src/customizations/volto/components/manage/Form/ModalForm.jsx +0 -326
  51. package/src/customizations/volto/components/manage/Sharing/Sharing.jsx +0 -528
  52. package/src/customizations/volto/components/manage/Sharing/Sharing.test.jsx +0 -72
  53. package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.test.jsx +0 -193
  54. package/src/customizations/volto/components/theme/AppExtras/AppExtras.jsx +0 -27
package/.eslintrc.js CHANGED
@@ -48,8 +48,13 @@ const defaultConfig = {
48
48
  },
49
49
  },
50
50
  rules: {
51
- 'react/jsx-no-target-blank': 'off',
52
- },
51
+ 'react/jsx-no-target-blank': [
52
+ 'error',
53
+ {
54
+ allowReferrer: true,
55
+ },
56
+ ],
57
+ }
53
58
  };
54
59
 
55
60
  const config = addonExtenders.reduce(
package/CHANGELOG.md CHANGED
@@ -4,7 +4,59 @@ 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
- ### [1.33.2](https://github.com/eea/volto-eea-website-theme/compare/1.33.1...1.33.2) - 13 April 2024
7
+ ### [2.0.1](https://github.com/eea/volto-eea-website-theme/compare/2.0.0...2.0.1) - 14 May 2024
8
+
9
+ #### :nail_care: Enhancements
10
+
11
+ - change(draft-image): check if parent has effective date in future for draft image showing [David Ichim - [`608cbb0`](https://github.com/eea/volto-eea-website-theme/commit/608cbb0d06d014c3dc8ba99e2b79cf9103291d68)]
12
+
13
+ #### :hammer_and_wrench: Others
14
+
15
+ - modified param of dateIsInFuture to signal it's a string and not a date [David Ichim - [`e3243a0`](https://github.com/eea/volto-eea-website-theme/commit/e3243a075969a379462ba0b7889fe0d8b52af62a)]
16
+ ## [2.0.0](https://github.com/eea/volto-eea-website-theme/compare/1.34.0...2.0.0) - 13 May 2024
17
+
18
+ #### :rocket: New Features
19
+
20
+ - feat: Volto 17 support - refs #259049 [Teodor Voicu - [`79ce620`](https://github.com/eea/volto-eea-website-theme/commit/79ce6202469523c21462a86cb72b93016b10a2b6)]
21
+
22
+ #### :bug: Bug Fixes
23
+
24
+ - fix(draft_image): added fixes from 1.x.x branch [David Ichim - [`b62de0a`](https://github.com/eea/volto-eea-website-theme/commit/b62de0a8fd9c953210f4b371b5117d70f9e04040)]
25
+
26
+ #### :nail_care: Enhancements
27
+
28
+ - change(draft_image): added comments to DraftBackground code [David Ichim - [`0cb4304`](https://github.com/eea/volto-eea-website-theme/commit/0cb430424281f0d0a78e2f9b1326b6bc4128a6f3)]
29
+ - refactor: Disable data figure and plotly chart - refs #269278 [dobri1408 - [`d8fd0da`](https://github.com/eea/volto-eea-website-theme/commit/d8fd0dab10aceebf57cff5c1e777b795117b5a04)]
30
+ - change(draft-image): to remove image when published date is set to the future [David Ichim - [`9b9e023`](https://github.com/eea/volto-eea-website-theme/commit/9b9e0232fe6cd624c19dc87bc8152477f1ee83fd)]
31
+ - refactor: Move all customizations from volto-eea-website-policy [alin - [`07650fe`](https://github.com/eea/volto-eea-website-theme/commit/07650fe1c55571ec628dbe2cf8394709f0c7ae2d)]
32
+
33
+ #### :house: Internal changes
34
+
35
+ - style: Automated code fix [eea-jenkins - [`e671c83`](https://github.com/eea/volto-eea-website-theme/commit/e671c834773f9091e1dafb8ec7a1cbea88e53ee2)]
36
+ - style: Automated code fix [eea-jenkins - [`5156bb5`](https://github.com/eea/volto-eea-website-theme/commit/5156bb54b48f9731278ea860847a019fff10a84f)]
37
+
38
+ #### :hammer_and_wrench: Others
39
+
40
+ - Bump package version to 2.0.0 to signal major release due to Volto 17 jump [David Ichim - [`ffe3049`](https://github.com/eea/volto-eea-website-theme/commit/ffe3049b3b656093a44f05044dbe7cd63bac495f)]
41
+ ### [1.34.0](https://github.com/eea/volto-eea-website-theme/compare/1.33.2...1.34.0) - 9 May 2024
42
+
43
+ #### :bug: Bug Fixes
44
+
45
+ - fix: Make sure effectiveDate is not null/undefined [alin - [`43400bc`](https://github.com/eea/volto-eea-website-theme/commit/43400bcce422049d9d38f17c7cc29e88062da902)]
46
+ - fix: DraftBackground for effectiveDate in the future [alin - [`da7fa80`](https://github.com/eea/volto-eea-website-theme/commit/da7fa806e5d6edbb7b016f0356d5a886b75ba892)]
47
+
48
+ #### :nail_care: Enhancements
49
+
50
+ - refactor: Disable data figure and plotly chart - refs #269278 [dobri1408 - [`002ef00`](https://github.com/eea/volto-eea-website-theme/commit/002ef003ad872ea8dd5c74acf74a85ca1fd1992b)]
51
+ - change(draft-image): show draft image for items with publishing date in the future [David Ichim - [`59a3873`](https://github.com/eea/volto-eea-website-theme/commit/59a387364f40d8d66a747921ccff946e7f8814e1)]
52
+
53
+ #### :hammer_and_wrench: Others
54
+
55
+ - Release 1.34.0 [alin - [`92cc065`](https://github.com/eea/volto-eea-website-theme/commit/92cc065730f44412a04b2df7159c540d858f4607)]
56
+ - Revert "Release 1.40.0" [alin - [`c1a4f30`](https://github.com/eea/volto-eea-website-theme/commit/c1a4f3042a91ebb4a1d674914d3bccf68954c94f)]
57
+ - Revert "fix: DraftBackground for effectiveDate in the future" [alin - [`ed2ca9b`](https://github.com/eea/volto-eea-website-theme/commit/ed2ca9b5881c6991d82bb2a8d3f0fe8e29f1a6d7)]
58
+ - Release 1.40.0 [alin - [`210f833`](https://github.com/eea/volto-eea-website-theme/commit/210f83384b6401f7c9a0e08070d69dd1fed690b1)]
59
+ ### [1.33.2](https://github.com/eea/volto-eea-website-theme/compare/1.33.1...1.33.2) - 16 April 2024
8
60
 
9
61
  #### :bug: Bug Fixes
10
62
 
@@ -15,7 +15,7 @@ services:
15
15
  args:
16
16
  ADDON_NAME: "${ADDON_NAME}"
17
17
  ADDON_PATH: "${ADDON_PATH}"
18
- VOLTO_VERSION: ${VOLTO_VERSION:-16}
18
+ VOLTO_VERSION: ${VOLTO_VERSION:-17}
19
19
  ports:
20
20
  - "3000:3000"
21
21
  - "3001:3001"
@@ -14,6 +14,8 @@ module.exports = {
14
14
  '@package/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
15
15
  '@root/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
16
16
  '@plone/volto-quanta/(.*)$': '<rootDir>/src/addons/volto-quanta/src/$1',
17
+ '@eeacms/search/(.*)$': '<rootDir>/src/addons/volto-searchlib/searchlib/$1',
18
+ '@eeacms/search': '<rootDir>/src/addons/volto-searchlib/searchlib',
17
19
  '@eeacms/(.*?)/(.*)$': '<rootDir>/node_modules/@eeacms/$1/src/$2',
18
20
  '@plone/volto-slate$':
19
21
  '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src',
@@ -28,6 +30,7 @@ module.exports = {
28
30
  ],
29
31
  transform: {
30
32
  '^.+\\.js(x)?$': 'babel-jest',
33
+ '^.+\\.ts(x)?$': 'babel-jest',
31
34
  '^.+\\.(png)$': 'jest-file',
32
35
  '^.+\\.(jpg)$': 'jest-file',
33
36
  '^.+\\.(svg)$': './node_modules/@plone/volto/jest-svgsystem-transform.js',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-theme",
3
- "version": "1.34.0",
3
+ "version": "2.0.1",
4
4
  "description": "@eeacms/volto-eea-website-theme: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -34,6 +34,7 @@
34
34
  "babel-plugin-transform-class-properties": "^6.24.1",
35
35
  "dotenv": "^16.3.2",
36
36
  "husky": "^8.0.3",
37
+ "jsonwebtoken": "9.0.0",
37
38
  "lint-staged": "^14.0.1",
38
39
  "md5": "^2.3.0",
39
40
  "postcss-less": "6.0.0"
@@ -3,7 +3,7 @@ import LayoutSettingsView from './LayoutSettingsView';
3
3
  import LayoutSettingsEdit from './LayoutSettingsEdit';
4
4
  import BlockSettingsSchema from '@plone/volto/components/manage/Blocks/Block/Schema';
5
5
 
6
- export default (config) => {
6
+ const applyConfig = (config) => {
7
7
  config.blocks.blocksConfig.layoutSettings = {
8
8
  id: 'layoutSettings',
9
9
  title: 'Layout settings',
@@ -19,3 +19,5 @@ export default (config) => {
19
19
 
20
20
  return config;
21
21
  };
22
+
23
+ export default applyConfig;
@@ -1,7 +1,7 @@
1
1
  import Edit from './Edit';
2
2
  import View from './View';
3
3
 
4
- export default (config) => {
4
+ const applyConfig = (config) => {
5
5
  config.blocks.blocksConfig.title = {
6
6
  ...config.blocks.blocksConfig.title,
7
7
  edit: Edit,
@@ -12,3 +12,5 @@ export default (config) => {
12
12
 
13
13
  return config;
14
14
  };
15
+
16
+ export default applyConfig;
@@ -54,7 +54,7 @@ const RSSLink = {
54
54
  required: [],
55
55
  };
56
56
 
57
- export default {
57
+ const titleSchema = {
58
58
  title: 'Page header',
59
59
  fieldsets: [
60
60
  {
@@ -175,3 +175,5 @@ export default {
175
175
 
176
176
  required: [],
177
177
  };
178
+
179
+ export default titleSchema;
@@ -89,6 +89,9 @@ const View = (props) => {
89
89
  const copyrightPrefix =
90
90
  config.blocks.blocksConfig.title.copyrightPrefix || '';
91
91
 
92
+ const contentTypesWithoutHeaderImage =
93
+ config.settings?.eea?.contentTypesWithoutHeaderImage || [];
94
+
92
95
  // Set dates
93
96
  const getDate = useCallback(
94
97
  (hidden, key) => {
@@ -96,10 +99,10 @@ const View = (props) => {
96
99
  },
97
100
  [metadata],
98
101
  );
99
- const creationDate = useMemo(() => getDate(hideCreationDate, 'created'), [
100
- getDate,
101
- hideCreationDate,
102
- ]);
102
+ const creationDate = useMemo(
103
+ () => getDate(hideCreationDate, 'created'),
104
+ [getDate, hideCreationDate],
105
+ );
103
106
  const publishingDate = useMemo(
104
107
  () => getDate(hidePublishingDate, 'effective'),
105
108
  [getDate, hidePublishingDate],
@@ -110,7 +113,11 @@ const View = (props) => {
110
113
  );
111
114
 
112
115
  // Set image source
113
- const image = getImageSource(metadata['image']);
116
+ const image = contentTypesWithoutHeaderImage.includes(
117
+ props.properties['@type'],
118
+ )
119
+ ? false
120
+ : getImageSource(metadata['image']);
114
121
  // Get type
115
122
  const type = metadata.type_title || friendlyId(metadata['@type']);
116
123
 
@@ -1,10 +1,8 @@
1
- import React from 'react';
1
+ import { BodyClass, flattenToAppURL } from '@plone/volto/helpers';
2
2
  import { connect } from 'react-redux';
3
- import './draft.css';
4
- import { BodyClass } from '@plone/volto/helpers';
5
3
  import { withRouter } from 'react-router-dom';
6
4
  import { compose } from 'redux';
7
- import { flattenToAppURL } from '@plone/volto/helpers';
5
+ import './draft.css';
8
6
 
9
7
  /**
10
8
  * Removes any trailing slashes from the given string.
@@ -16,6 +14,18 @@ const removeTrailingSlash = (str) => {
16
14
  return str.replace(/\/+$/, '');
17
15
  };
18
16
 
17
+ /**
18
+ * Checks if a given date is in the future.
19
+ *
20
+ * @param {string} date - The date to check.
21
+ * @returns {boolean} `true` if the date is in the future, `false` otherwise.
22
+ */
23
+ const dateIsInFuture = (date) => {
24
+ return (
25
+ date && date !== 'None' && new Date(date).getTime() > new Date().getTime()
26
+ );
27
+ };
28
+
19
29
  /**
20
30
  * Checks if the current content is published.
21
31
  *
@@ -38,36 +48,35 @@ export const checkIfPublished = (props) => {
38
48
  // set draft image if effective date is set and is in the future
39
49
  // regardless of review_state
40
50
  const effectiveDate = props?.content?.effective;
41
- if (
42
- effectiveDate &&
43
- effectiveDate !== 'None' &&
44
- new Date(effectiveDate).getTime() > new Date().getTime()
45
- ) {
51
+ if (dateIsInFuture(effectiveDate)) {
46
52
  return false;
47
53
  }
48
54
 
55
+ const reviewState = props?.review_state;
56
+
49
57
  //case 1 : review_state published
50
- if (props?.review_state === 'published') return true;
58
+ if (reviewState === 'published') return true;
51
59
 
52
60
  //case 2: review_state null, but parent is published eg:Image in published folder
53
- if (
54
- !props?.review_state &&
55
- props?.content?.parent?.review_state === 'published'
56
- )
61
+ // is marked as published, or not published if the effective date of parent
62
+ // is in the future
63
+ const parent = props?.content?.parent;
64
+ const parentReviewState = parent?.review_state;
65
+ if (!reviewState && parentReviewState === 'published') {
66
+ if (dateIsInFuture(parent?.effective)) {
67
+ return false;
68
+ }
57
69
  return true;
70
+ }
58
71
 
59
72
  //case 3: review_state null, but there is no parent eg: PloneSite
60
- if (
61
- !props?.review_state &&
62
- Object.keys(props?.content?.parent || {}).length === 0
63
- )
64
- return true;
73
+ if (!reviewState && Object.keys(parent || {}).length === 0) return true;
65
74
 
66
75
  //case 4: review_state null, and review state of parent is null, eg: Image in PloneSite
67
- if (!props?.review_state && !props?.content?.parent?.review_state)
68
- return true;
76
+ if (!reviewState && !parentReviewState) return true;
69
77
  return false;
70
78
  };
79
+
71
80
  const DraftBackground = (props) => {
72
81
  let draftClass = 'wf-state-is-draft';
73
82
  if (checkIfPublished(props)) {
@@ -0,0 +1,85 @@
1
+ import { checkIfPublished } from './DraftBackground';
2
+ describe('checkIfPublished', () => {
3
+ it('should return true if contentId does not match pathname', () => {
4
+ const props = {
5
+ contentId: '/page1',
6
+ pathname: '/page2',
7
+ };
8
+
9
+ expect(checkIfPublished(props)).toBe(true);
10
+ });
11
+
12
+ it('should return false if effective date is in the future', () => {
13
+ const futureDate = new Date();
14
+ futureDate.setDate(futureDate.getDate() + 10);
15
+ const props = {
16
+ contentId: '/page1',
17
+ pathname: '/page1',
18
+ content: {
19
+ effective: futureDate.toISOString(),
20
+ },
21
+ };
22
+ expect(checkIfPublished(props)).toBe(false);
23
+ });
24
+
25
+ it('should return true if review_state is published', () => {
26
+ const props = {
27
+ contentId: '/page1',
28
+ pathname: '/page1',
29
+ review_state: 'published',
30
+ };
31
+ expect(checkIfPublished(props)).toBe(true);
32
+ });
33
+
34
+ it('should return true if review_state is null and parent is published', () => {
35
+ const props = {
36
+ contentId: '/page1',
37
+ pathname: '/page1',
38
+ review_state: null,
39
+ content: {
40
+ parent: {
41
+ review_state: 'published',
42
+ },
43
+ },
44
+ };
45
+ expect(checkIfPublished(props)).toBe(true);
46
+ });
47
+
48
+ it('should return true if review_state is null and parent is empty', () => {
49
+ const props = {
50
+ contentId: '/page1',
51
+ pathname: '/page1',
52
+ review_state: null,
53
+ content: {
54
+ parent: {},
55
+ },
56
+ };
57
+ expect(checkIfPublished(props)).toBe(true);
58
+ });
59
+
60
+ it('should return true if review_state is null and parent review_state is null', () => {
61
+ const props = {
62
+ contentId: '/page1',
63
+ pathname: '/page1',
64
+ review_state: null,
65
+ content: {
66
+ parent: {
67
+ review_state: null,
68
+ },
69
+ },
70
+ };
71
+ expect(checkIfPublished(props)).toBe(true);
72
+ });
73
+
74
+ it('should return false if review_state is not published and effective date is not in the future', () => {
75
+ const props = {
76
+ contentId: '/page1',
77
+ pathname: '/page1',
78
+ review_state: 'private',
79
+ content: {
80
+ effective: '2023-01-01T00:00:00Z',
81
+ },
82
+ };
83
+ expect(checkIfPublished(props)).toBe(false);
84
+ });
85
+ });
package/src/config.js CHANGED
@@ -342,3 +342,5 @@ export const colors = [
342
342
  '#F9F9F9',
343
343
  '#FFFFFF',
344
344
  ];
345
+
346
+ export const contentTypesWithoutHeaderImage = ['Image'];
@@ -0,0 +1,32 @@
1
+ import {
2
+ serializeNodes,
3
+ serializeNodesToText,
4
+ } from '@plone/volto-slate/editor/render';
5
+ import config from '@plone/volto/registry';
6
+ import { isEqual } from 'lodash';
7
+ import Slugger from 'github-slugger';
8
+ import { normalizeString } from '@plone/volto/helpers';
9
+
10
+ const TextBlockView = (props) => {
11
+ const { id, data, styling = {} } = props;
12
+ const { value, override_toc } = data;
13
+ const metadata = props.metadata || props.properties;
14
+ const { topLevelTargetElements } = config.settings.slate;
15
+
16
+ const getAttributes = (node, path) => {
17
+ const res = { ...styling };
18
+ if (node.type && isEqual(path, [0])) {
19
+ if (topLevelTargetElements.includes(node.type) || override_toc) {
20
+ const text = serializeNodesToText([node] || []);
21
+ const slug = Slugger.slug(normalizeString(text));
22
+ res.id = slug || id;
23
+ res['data-block'] = id;
24
+ }
25
+ }
26
+ return res;
27
+ };
28
+
29
+ return serializeNodes(value, getAttributes, { metadata: metadata });
30
+ };
31
+
32
+ export default TextBlockView;
@@ -1,9 +1,19 @@
1
1
  import React from 'react';
2
2
  import { renderToStaticMarkup } from 'react-dom/server';
3
+ import { useLocation } from 'react-router-dom';
4
+ import { toast } from 'react-toastify';
5
+ import { useIntl } from 'react-intl';
6
+ import { useSelector } from 'react-redux';
3
7
  import { Node, Text } from 'slate';
4
8
  import cx from 'classnames';
5
9
  import { isEmpty, isEqual, omit } from 'lodash';
10
+ import { UniversalLink, Toast } from '@plone/volto/components';
11
+ import { messages, addAppURL } from '@plone/volto/helpers';
12
+ import useClipboard from '@plone/volto/hooks/clipboard/useClipboard';
6
13
  import config from '@plone/volto/registry';
14
+ import linkSVG from '@plone/volto/icons/link.svg';
15
+
16
+ import '@plone/volto-slate/editor/less/slate.less';
7
17
 
8
18
  const OMITTED = ['editor', 'path'];
9
19
 
@@ -177,3 +187,68 @@ export const serializeNodesToText = (nodes) => {
177
187
 
178
188
  export const serializeNodesToHtml = (nodes) =>
179
189
  renderToStaticMarkup(serializeNodes(nodes));
190
+
191
+ export const renderLinkElement = (tagName) => {
192
+ function LinkElement({
193
+ attributes,
194
+ children,
195
+ mode = 'edit',
196
+ className = null,
197
+ }) {
198
+ const { slate = {} } = config.settings;
199
+ const Tag = tagName;
200
+ const slug = attributes.id || '';
201
+ const location = useLocation();
202
+ const token = useSelector((state) => state.userSession.token);
203
+ const appPathname = addAppURL(location.pathname);
204
+ // eslint-disable-next-line no-unused-vars
205
+ const [copied, copy, setCopied] = useClipboard(
206
+ appPathname.concat(`#${slug}`),
207
+ );
208
+ const intl = useIntl();
209
+ return !token || slate.useLinkedHeadings === false ? (
210
+ <Tag {...attributes} className={className} tabIndex={0}>
211
+ {children}
212
+ </Tag>
213
+ ) : (
214
+ <Tag {...attributes} className={className} tabIndex={0}>
215
+ {children}
216
+ {mode === 'view' && slug && (
217
+ <UniversalLink
218
+ className="anchor"
219
+ aria-hidden="true"
220
+ tabIndex={-1}
221
+ href={`#${slug}`}
222
+ >
223
+ <style>
224
+ {/* Prettify the unstyled flash of the link icon on development */}
225
+ {`
226
+ a.anchor svg {
227
+ height: var(--anchor-svg-height, 24px);
228
+ }
229
+ `}
230
+ </style>
231
+ <svg
232
+ {...linkSVG.attributes}
233
+ dangerouslySetInnerHTML={{ __html: linkSVG.content }}
234
+ height={null}
235
+ onClick={() => {
236
+ copy();
237
+
238
+ toast.info(
239
+ <Toast
240
+ info
241
+ title={intl.formatMessage(messages.success)}
242
+ content={intl.formatMessage(messages.urlClipboardCopy)}
243
+ />,
244
+ );
245
+ }}
246
+ ></svg>
247
+ </UniversalLink>
248
+ )}
249
+ </Tag>
250
+ );
251
+ }
252
+ LinkElement.displayName = `${tagName}LinkElement`;
253
+ return LinkElement;
254
+ };
@@ -158,88 +158,89 @@ export const _isActiveElement = (elementType) => (editor) => {
158
158
  * @param {string|Object[]} elementType - this can be a string or an array of strings
159
159
  * @returns {Object|null} - found node
160
160
  */
161
- export const _getActiveElement = (elementType) => (
162
- editor,
163
- direction = 'any',
164
- ) => {
165
- const selection = editor.selection || editor.getSavedSelection();
166
- let found = [];
167
-
168
- try {
169
- found = Array.from(
170
- Editor.nodes(editor, {
171
- match: (n) =>
172
- Array.isArray(elementType)
173
- ? elementType.includes(n.type)
174
- : n.type === elementType,
175
- at: selection,
176
- }),
177
- );
178
- } catch (e) {
179
- return null;
180
- }
181
-
182
- if (found.length) return found[0];
183
-
184
- if (!selection) return null;
161
+ export const _getActiveElement =
162
+ (elementType) =>
163
+ (editor, direction = 'any') => {
164
+ const selection = editor.selection || editor.getSavedSelection();
165
+ let found = [];
185
166
 
186
- if (direction === 'any' || direction === 'backward') {
187
- const { path } = selection.anchor;
188
- const isAtStart =
189
- selection.anchor.offset === 0 && selection.focus.offset === 0;
167
+ try {
168
+ found = Array.from(
169
+ Editor.nodes(editor, {
170
+ match: (n) =>
171
+ Array.isArray(elementType)
172
+ ? elementType.includes(n.type)
173
+ : n.type === elementType,
174
+ at: selection,
175
+ }),
176
+ );
177
+ } catch (e) {
178
+ return null;
179
+ }
190
180
 
191
- if (isAtStart) {
192
- let found;
193
- try {
194
- found = Editor.previous(editor, {
195
- at: path,
196
- });
197
- } catch (ex) {
198
- // eslint-disable-next-line no-console
199
- console.warn('Unable to find previous node', editor, path);
200
- return;
201
- }
202
- if (found && found[0] && found[0].type === elementType) {
203
- if (
204
- (Array.isArray(elementType) && elementType.includes(found[0].type)) ||
205
- found[0].type === elementType
206
- ) {
207
- return found;
181
+ if (found.length) return found[0];
182
+
183
+ if (!selection) return null;
184
+
185
+ if (direction === 'any' || direction === 'backward') {
186
+ const { path } = selection.anchor;
187
+ const isAtStart =
188
+ selection.anchor.offset === 0 && selection.focus.offset === 0;
189
+
190
+ if (isAtStart) {
191
+ let found;
192
+ try {
193
+ found = Editor.previous(editor, {
194
+ at: path,
195
+ });
196
+ } catch (ex) {
197
+ // eslint-disable-next-line no-console
198
+ console.warn('Unable to find previous node', editor, path);
199
+ return;
200
+ }
201
+ if (found && found[0] && found[0].type === elementType) {
202
+ if (
203
+ (Array.isArray(elementType) &&
204
+ elementType.includes(found[0].type)) ||
205
+ found[0].type === elementType
206
+ ) {
207
+ return found;
208
+ }
209
+ } else {
210
+ return null;
208
211
  }
209
- } else {
210
- return null;
211
212
  }
212
213
  }
213
- }
214
-
215
- if (direction === 'any' || direction === 'forward') {
216
- const { path } = selection.anchor;
217
- const isAtStart =
218
- selection.anchor.offset === 0 && selection.focus.offset === 0;
219
214
 
220
- if (isAtStart) {
221
- let found;
222
- try {
223
- found = Editor.next(editor, {
224
- at: path,
225
- });
226
- } catch (e) {
227
- // eslint-disable-next-line
228
- console.warn('Unable to find next node', editor, path);
229
- return;
230
- }
231
- if (found && found[0] && found[0].type === elementType) {
232
- if (
233
- (Array.isArray(elementType) && elementType.includes(found[0].type)) ||
234
- found[0].type === elementType
235
- ) {
236
- return found;
215
+ if (direction === 'any' || direction === 'forward') {
216
+ const { path } = selection.anchor;
217
+ const isAtStart =
218
+ selection.anchor.offset === 0 && selection.focus.offset === 0;
219
+
220
+ if (isAtStart) {
221
+ let found;
222
+ try {
223
+ found = Editor.next(editor, {
224
+ at: path,
225
+ });
226
+ } catch (e) {
227
+ // eslint-disable-next-line
228
+ console.warn('Unable to find next node', editor, path);
229
+ return;
230
+ }
231
+ if (found && found[0] && found[0].type === elementType) {
232
+ if (
233
+ (Array.isArray(elementType) &&
234
+ elementType.includes(found[0].type)) ||
235
+ found[0].type === elementType
236
+ ) {
237
+ return found;
238
+ }
239
+ } else {
240
+ return null;
237
241
  }
238
- } else {
239
- return null;
240
242
  }
241
243
  }
242
- }
243
244
 
244
- return null;
245
- };
245
+ return null;
246
+ };