@eeacms/volto-cca-policy 0.1.13 → 0.1.14

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.
@@ -0,0 +1,357 @@
1
+ import ReactDOM from 'react-dom';
2
+ import { v4 as uuid } from 'uuid';
3
+ import {
4
+ addBlock,
5
+ changeBlock,
6
+ getBlocksFieldname,
7
+ getBlocksLayoutFieldname,
8
+ } from '@plone/volto/helpers';
9
+ import { Transforms, Editor, Node, Text, Path } from 'slate';
10
+ import { serializeNodesToText } from '@plone/volto-slate/editor/render';
11
+ import { omit } from 'lodash';
12
+ import config from '@plone/volto/registry';
13
+
14
+ function fromEntries(pairs) {
15
+ const res = {};
16
+ pairs.forEach((p) => {
17
+ res[p[0]] = p[1];
18
+ });
19
+ return res;
20
+ }
21
+
22
+ // TODO: should be made generic, no need for "prevBlock.value"
23
+ export function mergeSlateWithBlockBackward(editor, prevBlock, event) {
24
+ // To work around current architecture limitations, read the value from
25
+ // previous block. Replace it in the current editor (over which we have
26
+ // control), join with current block value, then use this result for previous
27
+ // block, delete current block
28
+
29
+ const prev = prevBlock.value;
30
+
31
+ // collapse the selection to its start point
32
+ Transforms.collapse(editor, { edge: 'start' });
33
+
34
+ let rangeRef;
35
+ let end;
36
+
37
+ Editor.withoutNormalizing(editor, () => {
38
+ // insert block #0 contents in block #1 contents, at the beginning
39
+ Transforms.insertNodes(editor, prev, {
40
+ at: Editor.start(editor, []),
41
+ });
42
+
43
+ // the contents that should be moved into the `ul`, as the last `li`
44
+ rangeRef = Editor.rangeRef(editor, {
45
+ anchor: Editor.start(editor, [1]),
46
+ focus: Editor.end(editor, [1]),
47
+ });
48
+
49
+ const source = rangeRef.current;
50
+
51
+ end = Editor.end(editor, [0]);
52
+
53
+ let endPoint;
54
+
55
+ Transforms.insertNodes(editor, { text: '' }, { at: end });
56
+
57
+ end = Editor.end(editor, [0]);
58
+
59
+ Transforms.splitNodes(editor, {
60
+ at: end,
61
+ always: true,
62
+ height: 1,
63
+ mode: 'highest',
64
+ match: (n) => n.type === 'li' || Text.isText(n),
65
+ });
66
+
67
+ endPoint = Editor.end(editor, [0]);
68
+
69
+ Transforms.moveNodes(editor, {
70
+ at: source,
71
+ to: endPoint.path,
72
+ mode: 'all',
73
+ match: (n, p) => p.length === 2,
74
+ });
75
+ });
76
+
77
+ const [n] = Editor.node(editor, [1]);
78
+
79
+ if (Editor.isEmpty(editor, n)) {
80
+ Transforms.removeNodes(editor, { at: [1] });
81
+ }
82
+
83
+ rangeRef.unref();
84
+
85
+ const [, lastPath] = Editor.last(editor, [0]);
86
+
87
+ end = Editor.start(editor, Path.parent(lastPath));
88
+
89
+ return end;
90
+ }
91
+
92
+ export function mergeSlateWithBlockForward(editor, nextBlock, event) {
93
+ // To work around current architecture limitations, read the value from next
94
+ // block. Replace it in the current editor (over which we have control), join
95
+ // with current block value, then use this result for next block, delete
96
+ // current block
97
+
98
+ const next = nextBlock.value;
99
+
100
+ // collapse the selection to its start point
101
+ Transforms.collapse(editor, { edge: 'end' });
102
+ Transforms.insertNodes(editor, next, {
103
+ at: Editor.end(editor, []),
104
+ });
105
+
106
+ Editor.deleteForward(editor, { unit: 'character' });
107
+ }
108
+
109
+ export function syncCreateSlateBlock(value) {
110
+ const id = uuid();
111
+ const block = {
112
+ '@type': 'slate',
113
+ value: JSON.parse(JSON.stringify(value)),
114
+ plaintext: serializeNodesToText(value),
115
+ };
116
+ return [id, block];
117
+ }
118
+
119
+ export function createImageBlock(url, index, props) {
120
+ const { properties, onChangeField, onSelectBlock } = props;
121
+ const blocksFieldname = getBlocksFieldname(properties);
122
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
123
+
124
+ const [id, formData] = addBlock(properties, 'image', index + 1);
125
+ const newFormData = changeBlock(formData, id, { '@type': 'image', url });
126
+
127
+ ReactDOM.unstable_batchedUpdates(() => {
128
+ onChangeField(blocksFieldname, newFormData[blocksFieldname]);
129
+ onChangeField(blocksLayoutFieldname, newFormData[blocksLayoutFieldname]);
130
+ onSelectBlock(id);
131
+ });
132
+ }
133
+
134
+ export const createAndSelectNewBlockAfter = (editor, blockValue) => {
135
+ const blockProps = editor.getBlockProps();
136
+
137
+ const { onSelectBlock, properties, index, onChangeField } = blockProps;
138
+
139
+ const [blockId, formData] = addBlock(properties, 'slate', index + 1);
140
+
141
+ const options = {
142
+ '@type': 'slate',
143
+ value: JSON.parse(JSON.stringify(blockValue)),
144
+ plaintext: serializeNodesToText(blockValue),
145
+ };
146
+
147
+ const newFormData = changeBlock(formData, blockId, options);
148
+
149
+ const blocksFieldname = getBlocksFieldname(properties);
150
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
151
+
152
+ ReactDOM.unstable_batchedUpdates(() => {
153
+ blockProps.saveSlateBlockSelection(blockId, 'start');
154
+ onChangeField(blocksFieldname, newFormData[blocksFieldname]);
155
+ onChangeField(blocksLayoutFieldname, newFormData[blocksLayoutFieldname]);
156
+ onSelectBlock(blockId);
157
+ });
158
+ };
159
+
160
+ export function getNextVoltoBlock(index, properties) {
161
+ // TODO: look for any next slate block
162
+ // join this block with previous block, if previous block is slate
163
+ const blocksFieldname = getBlocksFieldname(properties);
164
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
165
+
166
+ const blocks_layout = properties[blocksLayoutFieldname];
167
+
168
+ if (index === blocks_layout.items.length) return;
169
+
170
+ const nextBlockId = blocks_layout.items[index + 1];
171
+ const nextBlock = properties[blocksFieldname][nextBlockId];
172
+
173
+ return [nextBlock, nextBlockId];
174
+ }
175
+
176
+ export function getPreviousVoltoBlock(index, properties) {
177
+ // TODO: look for any prev slate block
178
+ if (index === 0) return;
179
+
180
+ const blocksFieldname = getBlocksFieldname(properties);
181
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
182
+
183
+ const blocks_layout = properties[blocksLayoutFieldname];
184
+ const prevBlockId = blocks_layout.items[index - 1];
185
+ const prevBlock = properties[blocksFieldname][prevBlockId];
186
+
187
+ return [prevBlock, prevBlockId];
188
+ }
189
+
190
+ // //check for existing img children
191
+ // const checkContainImg = (elements) => {
192
+ // var check = false;
193
+ // elements.forEach((e) =>
194
+ // e.children.forEach((c) => {
195
+ // if (c && c.type && c.type === 'img') {
196
+ // check = true;
197
+ // }
198
+ // }),
199
+ // );
200
+ // return check;
201
+ // };
202
+
203
+ // //check for existing table children
204
+ // const checkContainTable = (elements) => {
205
+ // var check = false;
206
+ // elements.forEach((e) => {
207
+ // if (e && e.type && e.type === 'table') {
208
+ // check = true;
209
+ // }
210
+ // });
211
+ // return check;
212
+ // };
213
+
214
+ /**
215
+ * The editor has the properties `dataTransferHandlers` (object) and
216
+ * `dataTransferFormatsOrder` and in `dataTransferHandlers` are functions which
217
+ * sometimes must call this function. Some types of data storeable in Slate
218
+ * documents can be and should be put into separate Volto blocks. The
219
+ * `deconstructToVoltoBlocks` function scans the contents of the Slate document
220
+ * and, through configured Volto block emitters, it outputs separate Volto
221
+ * blocks into the same Volto page form. The `deconstructToVoltoBlocks` function
222
+ * should be called only in key places where it is necessary.
223
+ *
224
+ * @example See the `src/editor/extensions/insertData.js` file.
225
+ *
226
+ * @param {Editor} editor The Slate editor object which should be deconstructed
227
+ * if possible.
228
+ *
229
+ * @returns {Promise}
230
+ */
231
+ export function deconstructToVoltoBlocks(editor) {
232
+ // Explodes editor content into separate blocks
233
+ // If the editor has multiple top-level children, split the current block
234
+ // into multiple slate blocks. This will delete and replace the current
235
+ // block.
236
+ //
237
+ // It returns a promise that, when resolved, will pass a list of Volto block
238
+ // ids that were affected
239
+ //
240
+ // For the Volto blocks manipulation we do low-level changes to the context
241
+ // form state, as that ensures a better performance (no un-needed UI updates)
242
+
243
+ if (!editor.getBlockProps) return;
244
+
245
+ const blockProps = editor.getBlockProps();
246
+ const { slate } = config.settings;
247
+ const { voltoBlockEmiters } = slate;
248
+
249
+ return new Promise((resolve, reject) => {
250
+ if (!editor?.children) return;
251
+
252
+ const {
253
+ properties,
254
+ onChangeFormData,
255
+ onSelectBlock,
256
+ } = editor.getBlockProps();
257
+ const blocksFieldname = getBlocksFieldname(properties);
258
+ const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
259
+
260
+ const { index } = blockProps;
261
+
262
+ // optimization to avoid replacing a single block
263
+ if (editor.children.length === 1) {
264
+ const pathRef = Editor.pathRef(editor, [0]);
265
+ const blocks = voltoBlockEmiters
266
+ .map((emit) => emit(editor, pathRef))
267
+ .flat(1);
268
+ const blockids = blocks.map((b) => b[0]);
269
+
270
+ if (blocks.length) {
271
+ const blocksData = omit(
272
+ {
273
+ ...properties[blocksFieldname],
274
+ ...fromEntries(blocks),
275
+ },
276
+ blockProps.block,
277
+ );
278
+ const layoutData = {
279
+ ...properties[blocksLayoutFieldname],
280
+ items: [
281
+ ...properties[blocksLayoutFieldname].items.slice(0, index),
282
+ ...blockids,
283
+ ...properties[blocksLayoutFieldname].items.slice(index),
284
+ ].filter((id) => id !== blockProps.block),
285
+ };
286
+
287
+ ReactDOM.unstable_batchedUpdates(() => {
288
+ onChangeFormData({
289
+ ...properties,
290
+ [blocksFieldname]: blocksData,
291
+ [blocksLayoutFieldname]: layoutData,
292
+ });
293
+
294
+ onSelectBlock(blockids[blockids.length - 1]);
295
+ resolve(blockids);
296
+ });
297
+ } else {
298
+ resolve([blockProps.block]);
299
+ }
300
+ return;
301
+ }
302
+
303
+ let blocks = [];
304
+
305
+ // TODO: should use Editor.levels() instead of Node.children
306
+ const pathRefs = Array.from(Node.children(editor, [])).map(([, path]) =>
307
+ Editor.pathRef(editor, path),
308
+ );
309
+
310
+ for (const pathRef of pathRefs) {
311
+ // extra nodes are always extracted after the text node
312
+ const extras = voltoBlockEmiters
313
+ .map((emit) => emit(editor, pathRef))
314
+ .flat(1);
315
+
316
+ // The node might have been replaced with a Volto block
317
+ if (pathRef.current) {
318
+ const [childNode] = Editor.node(editor, pathRef.current);
319
+ if (childNode && !Editor.isEmpty(editor, childNode))
320
+ blocks.push(syncCreateSlateBlock([childNode]));
321
+ }
322
+ blocks = [...blocks, ...extras];
323
+ }
324
+
325
+ const blockids = blocks.map((b) => b[0]);
326
+
327
+ // TODO: add the placeholder block, because we remove it
328
+ // (when we remove the current block)
329
+
330
+ const blocksData = omit(
331
+ {
332
+ ...properties[blocksFieldname],
333
+ ...fromEntries(blocks),
334
+ },
335
+ blockProps.block,
336
+ );
337
+ const layoutData = {
338
+ ...properties[blocksLayoutFieldname],
339
+ items: [
340
+ ...properties[blocksLayoutFieldname].items.slice(0, index),
341
+ ...blockids,
342
+ ...properties[blocksLayoutFieldname].items.slice(index),
343
+ ].filter((id) => id !== blockProps.block),
344
+ };
345
+
346
+ ReactDOM.unstable_batchedUpdates(() => {
347
+ onChangeFormData({
348
+ ...properties,
349
+ [blocksFieldname]: blocksData,
350
+ [blocksLayoutFieldname]: layoutData,
351
+ });
352
+
353
+ onSelectBlock(blockids[blockids.length - 1]);
354
+ Promise.resolve().then(resolve(blockids));
355
+ });
356
+ });
357
+ }
package/src/index.js CHANGED
@@ -77,6 +77,10 @@ const applyConfig = (config) => {
77
77
  ],
78
78
  social: [],
79
79
  actions: [
80
+ {
81
+ link: '/en/mission/the-mission/privacy',
82
+ title: 'Privacy',
83
+ },
80
84
  {
81
85
  link: '/en/mission/login',
82
86
  title: 'CMS Login',
@@ -85,14 +89,14 @@ const applyConfig = (config) => {
85
89
  contacts: [
86
90
  {
87
91
  icon: 'comment outline',
88
- text: 'About us',
89
- link: '/en/mission/about',
92
+ text: 'About',
93
+ link: '/en/mission/the-mission/about-the-mission',
90
94
  children: [],
91
95
  },
92
96
  {
93
97
  icon: 'comment outline',
94
- text: 'Contact us',
95
- link: '/en/mission/about/contact',
98
+ text: 'Contact',
99
+ link: '/en/mission/the-mission/contact-us',
96
100
  },
97
101
  // {
98
102
  // icon: 'envelope outline',