You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

238 lines
6.7 KiB

/**
* Represents the most basic and simple form of a Component.
* It is mainly a collection of wrapper methods
* around the HTMLElement methods to make them chainable.
* It serves as base for further functionallity extensions.
* @abstract
*/
class ElementWrapper {
/**
* The basic HTMLElement the Component is wrapped around.
* It will be modified in several ways and in the end returned.
* @type {HTMLElement}
*/
_element;
/**
* The auto-generated name of the component.
* @type {string}
*/
_compName;
/**
* The auto-generated name of the component.
* @type {string}
*/
_givenName;
/**
* @type {Component}
*/
_parentComponent;
/**
* Initializes the component
* @param {HTMLElement} element the base element
* @param {map<string,string>} attr Specific already known attributes
*/
constructor(element, attr = {}) {
helperFun.fillAttrsInContainerByCb(
attr,
element,
function (k, v, con) {
con.setAttribute(k, v);
}
);
this._compName = Page.autoRegisterComponent();
element.setAttribute('data-autocompel', this._compName);
this._element = element;
this.addStyleClass(this._compName);
}
setComponentName(name) {
this._givenName = name;
this.setAttribute('data-compel', name);
Page.registerComponent(name);
return this;
}
/**
* (Wrapper) Sets the innerText of the element
* @todo add Alignment of text functionality
* @param {string} text
* @returns {Component} this component object
*/
text(text) {
if (this._element instanceof HTMLInputElement && this._element.type === "text") {
this._element.value = text;
} else {
this._element.innerText = text;
}
return this;
}
/**
* Wrapper to set the HTMLElement.title attribute
* @param {string} text
* @returns {Component}
*/
title(text) {
this._element.title = text;
return this;
}
/**
* Wrapper for HTMLElement.classList.add()
* @param {string} styleClass
* @returns {Component} this component object
*/
addStyleClass(styleClass) {
this._element.classList.add(styleClass);
return this;
}
/**
* Wrapper for the HTMLElement.setAttribute() method.
* @param {string} key
* @param {string} value
* @returns {Component} this component object
*/
setAttribute(key, value) {
this._element.setAttribute(key, value);
return this;
}
/**
* Wrapper for the HTMLElement.addEventListener()
* @param {keyof WindowEventMap|CommonEvents} theEvent
* @param {Function} theListener
* @param {boolean|AddEventListenerOptions} options
* @returns {Component} this component object
*/
addEventListener(theEvent, theListener, options = null) {
this._element.addEventListener(theEvent, theListener, options)
return this;
}
}
/**
* @inheritdoc
* @extends ElementWrapper
* @abstract
*/
class ChildbearerComponent extends ElementWrapper {
/**
* @type {Array<Component>} children
*/
_children;
/**
* @type {Alignment} alignment
*/
_alignment;
/**
* @type {Arrangement} arrangement
*/
_arrangement;
constructor(element, attr = {}) {
super(element, attr);
this._children = [];
}
/**
* @todo: Unify logic extract modifications into responsible construct
* @todo: Make it work as expected, fix docu
* @todo: Differentiate between directions (horizontal, vertiacl)
*
* Sets the alignment (modifications) for this element or more specific for its children.
* @param {Alignment} alignment
* @returns {Component} this component object
*/
alignment(alignment) {
/*
this._modifier._modifications["display"] = "flex";
this._modifier._modifications["align-content"] = alignment;
this._modifier._modifications["align-items"] = alignment;
this._modifier._modifications["text-align"] = alignment;
*/
this._alignment = alignment;
return this;
}
/**
* @todo: Unify logic extract modifications into responsible construct
* @todo: Differentiate between directions (horizontal, vertical)
* @todo: Make it work as expected, fix docu
*
* Sets the arrangement (modifications) for this element or more specific for its children.
* @param {Arrangement} arrangement
* @returns {Component} this component object
*/
arrangement(arrangement) {
/*
this._modifier._modifications["justify-content"] = arrangement;
*/
this._arrangement = arrangement;
return this;
}
/**
* Opens a context to create children elements.
* Either as one component or a list/array of components.
* @param {Component|Array<Component>} component
* @returns {Component} this component object
*/
childContext(component) {
if (!component) return this;
if (arguments.length > 1) {
for (let i = 0; i < arguments.length; i++) {
this.childContext(arguments[i]);
}
} else if (component instanceof Array) {
for (let i = 0; i < component.length; i++) {
this.childContext(component[i]);
}
} else {
if (!(component instanceof Component)) {
this.childContext(component.toComponent())
} else {
this._children.push(component.end());
}
}
return this;
}
/**
* Ends chain for the current component.
* Returns the builder object.
* The Component that is selected there will be set as child to this one.
*
* This funciton is a convenience function.
* Mainly to offer the possibility to reduce the depth of method chains.
* Especially in the case of components with only one child.
*
* @returns {builder} the given
*/
chainChild() {
return builder._nextComponent(this);
}
/**
* Ends a chainChild - chain.
* If components are setup as chainChild
* they would be wrongfully taken through childContext().
* Therefore thoose chains are recursively resolved through this method.
* @returns {Component}
*/
end() {
let parent = this._parentComponent;
if (parent) {
this._parentComponent = null;
return parent
.childContext(this)
.end();
}
return this;
}
}