Merged PR 169: Fix bugs with flex + Disable obnoxious swals + Add selector text + Sort SVG scss to different files

- Disable PushBehavior and set it in a different file
- Fix behviors when parent === undefined
- Fix flex behavrios when using anchors
- Fix siblings not applying flex to theirs children on container delete
- Fix flex behavior when using anchors
- Enable flex by default
- Disable obnoxious swals
- Add selector text
- Sort SVG scss to different files

Others: Add some todos
This commit is contained in:
Eric Nguyen 2022-08-26 13:59:03 +00:00
parent a718268972
commit 3f58c5ba5e
15 changed files with 208 additions and 134 deletions

View file

@ -90,7 +90,7 @@ export function DeleteContainer(
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
}
Flex(container);
ApplyBehaviorsOnSiblings(container, current.Symbols);
// Select the previous container
// or select the one above
@ -275,8 +275,8 @@ export function AddContainer(
setHistoryCurrentStep(history.length - 1);
}
function UpdateParentChildrenList(parentClone: IContainerModel | null): void {
if (parentClone === null) {
function UpdateParentChildrenList(parentClone: IContainerModel | null | undefined): void {
if (parentClone === null || parentClone === undefined) {
return;
}
parentClone.children.sort(

View file

@ -3,6 +3,7 @@ import { IConfiguration } from '../../../Interfaces/IConfiguration';
import { getCircularReplacer } from '../../../utils/saveload';
import { ID } from '../../SVG/SVG';
import { IEditorState } from '../../../Interfaces/IEditorState';
import { SHOW_SELECTOR_TEXT } from '../../../utils/default';
export function SaveEditorAsJSON(
history: IHistoryState[],
@ -54,6 +55,9 @@ export function SaveEditorAsSVG(): void {
// remove the selector
const group = svg.children[svg.children.length - 1];
group.removeChild(group.children[group.children.length - 1]);
if (SHOW_SELECTOR_TEXT) {
group.removeChild(group.children[group.children.length - 1]);
}
// get svg source.
const serializer = new XMLSerializer();

View file

@ -17,9 +17,7 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
ApplyAnchor(container);
}
if (container.properties.isFlex) {
Flex(container);
}
Flex(container);
ApplyRigidBody(container);

View file

@ -1,77 +1,12 @@
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<IContainerModel>(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;
interface IFlexibleGroup {
group: IContainerModel[]
offset: number
size: number
}
/**
@ -83,26 +18,37 @@ export function Flex(container: IContainerModel): void {
if (container.parent === null || container.parent === undefined) {
return;
}
const flexibleContainers = container.parent.children
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 = container.parent.children
const fixedWidth = 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 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.'
});
// 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;
}
@ -110,8 +56,8 @@ export function Flex(container: IContainerModel): void {
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) {
let right = flexibleGroup.offset;
for (const sibling of children) {
if (!sibling.properties.isFlex) {
sibling.properties.x = right;
right += sibling.properties.width;
@ -132,13 +78,46 @@ export function Flex(container: IContainerModel): void {
// 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)
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) {
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;
}

View file

@ -0,0 +1,73 @@
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { reversePairwise } from '../../../utils/itertools';
import { Flex } from './FlexBehaviors';
/**
* 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<IContainerModel>(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;
}

View file

@ -9,7 +9,6 @@
import Swal from 'sweetalert2';
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { ISizePointer } from '../../../Interfaces/ISizePointer';
import { PushContainers } from './FlexBehaviors';
/**
* "Transform the container into a rigid body"
@ -24,7 +23,6 @@ export function ApplyRigidBody(
container: IContainerModel
): IContainerModel {
container = constraintBodyInsideParent(container);
container = PushContainers(container);
container = constraintBodyInsideUnallocatedWidth(container);
return container;
}
@ -118,7 +116,7 @@ function constraintBodyInsideSpace(
export function constraintBodyInsideUnallocatedWidth(
container: IContainerModel
): IContainerModel {
if (container.parent === null) {
if (container.parent === null || container.parent === undefined) {
return container;
}
@ -177,14 +175,14 @@ export function constraintBodyInsideUnallocatedWidth(
if (availableWidth === undefined) {
console.warn(`Container ${container.properties.id} cannot fit in any space due to its minimum width being to large. Consequently, its rigid body property is disabled.`);
Swal.fire({
position: 'top-end',
title: `Container ${container.properties.id} cannot fit!`,
text: 'Its rigid body property is now disabled. Change its the minimum width or free the parent container.',
timerProgressBar: true,
showConfirmButton: false,
timer: 5000
});
// Swal.fire({
// position: 'top-end',
// title: `Container ${container.properties.id} cannot fit!`,
// text: 'Its rigid body property is now disabled. Change its the minimum width or free the parent container.',
// timerProgressBar: true,
// showConfirmButton: false,
// timer: 5000
// });
return container;
}

View file

@ -4,21 +4,4 @@ svg {
width: 100%;
}
text {
font-size: 18px;
font-weight: 800;
fill: none;
fill-opacity: 0;
stroke: #000000;
stroke-width: 1px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 1;
transform: translateX(-50%);
transform-box: fill-box;
}
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}