@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.
- package/.eslintrc.js +7 -2
- package/CHANGELOG.md +53 -1
- package/docker-compose.yml +1 -1
- package/jest-addon.config.js +3 -0
- package/package.json +2 -1
- package/src/components/manage/Blocks/LayoutSettings/index.js +3 -1
- package/src/components/manage/Blocks/Title/index.js +3 -1
- package/src/components/manage/Blocks/Title/schema.js +3 -1
- package/src/components/theme/Banner/View.jsx +12 -5
- package/src/components/theme/DraftBackground/DraftBackground.jsx +30 -21
- package/src/components/theme/DraftBackground/DraftBackground.test.jsx +85 -0
- package/src/config.js +2 -0
- package/src/customizations/@plone/volto-slate/blocks/Text/TextBlockView.jsx +32 -0
- package/src/customizations/@plone/volto-slate/editor/render.jsx +75 -0
- package/src/customizations/@plone/volto-slate/elementEditor/utils.js +76 -75
- package/src/customizations/volto/components/manage/Blocks/Grid/Edit.jsx +70 -0
- package/src/customizations/volto/components/manage/Blocks/Grid/View.jsx +61 -0
- package/src/customizations/volto/components/manage/Blocks/Grid/readme.md +1 -0
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.jsx +82 -23
- package/src/customizations/volto/components/manage/Blocks/Image/Edit.test.jsx +10 -3
- package/src/customizations/volto/components/manage/Blocks/Image/View.jsx +110 -111
- package/src/customizations/volto/components/manage/Blocks/Image/schema.js +17 -2
- package/src/customizations/volto/components/manage/Blocks/LeadImage/Edit.jsx +35 -14
- package/src/customizations/volto/components/manage/Blocks/LeadImage/View.jsx +65 -79
- package/src/customizations/volto/components/manage/Display/Display.jsx +306 -0
- package/src/customizations/volto/components/manage/Display/Readme.md +1 -0
- package/src/customizations/volto/components/manage/Sidebar/SidebarPopup copy.jsx +82 -0
- package/src/customizations/volto/components/manage/Toolbar/More.jsx +541 -0
- package/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx +3 -1
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +24 -14
- package/src/customizations/volto/components/manage/Widgets/README.md +1 -0
- package/src/customizations/volto/components/manage/Workflow/README.txt +1 -0
- package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +324 -0
- package/src/customizations/volto/components/manage/Workflow/Workflow.test.jsx +81 -0
- package/src/customizations/volto/components/theme/Comments/Comments.jsx +1 -2
- package/src/customizations/volto/components/theme/ContactForm/ContactForm.jsx +1 -1
- package/src/customizations/volto/components/theme/EventDetails/EventDetails.jsx +1 -0
- package/src/index.js +21 -16
- package/src/middleware/ok.js +4 -2
- package/src/middleware/voltoCustom.js +4 -2
- package/src/slate.js +10 -8
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/README.txt +0 -1
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/StyleMenu.jsx +0 -157
- package/src/customizations/@plone/volto-slate/editor/plugins/StyleMenu/utils.js +0 -168
- package/src/customizations/volto/components/manage/Add/Add.jsx +0 -498
- package/src/customizations/volto/components/manage/Add/readme.md +0 -1
- package/src/customizations/volto/components/manage/Contents/ContentsPropertiesModal.jsx +0 -232
- package/src/customizations/volto/components/manage/Form/Form.jsx +0 -810
- package/src/customizations/volto/components/manage/Form/Form.test.jsx +0 -1124
- package/src/customizations/volto/components/manage/Form/ModalForm.jsx +0 -326
- package/src/customizations/volto/components/manage/Sharing/Sharing.jsx +0 -528
- package/src/customizations/volto/components/manage/Sharing/Sharing.test.jsx +0 -72
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.test.jsx +0 -193
- package/src/customizations/volto/components/theme/AppExtras/AppExtras.jsx +0 -27
@@ -1,810 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Form component.
|
3
|
-
* @module components/manage/Form/Form
|
4
|
-
*/
|
5
|
-
|
6
|
-
import { BlocksForm, Field, Icon, Toast } from '@plone/volto/components';
|
7
|
-
import {
|
8
|
-
difference,
|
9
|
-
FormValidation,
|
10
|
-
getBlocksFieldname,
|
11
|
-
getBlocksLayoutFieldname,
|
12
|
-
messages,
|
13
|
-
} from '@plone/volto/helpers';
|
14
|
-
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
15
|
-
import clearSVG from '@plone/volto/icons/clear.svg';
|
16
|
-
import {
|
17
|
-
findIndex,
|
18
|
-
isEmpty,
|
19
|
-
keys,
|
20
|
-
map,
|
21
|
-
mapValues,
|
22
|
-
pickBy,
|
23
|
-
without,
|
24
|
-
cloneDeep,
|
25
|
-
} from 'lodash';
|
26
|
-
import isBoolean from 'lodash/isBoolean';
|
27
|
-
import PropTypes from 'prop-types';
|
28
|
-
import React, { Component } from 'react';
|
29
|
-
import { injectIntl } from 'react-intl';
|
30
|
-
import { Portal } from 'react-portal';
|
31
|
-
import { connect } from 'react-redux';
|
32
|
-
import {
|
33
|
-
Button,
|
34
|
-
Container,
|
35
|
-
Form as UiForm,
|
36
|
-
Message,
|
37
|
-
Segment,
|
38
|
-
Tab,
|
39
|
-
} from 'semantic-ui-react';
|
40
|
-
import { v4 as uuid } from 'uuid';
|
41
|
-
import { toast } from 'react-toastify';
|
42
|
-
import { BlocksToolbar, UndoToolbar } from '@plone/volto/components';
|
43
|
-
import { setSidebarTab } from '@plone/volto/actions';
|
44
|
-
import { compose } from 'redux';
|
45
|
-
import config from '@plone/volto/registry';
|
46
|
-
|
47
|
-
/**
|
48
|
-
* Form container class.
|
49
|
-
* @class Form
|
50
|
-
* @extends Component
|
51
|
-
*/
|
52
|
-
class Form extends Component {
|
53
|
-
/**
|
54
|
-
* Property types.
|
55
|
-
* @property {Object} propTypes Property types.
|
56
|
-
* @static
|
57
|
-
*/
|
58
|
-
static propTypes = {
|
59
|
-
schema: PropTypes.shape({
|
60
|
-
fieldsets: PropTypes.arrayOf(
|
61
|
-
PropTypes.shape({
|
62
|
-
fields: PropTypes.arrayOf(PropTypes.string),
|
63
|
-
id: PropTypes.string,
|
64
|
-
title: PropTypes.string,
|
65
|
-
}),
|
66
|
-
),
|
67
|
-
properties: PropTypes.objectOf(PropTypes.any),
|
68
|
-
definitions: PropTypes.objectOf(PropTypes.any),
|
69
|
-
required: PropTypes.arrayOf(PropTypes.string),
|
70
|
-
}),
|
71
|
-
formData: PropTypes.objectOf(PropTypes.any),
|
72
|
-
pathname: PropTypes.string,
|
73
|
-
onSubmit: PropTypes.func,
|
74
|
-
onCancel: PropTypes.func,
|
75
|
-
submitLabel: PropTypes.string,
|
76
|
-
resetAfterSubmit: PropTypes.bool,
|
77
|
-
resetOnCancel: PropTypes.bool,
|
78
|
-
isEditForm: PropTypes.bool,
|
79
|
-
isAdminForm: PropTypes.bool,
|
80
|
-
title: PropTypes.string,
|
81
|
-
error: PropTypes.shape({
|
82
|
-
message: PropTypes.string,
|
83
|
-
}),
|
84
|
-
loading: PropTypes.bool,
|
85
|
-
hideActions: PropTypes.bool,
|
86
|
-
description: PropTypes.string,
|
87
|
-
visual: PropTypes.bool,
|
88
|
-
blocks: PropTypes.arrayOf(PropTypes.object),
|
89
|
-
isFormSelected: PropTypes.bool,
|
90
|
-
onSelectForm: PropTypes.func,
|
91
|
-
editable: PropTypes.bool,
|
92
|
-
onChangeFormData: PropTypes.func,
|
93
|
-
requestError: PropTypes.string,
|
94
|
-
allowedBlocks: PropTypes.arrayOf(PropTypes.string),
|
95
|
-
showRestricted: PropTypes.bool,
|
96
|
-
};
|
97
|
-
|
98
|
-
/**
|
99
|
-
* Default properties.
|
100
|
-
* @property {Object} defaultProps Default properties.
|
101
|
-
* @static
|
102
|
-
*/
|
103
|
-
static defaultProps = {
|
104
|
-
formData: null,
|
105
|
-
onSubmit: null,
|
106
|
-
onCancel: null,
|
107
|
-
submitLabel: null,
|
108
|
-
resetAfterSubmit: false,
|
109
|
-
resetOnCancel: false,
|
110
|
-
isEditForm: false,
|
111
|
-
isAdminForm: false,
|
112
|
-
title: null,
|
113
|
-
description: null,
|
114
|
-
error: null,
|
115
|
-
loading: null,
|
116
|
-
hideActions: false,
|
117
|
-
visual: false,
|
118
|
-
blocks: [],
|
119
|
-
pathname: '',
|
120
|
-
schema: {},
|
121
|
-
isFormSelected: true,
|
122
|
-
onSelectForm: null,
|
123
|
-
editable: true,
|
124
|
-
requestError: null,
|
125
|
-
allowedBlocks: null,
|
126
|
-
};
|
127
|
-
|
128
|
-
/**
|
129
|
-
* Constructor
|
130
|
-
* @method constructor
|
131
|
-
* @param {Object} props Component properties
|
132
|
-
* @constructs Form
|
133
|
-
*/
|
134
|
-
constructor(props) {
|
135
|
-
super(props);
|
136
|
-
const ids = {
|
137
|
-
title: uuid(),
|
138
|
-
text: uuid(),
|
139
|
-
};
|
140
|
-
let { formData } = props;
|
141
|
-
// TODO Tiberiu: customized here
|
142
|
-
formData = formData || {}; // when coming from login screen, formData is null
|
143
|
-
// this fixes a bug where, if you go to an /edit page, it will show login (you need to wait the 5 seconds timeout), after login you get redirected back to the edit, then it crashes
|
144
|
-
// end customized
|
145
|
-
const blocksFieldname = getBlocksFieldname(formData);
|
146
|
-
const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);
|
147
|
-
|
148
|
-
if (!props.isEditForm) {
|
149
|
-
// It's a normal (add form), get defaults from schema
|
150
|
-
formData = {
|
151
|
-
...mapValues(props.schema.properties, 'default'),
|
152
|
-
...formData,
|
153
|
-
};
|
154
|
-
}
|
155
|
-
// defaults for block editor; should be moved to schema on server side
|
156
|
-
// Adding fallback in case the fields are empty, so we are sure that the edit form
|
157
|
-
// shows at least the default blocks
|
158
|
-
if (
|
159
|
-
formData.hasOwnProperty(blocksFieldname) &&
|
160
|
-
formData.hasOwnProperty(blocksLayoutFieldname)
|
161
|
-
) {
|
162
|
-
if (
|
163
|
-
!formData[blocksLayoutFieldname] ||
|
164
|
-
isEmpty(formData[blocksLayoutFieldname].items)
|
165
|
-
) {
|
166
|
-
formData[blocksLayoutFieldname] = {
|
167
|
-
items: [ids.title, ids.text],
|
168
|
-
};
|
169
|
-
}
|
170
|
-
if (!formData[blocksFieldname] || isEmpty(formData[blocksFieldname])) {
|
171
|
-
formData[blocksFieldname] = {
|
172
|
-
[ids.title]: {
|
173
|
-
'@type': 'title',
|
174
|
-
},
|
175
|
-
[ids.text]: {
|
176
|
-
'@type': config.settings.defaultBlockType,
|
177
|
-
},
|
178
|
-
};
|
179
|
-
}
|
180
|
-
}
|
181
|
-
|
182
|
-
let selectedBlock = null;
|
183
|
-
if (
|
184
|
-
formData.hasOwnProperty(blocksLayoutFieldname) &&
|
185
|
-
formData[blocksLayoutFieldname].items.length > 0
|
186
|
-
) {
|
187
|
-
if (config.blocks?.initialBlocksFocus === null) {
|
188
|
-
selectedBlock = null;
|
189
|
-
} else if (this.props.type in config.blocks?.initialBlocksFocus) {
|
190
|
-
// Default selected is not the first block, but the one from config.
|
191
|
-
// TODO Select first block and not an arbitrary one.
|
192
|
-
Object.keys(formData[blocksFieldname]).forEach((b_key) => {
|
193
|
-
if (
|
194
|
-
formData[blocksFieldname][b_key]['@type'] ===
|
195
|
-
config.blocks?.initialBlocksFocus?.[this.props.type]
|
196
|
-
) {
|
197
|
-
selectedBlock = b_key;
|
198
|
-
}
|
199
|
-
});
|
200
|
-
} else {
|
201
|
-
selectedBlock = formData[blocksLayoutFieldname].items[0];
|
202
|
-
}
|
203
|
-
}
|
204
|
-
|
205
|
-
this.state = {
|
206
|
-
formData,
|
207
|
-
initialFormData: cloneDeep(formData),
|
208
|
-
errors: {},
|
209
|
-
selected: selectedBlock,
|
210
|
-
multiSelected: [],
|
211
|
-
isClient: false,
|
212
|
-
// Ensure focus remain in field after change
|
213
|
-
inFocus: {},
|
214
|
-
};
|
215
|
-
this.onChangeField = this.onChangeField.bind(this);
|
216
|
-
this.onSelectBlock = this.onSelectBlock.bind(this);
|
217
|
-
this.onSubmit = this.onSubmit.bind(this);
|
218
|
-
this.onCancel = this.onCancel.bind(this);
|
219
|
-
this.onTabChange = this.onTabChange.bind(this);
|
220
|
-
this.onBlurField = this.onBlurField.bind(this);
|
221
|
-
this.onClickInput = this.onClickInput.bind(this);
|
222
|
-
}
|
223
|
-
|
224
|
-
/**
|
225
|
-
* On updates caused by props change
|
226
|
-
* if errors from Backend come, these will be shown to their corresponding Fields
|
227
|
-
* also the first Tab to have any errors will be selected
|
228
|
-
* @param {Object} prevProps
|
229
|
-
*/
|
230
|
-
async componentDidUpdate(prevProps, prevState) {
|
231
|
-
let { requestError } = this.props;
|
232
|
-
let errors = {};
|
233
|
-
let activeIndex = 0;
|
234
|
-
|
235
|
-
if (requestError && prevProps.requestError !== requestError) {
|
236
|
-
errors = FormValidation.giveServerErrorsToCorrespondingFields(
|
237
|
-
requestError,
|
238
|
-
);
|
239
|
-
activeIndex = FormValidation.showFirstTabWithErrors({
|
240
|
-
errors,
|
241
|
-
schema: this.props.schema,
|
242
|
-
});
|
243
|
-
|
244
|
-
this.setState({
|
245
|
-
errors,
|
246
|
-
activeIndex,
|
247
|
-
});
|
248
|
-
}
|
249
|
-
|
250
|
-
if (this.props.onChangeFormData) {
|
251
|
-
if (
|
252
|
-
// TODO: use fast-deep-equal
|
253
|
-
JSON.stringify(prevState?.formData) !==
|
254
|
-
JSON.stringify(this.state.formData)
|
255
|
-
) {
|
256
|
-
this.props.onChangeFormData(this.state.formData);
|
257
|
-
}
|
258
|
-
}
|
259
|
-
}
|
260
|
-
|
261
|
-
/**
|
262
|
-
* Tab selection is done only by setting activeIndex in state
|
263
|
-
*/
|
264
|
-
onTabChange(e, { activeIndex }) {
|
265
|
-
const defaultFocus = this.props.schema.fieldsets[activeIndex].fields[0];
|
266
|
-
this.setState({
|
267
|
-
activeIndex,
|
268
|
-
...(defaultFocus ? { inFocus: { [defaultFocus]: true } } : {}),
|
269
|
-
});
|
270
|
-
}
|
271
|
-
|
272
|
-
/**
|
273
|
-
* If user clicks on input, the form will be not considered pristine
|
274
|
-
* this will avoid onBlur effects without interraction with the form
|
275
|
-
* @param {Object} e event
|
276
|
-
*/
|
277
|
-
onClickInput(e) {
|
278
|
-
this.setState({ isFormPristine: false });
|
279
|
-
}
|
280
|
-
|
281
|
-
/**
|
282
|
-
* Validate fields on blur
|
283
|
-
* @method onBlurField
|
284
|
-
* @param {string} id Id of the field
|
285
|
-
* @param {*} value Value of the field
|
286
|
-
* @returns {undefined}
|
287
|
-
*/
|
288
|
-
onBlurField(id, value) {
|
289
|
-
if (!this.state.isFormPristine) {
|
290
|
-
const errors = FormValidation.validateFieldsPerFieldset({
|
291
|
-
schema: this.props.schema,
|
292
|
-
formData: this.state.formData,
|
293
|
-
formatMessage: this.props.intl.formatMessage,
|
294
|
-
touchedField: { [id]: value },
|
295
|
-
});
|
296
|
-
|
297
|
-
this.setState({
|
298
|
-
errors,
|
299
|
-
});
|
300
|
-
}
|
301
|
-
}
|
302
|
-
|
303
|
-
/**
|
304
|
-
* Component did mount
|
305
|
-
* @method componentDidMount
|
306
|
-
* @returns {undefined}
|
307
|
-
*/
|
308
|
-
componentDidMount() {
|
309
|
-
this.setState({ isClient: true });
|
310
|
-
}
|
311
|
-
|
312
|
-
static getDerivedStateFromProps(props, state) {
|
313
|
-
let newState = { ...state };
|
314
|
-
if (!props.isFormSelected) {
|
315
|
-
newState.selected = null;
|
316
|
-
}
|
317
|
-
|
318
|
-
return newState;
|
319
|
-
}
|
320
|
-
|
321
|
-
/**
|
322
|
-
* Change field handler
|
323
|
-
* Remove errors for changed field
|
324
|
-
* @method onChangeField
|
325
|
-
* @param {string} id Id of the field
|
326
|
-
* @param {*} value Value of the field
|
327
|
-
* @returns {undefined}
|
328
|
-
*/
|
329
|
-
onChangeField(id, value) {
|
330
|
-
this.setState((prevState) => {
|
331
|
-
const { errors, formData } = prevState;
|
332
|
-
delete errors[id];
|
333
|
-
return {
|
334
|
-
errors,
|
335
|
-
formData: {
|
336
|
-
...formData,
|
337
|
-
// We need to catch also when the value equals false this fixes #888
|
338
|
-
[id]:
|
339
|
-
value || (value !== undefined && isBoolean(value)) ? value : null,
|
340
|
-
},
|
341
|
-
// Changing the form data re-renders the select widget which causes the
|
342
|
-
// focus to get lost. To circumvent this, we set the focus back to
|
343
|
-
// the input.
|
344
|
-
// This could fix other widgets too but currently targeted
|
345
|
-
// against the select widget only.
|
346
|
-
// Ensure field to be in focus after the change
|
347
|
-
inFocus: { [id]: true },
|
348
|
-
};
|
349
|
-
});
|
350
|
-
}
|
351
|
-
|
352
|
-
/**
|
353
|
-
* Select block handler
|
354
|
-
* @method onSelectBlock
|
355
|
-
* @param {string} id Id of the field
|
356
|
-
* @param {string} isMultipleSelection true if multiple blocks are selected
|
357
|
-
* @returns {undefined}
|
358
|
-
*/
|
359
|
-
onSelectBlock(id, isMultipleSelection, event) {
|
360
|
-
let multiSelected = [];
|
361
|
-
let selected = id;
|
362
|
-
|
363
|
-
if (isMultipleSelection) {
|
364
|
-
selected = null;
|
365
|
-
const blocksLayoutFieldname = getBlocksLayoutFieldname(
|
366
|
-
this.state.formData,
|
367
|
-
);
|
368
|
-
|
369
|
-
const blocks_layout = this.state.formData[blocksLayoutFieldname].items;
|
370
|
-
|
371
|
-
if (event.shiftKey) {
|
372
|
-
const anchor =
|
373
|
-
this.state.multiSelected.length > 0
|
374
|
-
? blocks_layout.indexOf(this.state.multiSelected[0])
|
375
|
-
: blocks_layout.indexOf(this.state.selected);
|
376
|
-
const focus = blocks_layout.indexOf(id);
|
377
|
-
|
378
|
-
if (anchor === focus) {
|
379
|
-
multiSelected = [id];
|
380
|
-
} else if (focus > anchor) {
|
381
|
-
multiSelected = [...blocks_layout.slice(anchor, focus + 1)];
|
382
|
-
} else {
|
383
|
-
multiSelected = [...blocks_layout.slice(focus, anchor + 1)];
|
384
|
-
}
|
385
|
-
}
|
386
|
-
|
387
|
-
if ((event.ctrlKey || event.metaKey) && !event.shiftKey) {
|
388
|
-
multiSelected = this.state.multiSelected || [];
|
389
|
-
if (!this.state.multiSelected.includes(this.state.selected)) {
|
390
|
-
multiSelected = [...multiSelected, this.state.selected];
|
391
|
-
selected = null;
|
392
|
-
}
|
393
|
-
if (this.state.multiSelected.includes(id)) {
|
394
|
-
selected = null;
|
395
|
-
multiSelected = without(multiSelected, id);
|
396
|
-
} else {
|
397
|
-
multiSelected = [...multiSelected, id];
|
398
|
-
}
|
399
|
-
}
|
400
|
-
}
|
401
|
-
|
402
|
-
this.setState({
|
403
|
-
selected,
|
404
|
-
multiSelected,
|
405
|
-
});
|
406
|
-
|
407
|
-
if (this.props.onSelectForm) {
|
408
|
-
if (event) event.nativeEvent.stopImmediatePropagation();
|
409
|
-
this.props.onSelectForm();
|
410
|
-
}
|
411
|
-
}
|
412
|
-
|
413
|
-
/**
|
414
|
-
* Cancel handler
|
415
|
-
* It prevents event from triggering submit, reset form if props.resetAfterSubmit
|
416
|
-
* and calls this.props.onCancel
|
417
|
-
* @method onCancel
|
418
|
-
* @param {Object} event Event object.
|
419
|
-
* @returns {undefined}
|
420
|
-
*/
|
421
|
-
onCancel(event) {
|
422
|
-
if (event) {
|
423
|
-
event.preventDefault();
|
424
|
-
}
|
425
|
-
if (this.props.resetOnCancel || this.props.resetAfterSubmit) {
|
426
|
-
this.setState({
|
427
|
-
formData: this.props.formData,
|
428
|
-
});
|
429
|
-
}
|
430
|
-
this.props.onCancel(event);
|
431
|
-
}
|
432
|
-
|
433
|
-
/**
|
434
|
-
* Submit handler also validate form and collect errors
|
435
|
-
* @method onSubmit
|
436
|
-
* @param {Object} event Event object.
|
437
|
-
* @returns {undefined}
|
438
|
-
*/
|
439
|
-
onSubmit(event) {
|
440
|
-
if (event) {
|
441
|
-
event.preventDefault();
|
442
|
-
}
|
443
|
-
|
444
|
-
const errors = this.props.schema
|
445
|
-
? FormValidation.validateFieldsPerFieldset({
|
446
|
-
schema: this.props.schema,
|
447
|
-
formData: this.state.formData,
|
448
|
-
formatMessage: this.props.intl.formatMessage,
|
449
|
-
})
|
450
|
-
: {};
|
451
|
-
|
452
|
-
if (keys(errors).length > 0) {
|
453
|
-
const activeIndex = FormValidation.showFirstTabWithErrors({
|
454
|
-
errors,
|
455
|
-
schema: this.props.schema,
|
456
|
-
});
|
457
|
-
this.setState(
|
458
|
-
{
|
459
|
-
errors,
|
460
|
-
activeIndex,
|
461
|
-
},
|
462
|
-
() => {
|
463
|
-
Object.keys(errors).forEach((err) =>
|
464
|
-
toast.error(
|
465
|
-
<Toast
|
466
|
-
error
|
467
|
-
title={this.props.schema.properties[err].title || err}
|
468
|
-
content={errors[err].join(', ')}
|
469
|
-
/>,
|
470
|
-
),
|
471
|
-
);
|
472
|
-
},
|
473
|
-
);
|
474
|
-
// Changes the focus to the metadata tab in the sidebar if error
|
475
|
-
this.props.setSidebarTab(0);
|
476
|
-
} else {
|
477
|
-
// Get only the values that have been modified (Edit forms), send all in case that
|
478
|
-
// it's an add form
|
479
|
-
if (this.props.isEditForm) {
|
480
|
-
this.props.onSubmit(this.getOnlyFormModifiedValues());
|
481
|
-
} else {
|
482
|
-
this.props.onSubmit(this.state.formData);
|
483
|
-
}
|
484
|
-
if (this.props.resetAfterSubmit) {
|
485
|
-
this.setState({
|
486
|
-
formData: this.props.formData,
|
487
|
-
});
|
488
|
-
}
|
489
|
-
}
|
490
|
-
}
|
491
|
-
|
492
|
-
/**
|
493
|
-
* getOnlyFormModifiedValues handler
|
494
|
-
* It returns only the values of the fields that are have really changed since the
|
495
|
-
* form was loaded. Useful for edit forms and PATCH operations, when we only want to
|
496
|
-
* send the changed data.
|
497
|
-
* @method getOnlyFormModifiedValues
|
498
|
-
* @param {Object} event Event object.
|
499
|
-
* @returns {undefined}
|
500
|
-
*/
|
501
|
-
getOnlyFormModifiedValues = () => {
|
502
|
-
const fieldsModified = Object.keys(
|
503
|
-
difference(this.state.formData, this.state.initialFormData),
|
504
|
-
);
|
505
|
-
return {
|
506
|
-
...pickBy(this.state.formData, (value, key) =>
|
507
|
-
fieldsModified.includes(key),
|
508
|
-
),
|
509
|
-
...(this.state.formData['@static_behaviors'] && {
|
510
|
-
'@static_behaviors': this.state.formData['@static_behaviors'],
|
511
|
-
}),
|
512
|
-
};
|
513
|
-
};
|
514
|
-
|
515
|
-
/**
|
516
|
-
* Removed blocks and blocks_layout fields from the form.
|
517
|
-
* @method removeBlocksLayoutFields
|
518
|
-
* @param {object} schema The schema definition of the form.
|
519
|
-
* @returns A modified copy of the given schema.
|
520
|
-
*/
|
521
|
-
removeBlocksLayoutFields = (schema) => {
|
522
|
-
const newSchema = { ...schema };
|
523
|
-
const layoutFieldsetIndex = findIndex(
|
524
|
-
newSchema.fieldsets,
|
525
|
-
(fieldset) => fieldset.id === 'layout',
|
526
|
-
);
|
527
|
-
if (layoutFieldsetIndex > -1) {
|
528
|
-
const layoutFields = newSchema.fieldsets[layoutFieldsetIndex].fields;
|
529
|
-
newSchema.fieldsets[layoutFieldsetIndex].fields = layoutFields.filter(
|
530
|
-
(field) => field !== 'blocks' && field !== 'blocks_layout',
|
531
|
-
);
|
532
|
-
if (newSchema.fieldsets[layoutFieldsetIndex].fields.length === 0) {
|
533
|
-
newSchema.fieldsets = [
|
534
|
-
...newSchema.fieldsets.slice(0, layoutFieldsetIndex),
|
535
|
-
...newSchema.fieldsets.slice(layoutFieldsetIndex + 1),
|
536
|
-
];
|
537
|
-
}
|
538
|
-
}
|
539
|
-
return newSchema;
|
540
|
-
};
|
541
|
-
|
542
|
-
/**
|
543
|
-
* Render method.
|
544
|
-
* @method render
|
545
|
-
* @returns {string} Markup for the component.
|
546
|
-
*/
|
547
|
-
render() {
|
548
|
-
const { settings } = config;
|
549
|
-
const { schema: originalSchema, onCancel, onSubmit } = this.props;
|
550
|
-
const { formData } = this.state;
|
551
|
-
const schema = this.removeBlocksLayoutFields(originalSchema);
|
552
|
-
|
553
|
-
return this.props.visual ? (
|
554
|
-
// Removing this from SSR is important, since react-beautiful-dnd supports SSR,
|
555
|
-
// but draftJS don't like it much and the hydration gets messed up
|
556
|
-
this.state.isClient && (
|
557
|
-
<div className="ui container">
|
558
|
-
<BlocksToolbar
|
559
|
-
formData={this.state.formData}
|
560
|
-
selectedBlock={this.state.selected}
|
561
|
-
selectedBlocks={this.state.multiSelected}
|
562
|
-
onChangeBlocks={(newBlockData) =>
|
563
|
-
this.setState({
|
564
|
-
formData: {
|
565
|
-
...formData,
|
566
|
-
...newBlockData,
|
567
|
-
},
|
568
|
-
})
|
569
|
-
}
|
570
|
-
onSetSelectedBlocks={(blockIds) =>
|
571
|
-
this.setState({ multiSelected: blockIds })
|
572
|
-
}
|
573
|
-
onSelectBlock={this.onSelectBlock}
|
574
|
-
/>
|
575
|
-
<UndoToolbar
|
576
|
-
state={{
|
577
|
-
formData: this.state.formData,
|
578
|
-
selected: this.state.selected,
|
579
|
-
multiSelected: this.state.multiSelected,
|
580
|
-
}}
|
581
|
-
enableHotKeys
|
582
|
-
onUndoRedo={({ state }) => this.setState(state)}
|
583
|
-
/>
|
584
|
-
<BlocksForm
|
585
|
-
onChangeFormData={(newFormData) =>
|
586
|
-
this.setState({
|
587
|
-
formData: {
|
588
|
-
...formData,
|
589
|
-
...newFormData,
|
590
|
-
},
|
591
|
-
})
|
592
|
-
}
|
593
|
-
onChangeField={this.onChangeField}
|
594
|
-
onSelectBlock={this.onSelectBlock}
|
595
|
-
properties={formData}
|
596
|
-
pathname={this.props.pathname}
|
597
|
-
selectedBlock={this.state.selected}
|
598
|
-
multiSelected={this.state.multiSelected}
|
599
|
-
manage={this.props.isAdminForm}
|
600
|
-
allowedBlocks={this.props.allowedBlocks}
|
601
|
-
showRestricted={this.props.showRestricted}
|
602
|
-
editable={this.props.editable}
|
603
|
-
isMainForm={this.props.editable}
|
604
|
-
/>
|
605
|
-
{this.state.isClient && this.props.editable && (
|
606
|
-
<Portal
|
607
|
-
node={__CLIENT__ && document.getElementById('sidebar-metadata')}
|
608
|
-
>
|
609
|
-
<UiForm
|
610
|
-
method="post"
|
611
|
-
onSubmit={this.onSubmit}
|
612
|
-
error={keys(this.state.errors).length > 0}
|
613
|
-
>
|
614
|
-
{schema &&
|
615
|
-
map(schema.fieldsets, (item) => [
|
616
|
-
<Segment
|
617
|
-
secondary
|
618
|
-
attached
|
619
|
-
className={`fieldset-${item.id}`}
|
620
|
-
key={item.title}
|
621
|
-
>
|
622
|
-
{item.title}
|
623
|
-
</Segment>,
|
624
|
-
<Segment attached key={`fieldset-contents-${item.title}`}>
|
625
|
-
{map(item.fields, (field, index) => (
|
626
|
-
<Field
|
627
|
-
{...schema.properties[field]}
|
628
|
-
id={field}
|
629
|
-
fieldSet={item.title.toLowerCase()}
|
630
|
-
formData={this.state.formData}
|
631
|
-
focus={this.state.inFocus[field]}
|
632
|
-
value={this.state.formData?.[field]}
|
633
|
-
required={schema.required.indexOf(field) !== -1}
|
634
|
-
onChange={this.onChangeField}
|
635
|
-
onBlur={this.onBlurField}
|
636
|
-
onClick={this.onClickInput}
|
637
|
-
key={field}
|
638
|
-
error={this.state.errors[field]}
|
639
|
-
/>
|
640
|
-
))}
|
641
|
-
</Segment>,
|
642
|
-
])}
|
643
|
-
</UiForm>
|
644
|
-
</Portal>
|
645
|
-
)}
|
646
|
-
</div>
|
647
|
-
)
|
648
|
-
) : (
|
649
|
-
<Container>
|
650
|
-
<UiForm
|
651
|
-
method="post"
|
652
|
-
onSubmit={this.onSubmit}
|
653
|
-
error={keys(this.state.errors).length > 0}
|
654
|
-
className={settings.verticalFormTabs ? 'vertical-form' : ''}
|
655
|
-
>
|
656
|
-
<fieldset className="invisible">
|
657
|
-
<Segment.Group raised>
|
658
|
-
{schema && schema.fieldsets.length > 1 && (
|
659
|
-
<>
|
660
|
-
{settings.verticalFormTabs && this.props.title && (
|
661
|
-
<Segment secondary attached key={this.props.title}>
|
662
|
-
{this.props.title}
|
663
|
-
</Segment>
|
664
|
-
)}
|
665
|
-
<Tab
|
666
|
-
menu={{
|
667
|
-
secondary: true,
|
668
|
-
pointing: true,
|
669
|
-
attached: true,
|
670
|
-
tabular: true,
|
671
|
-
className: 'formtabs',
|
672
|
-
vertical: settings.verticalFormTabs,
|
673
|
-
}}
|
674
|
-
grid={{ paneWidth: 9, tabWidth: 3, stackable: true }}
|
675
|
-
onTabChange={this.onTabChange}
|
676
|
-
activeIndex={this.state.activeIndex}
|
677
|
-
panes={map(schema.fieldsets, (item) => ({
|
678
|
-
menuItem: item.title,
|
679
|
-
render: () => [
|
680
|
-
!settings.verticalFormTabs && this.props.title && (
|
681
|
-
<Segment secondary attached key={this.props.title}>
|
682
|
-
{this.props.title}
|
683
|
-
</Segment>
|
684
|
-
),
|
685
|
-
item.description && (
|
686
|
-
<Message attached="bottom">
|
687
|
-
{item.description}
|
688
|
-
</Message>
|
689
|
-
),
|
690
|
-
...map(item.fields, (field, index) => (
|
691
|
-
<Field
|
692
|
-
{...schema.properties[field]}
|
693
|
-
isDisabled={!this.props.editable}
|
694
|
-
id={field}
|
695
|
-
formData={this.state.formData}
|
696
|
-
fieldSet={item.title.toLowerCase()}
|
697
|
-
focus={this.state.inFocus[field]}
|
698
|
-
value={this.state.formData?.[field]}
|
699
|
-
required={schema.required.indexOf(field) !== -1}
|
700
|
-
onChange={this.onChangeField}
|
701
|
-
onBlur={this.onBlurField}
|
702
|
-
onClick={this.onClickInput}
|
703
|
-
key={field}
|
704
|
-
error={this.state.errors[field]}
|
705
|
-
/>
|
706
|
-
)),
|
707
|
-
],
|
708
|
-
}))}
|
709
|
-
/>
|
710
|
-
</>
|
711
|
-
)}
|
712
|
-
{schema && schema.fieldsets.length === 1 && (
|
713
|
-
<Segment>
|
714
|
-
{this.props.title && (
|
715
|
-
<Segment className="primary">
|
716
|
-
<h1 style={{ fontSize: '16px' }}> {this.props.title}</h1>
|
717
|
-
</Segment>
|
718
|
-
)}
|
719
|
-
{this.props.description && (
|
720
|
-
<Segment secondary>{this.props.description}</Segment>
|
721
|
-
)}
|
722
|
-
{keys(this.state.errors).length > 0 && (
|
723
|
-
<Message
|
724
|
-
icon="warning"
|
725
|
-
negative
|
726
|
-
attached
|
727
|
-
header={this.props.intl.formatMessage(messages.error)}
|
728
|
-
content={this.props.intl.formatMessage(
|
729
|
-
messages.thereWereSomeErrors,
|
730
|
-
)}
|
731
|
-
/>
|
732
|
-
)}
|
733
|
-
{this.props.error && (
|
734
|
-
<Message
|
735
|
-
icon="warning"
|
736
|
-
negative
|
737
|
-
attached
|
738
|
-
header={this.props.intl.formatMessage(messages.error)}
|
739
|
-
content={this.props.error.message}
|
740
|
-
/>
|
741
|
-
)}
|
742
|
-
{map(schema.fieldsets[0].fields, (field) => (
|
743
|
-
<Field
|
744
|
-
{...schema.properties[field]}
|
745
|
-
id={field}
|
746
|
-
value={this.state.formData?.[field]}
|
747
|
-
required={schema.required.indexOf(field) !== -1}
|
748
|
-
onChange={this.onChangeField}
|
749
|
-
onBlur={this.onBlurField}
|
750
|
-
onClick={this.onClickInput}
|
751
|
-
key={field}
|
752
|
-
error={this.state.errors[field]}
|
753
|
-
/>
|
754
|
-
))}
|
755
|
-
</Segment>
|
756
|
-
)}
|
757
|
-
{!this.props.hideActions && (
|
758
|
-
<Segment className="actions" clearing>
|
759
|
-
{onSubmit && (
|
760
|
-
<Button
|
761
|
-
basic
|
762
|
-
icon
|
763
|
-
primary
|
764
|
-
floated="right"
|
765
|
-
type="submit"
|
766
|
-
aria-label={
|
767
|
-
this.props.submitLabel
|
768
|
-
? this.props.submitLabel
|
769
|
-
: this.props.intl.formatMessage(messages.save)
|
770
|
-
}
|
771
|
-
title={
|
772
|
-
this.props.submitLabel
|
773
|
-
? this.props.submitLabel
|
774
|
-
: this.props.intl.formatMessage(messages.save)
|
775
|
-
}
|
776
|
-
loading={this.props.loading}
|
777
|
-
>
|
778
|
-
<Icon className="circled" name={aheadSVG} size="30px" />
|
779
|
-
</Button>
|
780
|
-
)}
|
781
|
-
{onCancel && (
|
782
|
-
<Button
|
783
|
-
basic
|
784
|
-
icon
|
785
|
-
secondary
|
786
|
-
aria-label={this.props.intl.formatMessage(
|
787
|
-
messages.cancel,
|
788
|
-
)}
|
789
|
-
title={this.props.intl.formatMessage(messages.cancel)}
|
790
|
-
floated="right"
|
791
|
-
onClick={this.onCancel}
|
792
|
-
>
|
793
|
-
<Icon className="circled" name={clearSVG} size="30px" />
|
794
|
-
</Button>
|
795
|
-
)}
|
796
|
-
</Segment>
|
797
|
-
)}
|
798
|
-
</Segment.Group>
|
799
|
-
</fieldset>
|
800
|
-
</UiForm>
|
801
|
-
</Container>
|
802
|
-
);
|
803
|
-
}
|
804
|
-
}
|
805
|
-
|
806
|
-
const FormIntl = injectIntl(Form, { forwardRef: true });
|
807
|
-
|
808
|
-
export default compose(
|
809
|
-
connect(null, { setSidebarTab }, null, { forwardRef: true }),
|
810
|
-
)(FormIntl);
|