2022-04-27 12:55:43 +02:00
|
|
|
import React, { PropsWithChildren, ReactNode, useEffect, useState } from 'react';
|
|
|
|
import { AbstractFormComponent } from '../../models/form-component';
|
|
|
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
|
|
|
import { get as _get } from 'lodash';
|
|
|
|
|
|
|
|
export interface AbstractFormItemProps<TFieldValues> extends PropsWithChildren<AbstractFormComponent<TFieldValues>> {
|
|
|
|
id: string,
|
|
|
|
label?: string,
|
|
|
|
tooltip?: ReactNode,
|
|
|
|
className?: string,
|
2022-05-04 14:59:28 +02:00
|
|
|
disabled?: boolean|((id: string) => boolean),
|
2022-04-27 12:55:43 +02:00
|
|
|
readOnly?: boolean
|
2022-05-03 11:22:27 +02:00
|
|
|
onLabelClick?: (event: React.MouseEvent<HTMLLabelElement, MouseEvent>) => void,
|
2022-04-27 12:55:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This abstract component should not be used directly.
|
|
|
|
* Other forms components that are intended to be used with react-hook-form must extend this component.
|
|
|
|
*/
|
2022-05-03 11:22:27 +02:00
|
|
|
export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label, tooltip, className, disabled, readOnly, error, warning, rules, formState, onLabelClick, children }: AbstractFormItemProps<TFieldValues>) => {
|
2022-05-04 14:59:28 +02:00
|
|
|
const [isDirty, setIsDirty] = useState<boolean>(false);
|
|
|
|
const [fieldError, setFieldError] = useState<{ message: string }>(error);
|
|
|
|
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
2022-04-27 12:55:43 +02:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setIsDirty(_get(formState?.dirtyFields, id));
|
|
|
|
setFieldError(_get(formState?.errors, id));
|
|
|
|
}, [formState]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setFieldError(error);
|
|
|
|
}, [error]);
|
|
|
|
|
2022-05-04 14:59:28 +02:00
|
|
|
useEffect(() => {
|
|
|
|
if (typeof disabled === 'function') {
|
|
|
|
setIsDisabled(disabled(id));
|
|
|
|
} else {
|
|
|
|
setIsDisabled(disabled);
|
|
|
|
}
|
|
|
|
}, [disabled]);
|
|
|
|
|
2022-04-27 12:55:43 +02:00
|
|
|
// Compose classnames from props
|
|
|
|
const classNames = [
|
|
|
|
'form-item',
|
|
|
|
`${className || ''}`,
|
|
|
|
`${isDirty && fieldError ? 'is-incorrect' : ''}`,
|
|
|
|
`${isDirty && warning ? 'is-warned' : ''}`,
|
|
|
|
`${rules && rules.required ? 'is-required' : ''}`,
|
|
|
|
`${readOnly ? 'is-readonly' : ''}`,
|
2022-05-04 14:59:28 +02:00
|
|
|
`${isDisabled ? 'is-disabled' : ''}`
|
2022-04-27 12:55:43 +02:00
|
|
|
].join(' ');
|
|
|
|
|
2022-05-03 11:22:27 +02:00
|
|
|
/**
|
|
|
|
* This function is called when the label is clicked.
|
|
|
|
* It is used to focus the input.
|
|
|
|
*/
|
|
|
|
function handleLabelClick (event: React.MouseEvent<HTMLLabelElement, MouseEvent>) {
|
|
|
|
if (typeof onLabelClick === 'function') {
|
|
|
|
onLabelClick(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-27 12:55:43 +02:00
|
|
|
return (
|
2022-05-03 11:22:27 +02:00
|
|
|
<label className={classNames} onClick={handleLabelClick}>
|
2022-04-27 12:55:43 +02:00
|
|
|
{label && <div className='form-item-header'>
|
|
|
|
<p>{label}</p>
|
|
|
|
{tooltip && <div className="item-tooltip">
|
|
|
|
<span className="trigger"><i className="fa fa-question-circle" /></span>
|
|
|
|
<div className="content">{tooltip}</div>
|
|
|
|
</div>}
|
|
|
|
</div>}
|
|
|
|
<div className='form-item-field'>
|
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
{(isDirty && fieldError) && <div className="form-item-error">{fieldError.message}</div> }
|
|
|
|
{(isDirty && warning) && <div className="form-item-warning">{warning.message}</div> }
|
|
|
|
</label>
|
|
|
|
);
|
|
|
|
};
|