- [x] Implement drag drop to create an element at a specific index - Add Swap behavrior - Implement max contraints with simplex ~~- [ ] Implement drag drop to swap two container that flex~~ - Fixes tries number for simplex it can now go up to 2 * number of containers - Fixes flex calling another flex behavior when not needed (remember that flex behavior is the only behavior that needs to communicate with siblings) - Fix max width being ignored in input group
155 lines
4.6 KiB
TypeScript
155 lines
4.6 KiB
TypeScript
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;
|
|
}
|