/*
* This file is part of AUX.
*
* AUX is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* AUX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
import { element, addClass, createID } from './../utils/dom.js';
import { limitDigits } from './../utils/limit_digits.js';
import { FORMAT } from './../utils/sprintf.js';
import { Container } from './container.js';
import { Button } from './button.js';
import { Label } from './label.js';
import { defineChildWidget } from '../child_widget.js';
import { defineChildElement } from '../widget_helpers.js';
/**
* FileSelect is a file selector widget. It inherits all options of {@link Button}.
*
* @class FileSelect
*
* @extends Container
*
* @property {String} [options.accept=""] - The allowed file types as suffices
* starting with a dot or as mime types with optional asterisk,
* e.g. ".txt,.zip,.png,.jpg,image/*,application/pdf"
* @property {boolean} [options.multiple=false] - Defines if users can select multiple files.
* The label for the file name shows the amount of files selected instead of a single name
* and the label displaying the file size shows the sum of all selected files sizes.
* @property {String} [options.placeholder='No file(s) selected'] - The label to show as file name
* if no file is selected.
* @property {Function} [options.format_size=limitDigits(4, 'B', 1024)] - the formatting
* function for the file size label.
* @property {Function} [options.format_multiple=FORMAT('%d files selected') - the formatting
* function for the file size label.
* @property {FileList} [options.files=[]] -
* @property {String} [options.filename=false] - The name of the selected file or `false`
* if no file is selected. Read-only property!
* @property {Integer} [options.filesize=0] - The size of the selected filein bytes.
* Read-only property!
*/
function getFileName() {
const O = this.options;
const files = O.files;
if (!files.length) return O.placeholder;
if (files.length == 1) return files[0].name;
return O.format_multiple(files.length);
}
function getFileSize() {
const O = this.options;
const files = O.files;
if (!files.length) return 0;
if (files.length == 1) return files[0].size;
let sum = 0;
for (let i = 0, m = files.length; i < m; ++i) {
sum += files[i].size;
}
return sum;
}
export class FileSelect extends Container {
static get _options() {
return Object.assign({}, Container.getOptionTypes(), {
accept: 'string',
multiple: 'boolean',
placeholder: 'string',
files: 'object',
filename: 'string|boolean',
filesize: 'number',
format_size: 'function',
format_multiple: 'function',
});
}
static get options() {
return {
accept: '',
multiple: false,
placeholder: 'No file(s) selected',
files: [],
filename: false,
filesize: 0,
format_size: limitDigits(4, 'B', 1024),
format_multiple: FORMAT('%d files selected'),
icon: 'open',
};
}
static get static_events() {
return {
set_files: function (files) {
this.set('filename', getFileName.call(this));
this.set('filesize', getFileSize.call(this));
},
};
}
initialize(options) {
super.initialize(options);
}
draw(O, element) {
const id = createID('aux-fileinput-');
super.draw(O, element);
addClass(element, 'aux-fileselect');
this._input.addEventListener('input', this._onInput.bind(this));
this._input.setAttribute('id', id);
this._label.setAttribute('for', id);
this.set('filename', getFileName.call(this));
this.set('filesize', getFileSize.call(this));
}
redraw() {
const I = this.invalid;
const O = this.options;
if (I.validate('filesize')) {
this.size.set('label', O.format_size(O.filesize));
}
if (I.validate('filename')) {
this.name.set('label', O.filename);
}
if (I.validate('format_size')) {
this.set('filesize', getFileSize.call(this));
}
if (I.validate('format_multiple')) {
this.set('filename', getFileName.call(this));
}
if (I.validate('accept')) {
this._input.setAttribute('accept', O.accept);
}
if (I.validate('multiple')) {
if (O.multiple) this._input.setAttribute('multiple', true);
else this._input.removeAttribute('multiple');
}
super.redraw();
}
_onInput(e) {
this.userset('files', this._input.files);
/**
* Is fired when one or more or no files were selected by the user.
*
* @event Fader#scalechanged
*
* @param {string} key - The key of the option.
* @param {mixed} value - The value to which it was set.
*/
this.emit('select', this.options.files);
}
}
/** @member {HTMLFileInput} FileSelect#_input - HTMLFileInput element.
* Has class <code>.aux-fileinput</code>.
*/
defineChildElement(FileSelect, 'input', {
show: true,
create: function () {
return element('INPUT', {
type: 'file',
class: 'aux-fileinput',
});
},
});
/** @member {HTMLLabel} FileSelect#_label - The HTMLLabel element connecting
* the widgets with the file input.
* Has class <code>.aux-filelabel</code>.
*/
defineChildElement(FileSelect, 'label', {
show: true,
create: function () {
return element('LABEL', {
type: 'file',
class: 'aux-filelabel',
});
},
option: 'foobar',
});
/**
* @member {Button} FileSelect#button - The {@link Button} for opening the file selector.
*/
defineChildWidget(FileSelect, 'button', {
create: Button,
show: true,
toggle_class: true,
inherit_options: true,
append: function () {
this._label.appendChild(this.button.element);
},
option: 'foobar',
static_events: {
keydown: function (e) {
if (e.code === 'Enter' || e.code === 'Space') {
this.parent._input.click();
}
},
},
});
/**
* @member {Label} FileSelect#name - The {@link Label} for displaying the file name.
* Has class `aux-name`.
*/
defineChildWidget(FileSelect, 'name', {
create: Label,
show: true,
toggle_class: true,
default_options: {
class: 'aux-name',
},
});
/**
* @member {Label} FileSelect#size - The {@link Label} for displaying the file size.
* Has class `aux-size`.
*/
defineChildWidget(FileSelect, 'size', {
create: Label,
show: true,
toggle_class: true,
default_options: {
class: 'aux-size',
},
});