diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/README.md b/README.md index 90cf692..c2bc8c3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,35 @@ # jpc-like-websites +> Jetpack Compose like websites +The building method of Jetpack Compose (for android), +feels really straight forward +and intuitive for me. +Furthermore I personally found it more readable +than my limited experiences with flutter +(which were just two "get to know" bootstrap projects to be fair). +Since I found myself frequently building small (local) websites for minor tasks (calculations, tasks etc.). +I was looking for an option +to build those websites more or less fast (easy) +and reliable. +While they do look the way I intend them to. + +After developing some minor apps with Jetpack Compose I liked a lot about the way +the components are build/structured. +The reusability and so on... + +Further I love to write code in a "method-chaning" kind of way: +Every line is a chain-link concerning one particular task, +the IDE offers an overview of possibilities. +Method-chaining is more or less native in Kotlin. +Logical support for git-line-wise change detection. + +After some (very little) searching, +I choose to give it a couple of hours +and develop a small "lib" to enable myself to do exactly that. +> Develop jetpack compose like components in html/javascript. + +Javascript is by far not my strongest field, +typescript even less. +So it is recommended to not expect to much, +you have been warned. + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..9346fac --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + + Sample Page + + + + +
+
+
+ + + \ No newline at end of file diff --git a/join_js_files.sh b/join_js_files.sh new file mode 100644 index 0000000..d2eaf9e --- /dev/null +++ b/join_js_files.sh @@ -0,0 +1,9 @@ +TARGET="jpc-like-websites.js" +SRC="src/js" +ORDERED_LIST="context.js componentAttribute.js modifier.js component.js baseComponents.js builder.js" + +echo "" > $TARGET + +for i in $ORDERED_LIST; do + cat $SRC/$i >> $TARGET +done \ No newline at end of file diff --git a/jpc-like-websites.js b/jpc-like-websites.js new file mode 100644 index 0000000..8dcbbfd --- /dev/null +++ b/jpc-like-websites.js @@ -0,0 +1,551 @@ + +/** + * 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()) + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..80ad93c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,36 @@ +{ + "name": "websites-like-jpc", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "typescript": "^5.5.4" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + }, + "dependencies": { + "typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fa58ec5 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "websites-like-jpc", + "version": "1.0.0", + "description": "Framework to build websites in a Jetpack Compose like manner, as well as an extensive use of method-chaingin.", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "files": [ + "/lib" + ], + "scripts": { + "prepare": "npm run build", + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "typescript": "^5.5.4" + } +} diff --git a/samplePage.js b/samplePage.js new file mode 100644 index 0000000..811a365 --- /dev/null +++ b/samplePage.js @@ -0,0 +1,67 @@ +const fileSelection = function (labelText) { + return builder.row() + .arrangement(Arrangement.SPACE_BETWEEN) + .modifier( + new Modifier().fillMaxWidth() + .padding( + new Siding() + .horizontal(64) + .vertical(16) + ) + ) + .childContext([ + builder.label().text(labelText), + builder.input().setAttribute("type", "file") + ]) +} + +const fileSelectionSection = function () { + return builder.column() + .arrangement(Arrangement.CENTER) + .modifier( + new Modifier() + .fillMaxWidth() + .padding(new Siding().vertical(16)) + ) + .childContext([ + fileSelection("Script"), + builder.row() + .arrangement(Arrangement.CENTER) + .modifier( + new Modifier().fillMaxWidth() + ) + .childContext([ + builder.button() + .text('+') + .setEvent(CommonEvents.ONCLICK, "addFileSelection") + ]) + ]) +} + +builder.page ( + builder.column() + .modifier( + new Modifier() + .fillMaxSize() + ) + .childContext([ + builder.row() + .arrangement(Arrangement.SPACE_BETWEEN) + .modifier( + new Modifier().fillMaxWidth() + ) + .childContext([ + builder.header(1).text("Script Executer"), + builder.img().setAttribute("alt", "Logo") + ]) + , + fileSelectionSection() + , + builder.row() + .arrangement(Arrangement.CENTER) + .childContext( + builder.button() + .text("Execute Script") + ) + ]) +) diff --git a/src/js/baseComponents.js b/src/js/baseComponents.js new file mode 100644 index 0000000..ac93f36 --- /dev/null +++ b/src/js/baseComponents.js @@ -0,0 +1,60 @@ +/** + * 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") + ) + } +} diff --git a/src/js/builder.js b/src/js/builder.js new file mode 100644 index 0000000..2c52269 --- /dev/null +++ b/src/js/builder.js @@ -0,0 +1,26 @@ +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()) + } +} diff --git a/src/js/component.js b/src/js/component.js new file mode 100644 index 0000000..70091bb --- /dev/null +++ b/src/js/component.js @@ -0,0 +1,140 @@ +/** + * 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)`) + } + +} diff --git a/src/js/componentAttribute.js b/src/js/componentAttribute.js new file mode 100644 index 0000000..4af42ec --- /dev/null +++ b/src/js/componentAttribute.js @@ -0,0 +1,164 @@ +/** + * 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, +}) diff --git a/src/js/context.js b/src/js/context.js new file mode 100644 index 0000000..c30bc6f --- /dev/null +++ b/src/js/context.js @@ -0,0 +1,32 @@ +/** + * 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(); diff --git a/src/js/modifier.js b/src/js/modifier.js new file mode 100644 index 0000000..e09d449 --- /dev/null +++ b/src/js/modifier.js @@ -0,0 +1,128 @@ +/** + * 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; + } + +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e3ed51a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "target": "ES2020", + "declaration": true, + "outDir": "./lib" + }, + "include": [ + "./src/ts/*" + ], + "exclude": [ + "node_modules", + "./lib/**/*" + ] +}