Merged PR 175: Implement drag drop

- [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
This commit is contained in:
Eric Nguyen 2022-09-05 07:56:45 +00:00
parent 4d4ecd67d0
commit 353f461f4b
13 changed files with 220 additions and 59 deletions

View file

@ -6,7 +6,7 @@ import { GetCurrentHistory, UpdateCounters } from '../Editor';
import { AddMethod } from '../../../Enums/AddMethod';
import { IAvailableContainer } from '../../../Interfaces/IAvailableContainer';
import { GetDefaultContainerProps, DEFAULTCHILDTYPE_ALLOW_CYCLIC, DEFAULTCHILDTYPE_MAX_DEPTH } from '../../../utils/default';
import { ApplyBehaviors, ApplyBehaviorsOnSiblings } from '../Behaviors/Behaviors';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import Swal from 'sweetalert2';
import { ApplyMargin, TransformX } from '../../../utils/svg';
@ -83,7 +83,7 @@ export function DeleteContainer(
throw new Error('[DeleteContainer] Could not find container among parent\'s children');
}
ApplyBehaviorsOnSiblings(container, current.symbols);
ApplyBehaviorsOnSiblingsChildren(container, current.symbols);
// Select the previous container
// or select the one above
@ -285,7 +285,7 @@ export function AddContainers(
ApplyBehaviors(newContainer, current.symbols);
// Then, apply the behaviors on its siblings (mostly for flex)
ApplyBehaviorsOnSiblings(newContainer, current.symbols);
ApplyBehaviorsOnSiblingsChildren(newContainer, current.symbols);
// Sort the parent children by x
UpdateParentChildrenList(parentClone);
@ -547,7 +547,7 @@ function SetContainer(
ApplyBehaviors(container, symbols);
// Apply special behaviors on siblings
ApplyBehaviorsOnSiblings(container, symbols);
ApplyBehaviorsOnSiblingsChildren(container, symbols);
// sort the children list by their position
UpdateParentChildrenList(container.parent);

View file

@ -5,7 +5,7 @@ import { ISymbolModel } from '../../../Interfaces/ISymbolModel';
import { GetDefaultSymbolModel } from '../../../utils/default';
import { FindContainerById } from '../../../utils/itertools';
import { RestoreX } from '../../../utils/svg';
import { ApplyBehaviors, ApplyBehaviorsOnSiblings } from '../Behaviors/Behaviors';
import { ApplyBehaviors, ApplyBehaviorsOnSiblingsChildren } from '../Behaviors/Behaviors';
import { GetCurrentHistory, UpdateCounters } from '../Editor';
export function AddSymbol(
@ -150,7 +150,7 @@ export function OnPropertyChange(
ApplyBehaviors(container, newSymbols);
ApplyBehaviorsOnSiblings(container, newSymbols);
ApplyBehaviorsOnSiblingsChildren(container, newSymbols);
});
history.push({

View file

@ -44,7 +44,7 @@ export function ApplyAnchor(container: IContainerModel): IContainerModel {
* @param containers A list of containers
* @returns A list of overlapping containers
*/
function GetOverlappingContainers(
export function GetOverlappingContainers(
container: IContainerModel,
containers: IContainerModel[]
): IContainerModel[] {

View file

@ -4,6 +4,7 @@ import { APPLY_BEHAVIORS_ON_CHILDREN } from '../../../utils/default';
import { ApplyAnchor } from './AnchorBehaviors';
import { Flex } from './FlexBehaviors';
import { ApplyRigidBody } from './RigidBodyBehaviors';
import { ApplySwap } from './SwapBehaviors';
import { ApplySymbol } from './SymbolBehaviors';
/**
@ -13,28 +14,26 @@ import { ApplySymbol } from './SymbolBehaviors';
* @returns Updated container
*/
export function ApplyBehaviors(container: IContainerModel, symbols: Map<string, ISymbolModel>): IContainerModel {
try {
const symbol = symbols.get(container.properties.linkedSymbolId);
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
ApplySymbol(container, symbol);
const symbol = symbols.get(container.properties.linkedSymbolId);
if (container.properties.linkedSymbolId !== '' && symbol !== undefined) {
ApplySymbol(container, symbol);
}
if (container.properties.isAnchor) {
ApplyAnchor(container);
}
ApplySwap(container);
Flex(container);
ApplyRigidBody(container);
if (APPLY_BEHAVIORS_ON_CHILDREN) {
// Apply DFS by recursion
for (const child of container.children) {
ApplyBehaviors(child, symbols);
}
if (container.properties.isAnchor) {
ApplyAnchor(container);
}
Flex(container);
ApplyRigidBody(container);
if (APPLY_BEHAVIORS_ON_CHILDREN) {
// Apply DFS by recursion
for (const child of container.children) {
ApplyBehaviors(child, symbols);
}
}
} catch (error) {
console.debug(error);
}
return container;
@ -46,7 +45,7 @@ export function ApplyBehaviors(container: IContainerModel, symbols: Map<string,
* @param symbols
* @returns
*/
export function ApplyBehaviorsOnSiblings(newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
export function ApplyBehaviorsOnSiblingsChildren(newContainer: IContainerModel, symbols: Map<string, ISymbolModel>): void {
if (newContainer.parent === null || newContainer.parent === undefined) {
return;
}
@ -57,6 +56,8 @@ export function ApplyBehaviorsOnSiblings(newContainer: IContainerModel, symbols:
return;
}
ApplyBehaviors(container, symbols);
for (const child of container.children) {
ApplyBehaviors(child, symbols);
}
});
}

View file

@ -1,3 +1,4 @@
import Swal from 'sweetalert2';
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { Simplex } from '../../../utils/simplex';
import { ApplyWidthMargin, ApplyXMargin } from '../../../utils/svg';
@ -49,16 +50,16 @@ function FlexGroup(flexibleGroup: IFlexibleGroup): void {
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.'
// });
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) {
if (maxMinWidths * minWidths.length <= requiredMaxWidth) {
const wantedWidth = requiredMaxWidth / minWidths.length;
// it fits, flex with maxMinWidths and fixed width
let right = flexibleGroup.offset;
@ -79,7 +80,9 @@ function FlexGroup(flexibleGroup: IFlexibleGroup): void {
// does not fit
/// SIMPLEX ///
const solutions: number[] = Simplex(minWidths, requiredMaxWidth);
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++) {

View file

@ -0,0 +1,31 @@
/**
* Swap two flex container when one is overlapping another
*/
import { IContainerModel } from '../../../Interfaces/IContainerModel';
import { GetOverlappingContainers } from './AnchorBehaviors';
export function ApplySwap(container: IContainerModel): void {
if (container.parent === null || container.parent === undefined) {
return;
}
const children = container.parent.children;
const overlappingContainers = GetOverlappingContainers(container, children);
if (overlappingContainers.length > 1 || overlappingContainers.length === 0) {
return;
}
const overlappingContainer = overlappingContainers.pop();
if (overlappingContainer === null || overlappingContainer === undefined) {
return;
}
// swap positions
[overlappingContainer.properties.x, container.properties.x] = [container.properties.x, overlappingContainer.properties.x];
const indexContainer = children.indexOf(container);
const indexOverlapping = children.indexOf(overlappingContainer);
[children[indexContainer], children[indexOverlapping]] = [children[indexOverlapping], children[indexContainer]];
}

View file

@ -4,7 +4,7 @@ import { IConfiguration } from '../../Interfaces/IConfiguration';
import { SVG } from '../SVG/SVG';
import { IHistoryState } from '../../Interfaces/IHistoryState';
import { UI } from '../UI/UI';
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, OnPropertyChange, AddContainers } from './Actions/ContainerOperations';
import { SelectContainer, DeleteContainer, AddContainerToSelectedContainer, OnPropertyChange, AddContainer } from './Actions/ContainerOperations';
import { SaveEditorAsJSON, SaveEditorAsSVG } from './Actions/Save';
import { OnKey } from './Actions/Shortcuts';
import { events as EVENTS } from '../../Events/EditorEvents';
@ -240,6 +240,16 @@ export function Editor(props: IEditorProps): JSX.Element {
setNewHistory(newHistory);
}
}}
addContainerAt={(index, type, parent) => setNewHistory(
AddContainer(
index,
type,
parent,
configuration,
history,
historyCurrentStep
)
)}
addSymbol={(type) => setNewHistory(
AddSymbol(
type,