/*
* 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';
import { defineMeasure, defineRender } from '../renderer.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!
*/
export class FileSelect extends Container {
static get _options() {
return {
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 renderers() {
return [
defineMeasure(['files', 'placeholder', 'format_multiple'], function (
files,
placeholder,
format_multiple
) {
let filename;
if (!files.length) {
filename = placeholder;
} else if (files.length === 1) {
filename = files[0].name;
} else {
filename = format_multiple(files.length);
}
this.set('filename', filename);
}),
defineMeasure('files', function (files) {
let filesize = 0;
[...files].forEach((file) => (filesize += file.size));
this.set('filesize', filesize);
}),
defineMeasure(['filesize', 'format_size'], function (
filesize,
format_size
) {
this.size.set('label', format_size(filesize));
}),
defineMeasure('filename', function (filename) {
this.name.set('label', filename);
}),
defineRender('accept', function (accept) {
this._input.setAttribute('accept', accept);
}),
defineRender('multiple', function (multiple) {
const _input = this._input;
if (multiple) _input.setAttribute('multiple', true);
else _input.removeAttribute('multiple');
}),
];
}
constructor(options) {
super(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);
}
_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();
return false;
}
},
},
});
/**
* @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',
},
});