Skip to content

Commit

Permalink
feat: Modified layers sorting functionality after adding f-group
Browse files Browse the repository at this point in the history
  • Loading branch information
siarheihuzarevich committed Sep 7, 2024
1 parent 65902f5 commit 3c60249
Show file tree
Hide file tree
Showing 52 changed files with 552 additions and 250 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ClearSelectionExecution } from './clear-selection.execution';
import { ISelectable } from '../../f-connection';

export const MOCK_SELECTABLE_ITEM: ISelectable = {
fId: '1',
fSelectionDisabled: false,
hostElement: document.createElement('svg'),
select: jasmine.createSpy('select'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Injectable } from '@angular/core';
import { GetChildrenNodesRequest } from './get-children-nodes.request';
import { FExecutionRegister, IExecution } from '../../../infrastructure';
import { FNodeBase } from '../../../f-node';
import { FComponentsStore } from '../../../f-storage';
import { GetDeepChildrenNodesAndGroupsRequest } from './get-deep-children-nodes-and-groups.request';
import { FComponentsStore } from '../../f-storage';
import { FNodeBase } from '../../f-node';
import { FExecutionRegister, IExecution } from '../../infrastructure';

@Injectable()
@FExecutionRegister(GetChildrenNodesRequest)
export class GetChildrenNodesExecution
implements IExecution<GetChildrenNodesRequest, FNodeBase[]> {
@FExecutionRegister(GetDeepChildrenNodesAndGroupsRequest)
export class GetDeepChildrenNodesAndGroupsExecution
implements IExecution<GetDeepChildrenNodesAndGroupsRequest, FNodeBase[]> {

constructor(
private fComponentsStore: FComponentsStore
) {
}

public handle(request: GetChildrenNodesRequest): FNodeBase[] {
public handle(request: GetDeepChildrenNodesAndGroupsRequest): FNodeBase[] {
return this.getChildrenNodes(request.fId);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class GetDeepChildrenNodesAndGroupsRequest {

constructor(
public fId: string
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './get-deep-children-nodes-and-groups.execution';

export * from './get-deep-children-nodes-and-groups.request';
6 changes: 4 additions & 2 deletions projects/f-flow/src/domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export * from './emit-transform-changes';

export * from './get-can-be-selected-items';

export * from './get-deep-children-nodes-and-groups';

export * from './get-connection-line';

export * from './get-element-rect-in-flow';
Expand All @@ -32,13 +34,13 @@ export * from './select-all';

export * from './select-and-update-node-layer';

export * from './sort-items-layer';
export * from './sort-item-layers';

export * from './subscribe-on-transform-changes';

export * from './intersections';

export * from './update-item-layer';
export * from './update-item-and-children-layers';

export * from './cast-to-enum';

Expand Down
21 changes: 16 additions & 5 deletions projects/f-flow/src/domain/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import { GetNodesRectExecution } from './get-nodes-rect';
import { GetElementRectInFlowExecution } from './get-element-rect-in-flow';
import { GetInputRectInFlowExecution } from './get-input-rect-in-flow';
import { SelectExecution } from './select';
import { UpdateItemLayerExecution } from './update-item-layer';
import {
MoveFrontElementsBeforeTargetElementExecution,
UpdateItemAndChildrenLayersExecution
} from './update-item-and-children-layers';
import { GetPositionInFlowExecution } from './get-position-in-flow';
import { CreateConnectionMarkersExecution } from './create-connection-markers';
import { GetCanBeSelectedItemsExecution } from './get-can-be-selected-items';
Expand All @@ -17,8 +20,8 @@ import { SelectAndUpdateNodeLayerExecution } from './select-and-update-node-laye
import { GetScaledNodeRectsWithFlowPositionExecution } from './get-scaled-node-rects-with-flow-position';
import { EmitTransformChangesExecution } from './emit-transform-changes';
import { SubscribeOnTransformChangesExecution } from './subscribe-on-transform-changes';
import { SortItemsLayerExecution } from './sort-items-layer';

import { SortItemLayersExecution, SortItemsByParentExecution, SortNodeLayersExecution } from './sort-item-layers';
import { GetDeepChildrenNodesAndGroupsExecution } from './get-deep-children-nodes-and-groups';

export const COMMON_PROVIDERS = [

Expand All @@ -30,6 +33,8 @@ export const COMMON_PROVIDERS = [

GetCanBeSelectedItemsExecution,

GetDeepChildrenNodesAndGroupsExecution,

GetConnectionLineExecution,

GetElementRectInFlowExecution,
Expand All @@ -54,11 +59,17 @@ export const COMMON_PROVIDERS = [

SelectAndUpdateNodeLayerExecution,

SortItemsLayerExecution,
SortItemLayersExecution,

SortItemsByParentExecution,

SortNodeLayersExecution,

SubscribeOnTransformChangesExecution,

UpdateItemLayerExecution,
UpdateItemAndChildrenLayersExecution,

MoveFrontElementsBeforeTargetElementExecution,

GetInputRectInFlowExecution,
];
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Injectable } from '@angular/core';
import { FExecutionRegister, FFlowMediator } from '../../infrastructure';
import { FNodeBase } from '../../f-node';
import { FDraggableDataContext } from '../../f-draggable';
import { UpdateItemLayerRequest } from '../update-item-layer';
import { UpdateItemAndChildrenLayersRequest } from '../update-item-and-children-layers';

@Injectable()
@FExecutionRegister(SelectAndUpdateNodeLayerRequest)
Expand All @@ -19,7 +19,7 @@ export class SelectAndUpdateNodeLayerExecution implements IHandler<SelectAndUpda
this.selectNodeIfNotSelected(request.node);

this.fMediator.send<void>(
new UpdateItemLayerRequest(request.node, request.node.hostElement.parentElement as HTMLElement)
new UpdateItemAndChildrenLayersRequest(request.node, request.node.hostElement.parentElement as HTMLElement)
);
}

Expand Down
7 changes: 7 additions & 0 deletions projects/f-flow/src/domain/sort-item-layers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './sort-node-layers-by-groups';

export * from './sort-items-by-parent';

export * from './sort-item-layers.execution';

export * from './sort-item-layers.request';
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { TestBed } from '@angular/core/testing';
import {
FCanvasBase,
FComponentsStore,
FFlowMediator,
FNodeBase, GetDeepChildrenNodesAndGroupsExecution, SortItemLayersExecution, SortItemLayersRequest, SortItemsByParentExecution,
SortNodeLayersExecution,
} from '@foblex/flow';
import { setupTestModule } from '../test-setup';

function createNode(id: string, element: HTMLElement, parentId?: string): FNodeBase {
return {
fId: id,
fParentId: parentId,
hostElement: element,
} as FNodeBase;
}

function getFCanvasBase(): FCanvasBase {
return {
fGroupsContainer: {
nativeElement: document.createElement('div') as HTMLElement,
},
fNodesContainer: {
nativeElement: document.createElement('div') as HTMLElement,
}
} as FCanvasBase;
}

function createElementWithId(id: string): HTMLElement {
const element = document.createElement('div');
element.id = id;
return element;
}

describe('SortItemLayersExecution, SortNodeLayersByGroups, SortItemsByParent', () => {
let fMediator: FFlowMediator;
let fComponentsStore: FComponentsStore;

beforeEach(() => {
setupTestModule([ SortItemLayersExecution, SortItemsByParentExecution, GetDeepChildrenNodesAndGroupsExecution, SortNodeLayersExecution ]);
fMediator = TestBed.inject(FFlowMediator) as jasmine.SpyObj<FFlowMediator>;
fComponentsStore = TestBed.inject(FComponentsStore);
});

it('should sort nodes and groups by parents', () => {
fComponentsStore.fCanvas = getFCanvasBase();

const group1 = createNode('group1', createElementWithId('group1'), 'group2');
const group2 = createNode('group2', createElementWithId('group2'));
fComponentsStore.fCanvas.fGroupsContainer.nativeElement.append(group1.hostElement, group2.hostElement);

const node3 = createNode('node3', createElementWithId('node3'), 'node2');
const node1 = createNode('node1', createElementWithId('node1'), 'group1');
const node2 = createNode('node2', createElementWithId('node2'), 'group2');
fComponentsStore.fCanvas.fNodesContainer.nativeElement.append(node1.hostElement, node2.hostElement, node3.hostElement);

fComponentsStore.fNodes = [ group1, group2, node1, node2, node3 ];

fMediator.send(new SortItemLayersRequest());

expect(fComponentsStore.fCanvas.fNodesContainer.nativeElement.children.item(0)).toEqual(node2.hostElement);
expect(fComponentsStore.fCanvas.fNodesContainer.nativeElement.children.item(1)).toEqual(node3.hostElement);
expect(fComponentsStore.fCanvas.fNodesContainer.nativeElement.children.item(2)).toEqual(node1.hostElement);

expect(fComponentsStore.fCanvas.fGroupsContainer.nativeElement.children.item(0)).toEqual(group2.hostElement);
expect(fComponentsStore.fCanvas.fGroupsContainer.nativeElement.children.item(1)).toEqual(group1.hostElement);
});

it('should do nothing if there are nothing to sort', () => {
fComponentsStore.fCanvas = getFCanvasBase();

const group1 = createNode('group1', createElementWithId('group1'));
const group2 = createNode('group2', createElementWithId('group2'));
fComponentsStore.fCanvas.fGroupsContainer.nativeElement.append(group1.hostElement, group2.hostElement);

const node1 = createNode('node1', createElementWithId('node1'));
const node2 = createNode('node2', createElementWithId('node2'));
fComponentsStore.fCanvas.fNodesContainer.nativeElement.append(node1.hostElement, node2.hostElement);

fComponentsStore.fNodes = [ group1, group2, node1, node2 ];

fMediator.send(new SortItemLayersRequest());

expect(fComponentsStore.fCanvas.fNodesContainer.nativeElement.children.item(0)).toEqual(node1.hostElement);
expect(fComponentsStore.fCanvas.fNodesContainer.nativeElement.children.item(1)).toEqual(node2.hostElement);

expect(fComponentsStore.fCanvas.fGroupsContainer.nativeElement.children.item(0)).toEqual(group1.hostElement);
expect(fComponentsStore.fCanvas.fGroupsContainer.nativeElement.children.item(1)).toEqual(group2.hostElement);
});

it('should sort nodes and groups by parents and ignore items with mistakes in parent id', () => {
fComponentsStore.fCanvas = getFCanvasBase();

const group1 = createNode('group1', createElementWithId('group1'), 'node1');
const group2 = createNode('group2', createElementWithId('group2'), 'group1');
fComponentsStore.fCanvas.fGroupsContainer.nativeElement.append(group1.hostElement, group2.hostElement);

const node1 = createNode('node1', createElementWithId('node1'), 'group3');
const node2 = createNode('node2', createElementWithId('node2'), 'group4');
fComponentsStore.fCanvas.fNodesContainer.nativeElement.append(node2.hostElement, node1.hostElement);

fComponentsStore.fNodes = [ group1, group2, node1, node2 ];

fMediator.send(new SortItemLayersRequest());

expect(fComponentsStore.fCanvas.fNodesContainer.nativeElement.children.item(0)).toEqual(node2.hostElement);
expect(fComponentsStore.fCanvas.fNodesContainer.nativeElement.children.item(1)).toEqual(node1.hostElement);

expect(fComponentsStore.fCanvas.fGroupsContainer.nativeElement.children.item(0)).toEqual(group1.hostElement);
expect(fComponentsStore.fCanvas.fGroupsContainer.nativeElement.children.item(1)).toEqual(group2.hostElement);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { SortItemLayersRequest } from './sort-item-layers.request';
import { Injectable } from '@angular/core';
import { FExecutionRegister, FFlowMediator, IExecution } from '../../infrastructure';
import { SortNodeLayersRequest } from './sort-node-layers-by-groups';
import { SortItemsByParentRequest } from './sort-items-by-parent';
import { FComponentsStore } from '../../f-storage';

@Injectable()
@FExecutionRegister(SortItemLayersRequest)
export class SortItemLayersExecution implements IExecution<SortItemLayersRequest, void> {

constructor(
private fMediator: FFlowMediator,
private fComponentsStore: FComponentsStore,
) {
}

public handle(request: SortItemLayersRequest): void {
this.fMediator.send(new SortItemsByParentRequest(this.fComponentsStore.fCanvas!.fGroupsContainer.nativeElement));
this.fMediator.send(new SortNodeLayersRequest());
this.fMediator.send(new SortItemsByParentRequest(this.fComponentsStore.fCanvas!.fNodesContainer.nativeElement));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class SortItemLayersRequest {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './sort-items-by-parent.execution';

export * from './sort-items-by-parent.request';
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { SortItemsByParentRequest } from './sort-items-by-parent.request';
import { Injectable } from '@angular/core';
import { WindowService } from '@foblex/core';
import { FExecutionRegister, FFlowMediator, IExecution } from '../../../infrastructure';
import { FComponentsStore } from '../../../f-storage';
import { FNodeBase } from '../../../f-node';
import { GetDeepChildrenNodesAndGroupsRequest } from '../../get-deep-children-nodes-and-groups';

@Injectable()
@FExecutionRegister(SortItemsByParentRequest)
export class SortItemsByParentExecution implements IExecution<SortItemsByParentRequest, void> {

private fItemsContainer!: HTMLElement;

private get fItemsFromContainer(): HTMLElement[] {
return Array.from(this.fItemsContainer.children) as HTMLElement[];
}

constructor(
private fComponentsStore: FComponentsStore,
private fMediator: FFlowMediator,
private windowService: WindowService
) {
}

public handle(request: SortItemsByParentRequest): void {
this.fItemsContainer = request.fItemsContainer;
this.getItems().forEach((parent: FNodeBase) => {
this.moveChildrenItems(this.getSortedChildrenItems(parent), parent);
});
}

private getItems(): FNodeBase[] {
return this.fComponentsStore.fNodes.filter((x) => this.fItemsContainer.contains(x.hostElement));
}

private getSortedChildrenItems(
parent: FNodeBase,
): HTMLElement[] {
const allElements = this.fItemsFromContainer;
const parentIndex = allElements.indexOf(parent.hostElement);
return this.getChildrenGroups(parent.fId)
.filter((child: HTMLElement) => allElements.indexOf(child) < parentIndex)
.sort((a, b) => allElements.indexOf(a) - allElements.indexOf(b));
}

private getChildrenGroups(fId: string): HTMLElement[] {
return this.fMediator.send<FNodeBase[]>(new GetDeepChildrenNodesAndGroupsRequest(fId))
.filter((x) => this.fItemsContainer.contains(x.hostElement)).map((x) => x.hostElement);
}

private moveChildrenItems(
sortedChildrenItems: HTMLElement[],
parent: FNodeBase,
): void {
let nextSibling = parent.hostElement.nextElementSibling;

const fragment = this.windowService.getWindow().document.createDocumentFragment();

sortedChildrenItems.forEach((child: HTMLElement) => {
fragment.appendChild(child); // Append automatically removes the element from its current position
});
this.fItemsContainer.insertBefore(fragment, nextSibling);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class SortItemsByParentRequest {

constructor(
public fItemsContainer: HTMLElement // fGroupsContainer || fNodesContainer
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './sort-node-layers.execution';

export * from './sort-node-layers.request';
Loading

0 comments on commit 3c60249

Please sign in to comment.