478 lines
11 KiB

/**
* This file is part of the jps-like-websites lib
* URL: https://git.labos.goip.de/chris/jpc-like-websites
* @copyright by its creator Christian Martin
*/
/**
* Enum providing predefined set of Size-Units
*/
const SizeUnits = Object.freeze({
PIXEL: "px",
PERCENT: "%"
})
/**
* @abstract
*/
class DirectionUnitDependentAttribute {
_unit;
_fFirst;
_fSecond;
_fThird;
_fForth;
constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
this._unit = defaultUnit;
this._fFirst = defaultValue;
this._fSecond = defaultValue;
this._fThird = defaultValue;
this._fForth = defaultValue;
}
/**
*
* @param {Units} unit The unit of the amount or style
* @returns {DirectionUnitDependentAttribute} this - Object
*/
setUnit(unit) {
this._unit = unit;
return this;
}
/**
*
* @returns {array<*>} list of attributes
*/
getOrderedAttributes() {
return [this._fFirst, this._fSecond, this._fThird, this._fForth];
}
/**
* @returns {Array<string>}
*/
getOrderedValues() {
return this.getOrderedAttributes().map(a => a + this._unit);
}
/**
* Since the basic values are from "first" to "fourth",
* they can be also accessed in the ordered way.
*
* Mainly used by the setup of directions of subclasses.
* @param {number} index [1,4]
* @param {number} value
* @returns {DirectionUnitDependentAttribute} this
*/
setByIndex(index, value) {
switch (index) {
case 1:
this._fFirst = value;
break;
case 2:
this._fSecond = value;
break;
case 3:
this._fThird = value;
break;
case 4:
this._fForth = value;
break;
default:
this._fFirst = value;
break;
}
return this;
}
/**
* Since the basic values are from "first" to "fourth",
* they can be also accessed in the ordered way.
*
* Mainly used by the setup of directions of subclasses.
* @param {number} index [1,4]
* @returns {*} this value of index
*/
getByIndex(index) {
switch (index) {
case 1:
return this._fFirst;
case 2:
return this._fSecond;
case 3:
return this._fThird;
case 4:
return this._fForth;
default:
return this._fFirst;
}
}
/**
* Placeholder for overrides
* @returns {Object}
*/
toModifications() {
return this.getOrderedValues()
}
/**
* sets the amount-value for all directions.
* @param {number} amount value to set for all directions
* @returns {DirectionUnitDependentAttribute} this
*/
all(amount) {
this._fFirst = amount;
this._fSecond = amount;
this._fThird = amount;
this._fForth = amount;
return this;
}
}
const SideDirections = Object.freeze({
LEFT: 1,
TOP: 2,
RIGHT: 3,
BOTTOM: 4
});
const SideTransitionDirection = Object.freeze({
HORIZONTAL: 0,
VERTICAL: 1
});
const Corners = Object.freeze({
TOP_LEFT: 0,
TOP_RIGHT: 1,
BOTTOM_LEFT: 2,
BOTTOM_RIGHT: 3
});
const CornerTransitionDirection = Object.freeze({
TOP_LEFT_BOTTOM_RIGHT: 0,
TOP_RIGHT_BOTTOM_LEFT: 1
});
/**
* @inheritdoc
* @extends DirectionUnitDependentAttribute
*
*/
class Sides extends DirectionUnitDependentAttribute {
/**
*
* @param {number|string} defaultValue
* @param {SizeUnits} defaultUnit
*/
constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
super(defaultValue, defaultUnit);
}
/**
* sets the amount-value for the left side.
* @param {number} amount siding for left
* @returns {Siding} this Siding Object
*/
left(amount) {
return this.setByIndex(1, amount);
}
/**
* sets the amount-value for the right side.
* @param {number} amount siding for right
* @returns {Siding} this Siding Object
*/
right(amount) {
return this.setByIndex(3, amount);
}
/**
* sets the amount-value for the top side.
* @param {number} amount siding for top
* @returns {Siding} this Siding Object
*/
top(amount) {
return this.setByIndex(2, amount);
}
/**
* sets the amount-value for the bottom side.
* @param {number} amount siding for bottom
* @returns {Siding} this Siding Object
*/
bottom(amount) {
return this.setByIndex(4, amount);
}
/**
* sets the amount-value for the horizontal sides (left and right).
* @param {number} amount siding for left and right.
* @returns {Sides} this Siding Object
*/
horizontal(amount) {
return this.left(amount).right(amount);
}
/**
* sets the amount-value for the vertical sides (left and right).
* @param {number} amount siding for top and bottom.
* @returns {Sides} this Siding Object
*/
vertical(amount) {
return this.top(amount).bottom(amount);
}
/**
*
* @returns {Object}
*/
getValues() {
return {
"left": this._fFirst,
"top": this._fSecond,
"right": this._fThird,
"bottom": this._fForth,
"horizontal": this._fFirst + this._fThird,
"vertical": this._fSecond + this._fForth
}
}
toModifications() {
return [
{ key: "left", value: this._fFirst + this._unit },
{ key: "top", value: this._fSecond + this._unit },
{ key: "right", value: this._fThird + this._unit },
{ key: "bottom", value: this._fForth + this._unit }
]
}
}
/**
* @inheritdoc
* @extends Sides
*/
class PaddingChain extends Sides {
_modifier;
constructor(modifier) {
super();
this._modifier = modifier;
}
toModifier() {
return this._modifier
.padding(this);
}
/**
* Returns the corresponding Modifier.
* Basically climbs up the chain level.
* @returns {Modifier}
*/
ensureModifier() {
return this.toModifier()
}
/**
* Returns the style-modifications of the class.
* @returns {Map<string,string>}
*/
toModifications() {
return [
{ key: "padding-left", value: this._fFirst + this._unit },
{ key: "padding-top", value: this._fSecond + this._unit },
{ key: "padding-right", value: this._fThird + this._unit },
{ key: "padding-bottom", value: this._fForth + this._unit }
]
}
/**
*
* @returns {Component} the Component that was (supposed to be) modified by this obj.
*/
toComponent() {
return this._modifier
.padding(this)
.toComponent();
}
/**
*
* @param {Component|Array<Component>} innerComponent children of the Component under modification.
* @returns {Component}
*/
childContext(innerComponent) {
return this._modifier
.padding(this)
.toComponent()
.childContext(innerComponent);
}
}
/**
*
*/
class TwoDimPoint {
/**
*
* @param {number} x
* @param {number} y
*/
constructor(x, y) {
this.x = x;
this.y = y;
}
/**
*
* @param {number} delta
* @returns {TwoDimPoint}
*/
xMinus(delta) {
this.x = this.x - delta;
return this;
}
/**
*
* @param {number} delta
* @returns {TwoDimPoint}
*/
xPlus(delta) {
this.x = this.x + delta;
return this;
}
/**
*
* @param {number} delta
* @returns {TwoDimPoint}
*/
yMinus(delta) {
this.y = this.y - delta;
return this;
}
/**
*
* @param {number} delta
* @returns {TwoDimPoint}
*/
yPlus(delta) {
this.y = this.y + delta;
return this;
}
/**
*
* @param {TwoDimPoint} delta
* @returns {TwoDimPoint}
*/
minusTDP(delta) {
this.x = this.x - delta.x;
this.y = this.y - delta.y;
return this;
}
/**
*
* @param {TwoDimPoint} delta
* @returns {TwoDimPoint}
*/
plusTDP(delta) {
this.x = this.x + delta.x;
this.y = this.y + delta.y;
return this;
}
}
/**
*
* @param {number} start
* @param {number} end
* @param {number} value
* @param {number} tolerance
* @param {boolean} usePercentage
* @returns {boolean}
*/
function isValueInBounds(start, end, value, tolerance = 0, usePercentage = false) {
if (tolerance !== 0) {
if (usePercentage) {
start = start * (1 - tolerance / 100);
end = end * (1 + tolerance / 100);
} else {
start = start - tolerance;
end = end + tolerance;
}
}
return value >= start && value <= end;
}
/**
* @param {number} x
* @param {number} y
* @param {Map<SideDirections,number>} area
* @param {number} tolerance
* @param {boolean} usePercentage if tolerance is given and this is set to true,
* the tolerance will be calculated by percentage,
* otherwise it will be subtracted/added
* @returns {boolean}
*/
function areXYInArea(x, y, area, tolerance = 0, usePercentage = false) {
return isValueInBounds(
area.get(SideDirections.LEFT),
area.get(SideDirections.RIGHT),
x,
tolerance,
usePercentage
) && isValueInBounds(
area.get(SideDirections.TOP),
area.get(SideDirections.BOTTOM),
y,
tolerance,
usePercentage
);
}
/**
*
* @param {TwoDimPoint} point
* @param {Map<SideDirections,number>} area
* @param {number} tolerance
* @param {boolean} usePercentage if tolerance is given and this is set to true,
* the tolerance will be calculated by percentage,
* otherwise it will be subtracted/added
*/
function isPointInArea(point, area, tolerance = 0, usePercentage = false) {
return areXYInArea(
point.x,
point.y,
area,
tolerance,
usePercentage
);
}
/**
*
* @param {HTMLElement} element
* @returns {Object<SideDirections, number}
*/
function getEnclosingBounds(element) {
let area = element.getBoundingClientRect();
let parentArea = element.parentElement.getBoundingClientRect();
return {
[SideDirections.LEFT]: Math.min(area.left, parentArea.left),
[SideDirections.RIGHT]: Math.max(area.right, parentArea.right),
[SideDirections.TOP]: Math.min(area.top, parentArea.top),
[SideDirections.BOTTOM]: Math.max(area.bottom, parentArea.bottom)
}
}