UNPKG

@fancyapps/ui

Version:

JavaScript UI Component Library

255 lines (207 loc) 5.88 kB
import { extend } from "../utils/extend.js"; import { resolve } from "../utils/resolve.js"; import { isPlainObject } from "../utils/isPlainObject.js"; /** * Base class, all components inherit from this class */ export class Base { /** * Base constructor * @param {Object} [options] - Options as `key: value` pairs */ constructor(options = {}) { this.options = extend(true, {}, options); this.plugins = []; this.events = {}; // * Prefill with initial events for (const type of ["on", "once"]) { for (const args of Object.entries(this.options[type] || {})) { this[type](...args); } } } /** * Retrieve option value by key, supports subkeys * @param {String} key Option name * @param {*} [fallback] Fallback value for non-existing key * @returns {*} */ option(key, fallback, ...rest) { // Make sure it is string key = String(key); let value = resolve(key, this.options); // Allow to have functions as options if (typeof value === "function") { value = value.call(this, this, ...rest); } return value === undefined ? fallback : value; } /** * Simple l10n support - replaces object keys * found in template with corresponding values * @param {String} str String containing values to localize * @param {Array} params Substitute parameters * @returns {String} */ localize(str, params = []) { str = String(str).replace(/\{\{(\w+).?(\w+)?\}\}/g, (match, key, subkey) => { let rez = ""; // Plugins have `Plugin.l10n.KEY` if (subkey) { rez = this.option(`${key[0] + key.toLowerCase().substring(1)}.l10n.${subkey}`); } else if (key) { rez = this.option(`l10n.${key}`); } if (!rez) { rez = match; } for (let index = 0; index < params.length; index++) { rez = rez.split(params[index][0]).join(params[index][1]); } return rez; }); str = str.replace(/\{\{(.*)\}\}/, (match, key) => { return key; }); return str; } /** * Subscribe to an event * @param {String} name * @param {Function} callback * @returns {Object} */ on(name, callback) { if (isPlainObject(name)) { for (const args of Object.entries(name)) { this.on(...args); } return this; } String(name) .split(" ") .forEach((item) => { const listeners = (this.events[item] = this.events[item] || []); if (listeners.indexOf(callback) == -1) { listeners.push(callback); } }); return this; } /** * Subscribe to an event only once * @param {String} name * @param {Function} callback * @returns {Object} */ once(name, callback) { if (isPlainObject(name)) { for (const args of Object.entries(name)) { this.once(...args); } return this; } String(name) .split(" ") .forEach((item) => { const listener = (...details) => { this.off(item, listener); callback.call(this, this, ...details); }; listener._ = callback; this.on(item, listener); }); return this; } /** * Unsubscribe event with name and callback * @param {String} name * @param {Function} callback * @returns {Object} */ off(name, callback) { if (isPlainObject(name)) { for (const args of Object.entries(name)) { this.off(...args); } return; } name.split(" ").forEach((item) => { const listeners = this.events[item]; if (!listeners || !listeners.length) { return this; } let index = -1; for (let i = 0, len = listeners.length; i < len; i++) { const listener = listeners[i]; if (listener && (listener === callback || listener._ === callback)) { index = i; break; } } if (index != -1) { listeners.splice(index, 1); } }); return this; } /** * Emit an event. * If present, `"*"` handlers are invoked after name-matched handlers. * @param {String} name * @param {...any} details * @returns {Boolean} */ trigger(name, ...details) { for (const listener of [...(this.events[name] || [])].slice()) { if (listener && listener.call(this, this, ...details) === false) { return false; } } // A wildcard "*" event type for (const listener of [...(this.events["*"] || [])].slice()) { if (listener && listener.call(this, name, this, ...details) === false) { return false; } } return true; } /** * Add given plugins to this instance, * this will end up calling `attach` method of each plugin * @param {Object} Plugins * @returns {Object} */ attachPlugins(plugins) { const newPlugins = {}; for (const [key, Plugin] of Object.entries(plugins || {})) { // Check if this plugin is not disabled by option if (this.options[key] !== false && !this.plugins[key]) { // Populate options with defaults from the plugin this.options[key] = extend({}, Plugin.defaults || {}, this.options[key]); // Initialise plugin newPlugins[key] = new Plugin(this); } } for (const [key, plugin] of Object.entries(newPlugins)) { plugin.attach(this); } this.plugins = Object.assign({}, this.plugins, newPlugins); return this; } /** * Remove all plugin instances from this instance, * this will end up calling `detach` method of each plugin * @returns {Object} */ detachPlugins() { for (const key in this.plugins) { let plugin; if ((plugin = this.plugins[key]) && typeof plugin.detach === "function") { plugin.detach(this); } } this.plugins = {}; return this; } }