/** * 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 */ /** * A chained class that sets most of the stylings of an element * Attributes: * _modifications: {Object} */ class Modifier { /** * @type {Map} _modifications */ _modifications; /** * @type {Array} */ _removeMods; /** * @type {Shape} */ _shape; /** * @type {Sides} paddingValues */ _paddingValues; constructor() { this._modifications = new Object(); this._removeMods = []; } /** * Sets the modifications for widht and height to 100%. * @returns {Modifier} this modifier object */ fillMaxSize(widthFraction = 1, heightFraction = 1) { return this.fillMaxWidth(widthFraction) .fillMaxHeight(heightFraction); } /** * Sets the modification for width to the given fraction of 1 (default 1 := 100%). * @param {number} fraction * @returns {Modifier} this modifier object */ fillMaxWidth(fraction = 1) { this._modifications["width"] = (100 * fraction) + "%"; this._modifications["max-width"] = (100 * fraction) + "%"; return this; } /** * Sets the modification for height to the given fraction of 1 (default 1 := 100%). * @param {number} fraction * @returns {Modifier} this modifier object */ fillMaxHeight(fraction = 1) { this._modifications["height"] = (100 * fraction) + "%"; this._modifications["max-height"] = (100 * fraction) + "%"; return this; } /** * * @param {string} keyWord weither 'height' or 'width' that will be adjusted and looked for * @param {Sides} parentalPadding */ _updateDimensionsBy(parentalPadding) { function updateDirection(keyWord, modifications, parentalAdjustment) { let refKeys = Object.keys(modifications) .filter(k => k.includes(keyWord)); if (refKeys.length > 0) { for (let i = 0; i < refKeys.length; i++) { let key = refKeys[i]; let value = modifications[key]; if (key.includes("calc")) { console.log( `Modifier._updateByParent... ${keyWord } - unexpected value '${value }' for '${key }', skipping...` ); } else { let newValue = `calc(${value} - ${parentalAdjustment});`; modifications[key] = newValue.trim(); } } } return modifications; } if (parentalPadding) { let pval = parentalPadding.getValues(); if (pval["horizontal"] > 0) { this._modifications = updateDirection("width", this._modifications, pval["horizontal"]+parentalPadding._unit); } if (pval["vertical"] > 0) { this._modifications = updateDirection("height", this._modifications, pval["vertical"]+parentalPadding._unit); } } return this; } /** * Sets modifications according to the dimensions object. * @param {Dimensions} dimensions * @returns {Modifier} this modifier object */ dimensions(dimensions) { dimensions.toModifications() .forEach(kvpair => { this._modifications[kvpair.key] = kvpair.value; }) return this; } /** * Sets the padding on all sides according to the given padding object. * Currently the padding will always be set * to the most recent padding/padding. * @param {Sides} siding * @returns {Modifier} this modifier object */ padding(siding) { this._paddingValues = siding; let keyToAdd = ""; if (siding instanceof PaddingChain) { /* TODO what is this supposed to do */ } else if (siding instanceof Sides) { keyToAdd = "padding-" } siding.toModifications() .forEach(kvpair => { this._modifications[keyToAdd + kvpair.key] = kvpair.value; }) 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 {Sides} siding * @returns {Modifier} this modifier object */ margin(siding) { let keyToAdd = ""; if (siding instanceof Sides) { keyToAdd = "margin-" } siding.toModifications() .forEach(kvpair => { this._modifications[keyToAdd + kvpair.key] = kvpair.value; }); return this; } /** * Sets the background-color as a rgb color. * @param {Color} color * @returns {Modifier} this modifier object */ background(color) { this._modifications["background-color"] = color.cssRGBString(); return this; } /** * Sets the color as a rgb color. * @param {Color} color * @returns {Modifier} 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 overwritten. * @param modifier The "new" Modifier * @returns {Modifier} The "old/current" Modifier, * extended with the modifications of the given Modifier. */ join(modifier, modifications = {}) { var keys = Object.keys(modifier.ensureModifier()._modifications); for (let i = 0; i < keys.length; i++) { /* if (!this._modifications.hasOwnProperty(keys[i])) */ this._modifications[keys[i]] = modifier.ensureModifier()._modifications[keys[i]]; } let removeMods = modifier.ensureModifier()._removeMods; if (removeMods.length > 0) { for (let i = 0; i < removeMods.length; i++) { delete this._modifications[removeMods[i]]; } } return this; } /** * * @param {string} key a css style rule * @param {string} value the corresponding value to the css style rule * @returns {Modifier} this modifier object */ setStyleRule(key, value) { this._modifications[key] = value; return this; } /** * * @param {string} key * @returns {Modifier} this modifier object */ removeStyleRule(key) { this._removeMods.push(key); if (Object.keys(this._modifications).includes(key)) { delete this._modifications[key]; } return this; } /** * Sets a border line (with given linestyle) to all sides. * If lineStyle is an array, the containing LineStyles, * are applied in the order: [top, right, bottom, left]. * If the border has a shape defined, * this shape will override earlier shape definitions. * Otherwise existing shape definitions will be applied. * @param {Border} border the style of the border line * @returns {Modifier} this modifier object */ border(border) { if (border._shape) { this.clip(border._shape); } else if (this._shape) { border._shape = this._shape; } border.toModifications() .forEach(e => this._modifications[e.key] = e.value); return this; } /** * * @param {Shape} shape * @returns {Modifier} */ clip(shape) { this._shape = shape; this._modifications["border-radius"] = shape.getOrderedValues().join(' '); return this; } /** * * @param {number} size of width and height in pixels * @returns {DimensionsChain} */ linkDimensions(size = -1) { if (size === -1) { return new DimensionsChain(this); } else { return new DimensionsChain(this).all(size).ensureModifier() } } /** * * @param {number} amount the padding for all four sides * @returns {PaddingChain} */ linkPadding(amount = -1) { if (amount === -1) { return new PaddingChain(this); } else { return new PaddingChain(this).all(amount); } } /** * * @param {number} cornerRadius will create a rounded rectangle with the given cornerRadius * @returns {ShapeChain} */ linkClip(cornerRadius = -1) { if (cornerRadius === -1) { return new ShapeChain(this); } else { return new ShapeChain(this).all(cornerRadius); } } /** * * @param {number} borderWidth sets the width of all four border sides * @returns {BorderChain} */ linkBorder(borderWidth = -1) { if (borderWidth === -1) { return new BorderChain(this); } else { return new BorderChain(this).width(borderWidth); } } /** * * @returns {Modifier} */ ensureModifier() { return this; } } class ChainableModifier extends Modifier { _component; constructor(component) { super(); this._component = component; } /** * * @returns {Component} */ toComponent() { return this._component.modifier(this); } /** * * @param {Component|Array} innerComponent * @returns {Component} the parent Component */ childContext(innerComponent) { return this._component .modifier(this) .childContext(innerComponent); } }