122 lines
3.6 KiB
TypeScript
122 lines
3.6 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
|
|
function FlexGroup(flexibleGroup: IFlexibleGroup): void {
|
|
const children = flexibleGroup.group;
|
|
const flexibleContainers = children
|
|
.filter(sibling => sibling.properties.isFlex);
|
|
|
|
const minWidths = flexibleContainers
|
|
.map(sibling => sibling.properties.minWidth);
|
|
|
|
const fixedWidth = children
|
|
.filter(sibling => !sibling.properties.isFlex)
|
|
.map(sibling => sibling.properties.width)
|
|
.reduce((partialSum, a) => partialSum + a, 0);
|
|
|
|
const requiredMaxWidth = flexibleGroup.size - 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.'
|
|
// });
|
|
console.error('[FlexBehavior] 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 = 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 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 = flexibleGroup.offset;
|
|
for (const sibling of children) {
|
|
sibling.properties.x = ApplyXMargin(right, sibling.properties.margin.left);
|
|
right += sibling.properties.width;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|