UNPKG

activator-oce-exporter

Version:

Extract Activator binder and convert it to valid OCE mono pacakge

221 lines (193 loc) 7.65 kB
import { DOMObservable } from '../services/dom-observable'; import { FusionApi } from '../api'; import { getValueObject } from '../utils'; /** * Dom observable for add subscription for changes DOM. Class works with default configuration. */ const DEFAULT_WIDTH = 600; export function VerticalAlignObservable(superClass) { return class extends superClass { constructor() { super(); this.domObservable = new DOMObservable(); } setupObserverForChildChanges() { const config = { characterData: false, attributeFilter: ['style', 'data-mo-selected'], }; this.domObservable.setConfig(config); this.domObservable.observe(this); } firstUpdated(changedProps) { super.firstUpdated(changedProps); this.observe = this.domObservable.init(this.updateVerticalAlign.bind(this), true); this.setupObserverForChildChanges(); } /** * @description Receiving virtual rows of children dom elements * @param {HTMLElement[]} children - inner dom elements * @returns {HTMLElement[]} multidimensional array from children dom elements */ getVirtualRows(children) { const containerWidth = this.offsetWidth || DEFAULT_WIDTH; const virtualRows = []; let rowIndex = 0; children.reduce((totalRowWidth, item) => { const currentItemWidth = getValueObject(item.width).num; const itemNotFit = totalRowWidth + currentItemWidth > containerWidth; rowIndex = itemNotFit ? rowIndex += 1 : rowIndex; if (!virtualRows[rowIndex]) { totalRowWidth = 0; virtualRows[rowIndex] = []; } totalRowWidth += currentItemWidth; virtualRows[rowIndex].push(item); return totalRowWidth; }, 0); return virtualRows; } updateVerticalAlign() { const children = this.getChildComponents(); const virtualRows = this.getVirtualRows(children); virtualRows.forEach((row) => { if (this.constructor.isMiddleAlignedItems(row) || this.constructor.isNotMiddleAlignedItems(row)) { const columnMaxHeight = this.constructor.getBiggestColumnHeight(row); this.setHeightForSameColumns(row, columnMaxHeight); } else { this.setHeightForDifferentColumns(row, this.constructor.getMaxHeightColumn(row)); } }); } static getMaxHeightColumn(row) { return row.reduce((maxItem, nextItem) => ( this.getColumnHeight(maxItem) < this.getColumnHeight(nextItem) ? nextItem : maxItem)); } static isMiddleAlignedItems(row) { return row.every(item => item.getAttribute('vertical-align') === 'middle'); } static isNotMiddleAlignedItems(row) { return row.every(item => item.getAttribute('vertical-align') !== 'middle'); } static filterMiddlePositionedChildren(children) { return children.filter(data => data.getAttribute('vertical-align') === 'middle'); } static getValidHeight(height) { return !height || height === 'auto' ? '0px' : height; } static getColumnHeight(column) { const columnHeight = window.getComputedStyle(column).height; return getValueObject(this.getValidHeight(columnHeight)).num; } static getBiggestColumnHeight(children) { return children.reduce((result, column) => Math.max(result, this.getColumnHeight(column)), 0); } isBiggestMiddleItem(element, maxMiddleItem) { const targetElemHeight = this.constructor.getColumnHeight(element); const middleElemHeight = this.constructor.getColumnHeight(maxMiddleItem); return targetElemHeight === middleElemHeight; } /** * @description Align elements in case if we have different types of align in one row (middle, top, bottom) * @param {HTMLElement[]} children - inner dom elements * @param {HTMLElement} maxItem - inner child - dom element with max height */ setHeightForDifferentColumns(children, maxItem) { const maxItemHeight = this.constructor.getColumnHeight(maxItem); const middleItems = this.constructor.filterMiddlePositionedChildren(children); const maxMiddleItem = this.constructor.getMaxHeightColumn(middleItems); return children.forEach((element) => { if (middleItems.includes(element)) { if (this.isBiggestMiddleItem(element, maxMiddleItem)) { this.applyPosition(maxItem, maxItemHeight, element); } else { this.applyMiddlePosition(maxMiddleItem, maxItem, element); } } else { this.applyPosition(element, this.constructor.getColumnHeight(element)); } }); } applyMiddlePosition(maxMiddleItem, maxItem, element) { const heightObj = this.getIndentObj(maxMiddleItem, maxItem); const elemSide = element.getAttribute('vertical-align'); this[`${elemSide}Apply`](element, heightObj); } /** * @typedef {object} IndentObj * @property {number} maxHeight - indent foe align * @property {string} [offsetParent] - additional indent for case if we have bottom align */ /** * @param {HTMLElement} maxMiddleItem - dom element with max height and middle align * @param {HTMLElement} maxItem - dom element with max height * @returns {IndentObj} object with parameters for calculation of top indent */ getIndentObj(maxMiddleItem, maxItem) { let indentObj = {}; const maxMiddleItemHeight = this.constructor.getColumnHeight(maxMiddleItem); if (maxItem.getAttribute('vertical-align') === 'bottom') { const height = (maxMiddleItemHeight / 2 + maxMiddleItem.offsetTop) * 2; indentObj = { maxHeight: height, offsetParent: maxItem.offsetTop }; } else { indentObj = { maxHeight: maxMiddleItemHeight }; } return indentObj; } setHeightForSameColumns(children, maxHeight) { return children.forEach((element) => { this.applyPosition(element, maxHeight); }); } applyPosition(target, maxHeight, element = target) { const side = target.getAttribute('vertical-align'); this[`${side}Apply`](element, { maxHeight }); } static doStyleAction(element, styleObj, style) { if (styleObj[style] === '') { element.style.removeProperty(style); } else { element.style.setProperty(style, styleObj[style]); } } applyStyles(styleObj, element) { if (this.constructor.styleChanged(styleObj, element)) { Object.keys(styleObj).forEach((key) => { this.constructor.doStyleAction(element, styleObj, key); }); FusionApi.saveStyles(`#${element.id}`, styleObj); } } static styleChanged(styleObj, element) { const { transform, ...otherStyles } = styleObj; return Object.keys(otherStyles).some(key => element.style[key] !== styleObj[key]); } topApply(element) { const styles = { top: '', transform: '', 'align-self': 'flex-start', }; this.applyStyles(styles, element); } middleApply(element, props = {}) { const styles = { top: `${this.constructor.calcHeight(props)}px`, transform: 'translate3d(0, -50%, 0)', 'align-self': 'flex-start', }; this.applyStyles(styles, element); } bottomApply(element) { const styles = { top: '', transform: '', 'align-self': 'flex-end', }; this.applyStyles(styles, element); } static calcHeight({ maxHeight, offsetParent = 0 }) { return parseInt(maxHeight / 2, 10) - offsetParent; } }; }