import Swal from 'sweetalert2'; import { IContainerModel } from '../../../Interfaces/IContainerModel'; import { Simplex } from '../../../utils/simplex'; import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg'; interface IFlexibleGroup { group: IContainerModel[] offset: number size: number } /** * 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 flexibleGroups = GetFlexibleGroups(container.parent); for (const flexibleGroup of flexibleGroups) { FlexGroup(flexibleGroup); } } /** * Apply flex to the group * @param flexibleGroup Group that contains a list of flexible containers * @returns */ function FlexGroup(flexibleGroup: IFlexibleGroup): void { const children = flexibleGroup.group; const { flexibleContainers, nonFlexibleContainers } = SeparateFlexibleContainers(children); const minWidths = flexibleContainers .map(sibling => sibling.properties.minWidth); const fixedWidth = nonFlexibleContainers .map(sibling => sibling.properties.width) .reduce((widthSum, a) => widthSum + a, 0); const requiredMaxWidth = flexibleGroup.size - fixedWidth; const minimumPossibleWidth = minWidths.reduce((widthSum, a) => widthSum + a, 0); // sum(minWidths) const checkSumMinWidthsIsFitting = minimumPossibleWidth > requiredMaxWidth; if (checkSumMinWidthsIsFitting) { Swal.fire({ icon: 'error', title: 'Cannot fit!', text: 'Cannot fit at all even when squeezing all flex containers to the minimum.' }); throw new Error('[FlexBehavior] Cannot fit at all even when squeezing all flex containers to the minimum.'); } 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 = flexibleGroup.offset; for (const sibling of 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 maxWidths = flexibleContainers .map(sibling => sibling.properties.maxWidth); const solutions: number[] = Simplex(minWidths, maxWidths, 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 = flexibleGroup.offset; for (const sibling of children) { sibling.properties.x = ApplyXMargin(right, sibling.properties.margin.left); right += sibling.properties.width; } } function SeparateFlexibleContainers( containers: IContainerModel[] ): { flexibleContainers: IContainerModel[], nonFlexibleContainers: IContainerModel[] } { const flexibleContainers: IContainerModel[] = []; const nonFlexibleContainers: IContainerModel[] = []; containers.forEach((container) => { if (container.properties.isFlex) { flexibleContainers.push(container); return; } nonFlexibleContainers.push(container); }); return { flexibleContainers, nonFlexibleContainers }; } /** * Returns a list of groups of flexible containers * @param parent Parent in which the flexible children will be set in groups * @returns a list of groups of flexible containers */ export function GetFlexibleGroups(parent: IContainerModel): IFlexibleGroup[] { const flexibleGroups: IFlexibleGroup[] = []; let group: IContainerModel[] = []; let offset = 0; let size = 0; for (const child of parent.children) { if (child.properties.isAnchor) { size = child.properties.x - offset; const flexibleGroup: IFlexibleGroup = { group, offset, size }; flexibleGroups.push(flexibleGroup); offset = child.properties.x + child.properties.width; group = []; continue; } group.push(child); } size = parent.properties.width - offset; const flexibleGroup: IFlexibleGroup = { group, offset, size }; flexibleGroups.push(flexibleGroup); return flexibleGroups; }