478 lines
11 KiB
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)
|
|
}
|
|
}
|