1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-25 14:52:20 +01:00
2022-08-03 10:25:36 +02:00

120 lines
4.1 KiB
TypeScript

import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Path } from 'react-hook-form';
import { UnpackNestedValue, UseFormSetValue } from 'react-hook-form/dist/types/form';
import { FieldPathValue } from 'react-hook-form/dist/types/path';
import { FieldValues } from 'react-hook-form/dist/types/fields';
import { FormInput } from '../form/form-input';
import { FormComponent } from '../../models/form-component';
import { AbstractFormItemProps } from './abstract-form-item';
import { FabButton } from '../base/fab-button';
import noAvatar from '../../../../images/no_avatar.png';
export interface ImageType {
id?: number,
attachment_name?: string,
attachment_url?: string
}
interface FormImageUploadProps<TFieldValues> extends FormComponent<TFieldValues>, AbstractFormItemProps<TFieldValues> {
setValue: UseFormSetValue<TFieldValues>,
defaultImage?: ImageType,
accept?: string,
size?: 'small' | 'large'
onFileChange?: (value: ImageType) => void,
onFileRemove?: () => void,
}
/**
* This component allows to upload image, in forms managed by react-hook-form.
*/
export const FormImageUpload = <TFieldValues extends FieldValues>({ id, register, defaultImage, className, rules, disabled, error, warning, formState, onFileChange, onFileRemove, accept, setValue, size }: FormImageUploadProps<TFieldValues>) => {
const { t } = useTranslation('shared');
const [file, setFile] = useState<ImageType>(defaultImage);
const [image, setImage] = useState<string | ArrayBuffer>(defaultImage.attachment_url);
/**
* Check if image is selected
*/
const hasImage = (): boolean => {
return !!file?.attachment_name;
};
/**
* Callback triggered when the user has ended its selection of a file (or when the selection has been cancelled).
*/
function onFileSelected (event: React.ChangeEvent<HTMLInputElement>) {
const f = event.target?.files[0];
if (f) {
const reader = new FileReader();
reader.onload = (): void => {
setImage(reader.result);
};
reader.readAsDataURL(f);
setFile({
attachment_name: f.name
});
setValue(
`${id}[_destroy]` as Path<TFieldValues>,
false as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
);
if (typeof onFileChange === 'function') {
onFileChange({ attachment_name: f.name });
}
}
}
/**
* Callback triggered when the user clicks on the delete button.
*/
function onRemoveFile () {
if (file?.id) {
setValue(
`${id}[_destroy]` as Path<TFieldValues>,
true as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
);
}
setValue(
`${id}[attachment_files]` as Path<TFieldValues>,
null as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
);
setFile(null);
setImage(null);
if (typeof onFileRemove === 'function') {
onFileRemove();
}
}
// Compose classnames from props
const classNames = [
`${className || ''}`
].join(' ');
return (
<div className={`form-image-upload form-image-upload--${size} ${classNames}`}>
<div className={`image image--${size}`}>
<img src={image || noAvatar} />
</div>
<div className="buttons">
<FabButton className="select-button">
{!hasImage() && <span>{t('app.shared.form_image_upload.browse')}</span>}
{hasImage() && <span>{t('app.shared.form_image_upload.edit')}</span>}
<FormInput className="image-file-input"
type="file"
accept={accept}
register={register}
formState={formState}
rules={rules}
disabled={disabled}
error={error}
warning={warning}
id={`${id}[attachment_files]`}
onChange={onFileSelected}/>
</FabButton>
{hasImage() && <FabButton onClick={onRemoveFile} icon={<i className="fa fa-trash-o"/>} className="delete-image" />}
</div>
</div>
);
};