@eeacms/volto-eea-website-theme 2.4.0 → 3.0.0

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