import Swal from 'sweetalert2'; import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { reversePairwise } from '../../../utils/itertools'; import { Simplex } from '../../../utils/simplex'; import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg'; /** * Try to push the siblings * @param container * @returns */ export function PushContainers(container: IContainerModel): IContainerModel { if (container.parent === null) { return container; } if (container.parent.children.length <= 1) { return container; } const prevIndex = container.parent.children.length - 2; const prev: IContainerModel = container.parent.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(container.parent.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 = container.parent.children.indexOf(lastContainer); for (let i = indexLastContainer; i <= container.parent.children.length - 2; i++) { const sibling = container.parent.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 = container.parent.children[0].properties.x; if (space > 0) { for (let i = 0; i <= container.parent.children.length - 2; i++) { const sibling = container.parent.children[i]; sibling.properties.x -= space; } return container; } } Flex(container); return container; } /** * Flex the container and its siblings (mutate) * @param container Container to flex * @returns Flexed container */ export function Flex(container: IContainerModel): void { if (container.parent === null || container.parent === undefined) { return; } const flexibleContainers = container.parent.children .filter(sibling => sibling.properties.isFlex); const minWidths = flexibleContainers .map(sibling => sibling.properties.minWidth); const fixedWidth = container.parent.children .filter(sibling => !sibling.properties.isFlex) .map(sibling => sibling.properties.width) .reduce((partialSum, a) => partialSum + a, 0); const requiredMaxWidth = container.parent.properties.width - fixedWidth; const minimumPossibleWidth = minWidths.reduce((partialSum, a) => partialSum + a, 0); if (minimumPossibleWidth > requiredMaxWidth) { Swal.fire({ icon: 'error', title: 'Cannot fit!', text: 'Cannot fit at all even when squeezing all flex containers to the minimum.' }); return; } const maxMinWidths = Math.max(...minWidths); if (maxMinWidths * minWidths.length < requiredMaxWidth) { const wantedWidth = requiredMaxWidth / minWidths.length; // it fits, flex with maxMinWidths and fixed width let right = 0; for (const sibling of container.parent.children) { if (!sibling.properties.isFlex) { sibling.properties.x = right; right += sibling.properties.width; continue; } sibling.properties.x = ApplyXMargin(right, sibling.properties.margin.left); sibling.properties.width = ApplyWidthMargin(wantedWidth, sibling.properties.margin.left, sibling.properties.margin.right); right += wantedWidth; } return; } // does not fit /// SIMPLEX /// const solutions: number[] = Simplex(minWidths, requiredMaxWidth); // apply the solutions for (let i = 0; i < flexibleContainers.length; i++) { flexibleContainers[i].properties.width = ApplyWidthMargin(solutions[i], flexibleContainers[i].properties.margin.left, flexibleContainers[i].properties.margin.right) } // move the containers let right = 0; for (const sibling of container.parent.children) { sibling.properties.x = ApplyXMargin(right, sibling.properties.margin.left); right += sibling.properties.width; } }