import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { Orientation } from '../../../Interfaces/Orientation'; import { ReversePairwise } from '../../../utils/itertools'; import { Flex } from './FlexBehaviors'; /** * Try to push the siblings * @param container * @returns */ export function ApplyPush(container: IContainerModel, parent: IContainerModel): IContainerModel { if (parent.children.length <= 1) { return container; } const children = parent.children; const isHorizontal = parent.properties.orientation === Orientation.Horizontal; if (isHorizontal) { PushContainersHorizontally(container, children); } else { PushContainersVertically(container, children); } Flex(container, parent); return container; } export function PushContainersHorizontally(container: IContainerModel, children: IContainerModel[]): IContainerModel { const prevIndex = children.length - 2; const prev: IContainerModel = children[prevIndex]; const isOverlapping = prev.properties.x + prev.properties.width > container.properties.x; if (!isOverlapping) { return container; } // find hole let lastContainer: IContainerModel | null = null; let space: number = 0; while (space.toFixed(2) < container.properties.width.toFixed(2)) { // FIXME: possible infinite loop due to floating point // FIXME: A fix was applied using toFixed(2). // FIXME: A coverture check must be done to ensure that all scenarios are covered const it = ReversePairwise(children.filter(child => child !== container)); for (const { cur, next } of it) { const hasSpaceBetween = next.properties.x + next.properties.width < cur.properties.x; if (hasSpaceBetween) { lastContainer = cur; space = cur.properties.x - (next.properties.x + next.properties.width); break; } } if (lastContainer === null) { // no space between break; } const indexLastContainer = children.indexOf(lastContainer); for (let i = indexLastContainer; i <= children.length - 2; i++) { const sibling = children[i]; sibling.properties.x -= space; } } const hasNoSpaceBetween = lastContainer === null; if (hasNoSpaceBetween) { // test gap between the left of the parent and the first container space = children[0].properties.x; if (space > 0) { for (let i = 0; i <= children.length - 2; i++) { const sibling = children[i]; sibling.properties.x -= space; } return container; } } return container; } export function PushContainersVertically(container: IContainerModel, children: IContainerModel[]): IContainerModel { const prevIndex = children.length - 2; const prev: IContainerModel = children[prevIndex]; const isOverlapping = prev.properties.y + prev.properties.height > container.properties.y; if (!isOverlapping) { return container; } // find hole let lastContainer: IContainerModel | null = null; let space: number = 0; while (space.toFixed(2) < container.properties.height.toFixed(2)) { // FIXME: possible infinite loop due to floating point // FIXME: A fix was applied using toFixed(2). // FIXME: A coverture check must be done to ensure that all scenarios are covered const it = ReversePairwise(children.filter(child => child !== container)); for (const { cur, next } of it) { const hasSpaceBetween = next.properties.y + next.properties.height < cur.properties.y; if (hasSpaceBetween) { lastContainer = cur; space = cur.properties.y - (next.properties.y + next.properties.height); break; } } if (lastContainer === null) { // no space between break; } const indexLastContainer = children.indexOf(lastContainer); for (let i = indexLastContainer; i <= children.length - 2; i++) { const sibling = children[i]; sibling.properties.y -= space; } } const hasNoSpaceBetween = lastContainer === null; if (hasNoSpaceBetween) { // test gap between the left of the parent and the first container space = children[0].properties.y; if (space > 0) { for (let i = 0; i <= children.length - 2; i++) { const sibling = children[i]; sibling.properties.y -= space; } return container; } } return container; }