/** * The class provides overreaching options for building the website. */ class PageBuilder { #cssClasses; #functions; constructor() { this.#cssClasses = document.createElement("style"); this.#functions = document.createElement("script"); } /** * Registers a function to be added later in a script tag in the head of the document. * @param {string} name * @param {func} fun */ registerFunction(name, fun) { this.#functions.innerText += `const ${name} = ${fun}`; } /** * Adds a script tag into the head of the document. */ generate() { document.querySelector("head") .appendChild(this.#functions) } } const Page = new PageBuilder(); /** * Enum to access common events */ const CommonEvents = Object.freeze({ ONCLICK: "onClick", }) /** * A simple Color class for rgb set color values. */ class Color { #red; #green; #blue; constructor(red, green, blue) { this.#red = red this.#green = green this.#blue = blue } /** * * @returns {string} an rgb object string */ cssRGBString() { return `rgb(${this.#red}, ${this.#green}, ${this.#blue})`; } } /** * Simple Dimensions container for the height and width in pixels. */ class Dimensions { #x; #y; /** * Sets width (x) value of pixels * @param {number} pixels * @returns this Dimensions Modifier */ width(pixels) { this.x = pixels; return this; } /** * Sets height (y) value of pixels * @param {number} pixels * @returns this Dimensions Modifier */ height(pixels) { this.y = pixels; return this; } } /** * The Class holds values from each side/direction of an element. * Used for margin/padding. */ class Siding { pxLeft = 0; pxRight = 0; pxTop = 0; pxBottom = 0; /** * sets the pixels-value for all sides. * @param {number} pixels siding from all sides * @returns this Siding Object */ all(pixels) { this.pxLeft = pixels; this.pxRight = pixels; this.pxTop = pixels; this.pxBottom = pixels; return this; } /** * sets the pixels-value for the horizontal sides (left and right). * @param {number} pixels siding for left and right. * @returns this Siding Object */ horizontal(pixels) { this.pxLeft = pixels; this.pxRight = pixels; return this; } /** * sets the pixels-value for the vertical sides (left and right). * @param {number} pixels siding for top and bottom. * @returns this Siding Object */ vertical(pixels) { this.pxTop = pixels; this.pxBottom = pixels; return this; } /** * sets the pixels-value for the left side. * @param {number} pixels siding for left * @returns this Siding Object */ left(pixels) { this.pxLeft = pixels; return this; } /** * sets the pixels-value for the right side. * @param {number} pixels siding for right * @returns this Siding Object */ right(pixels) { this.pxRight = pixels; return this; } /** * sets the pixels-value for the top side. * @param {number} pixels siding for top * @returns this Siding Object */ top(pixels) { this.pxTop = pixels; return this; } /** * sets the pixels-value for the bottom side. * @param {number} pixels siding for bottom * @returns this Siding Object */ bottom(pixels) { this.pxBottom = pixels; return this; } } /** * Enum providing common alignment rules */ const Alignment = Object.freeze({ BEGIN_BORDER: 0, CENTER: 1, END_BORDER: 2, }) /** * Enum providing common alignment rules */ const Arrangement = Object.freeze({ START: 0, END: 1, CENTER: 2, SPACE_BETWEEN: 3, SPACE_EVENLY: 4, SPACE_AROUND: 5, }) /** * A chained class that sets most of the stylings of an element. */ class Modifier { modifications = {}; constructor() { this.modifications = new Object(); } /** * Sets the modifications for widht and height to 100%. * @returns this modifier object */ fillMaxSize() { this.modifications["width"] = "100%"; this.modifications["height"] = "100%"; return this; } /** * Sets the modification for width to 100%. * @returns this modifier object */ fillMaxWidth() { this.modifications["width"] = "100%"; return this; } /** * Sets the modification for height to 100%. * @returns this modifier object */ fillMaxHeight() { this.modifications["height"] = "100%"; return this; } /** * Sets modifications according to the dimensions object. * @param {Dimensions} dimensions * @returns this modifier object */ size(dimensions) { this.modifications["height"] = dimensions.height + "px"; this.modifications["width"] = dimensions.width + "px"; return this; } /** * Sets the padding on all sides according to the given siding object. * Currently the padding will always be set * to the most recent padding/siding. * @param {Siding} siding * @returns this modifier object */ padding(siding) { this.modifications["padding-right"] = siding.pxRight + "px"; this.modifications["padding-left"] = siding.pxLeft + "px"; this.modifications["padding-top"] = siding.pxTop + "px"; this.modifications["padding-bottom"] = siding.pxBottom + "px"; return this; } /** * Sets the margin on all sides according to the given siding object. * Currently the margin will always be set * to the most recent margin/siding. * @param {Siding} siding * @returns this modifier object */ margin(siding) { this.modifications["margin-right"] = siding.pxRight + "px"; this.modifications["margin-left"] = siding.pxLeft + "px"; this.modifications["margin-top"] = siding.pxTop + "px"; this.modifications["margin-bottom"] = siding.pxBottom + "px"; return this; } /** * Sets the background-color as a rgb color. * @param {Color} color * @returns this modifier object */ background(color) { this.modifications["background-color"] = color.cssRGBString(); return this; } /** * Sets the color as a rgb color. * @param {Color} color * @returns this modifier object */ color(color) { this.modifications["color"] = color.cssRGBString(); return this; } /** * Adds the modifications of the given Modifier to current Modifier. * This is especailly used in the cases of extending existing/pre defined * Components. * CAUTION matching existing modifications will be ignored. * @param modifier The "new" Modifier * @returns The "old/current" Modifier, * extended with the modifications of the given Modifier. */ join(modifier, modifications = {}) { var keys = Object.keys(modifier.modifications); for (let i = 0; i < keys.length; i++) { if (!this.modifications.hasOwnProperty(keys[i])) this.modifications[keys[i]] = modifier.modifications[keys[i]]; } return this; } /** * * @param {string} key a css style rule * @param {string} value the corresponding value to the css style rule * @returns this modifier object */ setStyleRule(key, value) { this.modifications[key] = value; return this; } } /** * A chainable HTMLElement builder. */ class Component { _element; _modifier _alignment; _arrangement; constructor(element, attr = {}) { this._modifier = new Modifier().margin(new Siding().all(0)); this._element = element; Object.keys(attr) .forEach(key => { this._element.setAttribute(key, attr[key]); }) } /** * Sets the alignment (modifications) for this element or more specific for its children. * @param {Alignment} alignment * @returns this component object */ alignment(alignment) { this._alignment = alignment; return this; } /** * Sets the arrangement (modifications) for this element or more specific for its children. * @param {Arrangement} arrangement * @returns this component object */ arrangement(arrangement) { this._arrangement = arrangement; switch (arrangement) { case Arrangement.START: this._modifier.modifications["justify-content", "start"] break; case Arrangement.END: this._modifier.modifications["justify-content", "end"] break; case Arrangement.CENTER: this._modifier.modifications["justify-content", "center"] break; case Arrangement.SPACE_AROUND: this._modifier.modifications["justify-content", "space-around"] break; case Arrangement.SPACE_BETWEEN: this._modifier.modifications["justify-content", "space-between"] break; case Arrangement.SPACE_EVENLY: this._modifier.modifications["justify-content", "space-evenly"] break; } return this; } /** * * @param {Modifier} modifier * @returns this component object */ modifier(modifier) { this._modifier = this._modifier.join(modifier) return this; } /** * Sets the innerText of the element * @param {string} text * @returns this component object */ text(text) { this._element.innerText = text; return this; } /** * * @param {string} styleClass * @returns this component object */ addStyleClass(styleClass) { this._element.classList.add(styleClass); return this; } /** * * @param {string} key * @param {string} value * @returns this component object */ setAttribute(key, value) { this._element.setAttribute(key, value); return this; } /** * Ends chain. * Applies all modifications on the element. * @returns {HTMLElemment} the html element */ generate() { var mkeys = Object.keys(this._modifier.modifications); for (let i = 0; i < mkeys.length; i++) { this._element.style[mkeys[i]] = this._modifier.modifications[mkeys[i]]; } return this._element; } /** * * @param {Component|Array} innerComponent * @returns this component object */ childContext(innerComponent) { if (innerComponent instanceof Array) { for (let i = 0; i < innerComponent.length; i++) { this._element.append(innerComponent[i].generate()); } } else { this._element.append(innerComponent.generate()); } return this; } /** * * @param {CommonEvent} commonEvent * @param {string} functionName * @returns this component object */ setEvent(commonEvent, functionName) { return this.setAttribute(commonEvent, `${functionName}(this)`) } } /** * Represents container Components. * Some predefined modifications are applied on the child components. */ class FlexContainerComponent extends Component { constructor(attr = {}) { super(document.createElement("div"), attr) .addStyleClass("flex-container-component") } /** * * @param {Component|Array} innerComponent * @returns this component object */ childContext(innerComponent) { if (innerComponent instanceof Array) { innerComponent.forEach(icomp => { icomp.modifier( new Modifier() .setStyleRule("flex", "none") ) }) } else { innerComponent.modifier( new Modifier() .setStyleRule("flex", "none") ) } return super.childContext(innerComponent); } } /** * A FlexContainerComponent, which organizes the children in a column like manner. */ class Column extends FlexContainerComponent { constructor(attr = {}) { super(document.createElement("div"), attr) .addStyleClass("column-component") .modifier( new Modifier() .setStyleRule("flex-direction", "column") ); } } /** * A FlexContainerComponent, which organizes the children in a row like manner. */ class Row extends FlexContainerComponent { constructor(attr = {}) { super(attr) .addStyleClass("row-component") .modifier( new Modifier() .setStyleRule("flex-direction", "row") ) } } const builder = { genTag: function (tag, attr = {}) { return new Component(document.createElement(tag), attr); }, anchor: function (attr = {}) { return builder.genTag("a", attr); }, label: function (attr = {}) { return builder.genTag("label", attr); }, button: function (attr = {}) { return builder.genTag("button", attr); }, input: function (attr = {}) { return builder.genTag("input", attr); }, div: function (attr = {}) { return builder.genTag("div", attr); }, paragraph: function (attr = {}) { return builder.genTag("paragraph", attr); }, header: function (sizeGroup, attr = {}) { return builder.genTag(`h${sizeGroup}`, attr); }, checkbox: function (attr = {}) { return builder.genTag("checkbox", attr); }, selection: function (attr = {}) { return builder.genTag("selection", attr); }, option: function (attr = {}) { return builder.genTag("option", attr); }, section: function (attr = {}) { return builder.genTag("section", attr); }, radioBtn: function (attr = {}) { return builder.genTag("radioBtn", attr); }, icon: function (attr = {}) { return builder.genTag("icon", attr); }, img: function (attr = {}) { return builder.genTag("img", attr); }, textarea: function (attr = {}) { return builder.genTag("textarea", attr); }, row: function (attr = {}) { return new Row(attr) }, column: function (attr = {}) { return new Column(attr) }, page: function(innerComponents){ Page.generate(); document.querySelector('#root').appendChild(innerComponents.generate()) } }