import { useEffect, useMemo, useState } from "react";
import { booleanString, processOptions, useVariables } from "../../_core";
import { Template } from "../templates";
import Resizer from "react-image-file-resizer";
const objectPath = require("object-path");
const defaultValue = (multiple_files, content) => {
if(booleanString(multiple_files)){
return !!content ? processOptions(content) : [];
}else{
return {name: null, content, type: null};
}
}
/**
* Componente que renderiza un input para cargar una imagen
* @name Reactor.Components.Input.UploadFile
* @param {string} name
* name del control
* @param {BooleanString} [use_change = "false"]
* establece si envia el evento change al cambiar el archivo
* @param {string} [label = "Seleccione un archivo..."]
* texto del boton (placeholder)
* @param {string} accept
* determina los tipos de archivos que aceptara el control file
* @param {BooleanString} [multiple_files = "false"]
* establece si se puede subir mas de un archivo con el mismo componente (por el momento solo funciona con uno)
* @param {string} preview_file_template
* nombre del template que renderiza una lista de templates de previews (solo si es multiple)
* @param {string} preview_container_template
* nombre del template que renderiza un preview
* @param {JsonString} delete_options
* opciones para el elemento descartar
* @param {JsonString} label_options
* opciones para el label (placeholder)
* @param {JsonString} resizer_options
* opciones para redimencionar la imagen
* resizer_options='{
"maxWidth": 300,
"maxHeight": 400,
"compressFormat": "JPEG",
"quality": 75
}'>
* @param {string?} storage_name El nombre que usara para guardar el dato en el storage, si no se pasa este parĂ¡metro se usara el name del input
* @param {string?} change_var Variable que tomarĂ¡ el valor del control cuando se modifique
* @param {string} [context = "global"] Contexto donde se encuentra la variable
* @param {string|JsonString} content
* valor previo del control,
* si es multiple_files enviar un array de objetos con esta estructura {name: null, content, type: null}
* si no lo es enviar un string DataUrl
* @class
* @example
<upload name="archivo" use_change="false" label="Seleccione la imagen" accept="image/png, image/jpeg" multiple_files="false" preview_file_template="TemplateParaMostrarFoto" delete_options='{}' label_options='{}'></upload>
*/
export const UploadFile = ({name, use_change = "false", label = "Seleccione un archivo...", accept, multiple_files = "false", preview_file_template, preview_container_template, delete_options, label_options, resizer_options, content, change_var, context = "global", storage_name, ...props}) => {
//console.log({label})
const variables = useVariables({variable: change_var, context, storage: storage_name});
const labelProps = useMemo(()=>processOptions(label_options), [label_options]);
const deleteProps = useMemo(()=>processOptions(delete_options), [delete_options]);
//{content: null, name: null, type: null}
const [fileData, setFileData] = useState(() => defaultValue(multiple_files, content));
const [hasData, setHasData] = useState(() => !content);
useEffect(() => {
setFileData(defaultValue(multiple_files, content));
setHasData(!!content);
if(!!content && change_var){
variables.setData(content);
}
}, [content])
// On file select (from the pop up)
const onFileChange = (event) => {
// Update the state
if(event.target.files.length > 0) {
var reader = new FileReader();
const files = Array.from(event.target.files);
reader.onloadend = function () {
if(resizer_options){
resizeImage(reader.result).then(result => {
setFileData({name: files[0].name, content: result, type: files[0].type});
if(change_var){
variables.setData(result);
}
})
}else{
if(booleanString(multiple_files)){
setFileData(files.map(file => ({name: file.name, content: reader.result, type: file.type})));
if(change_var){
variables.setData(files.map(file => reader.result));
}
}else{
if(change_var){
setFileData({name: files[0].name, content: reader.result, type: files[0].type});
}
variables.setData(reader.result);
}
}
setHasData(true);
}
files.map(file => reader.readAsDataURL(file));
event.target.value = [ ];
}
};
const resizeImage = (dataUrl) => {
return new Promise((resolve, reject) => {
const options = {
maxWidth: 2000, // Is the maxWidth of the resized new image.
maxHeight: 2000, // Is the maxHeight of the resized new image.
compressFormat: "JPEG", // Is the compressFormat of the resized new image.
quality: 75, // Is the quality of the resized new image.
rotation: 0, // Is the degree of clockwise rotation to apply to uploaded image.
outputType: "base64", // Is the output type of the resized new image.
minWidth: null, // Is the minWidth of the resized new image.
minHeight: null, // Is the minHeight of the resized new image
...processOptions(resizer_options)
};
fetch(dataUrl)
.then(res => res.blob())
.then(blob => {
const file = new File([blob], "Image")
try{
Resizer.imageFileResizer(
file,
options.maxWidth, // Is the maxWidth of the resized new image.
options.maxHeight, // Is the maxHeight of the resized new image.
options.compressFormat, // Is the compressFormat of the resized new image.
options.quality, // Is the quality of the resized new image.
options.rotation, // Is the degree of clockwise rotation to apply to uploaded image.
(uri) => {
resolve(uri);
},
options.outputType, // Is the output type of the resized new image.
options.minWidth, // Is the minWidth of the resized new image.
options.minHeight // Is the minHeight of the resized new image.*/
);
}catch(e){
console.log(e);
reject(e);
}
})
})
}
const handleFileCancel = (event) => {
setFileData(defaultValue(multiple_files));
setHasData(false);
variables.setData("");
}
//console.log({name, ...(fileData ? fileData : []), multiple_files, multiple_filesB: booleanString(multiple_files)})
return (
<div className="control-upload-file">
{booleanString(multiple_files) &&
fileData.map((file, index) =>
<>
<input type="hidden" name={`${name}.${index}.content`} value={file.content} use_change={use_change}/>
<input type="hidden" name={`${name}.${index}.name`} value={file.name} use_change={use_change}/>
<input type="hidden" name={`${name}.${index}.type`} value={file.type} use_change={use_change}/>
</>
)
}
{!booleanString(multiple_files) &&
<>
<input type="hidden" name={`${name}.content`} value={fileData.content} use_change={use_change}/>
<input type="hidden" name={`${name}.name`} value={fileData.name} use_change={use_change}/>
<input type="hidden" name={`${name}.type`} value={fileData.type} use_change={use_change}/>
</>
}
{!hasData &&
<>
<input
type="file"
id={name}
name={name}
accept={accept}
style={{display: "none"}}
onChange={onFileChange}
multiple_files={booleanString(multiple_files)}
use_change={use_change}
/>
<label {...labelProps} for={name} >{label}</label>
</>
}
{hasData &&
<>
{booleanString(multiple_files) && preview_file_template && preview_container_template &&
<Template content={{template: preview_container_template}} >
{fileData.map((file, index) => {
return <Template key={`file${index}`} content={{template: preview_file_template, ...file}} />
})}
</Template>
}
{!booleanString(multiple_files) && preview_file_template &&
<Template content={{template: preview_file_template, ...fileData}} />
}
<a {...deleteProps} onClick={handleFileCancel}>Descartar</a>
</>
}
</div>
)
}