You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
6.6 KiB
JavaScript
284 lines
6.6 KiB
JavaScript
/**
|
|
* Abstract widget.
|
|
*/
|
|
class Widget {
|
|
/** @type {string} */
|
|
id;
|
|
/** @type {string} */
|
|
type;
|
|
/** @type {Renderer} */
|
|
renderer;
|
|
/** @type {object} */
|
|
data;
|
|
|
|
/**
|
|
* @param {object} data
|
|
* @param {Renderer} renderer
|
|
*/
|
|
constructor(data, renderer) {
|
|
this.id = data.id;
|
|
this.type = data.type;
|
|
this.data = data;
|
|
this.renderer = renderer;
|
|
}
|
|
|
|
/**
|
|
* Build HTML tree from widget.
|
|
*
|
|
* Should be overriden.
|
|
* @returns {HTMLElement | null}
|
|
*/
|
|
build() {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Handle update.
|
|
*
|
|
* Should be overriden.
|
|
* @param {object} data
|
|
*/
|
|
update(data) {
|
|
for (const [k, v] of Object.entries(data))
|
|
this.data[k] = v;
|
|
}
|
|
|
|
/**
|
|
* Handle previous set (with ack) was timed out.
|
|
*
|
|
* Should be overriden.
|
|
*/
|
|
_handleSetError(err) {}
|
|
|
|
/**
|
|
* Handle ack for previous set.
|
|
*
|
|
* Should be overriden.
|
|
*/
|
|
_handleAck() {}
|
|
|
|
/**
|
|
* Handle renderer closing.
|
|
*
|
|
* Should be overriden to stop timers (if any).
|
|
*/
|
|
close() {}
|
|
|
|
/**
|
|
* Set widget value.
|
|
*
|
|
* Should be called from subclass.
|
|
* @param {any} value
|
|
* @param {boolean} ack
|
|
* @returns {Promise<undefined>}
|
|
*/
|
|
set(value, ack = true) {
|
|
this.renderer._set(this, value, ack);
|
|
}
|
|
|
|
/**
|
|
* Register an UI file to load.
|
|
*
|
|
* Should be called from subclass.
|
|
* @param {string} path
|
|
* @param {string} type
|
|
* @param {(string) => undefined} callback
|
|
*/
|
|
addFile(path, type, callback) {
|
|
this.renderer._addFile(this, path, type, callback);
|
|
}
|
|
|
|
_handleFileProgress(perc) {}
|
|
_handleFileError(err) {}
|
|
_handleFileLoaded(res) {}
|
|
}
|
|
|
|
/**
|
|
* Widget with container.
|
|
*/
|
|
class BaseWidget extends Widget {
|
|
/** @type {HTMLDivElement} */
|
|
#root;
|
|
/** @type {HTMLDivElement} */
|
|
#inner;
|
|
|
|
/** @type {HTMLDivElement} */
|
|
#cont;
|
|
|
|
/** @type {HTMLSpanElement} */
|
|
#hint;
|
|
/** @type {HTMLSpanElement} */
|
|
#label;
|
|
/** @type {HTMLSpanElement} */
|
|
#plabel;
|
|
/** @type {HTMLSpanElement} */
|
|
#suffix;
|
|
|
|
/** @type {HTMLDivElement} */
|
|
#container;
|
|
|
|
/**
|
|
* @param {object} data
|
|
* @param {Renderer} renderer
|
|
*/
|
|
constructor(data, renderer) {
|
|
super(data, renderer);
|
|
|
|
this.#root = createElement(this, {
|
|
type: 'div',
|
|
class: 'widget_main',
|
|
style: {
|
|
width: data.wwidth_t + '%',
|
|
}
|
|
});
|
|
|
|
this.#inner = createElement(this, {
|
|
type: 'div',
|
|
class: 'widget_inner'
|
|
});
|
|
this.#root.append(this.#inner);
|
|
|
|
this.#cont = createElement(this, {
|
|
type: 'div',
|
|
class: 'widget_label'
|
|
});
|
|
this.#inner.append(this.#cont);
|
|
|
|
this.#hint = createElement(this, {
|
|
type: 'span',
|
|
class: 'whint',
|
|
text: '?',
|
|
style: {
|
|
display: 'none',
|
|
},
|
|
also($hint) {
|
|
$hint.addEventListener('click', () => asyncAlert($hint.title));
|
|
}
|
|
});
|
|
this.#cont.append(this.#hint);
|
|
|
|
this.#label = createElement(this, {
|
|
type: 'span',
|
|
text: data.type.toUpperCase(),
|
|
});
|
|
this.#cont.append(this.#label);
|
|
|
|
this.#plabel = createElement(this, {
|
|
type: 'span',
|
|
});
|
|
this.#cont.append(this.#plabel);
|
|
|
|
this.#suffix = createElement(this, {
|
|
type: 'span',
|
|
class: 'wsuffix',
|
|
});
|
|
this.#cont.append(this.#suffix);
|
|
|
|
|
|
this.#container = createElement(this, {
|
|
type: 'div',
|
|
class: 'widget_body',
|
|
style: {
|
|
minHeight: data.wheight && data.wheight > 25 ? data.wheight + 'px' : '',
|
|
}
|
|
});
|
|
this.#inner.append(this.#container);
|
|
}
|
|
|
|
build() {
|
|
return this.#root;
|
|
}
|
|
|
|
/**
|
|
* Initialize widget layout.
|
|
*
|
|
* Should be called from subclass constructor.
|
|
* @param {object[]} obj
|
|
*/
|
|
makeLayout(...obj) {
|
|
this.#container.replaceChildren(...obj.map(o => createElement(this, o)));
|
|
}
|
|
|
|
/**
|
|
* Handle update.
|
|
*
|
|
* Subclass should override this method and call super.update(data) from it.
|
|
* @param {object} data
|
|
*/
|
|
update(data) {
|
|
super.update(data);
|
|
if ('label' in data) {
|
|
this.#label.textContent = data.label.length ? data.label : this.type.toUpperCase();
|
|
}
|
|
if ('suffix' in data) {
|
|
this.#suffix.textContent = data.suffix;
|
|
}
|
|
if ('nolabel' in data) {
|
|
if (data.nolabel) this.#cont.classList.add('wnolabel');
|
|
else this.#cont.classList.remove('wnolabel');
|
|
}
|
|
if ('square' in data) {
|
|
if (data.square) this.#root.classList.add('wsquare');
|
|
else this.#root.classList.remove('wsquare');
|
|
}
|
|
if ('notab' in data) {
|
|
if (data.notab) this.#inner.classList.add('widget_notab');
|
|
else this.#inner.classList.remove('widget_notab');
|
|
}
|
|
if ('disable' in data) {
|
|
if (data.disable) this.#container.classList.add('widget_dsbl');
|
|
else this.#container.classList.remove('widget_dsbl');
|
|
}
|
|
if ('hint' in data) {
|
|
const htext = 'name: ' + this.id + '\n' + (data.hint ?? '');
|
|
this.#label.title = htext;
|
|
this.#hint.title = htext;
|
|
this.#hint.style.display = (data.hint && data.hint.length) ? 'inline-block' : 'none';
|
|
}
|
|
}
|
|
|
|
disable(el, disable) {
|
|
if (disable) {
|
|
el.setAttribute('disabled', '1');
|
|
el.classList.add('disable');
|
|
} else { // null/undefined/0/false
|
|
el.removeAttribute('disabled');
|
|
el.classList.remove('disable');
|
|
}
|
|
}
|
|
|
|
align(align) {
|
|
this.#container.style.justifyContent = ["flex-start", "center", "flex-end"][Number(align ?? 1)];
|
|
}
|
|
|
|
setPlabel(text = null) {
|
|
this.#plabel.textContent = text ?? '';
|
|
}
|
|
|
|
setSuffix(text = null) {
|
|
this.#suffix.textContent = text ?? '';
|
|
}
|
|
|
|
_handleSetError(err){
|
|
this.setPlabel("[ERR]");
|
|
showPopupError(`Widget ${this.id}: ` + getError(err));
|
|
}
|
|
|
|
_handleAck() {
|
|
this.setPlabel();
|
|
}
|
|
|
|
_handleFileProgress(perc) {
|
|
this.setPlabel(`[${perc}%]`);
|
|
}
|
|
|
|
_handleFileError(err) {
|
|
this.setPlabel("[ERR]");
|
|
showPopupError(`Widget ${this.id}: ` + getError(err));
|
|
}
|
|
|
|
_handleFileLoaded() {
|
|
this.setPlabel();
|
|
}
|
|
}
|