UNPKG

@plone/volto

Version:
253 lines (238 loc) 7.31 kB
import React from 'react'; import PropTypes from 'prop-types'; import { Accordion, Segment, Message } from 'semantic-ui-react'; import { defineMessages, injectIntl } from 'react-intl'; import AnimateHeight from 'react-animate-height'; import keys from 'lodash/keys'; import map from 'lodash/map'; import isEqual from 'lodash/isEqual'; import { useAtom } from 'jotai'; import { inlineFormFieldsetsState } from './InlineFormState'; import { insertInArray, removeFromArray, arrayRange, } from '@plone/volto/helpers/Utils/Utils'; import Icon from '@plone/volto/components/theme/Icon/Icon'; import { Field } from '@plone/volto/components/manage/Form'; import { applySchemaDefaults } from '@plone/volto/helpers/Blocks/Blocks'; import upSVG from '@plone/volto/icons/up-key.svg'; import downSVG from '@plone/volto/icons/down-key.svg'; import config from '@plone/volto/registry'; const messages = defineMessages({ editValues: { id: 'Edit values', defaultMessage: 'Edit values', }, error: { id: 'Error', defaultMessage: 'Error', }, thereWereSomeErrors: { id: 'There were some errors', defaultMessage: 'There were some errors', }, }); const InlineForm = (props) => { const { block, description, error, // Such as {message: "It's not good"} errors = {}, formData, onChangeFormData, onChangeField, schema, title, icon, headerActions, actionButton, footer, focusIndex, intl, } = props; const _ = intl.formatMessage; const defaultFieldset = schema.fieldsets.find((o) => o.id === 'default'); const other = schema.fieldsets.filter((o) => o.id !== 'default'); React.useEffect(() => { // Will set field values from schema, by matching the default values const objectSchema = typeof schema === 'function' ? schema(props) : schema; const initialData = applySchemaDefaults({ data: formData, schema: objectSchema, intl, }); if (onChangeFormData) { onChangeFormData(initialData); } else { Object.keys(initialData).forEach((k) => { if (!isEqual(initialData[k], formData?.[k])) { onChangeField(k, initialData[k]); } }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const [currentActiveFieldset, setCurrentActiveFieldset] = useAtom( inlineFormFieldsetsState({ name: block, fielsetList: other, initialState: config.settings.blockSettingsTabFieldsetsInitialStateOpen ? arrayRange(0, other.length - 1, 1) : [], }), ); function handleCurrentActiveFieldset(e, blockProps) { const { index } = blockProps; if (currentActiveFieldset.includes(index)) { setCurrentActiveFieldset( removeFromArray( currentActiveFieldset, currentActiveFieldset.indexOf(index), ), ); } else { setCurrentActiveFieldset( insertInArray( currentActiveFieldset, index, currentActiveFieldset.length + 1, ), ); } } return ( <div className="ui form"> {title && ( <header className="header pulled"> {icon} <h2>{title || _(messages.editValues)}</h2> {headerActions} </header> )} {description && ( <Segment secondary className="attached"> {description} </Segment> )} {keys(errors).length > 0 && ( <Message icon="warning" negative attached header={_(messages.error)} content={_(messages.thereWereSomeErrors)} /> )} {error && ( <Message icon="warning" negative attached header={_(messages.error)} content={error.message} /> )} <div id={`blockform-fieldset-${defaultFieldset.id}`}> <Segment className="form attached"> {map(defaultFieldset.fields, (field, index) => ( <Field {...schema.properties[field]} id={field} fieldSet={defaultFieldset.title.toLowerCase()} focus={index === focusIndex} value={formData[field]} required={schema.required.indexOf(field) !== -1} onChange={(id, value, itemInfo) => { onChangeField(id, value, itemInfo); }} key={field} error={errors?.[block]?.[field] || {}} block={block} /> ))} {actionButton && ( <Segment className="attached actions">{actionButton}</Segment> )} </Segment> </div> {other.map((fieldset, index) => ( <Accordion fluid styled className="form" key={fieldset.id}> <div key={fieldset.id} id={`blockform-fieldset-${fieldset.id}`}> <Accordion.Title active={currentActiveFieldset.includes(index)} index={index} onClick={handleCurrentActiveFieldset} > {fieldset.title && <>{fieldset.title}</>} {currentActiveFieldset.includes(index) ? ( <Icon name={upSVG} size="20px" /> ) : ( <Icon name={downSVG} size="20px" /> )} </Accordion.Title> <Accordion.Content active={currentActiveFieldset.includes(index)}> <AnimateHeight animateOpacity duration={500} height={currentActiveFieldset.includes(index) ? 'auto' : 0} > <Segment className="attached"> {map(fieldset.fields, (field) => ( <Field {...schema.properties[field]} id={field} value={formData[field]} required={schema.required.indexOf(field) !== -1} onChange={(id, value) => { onChangeField(id, value); }} key={field} error={errors?.[block]?.[field] || {}} block={block} /> ))} </Segment> </AnimateHeight> </Accordion.Content> </div> </Accordion> ))} {footer} </div> ); }; InlineForm.defaultProps = { block: null, description: null, formData: null, onChangeField: null, error: null, errors: {}, schema: {}, focusIndex: null, }; InlineForm.propTypes = { block: PropTypes.string, description: PropTypes.string, schema: PropTypes.shape({ fieldsets: PropTypes.arrayOf( PropTypes.shape({ fields: PropTypes.arrayOf(PropTypes.string), id: PropTypes.string, title: PropTypes.string, }), ), properties: PropTypes.objectOf(PropTypes.any), definitions: PropTypes.objectOf(PropTypes.any), required: PropTypes.arrayOf(PropTypes.string), }), formData: PropTypes.objectOf(PropTypes.any), pathname: PropTypes.string, onChangeField: PropTypes.func, error: PropTypes.shape({ message: PropTypes.string, }), focusIndex: PropTypes.number, }; export default injectIntl(InlineForm, { forwardRef: true });