dbl-components
Version:
Framework based on bootstrap 5
216 lines (194 loc) • 6.11 kB
JavaScript
import React from "react";
import bytes from "bytes";
import * as LZMAObj from 'lzma/src/lzma_worker';
import { eventHandler } from "dbl-utils";
import JsonRender from "../../json-render";
import Field from "./field";
export default class FileField extends Field {
static defaultProps = {
...Field.defaultProps,
multiple: false,
format: 'base64',
zip: false
}
static jsClass = 'FileField';
constructor(props) {
super(props);
this.onDragEnter = this.onDragEnter.bind(this);
this.onDragLeave = this.onDragLeave.bind(this);
this.onDrop = this.onDrop.bind(this);
this.onDragOver = this.onDragOver.bind(this);
if (props.hidden)
this.state.localClasses = 'cursor-pointer';
if (props.mutations) {
const { mutations, ...propsSub } = props;
this.jsonRender = new JsonRender(propsSub, mutations);
}
}
get componentProps() {
const _props = this.props._props;
return {
onDragEnter: this.onDragEnter,
onDragLeave: this.onDragLeave,
onDrop: this.onDrop,
onDragOver: this.onDragOver,
..._props
}
}
onDragOver(e) {
e.preventDefault();
e.stopPropagation();
this.deleteClasses('drag-over');
}
onDragEnter(e) {
e.preventDefault();
e.stopPropagation();
this.addClasses('drag-over');
}
onDragLeave(e) {
e.preventDefault();
e.stopPropagation();
this.deleteClasses('drag-over');
}
onDrop(e) {
e.preventDefault();
e.stopPropagation();
this.deleteClasses('drag-over');
const dt = e.dataTransfer;
const files = dt.files;
this.input.current.files = files;
this.onChange({
target: this.input.current
});
}
get type() {
return 'file';
}
isInvalid(value) {
if (this.props.maxSize && this.input.current?.files) {
const files = Array.from(this.input.current.files);
const error = files.some(file => file.size > bytes(this.props.maxSize, { unit: 'B' }));
if (error) {
this.input.current.setCustomValidity(this.props.errorMessage);
return true;
}
}
return super.isInvalid(value);
}
async onChange(e) {
let { value, files } = e.target;
const arrayFiles = Array.from(files);
const newState = {
value: [],
error: this.isInvalid(value)
}
this.setState(newState);
if (!arrayFiles.length || newState.error) return this.returnData(null);
const p6s = arrayFiles.map(async file => {
const readFile = await this.readAs(file, this.props.format);
newState.value.push({
name: file.name,
file: readFile
});
return readFile;
});
const final = await Promise.all(p6s);
if (this.props.multiple) {
this.returnData(final);
} else {
this.returnData(final[0]);
}
}
readAs(file, format = 'base64') {
if (!file) return null;
return new Promise((resolve, reject) => {
const reader = new FileReader();
switch (format) {
case 'base64':
reader.readAsDataURL(file);
break;
case 'text':
reader.readAsText(file);
break;
case 'binaryString':
reader.readAsBinaryString(file);
break;
case 'zip':
reader.readAsArrayBuffer(file);
break;
case 'arrayBuffer':
reader.readAsArrayBuffer(file);
break;
case 'blob':
return resolve(file);
default:
break;
}
const onFinish = (result, error) => {
eventHandler.dispatch('zipping.' + this.props.name, { [this.props.name]: 'end' });
if (error) return reject(error);
resolve(result);
};
const onPercentage = (percentage) =>
eventHandler.dispatch('zipping.' + this.props.name, { [this.props.name]: percentage });
reader.onload = () => {
if (this.props.format !== 'zip') return resolve(reader.result);
eventHandler.dispatch('zipping.' + this.props.name, { [this.props.name]: 'start' });
const array = new Uint8Array(reader.result);
const mode = this.props.zip || 9;
LZMAObj.LZMA.compress(array, mode, onFinish, onPercentage);
};
reader.onerror = error => reject(error);
});
}
get inputProps() {
const ip = super.inputProps;
ip.required = ip.required && !(this.state.value);
delete ip.value;
return ip;
}
get inputNode() {
const { inline, disabled, readOnly } = this.props;
const { value } = this.state;
const links = (Array.isArray(value) ? value : [value])
.map((l, i) => {
if (!l) return false;
const nameFile = typeof l === 'string' ? l.split(/[\/\\]/).pop().split('?')[0] : l.name;
if (this.jsonRender)
return this.jsonRender.buildContent({
name: `${this.props.name}.file.${i}`,
tag: 'span', value: l,
content: nameFile
});
if (typeof l === 'string') {
return React.createElement('a',
{ href: l, target: "_blank", className: '' },
nameFile
)
} else {
return React.createElement('span',
{ name: l.name, classes: '' },
nameFile
)
}
}).filter(l => !!l);
const inputNode = React.createElement(React.Fragment, {},
!(value && (disabled || readOnly))
? React.createElement('input', { ...this.inputProps })
: React.createElement('p',
{ className: "form-control mb-1 disabled" },
...links
),
value && !(disabled || readOnly) &&
React.createElement('p',
{ className: "text-end my-1" },
React.createElement('small', {},
...links
)
)
);
return (inline
? React.createElement('div', { className: "col-auto" }, inputNode)
: inputNode);
}
}