design-comuni-plone-theme
Version:
Volto Theme for Italia design guidelines
453 lines (437 loc) • 13.7 kB
JSX
/**
* Field
* @module components/manage/Blocks/IconsBlocks/View
*
* CUSTOMIZATIONS:
* - customized to use design-react-kit elements instead semantic-ui elements
*/
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useIntl, defineMessages } from 'react-intl';
import { Input, FormGroup, Label } from 'design-react-kit';
import { Icon } from 'design-comuni-plone-theme/components/ItaliaTheme';
import FileWidget from 'design-comuni-plone-theme/components/ItaliaTheme/manage/Widgets/FileWidget';
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
import { TextBlockView } from '@plone/volto-slate/blocks/Text';
import { TextEditorWidget } from 'design-comuni-plone-theme/components/ItaliaTheme';
import config from '@plone/volto/registry';
import { fromHtml } from 'design-comuni-plone-theme/config/Slate/utils';
const messages = defineMessages({
select_a_value: {
id: 'form_select_a_value',
defaultMessage: 'Seleziona un valore',
},
static_field_placeholder: {
id: 'form_static_field_placeholder',
defaultMessage: 'Inserisci qui il testo statico da mostrare.',
},
open_menu: {
id: 'open_menu',
defaultMessage: 'Apri il menu',
},
});
const DropdownIndicator = injectLazyLibs('reactSelect')((props) => {
const intl = useIntl();
const components = props.reactSelect.components;
return (
<components.DropdownIndicator {...props}>
<Icon
icon="chevron-down"
style={{ ariaHidden: true }}
title={intl.formatMessage(messages.open_menu)}
/>
</components.DropdownIndicator>
);
});
/**
* Field class.
* @class View
* @extends Component
*/
const Field = ({
label,
description,
name,
required,
field_type,
field_id,
input_values,
value,
onChange,
isOnEdit,
valid,
disabled = false,
formHasErrors = false,
errorMessage,
id,
reactSelect,
autocomplete,
}) => {
const intl = useIntl();
const Select = reactSelect.default;
const [selected, setSelected] = useState(false);
const getLabel = () => {
return required ? label + ' *' : label;
};
const isInvalid = () => {
return !isOnEdit && !valid;
};
const infoText = errorMessage ? (
<>
<div className="form-text">{description}</div>
{errorMessage}
</>
) : (
description
);
let static_text_value = value;
if (field_type === 'static_text') {
if (value?.data) {
static_text_value = fromHtml(value);
} //per retrocompatibilità con il vecchio widget che usava draftjs
}
return (
<div className="field">
{field_type === 'text' && (
<Input
id={name}
name={name}
label={getLabel()}
type="text"
required={required}
infoText={infoText}
disabled={disabled}
readOnly={disabled}
invalid={isInvalid() ? 'true' : null}
onChange={(e) => {
onChange(name, e.target.value);
}}
value={value ?? ''}
autoComplete={autocomplete}
/>
)}
{field_type === 'textarea' && (
<Input
id={name}
name={name}
label={getLabel()}
tag="textarea"
rows={10}
required={required}
infoText={infoText}
disabled={disabled}
readOnly={disabled}
invalid={isInvalid() ? 'true' : null}
onChange={(e) => {
onChange(name, e.target.value);
}}
value={value ?? undefined}
autoComplete={autocomplete}
/>
)}
{field_type === 'number' && (
<Input
id={name}
name={name}
label={getLabel()}
type="number"
required={required}
infoText={infoText}
disabled={disabled}
readOnly={disabled}
invalid={isInvalid() ? 'true' : null}
onChange={(e) => {
onChange(name, e.target.value);
}}
onKeyDown={(e) => {
if (
!/[0-9eE+\-.,]/.test(e.key) &&
e.key !== 'Backspace' &&
e.key !== 'Tab' &&
e.key !== 'ArrowLeft' &&
e.key !== 'ArrowRight' &&
e.key !== 'Delete'
) {
e.preventDefault();
}
}}
value={value ?? ''}
autoComplete={autocomplete}
/>
)}
{field_type === 'select' && (
<div className="form-group">
<div
className={`bootstrap-select-wrapper ${isInvalid() ? 'is-invalid' : ''
}`}
>
<label id={`${name}-label`} htmlFor={name}>
{getLabel()}
</label>
<Select
components={{
IndicatorSeparator: null,
DropdownIndicator,
}}
inputId={name}
isSearchable={true}
onChange={(v) => {
onChange(name, v.value);
}}
options={[
...(input_values?.map((v) => ({ value: v, label: v })) ?? []),
]}
aria-live="polite"
isDisabled={disabled}
placeholder={intl.formatMessage(messages.select_a_value)}
aria-labelledby={`${name}-label`}
classNamePrefix="react-select"
className={isInvalid() ? 'is-invalid' : ''}
value={value ? [{ value: value, label: value }] : []}
autoComplete={autocomplete}
/>
{description && <small className="form-text">{description}</small>}
{errorMessage && (
<div className="invalid-feedback form-feedback just-validate-error-label form-text form-feedback just-validate-error-label">
{errorMessage}
</div>
)}
</div>
</div>
)}
{field_type === 'single_choice' && (
<div className="form-group">
<div
className={`bootstrap-checkbox-radio-wrapper ${isInvalid() ? 'is-invalid' : ''
}`}
>
<fieldset className="radio-group">
<legend>{getLabel()}</legend>
{input_values?.map((v, index) => (
<FormGroup check key={v + name + index}>
<Input
id={v + name}
name={name}
type="radio"
disabled={disabled}
readOnly={disabled}
onChange={(e) => {
onChange(name, v);
}}
addon // Needed to avoid application of form-control class as of kit v4.0.2
checked={value === v}
/>
<Label for={v + name} check>
{v}
</Label>
</FormGroup>
))}
{description && (
<small className="form-text">{description}</small>
)}
{errorMessage && (
<div className="invalid-feedback form-feedback just-validate-error-label form-text form-feedback just-validate-error-label">
{errorMessage}
</div>
)}
</fieldset>
</div>
</div>
)}
{field_type === 'multiple_choice' && (
<div className="form-group">
<div
className={`bootstrap-checkbox-radio-wrapper ${isInvalid() ? 'is-invalid' : ''
}`}
>
<fieldset className="checkbox-group">
<legend>{getLabel()}</legend>
{input_values?.map((v, index) => (
<FormGroup check key={v + name + index}>
<Input
id={v + name}
name={name}
type="checkbox"
checked={value?.indexOf(v) > -1}
onChange={(e) => {
let values = JSON.parse(JSON.stringify(value ?? []));
if (e.target.checked) {
values.push(v);
} else {
values.splice(values.indexOf(v), 1);
}
onChange(name, values);
}}
invalid={isInvalid() ? 'true' : null}
addon // Needed to avoid application of form-control class as of kit v4.0.2
/>
<Label for={v + name} check>
{v}
</Label>
</FormGroup>
))}
{description && (
<small className="form-text">{description}</small>
)}
{errorMessage && (
<div className="invalid-feedback form-feedback just-validate-error-label form-text form-feedback just-validate-error-label">
{errorMessage}
</div>
)}
</fieldset>
</div>
</div>
)}
{field_type === 'checkbox' && (
<div className="form-group">
<div
className={`bootstrap-checkbox-radio-wrapper ${isInvalid() ? 'is-invalid' : ''
}`}
>
<FormGroup check key={name}>
<Input
id={name}
name={name}
type="checkbox"
checked={!!value}
onChange={(e) => {
onChange(name, e.target.checked);
}}
invalid={isInvalid() ? 'true' : null}
required={required}
addon // Needed to avoid application of form-control class as of kit v4.0.2
/>
<Label for={name} check>
{getLabel()}
</Label>
</FormGroup>
{description && <small className="form-text">{description}</small>}
{errorMessage && (
<div className="invalid-feedback form-feedback just-validate-error-label form-text form-feedback just-validate-error-label">
{errorMessage}
</div>
)}
</div>
</div>
)}
{field_type === 'date' && (
<Input
id={name}
name={name}
label={getLabel()}
type="date"
required={required}
infoText={infoText}
disabled={disabled}
readOnly={disabled}
invalid={isInvalid() ? 'true' : null}
onChange={(e) => {
onChange(name, e.target.value);
}}
value={value ?? ''}
autoComplete={autocomplete}
/>
)}
{field_type === 'attachment' && (
<FileWidget
id={name}
name={name}
label={getLabel()}
type="file"
required={required}
infoText={infoText}
disabled={disabled}
readOnly={disabled}
invalid={isInvalid() ? 'true' : null}
onChange={onChange}
onEdit={isOnEdit}
value={value}
/>
)}
{(field_type === 'from' || field_type === 'email') && (
<Input
id={name}
name={name}
label={getLabel()}
type="email"
required={true}
infoText={infoText}
disabled={disabled}
readOnly={disabled}
invalid={isInvalid() ? 'true' : null}
validationText={errorMessage}
onChange={(e) => {
onChange(name, e.target.value);
}}
value={value ?? ''}
autoComplete={autocomplete}
/>
)}
{field_type === 'static_text' && (
<>
{isOnEdit ? (
<div className="mb-2">
<TextEditorWidget
value={static_text_value}
selected={selected}
setSelected={setSelected}
placeholder={intl.formatMessage(
messages.static_field_placeholder,
)}
showToolbar={true}
onChangeBlock={(block, data) => {
onChange(name, data.value);
}}
/>
</div>
) : value ? (
<div className="static-text">
<TextBlockView id={name} data={{ value: static_text_value }} />
</div>
) : (
<br />
)}
</>
)}
{config.blocks.blocksConfig.form.additionalFields?.reduce((acc, val) => {
if (val.id === field_type)
return [
...acc,
<val.component
id={name}
name={name}
title={label}
description={description}
infoText={infoText}
required={required}
onChange={onChange}
value={value}
isDisabled={disabled}
formHasErrors={formHasErrors}
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>,
];
return acc;
}, []) ?? []}
</div>
);
};
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
Field.propTypes = {
label: PropTypes.string,
name: PropTypes.string,
description: PropTypes.string,
required: PropTypes.bool,
field_type: PropTypes.string,
input_values: PropTypes.any,
value: PropTypes.any,
formHasErrors: PropTypes.bool,
onChange: PropTypes.func,
autoComplete: PropTypes.string,
};
export default injectLazyLibs('reactSelect')(Field);