activator-oce-exporter
Version:
Extract Activator binder and convert it to valid OCE mono pacakge
231 lines (207 loc) • 6.45 kB
JavaScript
import { getValueObject } from '../utils';
import { FusionLogger } from './fusion-logger';
import { FusionBase } from '../base';
export class FusionAligner {
static get config() {
return {
vertical: [
'top',
'center',
'bottom',
],
horizontal: [
'left',
'center',
'right',
],
};
}
/**
* @param {Align} align
* @returns {{styleFunc: String, positionFunc: String}} object with align functions
*/
static getAlignConfig({ direction, position }) {
const directions = {
vertical: {
position: {
top: 'alignSelfStart',
center: 'alignSelfCenter',
bottom: 'alignSelfEnd',
},
style: 'getItemTopStyle',
},
horizontal: {
position: {
left: 'justifyContentStart',
center: 'justifyContentCenter',
right: 'justifyContentEnd',
},
style: 'getItemLeftStyle',
},
};
return {
styleFunc: directions[direction].style,
positionFunc: directions[direction].position[position],
};
}
/**
* Returns align config object.
* @typedef {Object} Align
* @property {String} direction - align direction
* @property {String} position - align position
*/
/**
* @param {string} query - element query
* @param {Align} align
* @returns {Array} array with aligned elements
*/
static applyAlignToItems({ query, align }) {
const el = document.querySelector(`${query}`);
let itemsToSave = [];
if (el && this.isAlignPossible(align)) {
itemsToSave = this.alignItems(el, align);
} else {
FusionLogger.error('Element is not exist or align object is invalid', 'FusionAligner');
}
return itemsToSave;
}
/**
* @param {Align} align
*/
static isAlignPossible({ direction, position }) {
return this.config[direction] && this.config[direction].includes(position);
}
static alignSelfCenter({ parent, currentItem }) {
return (parent.height - currentItem.height) / 2;
}
static justifyContentCenter({ parent }) {
return (parent.width - parent.itemsTotalWidth) / 2;
}
static getParentBorderValue({ parent }) {
const borderPropValue = parent.el.getAttribute('border-width');
return borderPropValue ? getValueObject(borderPropValue).num : 0;
}
static alignSelfStart({ parent }) {
return this.getParentBorderValue({ parent });
}
static justifyContentStart({ parent }) {
return this.getParentBorderValue({ parent });
}
static justifyContentEnd({ parent }) {
return parent.width - parent.itemsTotalWidth - this.getParentBorderValue({ parent });
}
static alignSelfEnd({ parent, currentItem }) {
return parent.height - currentItem.height - this.getParentBorderValue({ parent });
}
static getItemLeftStyle({ currentItem, currentItemsWidth }) {
return { left: this.getValueWithUnit(currentItem.el, 'left', currentItemsWidth) };
}
static getItemTopStyle(data) {
const itemTop = this.getItemInitPosition(data);
const { currentItem } = data;
return { top: this.getValueWithUnit(currentItem.el, 'top', itemTop) };
}
static getItemInitPosition(data) {
const { align } = data;
const { positionFunc } = this.getAlignConfig(align);
return this[positionFunc](data);
}
static getValueWithUnit(el, style, value) {
const styleValue = el.style[style] || el.style.getPropertyValue(`--${style}`);
const { unit } = getValueObject(styleValue);
return `${value}${unit}`;
}
static getItemData(item) {
return {
el: item,
width: item.offsetWidth,
height: item.offsetHeight,
};
}
static applyItemStyles(styles, item) {
Object.keys(styles)
.filter(key => item.style[key] !== styles[key])
.forEach((style) => {
if (item.getAttribute(style)) {
item.setElementProp(style, styles[style]);
item.setAttribute(style, styles[style]);
} else {
item.style.setProperty(style, styles[style]);
}
});
}
static getItemsForAlign(el) {
return el.getChildComponents()
.filter(item => item instanceof FusionBase)
.sort((cur, next) => cur.offsetLeft - next.offsetLeft);
}
static isItemStyleChanged(styles, el) {
return Object.keys(styles).some(key => el.style[key] !== styles[key]);
}
static getItemsTotalWidth(items) {
return items.reduce((totalWidth, item) => {
totalWidth += item.offsetWidth;
return totalWidth;
}, 0);
}
/**
* Returns aligner config object.
* @typedef {Object} Aligner
* @property {Align} align
* @property {Object} parent - data of the parent aligner element
* @property {Object} currentItem - dynamic object with information about item to align
* @property {Number} currentItemsWidth - dynamic field with current items width
* @property {Array} alignedItems - dynamic array with items to align
*/
/**
* @param {Element} el - parent wrapper with elements to align
* @param {Align} align
* @returns {Aligner} aligner
*/
static getAlignerData(el, { direction, position }) {
const { width, height } = el.getBoundingClientRect();
const items = this.getItemsForAlign(el);
const itemsTotalWidth = this.getItemsTotalWidth(items);
return {
align: {
direction,
position,
},
parent: {
el,
width,
height,
items,
itemsTotalWidth,
},
currentItem: {
eL: null,
width: 0,
height: 0,
},
currentItemsWidth: 0,
alignedItems: [],
};
}
/**
* @param {Element} el - parent wrapper with elements to align
* @param {Align} align.
* @returns {Array} array with aligned elements
*/
static alignItems(el, align) {
const data = this.getAlignerData(el, align);
data.parent.items.reduce((totalWidth, item) => {
data.currentItem = this.getItemData(item);
const { styleFunc } = this.getAlignConfig(align);
data.currentItemsWidth = totalWidth;
const styles = { ...this[styleFunc](data) };
if (this.isItemStyleChanged(styles, item)) {
this.applyItemStyles(styles, item);
data.alignedItems.push({ id: item.id, styles });
}
totalWidth += data.currentItem.width;
return totalWidth;
}, this.getItemInitPosition(data));
return data.alignedItems;
}
}