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.
329 lines
9.8 KiB
329 lines
9.8 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.
|
|
* @extends StyleAndScriptStoringComponent
|
|
* @inheritdoc
|
|
*
|
|
*/
|
|
class Component extends StyleAndScriptStoringComponent {
|
|
/**
|
|
* @type {boolean}
|
|
*/
|
|
_isCompel;
|
|
/**
|
|
* @type {Array<any>}
|
|
*/
|
|
_toRegister;
|
|
/**
|
|
* @type {boolean}
|
|
*/
|
|
_isContextMenu;
|
|
|
|
|
|
/**
|
|
* Initializes the component
|
|
* @param {HTMLElement} element the base element
|
|
* @param {Map<string,string>} attr Specific already known attributes
|
|
*/
|
|
constructor(element, attr = {}) {
|
|
super(element, attr);
|
|
this._isCompel = false;
|
|
this._isContextMenu = false;
|
|
|
|
this._modifier = new Modifier()
|
|
.margin(0);
|
|
this._modifier._modifications['display'] = "flex";
|
|
this._modifier._modifications["box-sizing"] = "border-box";
|
|
|
|
this._toRegister = [];
|
|
}
|
|
|
|
/**
|
|
* Adds a class to classList via HTMLElement.classList.add() method.
|
|
* Further collects rules in a property until generate is called.
|
|
*
|
|
* @CAUGHTION implementation is not safe to use, ignoring extStore is recommended;
|
|
*
|
|
* @todo difference between stylings and classes, extStore logic in combination with the Page.register... logic
|
|
*
|
|
* @override
|
|
*
|
|
* @param {string} styleClass (without the '.' in the front)
|
|
* @param {string|Modifier|map<string,string>} styling
|
|
* @param {ExtStorage|ExtStoreType|ExtStorePosition|OverwriteBehaviour|EXPosConfer|ESOverwriteConfer} extStore
|
|
* if a unique definition is desired, all constants or configurator objects are allowed - they will be processed accordingly
|
|
* @returns {Component} this component object
|
|
*/
|
|
addStyleClass(styleClass, styling = null, extStore = null) {
|
|
if (!extStore) {
|
|
extStore = this._styleClassesExtStore;
|
|
} else if (extStore.isMissing()) {
|
|
extStore = extStore.fillBy(this._styleClassesExtStore);
|
|
}
|
|
|
|
if (styling) {
|
|
if (styling instanceof Modifier) {
|
|
styling = styling._modifications;
|
|
}
|
|
|
|
Page.registerStyling('.' + styleClass, styling);
|
|
}
|
|
|
|
this._element.classList.add(styleClass);
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {boolean} vertical Defines if the Component should overflow vertically (default: true)
|
|
* @param {boolean} horizontal Defines if the Component should overflow horizontally (default: false)
|
|
* @returns {Component}
|
|
*/
|
|
overflow(vertical = true, horizontal = false ) {
|
|
if (vertical || horizontal) {
|
|
this._modifier.join(
|
|
new Modifier()
|
|
.removeStyleRule("flex")
|
|
.setStyleRule("overflow", "hidden auto")
|
|
);
|
|
this.subscribeOnGenerate(CommonCompelGroups.OVERFLOWING);
|
|
}
|
|
|
|
if (vertical) {
|
|
this._modifier._modifications["overflow-y"] = "hidden auto";
|
|
}
|
|
if (horizontal) {
|
|
this._modifier._modifications["overflow-x"] = "hidden auto";
|
|
}
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @param {boolean} untilFound
|
|
* @returns {Component}
|
|
*/
|
|
hidden(untilFound = false) {
|
|
let styleClass = "compel-mech-hidden";
|
|
let hid = "hidden";
|
|
Page.registerStyling("." + styleClass, { [hid]: hid });
|
|
this.addStyleClass(styleClass);
|
|
|
|
this._modifier.removeStyleRule("display");
|
|
this.setAttribute(
|
|
hid,
|
|
(untilFound ? "until-found" : hid)
|
|
);
|
|
|
|
this.subscribeOnGenerate(CommonCompelGroups.HIDDEN_ON_START);
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Subscribes element under higher_compel group
|
|
* sets corr. variable true
|
|
* setAttribute("data-compel-isHCompel", "true")
|
|
*
|
|
* @returns {Component}
|
|
*/
|
|
isHigherComponent() {
|
|
this.subscribeOnGenerate(CommonCompelGroups.HIGHER_COMPEL);
|
|
this._isCompel = true;
|
|
this.addStyleClass("compel-higher");
|
|
return this.setAttribute("data-compel-isHCompel", "true")
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
getHigherCompelSelector() {
|
|
return this._element
|
|
.closest('[data-compel-isHCompel="true"].compel-higher')
|
|
.getAttribute("data-autocompel");
|
|
}
|
|
|
|
/**
|
|
* Collects the given List in the _toRegister attribute.
|
|
* When generate() is called,
|
|
* the created Element will be registered (added) in every list
|
|
* within the list.
|
|
* @param {*|string|Array<*>} listName
|
|
*/
|
|
subscribeOnGenerate(listName) {
|
|
this._toRegister.push(listName);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {Component}
|
|
*/
|
|
registerAsContextMenu() {
|
|
this.subscribeOnGenerate(CommonCompelGroups.IS_CONTEXT_MENU);
|
|
this._isContextMenu = true;
|
|
|
|
return this.addStyleClass('contextmenu')
|
|
.hidden();
|
|
}
|
|
|
|
/**
|
|
* @todo Positioning of the contextmenu element
|
|
* @todo extract into an extra function(allity) provider
|
|
*
|
|
* @param {Component} component
|
|
* @param {Sides|Function<HTMLElement|Event> => Sides} getRefPos
|
|
* @param {ExtStorage} [extStore=null]
|
|
* @returns {Component}
|
|
*/
|
|
contextMenu(component, getRefPos = null, extStore = null) {
|
|
if (!component._isContextMenu) {
|
|
component.registerAsContextMenu();
|
|
}
|
|
if (!getRefPos) {
|
|
getRefPos = function (cmEvent) {
|
|
return new Sides()
|
|
.left(cmEvent.pageX)
|
|
.top(cmEvent.pageY);
|
|
}
|
|
}
|
|
|
|
this.subscribeOnGenerate(CommonCompelGroups.HAS_CONTEXT_MENU);
|
|
|
|
let identifier = component._compName;
|
|
|
|
this.addEventListener(
|
|
"contextmenu",
|
|
DefaultContextMenu.openContextMenuAction(identifier, getRefPos)
|
|
);
|
|
|
|
return this.childContext(component);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {DragAndDropImplementation} dadImpl
|
|
* @returns {Component}
|
|
*/
|
|
draggable(dadImpl = new DragAndDropImplementation()) {
|
|
this.subscribeOnGenerate(CommonCompelGroups.DRAGGABLE);
|
|
this.subscribeOnGenerate(CommonCompelGroups.HAS_DRAG_EVENT);
|
|
|
|
let addedClass = "comp-el-mech-draggable";
|
|
let selector = this._element.getAttribute("data-autocompel");
|
|
|
|
selector = `.${addedClass}[data-autocompel="${selector}"]`;
|
|
|
|
return this.addStyleClass(addedClass)
|
|
.setAttribute("draggable", "true")
|
|
.setAttribute("dropEffect", "none")
|
|
.addEventListener(
|
|
CommonEvents.DRAG_START,
|
|
dadImpl.dragStartAction("text/plain", selector)
|
|
);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {EventDrag} dragEvent
|
|
* @param {Function} action
|
|
*/
|
|
onDrag(dragEvent, action = (e) => { e.preventDefault(); }) {
|
|
this.subscribeOnGenerate(CommonCompelGroups.HAS_DRAG_EVENT);
|
|
let selector = `comp-el-mech-drag${dragEvent}`;
|
|
|
|
return this.addEventListener(
|
|
'drag' + dragEvent,
|
|
action
|
|
);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {DragAndDropImplementation} dadImpl
|
|
* @returns {Component}
|
|
*/
|
|
dropTarget(dadImpl = new DragAndDropImplementation()) {
|
|
this.subscribeOnGenerate(CommonCompelGroups.DROP_TARGET);
|
|
let specialClass = "comp-el-mech-droptarget";
|
|
|
|
this.addStyleClass(specialClass)
|
|
.onDrag(EventDrag.OVER);
|
|
|
|
let selector = `.${specialClass}[data-autocompel="${this._compName}"]`
|
|
|
|
this._element.addEventListener(
|
|
"drop",
|
|
dadImpl.dropEventAction("text/plain", selector)
|
|
);
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
* An echo of Scope-Functions from kotlin for convenience
|
|
*
|
|
* Executes a given function injects this component into the function.
|
|
* @param {Function} func
|
|
* @returns {Component}
|
|
*/
|
|
apply(func) {
|
|
func(this);
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
* An echo of Scope-Functions from kotlin for convenience
|
|
*
|
|
* Executes a given function injects the htmlelement of this component into the function.
|
|
* @param {Function} func
|
|
* @returns {Component}
|
|
*/
|
|
applyToEl(func) {
|
|
func(this._element)
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Ends chain.
|
|
* Applies all modifications on the element.
|
|
* Processes all stored additions.
|
|
* Returns the constructed HTMLElement of this Component.
|
|
*
|
|
* @param {CompelGenerator} generator
|
|
* @param {Modifier | undefined} [modifier=null]
|
|
* @param {ExtStorage | undefined} [styleStore=null]
|
|
* @param {ExtStorage | undefined} [functionStore=null]
|
|
* @param {ExtStorage}
|
|
* @returns {WebTrinity} the constructed HTMLElement of this Component.
|
|
*/
|
|
generate(generator = singlepage, styleStore = null, functionStore = null) {
|
|
/**
|
|
* In the case that this component is a chainChild created one.
|
|
* The generation chain needs to be setup in the proper order
|
|
* so that the resulting element tree equals the expected/desired result.
|
|
*
|
|
* Therefore if this is a chainChild,
|
|
* it will be added to the parent via the regular childContext
|
|
* and the generation of the parent will be returned.
|
|
*
|
|
* The parent will generate this component on its generate().
|
|
*/
|
|
if (this._parentComponent) {
|
|
let parent = this._parentComponent;
|
|
this._parentComponent = null;
|
|
return parent.childContext(this)
|
|
.generate(generator, styleStore, functionStore);
|
|
}
|
|
|
|
return generator.generate(this, styleStore, functionStore);
|
|
}
|
|
}
|
|
|