"use strict"; import {Check} from "../Utils/Check.ecma.js"; /** * @typedef {import("../Application/OpoTests.ecma.js").OpoTests} OpoTests */ /** * @class Components * @constructor * @param {!OpoTests} ot * @returns {void} * @access public * @static */ export const Components = (function(){ /** * @callback components_event_callback * @param {!HTMLElement} item * @param {!Event} event * @returns {boolean|void} */ /** * @constructs Components * @param {!OpoTests} ot * @returns {void} * @access private * @static */ const Components = function(ot){ /** @type {Components} */ const self = this; /** * @returns {void} * @access private */ const constructor = () => {}; /** * @param {!(string|Array.)} i18n * @param {!string} [tag = "span"] * @param {?string} [_default = null] * @returns {[string, Object., string]} * @access public */ this.i18n = (i18n, tag = "span", _default = null) => [tag, {data_i18n : i18n}, ot.i18n.get(i18n, null, _default)]; /** * @param {!string} name * @param {!string} [tag = "span"] * @returns {[string, Object.]} * @access public */ this.icon = (name, tag = "span") => [tag, {data_icon : name}]; /** * @param {!string} name * @param {?components_event_callback} [on_click = null] * @param {!string} [type = "button"] * @param {?string} [default_text = null] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access public */ this.button = (name, on_click = null, type = "button", default_text) => ["button", { type : type, data_i18n : name, data_i18n_without : true, title : ot.i18n.get(name, null, default_text), ...(on_click ? {on_click : on_click} : {}) }, [ self.icon(name), self.i18n(name, "span", default_text) ]]; /** * @param {!string} type * @param {!string} name * @param {!boolean} checked * @param {?components_event_callback} on_change * @param {?string} default_text * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access private */ const select_item = (type, name, checked, on_change, default_text) => { /** @type {string} */ const id = name.slice(-2) == "[]" ? ot.identifiers.get() : name; return ["label", { for : id, class : type + "-input", data_i18n : name, data_i18n_without : true, title : ot.i18n.get(name, null, default_text) }, [ ["input", { type : type, id : id, name : name, ...(on_change ? {on_change : on_change} : {}), ...(checked ? {checked : "checked"} : {}) }], self.icon(type, "span"), self.i18n(name, "span", default_text) ]]; }; /** * @param {!string} name * @param {!boolean} [checked = false] * @param {?components_event_callback} [on_change = null] * @param {?string} [default_text = null] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} */ this.checkbox = (name, checked = false, on_change = null, default_text) => ( select_item("checkbox", name, checked, on_change, default_text) ); /** * @param {!string} name * @param {!boolean} [checked = false] * @param {?components_event_callback} [on_change = null] * @param {?string} [default_text = null] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} */ this.radio = (name, checked = false, on_change = null, default_text) => ( select_item("radio", name, checked, on_change, default_text) ); /** * @param {!string} name * @param {?number} [value = null] * @param {?number} [minimum = null] * @param {?number} [maximum = null] * @param {?number} [step = null] * @param {?components_event_callback} [on_change = null] * @param {?string} [default_text = null] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access public */ this.number = (name, value = null, minimum = null, maximum = null, step = null, on_change = null, default_text) => { /** @type {string} */ const text = ot.i18n.get(name, null, default_text); return ["label", { for : name, class : "number-input", data_i18n : name, data_i18n_without : true, title : text, }, [ ["input", { type : "number", id : name, name : name, ...(value !== null ? {value : value} : {}), ...(minimum !== null ? {min : minimum} : {}), ...(maximum !== null ? {max : maximum} : {}), ...(step !== null ? {step : step} : {}), ...(on_change ? {on_change : on_change} : {}), data_i18n : name, data_i18n_without : true, placeholder : text + "..." }], ["span", {class : "minimum"}, minimum || "-∞"], ["span", {class : "maximum"}, maximum || "∞"] ]]; }; /** * @param {!string} type * @param {!string} name * @param {?string} [value = null] * @param {?number} [maximum_length = null] * @param {?string} [pattern = null] * @param {?components_event_callback} [on_change = null] * @param {?string} [default_text = null] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access private */ const text = (type, name, value = null, maximum_length = null, pattern = null, on_change = null, default_text = null) => { /** @type {string} */ const text = ot.i18n.get(name, null, default_text); return ["label", { for : name, class : "input-" + type }, [ ["input", { type : type, id : name, name : name, ...(value !== null ? {value : value} : {}), ...(pattern !== null ? {pattern : pattern} : {}), ...(on_change ? {on_input : on_change} : {}), data_i18n : name, data_i18n_without : true, placeholder : text + "..." }], ["span", {class : "length"}, 0], ...(maximum_length ? [["span", {class : "maximum-length"}, maximum_length || "∞"]] : []) ]]; }; /** * @param {!string} name * @param {?string} [value = null] * @param {?number} [maximum_length = null] * @param {?string} [pattern = null] * @param {?components_event_callback} [on_change = null] * @param {?string} [default_text = null] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access public */ this.text = (name, value = null, maximum_length = null, pattern = null, on_change = null, default_text = null) => ( text("text", name, value, maximum_length, pattern, on_change, default_text) ); /** * @param {!string} name * @param {?string} [value = null] * @param {?number} [maximum_length = null] * @param {?string} [pattern = null] * @param {?components_event_callback} [on_change = null] * @param {?string} [default_text = null] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access public */ this.password = (name, value = null, maximum_length = null, pattern = null, on_change = null, default_text = null) => ( text("password", name, value, maximum_length, pattern, on_change, default_text) ); /** * @param {...[string, components_event_callback|null, string, string|null]} buttons * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access public */ this.buttons = (...buttons) => ["div", {class : "buttons"}, buttons.map(button => self.button(...button))]; /** * @param {!string} name * @param {...any} items * @returns {[string, Object., Array.<(string|[string, Object., string])>]} */ this.group = (name, ...items) => ["div", { class : "group", data_i18n : name, data_i18n_without : true, title : ot.i18n.get(name) }, items]; /** * @param {!string} name * @param {!Array.} structure * @param {?components_event_callback} on_submit * @param {...(components_event_callback|null)} [extra_actions] * @returns {[string, Object., Array.<(string|[string, Object., string])>]} * @access public */ this.form = (name, structure, on_submit, ...extra_actions) => ["form", { class : "form", data_name : name, method : "get", action : "#", ...(on_submit ? {on_submit : on_submit} : {}) }, [ ["fieldset", {}, [ self.i18n(name, "legend"), self.i18n(name + "_text", "p"), ["div", {class : "structure"}, structure.map(([type, name, ...item], i) => { return ["div", { data_i : i, data_type : type, data_i18n : name, data_i18n_without : true, title : ot.i18n.get(name, null, item[1] || name) }, [ ["label", {for : name}, [ self.i18n(name), self.i18n(name + "_description") ]], ["span", {class : "input"}, self[type] ? [self[type](name, ...item)] : item], ["ul", {class : "errors"}] ]]; })], ["ul", {class : "form-errors"}], self.buttons( ["clean", null, "clean"], ...extra_actions, on_submit ? ["submit", null, "submit"] : null ) ]] ]]; /** * @param {!HTMLElement} form * @returns {HTMLFormElement|null} * @access public */ this.get_form = form => { if(form) while(form.tagName.toLowerCase() != "form" && (form = form.parentNode)); return form; }; /** * @param {!HTMLElement} form * @returns {Object.)>} * @access public */ this.get_form_data = form => ( [...self.get_form(form).querySelectorAll("[name]")].reduce((data, item) => { /** @type {string} */ let name_value = item.getAttribute("name"); /** @type {string} */ const name = name_value.replace(/\[\]$/i, ""), /** @type {boolean} */ is_array = name_value != name; is_array && !(name in data) && (data[name] = []); switch(item.getAttribute("type")){ case "radio": data[name] == [] && (data[name] = false); item.checked && (data[name] = Number(item.value)); break; case "checkbox": if(is_array) data[name].push(item.checked); else data[name] = item.checked; break; case "number": if(is_array) data[name].push(Number(item.value)); else data[name] = Number(item.value); break; default: if(is_array) data[name].push(item.value); else data[name] = item.value; break; }; return data; }, {}) ); /** * @param {!(string|Array.)} sources * @param {?(string|Array.)} [i18n = null] * @param {?string} [default_text = null] * @returns {[string, Object., (string|[string, Object., string])>]} * @access public */ this.image = (sources, i18n = null, default_text = null) => { /** @type {number} */ let i = 0; Check.is_array(sources) || (sources = [sources]); return ["span", { class : "image", data_status : "loading", ...( i18n ? {data_i18n : i18n, title : ot.i18n.get(i18n, null, default_text)} : default_text ? {title : default_text} : {}), }, [ ["img", { src : sources[i], ...( i18n ? {data_i18n : i18n, alt : ot.i18n.get(i18n, null, default_text)} : default_text ? {alt : default_text} : {}), on_error : (image, event) => { if(i >= sources.length) image.parentNode.setAttribute("data-status", "error"); else image.parentNode.setAttribute("src", sources[++ i]); }, on_load : (image, event) => { image.parentNode.setAttribute("data-status", "loaded"); image.parentNode.querySelector("span").style.backgroundImage = "url('" + sources[i] + "')"; } }], ["span"] ]]; }; constructor(); }; return Components; })();