@plone/volto 18.0.0-alpha.32 → 18.0.0-alpha.34
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 +47 -0
- package/locales/ca/LC_MESSAGES/volto.po +17 -0
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +17 -0
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +17 -0
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +17 -0
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +17 -0
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +17 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +17 -0
- package/locales/fr.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +17 -0
- package/locales/hi.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +17 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +17 -0
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +17 -0
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +17 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +17 -0
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +17 -0
- package/locales/ro.json +1 -1
- package/locales/volto.pot +18 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +17 -0
- package/locales/zh_CN.json +1 -1
- package/package.json +10 -5
- package/razzle.config.js +58 -0
- package/src/actions/form/form.js +18 -2
- package/src/actions/index.js +1 -1
- package/src/components/manage/BlockChooser/BlockChooserSearch.jsx +1 -0
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +157 -81
- package/src/components/manage/Blocks/Block/BlocksForm.test.jsx +8 -0
- package/src/components/manage/Blocks/Block/Edit.jsx +37 -4
- package/src/components/manage/Blocks/Block/Order/Item.jsx +122 -0
- package/src/components/manage/Blocks/Block/Order/Order.jsx +367 -0
- package/src/components/manage/Blocks/Block/Order/SortableItem.jsx +58 -0
- package/src/components/manage/Blocks/Block/Order/utilities.js +113 -0
- package/src/components/manage/Blocks/Container/Edit.jsx +1 -0
- package/src/components/manage/Blocks/Grid/Edit.jsx +6 -4
- package/src/components/manage/Blocks/Image/schema.js +2 -0
- package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +1 -1
- package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +1 -1
- package/src/components/manage/Form/Form.jsx +159 -151
- package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +158 -65
- package/src/components/manage/Sidebar/ObjectBrowserNav.jsx +125 -71
- package/src/components/manage/Sidebar/Sidebar.jsx +28 -1
- package/src/components/manage/Widgets/IdWidget.jsx +138 -203
- package/src/components/manage/Widgets/InternalUrlWidget.jsx +10 -14
- package/src/components/manage/Widgets/TextWidget.jsx +92 -124
- package/src/components/manage/Widgets/TextWidget.stories.jsx +14 -3
- package/src/components/theme/App/App.jsx +5 -0
- package/src/components/theme/Footer/Footer.jsx +1 -0
- package/src/components/theme/FormattedDate/FormattedDate.jsx +13 -1
- package/src/components/theme/Icon/Icon.jsx +4 -4
- package/src/components/theme/Navigation/Navigation.jsx +1 -1
- package/src/components/theme/SlotRenderer/SlotRenderer.tsx +3 -4
- package/src/components/theme/View/View.jsx +1 -1
- package/src/config/Loadables.jsx +18 -0
- package/src/config/server.js +0 -1
- package/src/constants/ActionTypes.js +1 -0
- package/src/express-middleware/static.js +37 -19
- package/src/helpers/Blocks/Blocks.js +182 -1
- package/src/helpers/Blocks/Blocks.test.js +136 -0
- package/src/helpers/Html/Html.jsx +6 -8
- package/src/helpers/Slots/index.tsx +12 -5
- package/src/helpers/index.js +4 -0
- package/src/reducers/form/form.js +18 -1
- package/src/reducers/form/form.test.js +15 -1
- package/src/server.jsx +34 -36
- package/theme/themes/pastanaga/extras/blocks.less +7 -6
- package/theme/themes/pastanaga/extras/objectbrowser-widget.less +83 -0
- package/theme/themes/pastanaga/extras/sidebar.less +145 -0
- package/theme/themes/pastanaga/extras/widgets.less +19 -0
- package/types/actions/form/form.d.ts +8 -1
- package/types/actions/index.d.ts +1 -1
- package/types/components/manage/Blocks/Block/Order/Item.d.ts +2 -0
- package/types/components/manage/Blocks/Block/Order/Order.d.ts +13 -0
- package/types/components/manage/Blocks/Block/Order/SortableItem.d.ts +9 -0
- package/types/components/manage/Blocks/Block/Order/utilities.d.ts +9 -0
- package/types/components/manage/Blocks/Image/schema.d.ts +2 -0
- package/types/components/manage/Sidebar/ObjectBrowserNav.d.ts +2 -1
- package/types/components/manage/Widgets/IdWidget.d.ts +54 -2
- package/types/components/manage/Widgets/TextWidget.d.ts +54 -5
- package/types/components/manage/Widgets/TextWidget.stories.d.ts +13 -1
- package/types/components/manage/Widgets/index.d.ts +2 -2
- package/types/components/theme/Icon/Icon.d.ts +8 -8
- package/types/config/Loadables.d.ts +15 -162
- package/types/config/Widgets.d.ts +1 -1
- package/types/config/server.d.ts +0 -3
- package/types/constants/ActionTypes.d.ts +1 -0
- package/types/helpers/Blocks/Blocks.d.ts +13 -0
- package/types/helpers/Slots/index.d.ts +5 -3
- package/types/helpers/index.d.ts +2 -2
- package/types/start-client.d.ts +1 -1
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
|
+
import { cloneDeep, map } from 'lodash';
|
|
3
4
|
import EditBlock from './Edit';
|
|
4
5
|
import { DragDropList } from '@plone/volto/components';
|
|
5
6
|
import {
|
|
6
7
|
getBlocks,
|
|
7
8
|
getBlocksFieldname,
|
|
9
|
+
getBlocksLayoutFieldname,
|
|
8
10
|
applyBlockDefaults,
|
|
11
|
+
getBlocksHierarchy,
|
|
9
12
|
} from '@plone/volto/helpers';
|
|
10
13
|
import {
|
|
11
14
|
addBlock,
|
|
@@ -13,15 +16,19 @@ import {
|
|
|
13
16
|
changeBlock,
|
|
14
17
|
deleteBlock,
|
|
15
18
|
moveBlock,
|
|
19
|
+
moveBlockEnhanced,
|
|
16
20
|
mutateBlock,
|
|
17
21
|
nextBlockId,
|
|
18
22
|
previousBlockId,
|
|
19
23
|
} from '@plone/volto/helpers';
|
|
20
24
|
import EditBlockWrapper from './EditBlockWrapper';
|
|
21
|
-
import { setSidebarTab } from '@plone/volto/actions';
|
|
25
|
+
import { setSidebarTab, setUIState } from '@plone/volto/actions';
|
|
22
26
|
import { useDispatch } from 'react-redux';
|
|
23
27
|
import { useDetectClickOutside, useEvent } from '@plone/volto/helpers';
|
|
24
28
|
import config from '@plone/volto/registry';
|
|
29
|
+
import { createPortal } from 'react-dom';
|
|
30
|
+
|
|
31
|
+
import Order from './Order/Order';
|
|
25
32
|
|
|
26
33
|
const BlocksForm = (props) => {
|
|
27
34
|
const {
|
|
@@ -53,6 +60,12 @@ const BlocksForm = (props) => {
|
|
|
53
60
|
token,
|
|
54
61
|
} = props;
|
|
55
62
|
|
|
63
|
+
const [isClient, setIsClient] = useState(false);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
setIsClient(true);
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
56
69
|
const blockList = getBlocks(properties);
|
|
57
70
|
|
|
58
71
|
const dispatch = useDispatch();
|
|
@@ -183,6 +196,52 @@ const BlocksForm = (props) => {
|
|
|
183
196
|
onChangeFormData(newFormData);
|
|
184
197
|
};
|
|
185
198
|
|
|
199
|
+
const onMoveBlockEnhanced = ({ source, destination }) => {
|
|
200
|
+
const newFormData = moveBlockEnhanced(cloneDeep(properties), {
|
|
201
|
+
source,
|
|
202
|
+
destination,
|
|
203
|
+
});
|
|
204
|
+
const blocksFieldname = getBlocksFieldname(newFormData);
|
|
205
|
+
const blocksLayoutFieldname = getBlocksLayoutFieldname(newFormData);
|
|
206
|
+
let error = false;
|
|
207
|
+
|
|
208
|
+
const allowedBlocks = Object.keys(blocksConfig);
|
|
209
|
+
|
|
210
|
+
map(newFormData[blocksLayoutFieldname].items, (id) => {
|
|
211
|
+
const block = newFormData[blocksFieldname][id];
|
|
212
|
+
if (!allowedBlocks.includes(block['@type'])) {
|
|
213
|
+
error = true;
|
|
214
|
+
}
|
|
215
|
+
if (Array.isArray(block[blocksLayoutFieldname]?.items)) {
|
|
216
|
+
const size = block[blocksLayoutFieldname].items.length;
|
|
217
|
+
const allowedSubBlocks = [
|
|
218
|
+
...(blocksConfig[block['@type']].allowedBlocks || allowedBlocks),
|
|
219
|
+
'empty',
|
|
220
|
+
] || ['empty'];
|
|
221
|
+
if (size < 1 || size > (blocksConfig[block['@type']].maxLength || 4)) {
|
|
222
|
+
error = true;
|
|
223
|
+
}
|
|
224
|
+
map(block[blocksLayoutFieldname].items, (subId) => {
|
|
225
|
+
const subBlock = block[blocksFieldname][subId];
|
|
226
|
+
if (!allowedSubBlocks.includes(subBlock['@type'])) {
|
|
227
|
+
error = true;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (!error) {
|
|
234
|
+
onChangeFormData(newFormData);
|
|
235
|
+
dispatch(
|
|
236
|
+
setUIState({
|
|
237
|
+
selected: null,
|
|
238
|
+
multiSelected: [],
|
|
239
|
+
gridSelected: null,
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
186
245
|
const defaultBlockWrapper = ({ draginfo }, editBlock, blockProps) => (
|
|
187
246
|
<EditBlockWrapper draginfo={draginfo} blockProps={blockProps}>
|
|
188
247
|
{editBlock}
|
|
@@ -195,6 +254,7 @@ const BlocksForm = (props) => {
|
|
|
195
254
|
// Note they are alreaady filtered by DragDropList, but we also want them
|
|
196
255
|
// to be removed when the user saves the page next. Otherwise the invalid
|
|
197
256
|
// blocks would linger for ever.
|
|
257
|
+
|
|
198
258
|
for (const [n, v] of blockList) {
|
|
199
259
|
if (!v) {
|
|
200
260
|
const newFormData = deleteBlock(properties, n);
|
|
@@ -210,85 +270,101 @@ const BlocksForm = (props) => {
|
|
|
210
270
|
});
|
|
211
271
|
|
|
212
272
|
return (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
273
|
+
<>
|
|
274
|
+
{isMainForm &&
|
|
275
|
+
isClient &&
|
|
276
|
+
createPortal(
|
|
277
|
+
<div>
|
|
278
|
+
<Order
|
|
279
|
+
items={getBlocksHierarchy(properties)}
|
|
280
|
+
onMoveBlock={onMoveBlockEnhanced}
|
|
281
|
+
onDeleteBlock={onDeleteBlock}
|
|
282
|
+
onSelectBlock={onSelectBlock}
|
|
283
|
+
removable
|
|
284
|
+
/>
|
|
285
|
+
</div>,
|
|
286
|
+
document.getElementById('sidebar-order'),
|
|
287
|
+
)}
|
|
288
|
+
<div
|
|
289
|
+
className="blocks-form"
|
|
290
|
+
role="presentation"
|
|
291
|
+
ref={ref}
|
|
292
|
+
onKeyDown={(e) => {
|
|
293
|
+
if (stopPropagation) {
|
|
294
|
+
e.stopPropagation();
|
|
295
|
+
}
|
|
296
|
+
}}
|
|
297
|
+
>
|
|
298
|
+
<fieldset className="invisible" disabled={!editable}>
|
|
299
|
+
<DragDropList
|
|
300
|
+
childList={blockList}
|
|
301
|
+
onMoveItem={(result) => {
|
|
302
|
+
const { source, destination } = result;
|
|
303
|
+
if (!destination) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const newFormData = moveBlock(
|
|
307
|
+
properties,
|
|
308
|
+
source.index,
|
|
309
|
+
destination.index,
|
|
310
|
+
);
|
|
311
|
+
onChangeFormData(newFormData);
|
|
312
|
+
return true;
|
|
313
|
+
}}
|
|
314
|
+
direction={direction}
|
|
315
|
+
>
|
|
316
|
+
{(dragProps) => {
|
|
317
|
+
const { child, childId, index } = dragProps;
|
|
318
|
+
const blockProps = {
|
|
319
|
+
allowedBlocks,
|
|
320
|
+
showRestricted,
|
|
321
|
+
block: childId,
|
|
322
|
+
data: child,
|
|
323
|
+
handleKeyDown,
|
|
324
|
+
id: childId,
|
|
325
|
+
formTitle: title,
|
|
326
|
+
formDescription: description,
|
|
327
|
+
index,
|
|
328
|
+
manage,
|
|
329
|
+
onAddBlock,
|
|
330
|
+
onInsertBlock,
|
|
331
|
+
onChangeBlock,
|
|
332
|
+
onChangeField,
|
|
333
|
+
onChangeFormData,
|
|
334
|
+
onDeleteBlock,
|
|
335
|
+
onFocusNextBlock,
|
|
336
|
+
onFocusPreviousBlock,
|
|
337
|
+
onMoveBlock,
|
|
338
|
+
onMutateBlock,
|
|
339
|
+
onSelectBlock,
|
|
340
|
+
pathname,
|
|
341
|
+
metadata,
|
|
342
|
+
properties,
|
|
343
|
+
contentType: type,
|
|
344
|
+
navRoot,
|
|
345
|
+
blocksConfig,
|
|
346
|
+
selected: selectedBlock === childId,
|
|
347
|
+
multiSelected: multiSelected?.includes(childId),
|
|
348
|
+
type: child['@type'],
|
|
349
|
+
editable,
|
|
350
|
+
showBlockChooser: selectedBlock === childId,
|
|
351
|
+
detached: isContainer,
|
|
352
|
+
// Properties to pass to the BlocksForm to match the View ones
|
|
353
|
+
content: properties,
|
|
354
|
+
history,
|
|
355
|
+
location,
|
|
356
|
+
token,
|
|
357
|
+
};
|
|
358
|
+
return editBlockWrapper(
|
|
359
|
+
dragProps,
|
|
360
|
+
<EditBlock key={childId} {...blockProps} />,
|
|
361
|
+
blockProps,
|
|
362
|
+
);
|
|
363
|
+
}}
|
|
364
|
+
</DragDropList>
|
|
365
|
+
</fieldset>
|
|
366
|
+
</div>
|
|
367
|
+
</>
|
|
292
368
|
);
|
|
293
369
|
};
|
|
294
370
|
|
|
@@ -30,6 +30,9 @@ test('Allow override of blocksConfig', () => {
|
|
|
30
30
|
locale: 'en',
|
|
31
31
|
messages: {},
|
|
32
32
|
},
|
|
33
|
+
form: {
|
|
34
|
+
ui: {},
|
|
35
|
+
},
|
|
33
36
|
});
|
|
34
37
|
|
|
35
38
|
const data = {
|
|
@@ -68,6 +71,7 @@ test('Allow override of blocksConfig', () => {
|
|
|
68
71
|
const { container } = render(
|
|
69
72
|
<Provider store={store}>
|
|
70
73
|
<BlocksForm {...data} />
|
|
74
|
+
<div id="sidebar-order"></div>
|
|
71
75
|
</Provider>,
|
|
72
76
|
);
|
|
73
77
|
expect(container).toMatchSnapshot();
|
|
@@ -79,6 +83,9 @@ test('Removes invalid blocks on saving', () => {
|
|
|
79
83
|
locale: 'en',
|
|
80
84
|
messages: {},
|
|
81
85
|
},
|
|
86
|
+
form: {
|
|
87
|
+
ui: {},
|
|
88
|
+
},
|
|
82
89
|
});
|
|
83
90
|
|
|
84
91
|
const onChangeFormData = jest.fn(() => {});
|
|
@@ -120,6 +127,7 @@ test('Removes invalid blocks on saving', () => {
|
|
|
120
127
|
render(
|
|
121
128
|
<Provider store={store}>
|
|
122
129
|
<BlocksForm {...data} />
|
|
130
|
+
<div id="sidebar-order"></div>
|
|
123
131
|
</Provider>,
|
|
124
132
|
);
|
|
125
133
|
expect(onChangeFormData).toBeCalledWith({
|
|
@@ -9,7 +9,7 @@ import { compose } from 'redux';
|
|
|
9
9
|
import { connect } from 'react-redux';
|
|
10
10
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
11
11
|
import cx from 'classnames';
|
|
12
|
-
import { setSidebarTab } from '@plone/volto/actions';
|
|
12
|
+
import { setSidebarTab, setUIState } from '@plone/volto/actions';
|
|
13
13
|
import config from '@plone/volto/registry';
|
|
14
14
|
import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
|
|
15
15
|
import { applyBlockDefaults } from '@plone/volto/helpers';
|
|
@@ -79,7 +79,11 @@ export class Edit extends Component {
|
|
|
79
79
|
this.blockNode.current.focus();
|
|
80
80
|
}
|
|
81
81
|
const tab = this.props.manage ? 1 : blocksConfig?.[type]?.sidebarTab || 0;
|
|
82
|
-
if (
|
|
82
|
+
if (
|
|
83
|
+
this.props.selected &&
|
|
84
|
+
this.props.editable &&
|
|
85
|
+
this.props.sidebarTab !== 2
|
|
86
|
+
) {
|
|
83
87
|
this.props.setSidebarTab(tab);
|
|
84
88
|
}
|
|
85
89
|
}
|
|
@@ -105,7 +109,9 @@ export class Edit extends Component {
|
|
|
105
109
|
const tab = this.props.manage
|
|
106
110
|
? 1
|
|
107
111
|
: blocksConfig?.[nextProps.type]?.sidebarTab || 0;
|
|
108
|
-
this.props.
|
|
112
|
+
if (this.props.sidebarTab !== 2) {
|
|
113
|
+
this.props.setSidebarTab(tab);
|
|
114
|
+
}
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
|
|
@@ -138,6 +144,21 @@ export class Edit extends Component {
|
|
|
138
144
|
{Block !== null ? (
|
|
139
145
|
<div
|
|
140
146
|
role="presentation"
|
|
147
|
+
onMouseOver={() => {
|
|
148
|
+
if (this.props.hovered !== this.props.id) {
|
|
149
|
+
this.props.setUIState({ hovered: this.props.id });
|
|
150
|
+
}
|
|
151
|
+
}}
|
|
152
|
+
onFocus={() => {
|
|
153
|
+
// TODO: This `onFocus` steals somehow the focus from the slate block
|
|
154
|
+
// we have to investigate why this is happening
|
|
155
|
+
// Apparently, I can't see any difference in the behavior
|
|
156
|
+
// If any, we can fix it in successive iterations
|
|
157
|
+
// if (this.props.hovered !== this.props.id) {
|
|
158
|
+
// this.props.setUIState({ hovered: this.props.id });
|
|
159
|
+
// }
|
|
160
|
+
}}
|
|
161
|
+
onMouseLeave={() => this.props.setUIState({ hovered: null })}
|
|
141
162
|
onClick={(e) => {
|
|
142
163
|
const isMultipleSelection = e.shiftKey || e.ctrlKey || e.metaKey;
|
|
143
164
|
!this.props.selected &&
|
|
@@ -161,6 +182,7 @@ export class Edit extends Component {
|
|
|
161
182
|
className={cx('block', type, this.props.data.variation, {
|
|
162
183
|
selected: this.props.selected || this.props.multiSelected,
|
|
163
184
|
multiSelected: this.props.multiSelected,
|
|
185
|
+
hovered: this.props.hovered === this.props.id,
|
|
164
186
|
})}
|
|
165
187
|
style={{ outline: 'none' }}
|
|
166
188
|
ref={this.blockNode}
|
|
@@ -185,6 +207,11 @@ export class Edit extends Component {
|
|
|
185
207
|
) : (
|
|
186
208
|
<div
|
|
187
209
|
role="presentation"
|
|
210
|
+
onMouseOver={() =>
|
|
211
|
+
this.props.setUIState({ hovered: this.props.id })
|
|
212
|
+
}
|
|
213
|
+
onFocus={() => this.props.setUIState({ hovered: this.props.id })}
|
|
214
|
+
onMouseLeave={() => this.props.setUIState({ hovered: null })}
|
|
188
215
|
onClick={() =>
|
|
189
216
|
!this.props.selected && this.props.onSelectBlock(this.props.id)
|
|
190
217
|
}
|
|
@@ -218,5 +245,11 @@ export class Edit extends Component {
|
|
|
218
245
|
export default compose(
|
|
219
246
|
injectIntl,
|
|
220
247
|
withObjectBrowser,
|
|
221
|
-
connect(
|
|
248
|
+
connect(
|
|
249
|
+
(state, props) => ({
|
|
250
|
+
hovered: state.form?.ui.hovered || null,
|
|
251
|
+
sidebarTab: state.sidebar?.tab,
|
|
252
|
+
}),
|
|
253
|
+
{ setSidebarTab, setUIState },
|
|
254
|
+
),
|
|
222
255
|
)(Edit);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
4
|
+
import { includes } from 'lodash';
|
|
5
|
+
|
|
6
|
+
import { Icon } from '@plone/volto/components';
|
|
7
|
+
import { setUIState } from '@plone/volto/actions';
|
|
8
|
+
import config from '@plone/volto/registry';
|
|
9
|
+
|
|
10
|
+
import deleteSVG from '@plone/volto/icons/delete.svg';
|
|
11
|
+
import dragSVG from '@plone/volto/icons/drag.svg';
|
|
12
|
+
|
|
13
|
+
export const Item = forwardRef(
|
|
14
|
+
(
|
|
15
|
+
{
|
|
16
|
+
clone,
|
|
17
|
+
data,
|
|
18
|
+
depth,
|
|
19
|
+
disableSelection,
|
|
20
|
+
disableInteraction,
|
|
21
|
+
ghost,
|
|
22
|
+
id,
|
|
23
|
+
handleProps,
|
|
24
|
+
indentationWidth,
|
|
25
|
+
onRemove,
|
|
26
|
+
onSelectBlock,
|
|
27
|
+
parentId,
|
|
28
|
+
style,
|
|
29
|
+
value,
|
|
30
|
+
wrapperRef,
|
|
31
|
+
...props
|
|
32
|
+
},
|
|
33
|
+
ref,
|
|
34
|
+
) => {
|
|
35
|
+
const selected = useSelector((state) => state.form.ui.selected);
|
|
36
|
+
const hovered = useSelector((state) => state.form.ui.hovered);
|
|
37
|
+
const multiSelected = useSelector((state) => state.form.ui.multiSelected);
|
|
38
|
+
const gridSelected = useSelector((state) => state.form.ui.gridSelected);
|
|
39
|
+
const dispatch = useDispatch();
|
|
40
|
+
return (
|
|
41
|
+
<li
|
|
42
|
+
className={classNames(
|
|
43
|
+
'tree-item-wrapper',
|
|
44
|
+
clone && 'clone',
|
|
45
|
+
ghost && 'ghost',
|
|
46
|
+
disableSelection && 'disable-selection',
|
|
47
|
+
disableInteraction && 'disable-interaction',
|
|
48
|
+
)}
|
|
49
|
+
role="presentation"
|
|
50
|
+
onMouseOver={() => dispatch(setUIState({ hovered: id }))}
|
|
51
|
+
onFocus={() => dispatch(setUIState({ hovered: id }))}
|
|
52
|
+
onMouseLeave={() => dispatch(setUIState({ hovered: null }))}
|
|
53
|
+
onClick={(e) => {
|
|
54
|
+
if (depth === 0) {
|
|
55
|
+
const isMultipleSelection = e.shiftKey || e.ctrlKey || e.metaKey;
|
|
56
|
+
selected !== id &&
|
|
57
|
+
onSelectBlock(
|
|
58
|
+
id,
|
|
59
|
+
selected === id ? false : isMultipleSelection,
|
|
60
|
+
e,
|
|
61
|
+
);
|
|
62
|
+
} else {
|
|
63
|
+
dispatch(
|
|
64
|
+
setUIState({
|
|
65
|
+
selected: parentId,
|
|
66
|
+
multiSelected: [],
|
|
67
|
+
gridSelected: id,
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}}
|
|
72
|
+
ref={wrapperRef}
|
|
73
|
+
style={{
|
|
74
|
+
'--spacing': `${indentationWidth * depth}px`,
|
|
75
|
+
}}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
<div
|
|
79
|
+
className={classNames(
|
|
80
|
+
'tree-item',
|
|
81
|
+
(selected === id || gridSelected === id) && 'selected',
|
|
82
|
+
hovered === id && 'hovered',
|
|
83
|
+
includes(multiSelected, id) && 'multiSelected',
|
|
84
|
+
`depth-${depth}`,
|
|
85
|
+
)}
|
|
86
|
+
ref={ref}
|
|
87
|
+
style={style}
|
|
88
|
+
>
|
|
89
|
+
<button
|
|
90
|
+
ref={ref}
|
|
91
|
+
{...handleProps}
|
|
92
|
+
className={classNames('action', 'drag')}
|
|
93
|
+
tabIndex={0}
|
|
94
|
+
data-cypress="draggable-handle"
|
|
95
|
+
>
|
|
96
|
+
<Icon name={dragSVG} size="16px" />
|
|
97
|
+
</button>
|
|
98
|
+
<span className="text">
|
|
99
|
+
{config.blocks.blocksConfig[data?.['@type']]?.icon && (
|
|
100
|
+
<Icon
|
|
101
|
+
name={config.blocks.blocksConfig[data?.['@type']]?.icon}
|
|
102
|
+
size="20px"
|
|
103
|
+
style={{ verticalAlign: 'middle' }}
|
|
104
|
+
/>
|
|
105
|
+
)}{' '}
|
|
106
|
+
{data?.plaintext ||
|
|
107
|
+
config.blocks.blocksConfig[data?.['@type']]?.title}
|
|
108
|
+
</span>
|
|
109
|
+
{!clone && onRemove && (
|
|
110
|
+
<button
|
|
111
|
+
onClick={onRemove}
|
|
112
|
+
className={classNames('action', 'delete')}
|
|
113
|
+
tabIndex={0}
|
|
114
|
+
>
|
|
115
|
+
<Icon name={deleteSVG} size="18" />
|
|
116
|
+
</button>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
</li>
|
|
120
|
+
);
|
|
121
|
+
},
|
|
122
|
+
);
|