import { chain, error, callback, isObject, kebabCase } from '../Helpers/Functions';
export default class Component {
/**
* Abstract base class.
*
* @class Component
* @param {(object|undefined)} [attributes] - The instance attributes.
*/
constructor(attributes) {
this.setAttribute(Object.assign({
events: {}
}, attributes));
}
/**
* Get the `name` attribute.
*
* @type {string}
*/
get name() {
if(!(this.constructor.defineName instanceof Function)) {
error('Every class must define its name.');
}
return this.constructor.defineName();
}
/**
* The `events` attribute.
*
* @type {object}
*/
get events() {
return this.$events || {};
}
set events(value) {
this.$events = value;
}
/**
* Emit an event.
*
* @param {string} key - The event id/key.
* @return {Component} - Returns `this` instance.
*/
emit(key, ...args) {
if(this.events[key]) {
this.events[key].forEach(event => {
event.apply(this, args);
});
}
return this;
}
/**
* Start listening to an event.
*
* @param {string} key - The event id/key.
* @param {Function} fn - The listener callback function.
* @param {boolean} [once=false] - Should the event handler be fired a
* single time.
* @return {Component} - Returns `this` instance.
*/
on(key, fn, once = false) {
if(!this.events[key]) {
this.events[key] = [];
}
this.events[key].push(fn);
return this;
}
/**
* Stop listening to an event.
*
* @param {string} key - The event id/key.
* @param {(Function|undefined)} fn - The listener callback function. If no
* function is defined, all events with the specified id/key will be
* removed. Otherwise, only the event listeners matching the id/key AND
* callback will be removed.
* @return {Component} - Returns `this` instance.
*/
off(key, fn) {
if(this.events[key] && fn) {
this.events[key] = this.events[key].filter(event => {
return event !== fn;
});
}
else {
this.events[key] = [];
}
return this;
}
/**
* Listen to an event only one time.
*
* @param {string} key - The event id/key.
* @param {Function} fn - The listener callback function.
* @return {Component} - Returns `this` instance.
*/
once(key, fn) {
fn = chain(fn, () => this.off(key, fn));
return this.on(key, fn, true);
}
/**
* Get an attribute. Returns null if no attribute is defined.
*
* @param {string} key - The attribute name.
* @return {*} - The attribute value.
*/
getAttribute(key) {
return this.hasOwnProperty(key) ? this[key] : null;
}
/**
* Get all the atttributes for this instance.
*
* @return {object} - The attribute dictionary.
*/
getAttributes() {
const attributes = {};
Object.getOwnPropertyNames(this).forEach(key => {
attributes[key] = this.getAttribute(key);
});
return attributes;
}
/**
* Get only public the atttributes for this instance. Omits any attribute
* that starts with `$`, which is used internally.
*
* @return {object} - The attribute dictionary.
*/
getPublicAttributes() {
return Object.keys(this.getAttributes())
.filter(key => {
return !key.match(/^\$/);
})
.reduce((obj, key) => {
obj[key] = this.getAttribute(key);
return obj;
}, {});
}
/**
* Set an attribute key and value.
*
* @param {string} key - The attribute name.
* @param {*} value - The attribute value.
* @return {void}
*/
setAttribute(key, value) {
if(isObject(key)) {
this.setAttributes(key);
}
else {
this[key] = value;
}
}
/**
* Set an attributes by object of key/value pairs.
*
* @param {object} values - The object dictionary.
* @return {void}
*/
setAttributes(values) {
for(const i in values) {
this.setAttribute(i, values[i]);
}
}
/**
* Helper method to execute the `callback()` function.
*
* @param {Function} fn - The callback function.
* @return {*} - Returns the executed callback function.
*/
callback(fn) {
return callback.call(this, fn);
}
/**
* Factor method to static instantiate new instances. Useful for writing
* clean expressive syntax with chained methods.
*
* @param {...*} args - The callback arguments.
* @return {*} - The new component instance.
*/
static make(...args) {
return new this(...args);
}
}