/**
* File upload editor class.
*
* @arg this.props.column {Object} Table column.
* @arg this.props.column.name {string} Column name
* @arg this.props.column.editor_default {*} Editor default value
* @arg this.props.column.editor_min_upload_count {Integer} Minimum files count
* @arg this.props.column.editor_max_upload_count {Integer} Maximum files count
* @arg this.props.column.editor_max_upload_size {Integer} Maximum upload size
* @arg this.props.column.editor_allowed_ext {Array} List of allowed extention
*
* @arg this.props.row {Object} Row to edit, null if add, first if batch.
* @arg this.props.add {bool} Is adding.
* @arg this.props.batch {bool} Is batch editing.
* @arg this.props.onEditorChanges {CTable#onEditorChanges} Editor changes callback.
* @arg this.props.onDownloadFile {CTable#onDownloadFile} Download file callback.
* @arg this.props.onUploadFile{CTable#onUploadFile} Upload file callback.
* @arg this.props.askUser{CTable#askUser} Ask user callback.
* @arg this.props.showError{CTable#showError} Show error callback.
*
*/
class CFilesEditor extends Component {
constructor() {
super();
this.onUploadChange = this.onUploadChange.bind(this);
this.onUploadDelete = this.onUploadDelete.bind(this);
this.onDownloadClicked = this.onDownloadClicked.bind(this);
this.onResetClicked = this.onResetClicked.bind(this);
this.onNullClicked = this.onNullClicked.bind(this);
this.onOtherEditorChanged = this.onOtherEditorChanged.bind(this);
this.onUndoClicked = this.onUndoClicked.bind(this);
}
componentDidMount() {
var value = (this.props.add || this.props.batch) ? this.props.column.editor_default : this.props.row[this.props.column.name];
this.setState({
editor_value: value,
editor_modified: false,
editor_valid: false,
download_available: (value === null || value === "") ? [] : value.split(';').map(x => true)
}, () => {this.validateAndSend()});
}
validateAndSend() {
if (this.state.editor_value === null || this.state.editor_value === ""){
if (this.props.column.editor_min_upload_count == 0){
this.setState({editor_valid: true}, () => {this.sendChanges()});
} else {
this.setState({editor_valid: false}, () => {this.sendChanges()});
}
} else {
if (this.state.editor_value.split(';').length >= this.props.column.editor_min_upload_count && this.state.editor_value.split(';').length <= this.props.column.editor_max_upload_count){
this.setState({editor_valid: true}, () => {this.sendChanges()});
} else {
this.setState({editor_valid: false}, () => {this.sendChanges()});
}
}
}
sendChanges(){
this.props.onEditorChanges(this.props.column.name, this.state.editor_modified, this.state.editor_value, this.state.editor_valid);
}
onUploadChange(e){
var idx = parseInt(e.target.dataset['index']);
var self = this;
var error_msg = null;
for(var i =0; i<e.target.files.length; i++){
if(e.target.files[i].size > self.props.column.editor_max_upload_size){
error_msg = {code:-12, message: _("File \"%s\" is too large for upload").replace('%s', e.target.files[i].name)};
}
var cm = e.target.files[i].name.split('.');
var cmext = '.'+cm[cm.length -1];
if(self.props.column.editor_allowed_ext.indexOf(cmext) < 0){
error_msg = {code:-16, message: _("File \"%s\" extention has not allowed").replace('%s', e.target.files[i].name)};
}
}
if (error_msg !== null){
this.props.showError(error_msg);
} else {
this.props.onUploadFile(this.props.row, this.props.column.name, idx, e.target.files).then(x => {
var value_array = this.state.editor_value.split(';');
var new_download_available = this.state.download_available;
if (idx < 0){
value_array.push(x);
new_download_available.push(false);
} else {
value_array[idx] = x;
new_download_available[idx] = false;
}
var new_value = value_array.join(';');
this.setState({editor_value: new_value, download_available:new_download_available, editor_modified: true}, () => {this.validateAndSend()});
});
}
}
onDownloadClicked(e){
var idx = parseInt(unwind_button_or_link(e).dataset['index']);
this.props.onDownloadFile(this.props.row, this.props.column.name, idx);
}
onUploadDelete(e){
this.props.askUser(_("Remove uploaded file?")).then(x => {
var idx = parseInt(unwind_button_or_link(e).dataset['index']);
var new_value = this.state.editor_value.split(';').filter((x,i) => i != idx).join(';');
var new_download_available = this.state.download_available.filter((x,i) => i != idx);
this.setState({editor_value: new_value, download_available:new_download_available, editor_modified: true}, () => {this.validateAndSend()});
});
}
/**
* Request to set value to Default
*
* @method
* @listens CEditorFrame#cteditorreset
*/
onResetClicked(){
this.setState({editor_value: this.props.column.editor_default, editor_modified: false}, () => {this.validateAndSend()});
}
/**
* Request to set value to NULL.
*
* @method
* @listens CEditorFrame#cteditortonull
*/
onNullClicked(){
this.setState({editor_value: null, editor_modified: true}, () => {this.validateAndSend()});
}
/**
* Request to set value to value at start editing.
*
* @method
* @listens CEditorFrame#cteditorundo
*/
onUndoClicked(){
this.setState({editor_value: this.props.add ? this.props.column.editor_default : this.props.row[this.props.column.name] , editor_modified: false}, () => {this.validateAndSend()});
}
/**
* Notifiaction for changes in some editor.
*
* @method
* @listens CTable#cteditorchanged
*/
onOtherEditorChanged(e){
if(e.detail.initiator == this.props.column.name) return;
}
render () {
var self = this;
var comp = [];
if(Object.keys(this.state).length == 0) return;
if (self.state.editor_value !== null && self.state.editor_value !== ""){
self.state.editor_value.split(';').forEach(x => {
var m = x.split(':');
comp.push({file:m[0], size:m[1], name:m[2]});
});
}
return <div class={cls("control", self.state.editor_value === null ? "has-icons-left" : "")} oncteditortonull={self.onNullClicked} oncteditorreset={self.onResetClicked} oncteditorundo={self.onUndoClicked} oncteditorchanged={self.onOtherEditorChanged}>
{comp.map((x,i) => {
return <>
<div class="file has-name" style="display:inline-block;">
<label class="file-label">
<input class="file-input" type="file" data-index={i} onChange={self.onUploadChange} accept={self.props.column.editor_allowed_ext.join(',')}/>
<span class="file-cta">
<span class="material-symbols-outlined">upload</span>
</span>
<span class="file-name" style="border-radius: 0; width:20em; max-width:20em;">{x.name}</span>
</label>
</div>
{self.state.download_available[i] ?
<button class="button" style="border-radius: 0;" data-index={i} onClick={self.onDownloadClicked}>
<span class="material-symbols-outlined">download</span>
</button> : "" }
<button class="button" style="border-top-left-radius: 0; border-bottom-left-radius: 0;" data-index={i} onClick={self.onUploadDelete}>
<span class="material-symbols-outlined">delete</span>
</button>
<br/>
</>;
})}
{self.props.column.editor_max_upload_count > comp.length ?
<div>
<div class={cls("file", "has-name", self.state.editor_valid ? "" : "is-danger")} style="display:inline-block;">
<label class="file-label">
<input class="file-input" type="file" data-index="-1" onChange={self.onUploadChange} />
<span class="file-cta">
<span class="material-symbols-outlined">upload</span>
</span>
<span class="file-name" style="width:20em; max-width:20em;">{_("Upload...")}</span>
</label>
</div>
</div> : "" }
</div>;
}
}