15 changed files with 1300 additions and 0 deletions
@ -0,0 +1 @@ |
|||
node_modules/ |
@ -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. |
|||
|
@ -0,0 +1,18 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Sample Page</title> |
|||
<script src="jpc-like-websites.js"></script> |
|||
|
|||
</head> |
|||
<body> |
|||
<main> |
|||
<div id="root"></div> |
|||
</main> |
|||
<footer> |
|||
<script src="samplePage.js"></script> |
|||
</footer> |
|||
</body> |
|||
</html> |
@ -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 |
@ -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<Component>} 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<Component>} 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()) |
|||
} |
|||
} |
@ -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 |
|||
} |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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") |
|||
) |
|||
]) |
|||
) |
@ -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<Component>} 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") |
|||
) |
|||
} |
|||
} |
@ -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()) |
|||
} |
|||
} |
@ -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<Component>} 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)`) |
|||
} |
|||
|
|||
} |
@ -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, |
|||
}) |
@ -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(); |
@ -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; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,15 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"module": "CommonJS", |
|||
"target": "ES2020", |
|||
"declaration": true, |
|||
"outDir": "./lib" |
|||
}, |
|||
"include": [ |
|||
"./src/ts/*" |
|||
], |
|||
"exclude": [ |
|||
"node_modules", |
|||
"./lib/**/*" |
|||
] |
|||
} |
Loading…
Reference in new issue