Skip to content

Commit

Permalink
Merge pull request #77 from ditrit/feature/container_filter
Browse files Browse the repository at this point in the history
Feature: Container filter
  • Loading branch information
Zorin95670 authored Oct 4, 2022
2 parents 231f3dd + 79aa6d4 commit 2fdc68e
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 28 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Demo of `DefaultDrawer`.
- Add feature to drag and drop component into container components in `DefaultDrawer`.
- Install `interactjs` library.
- Added a feature to filter the components that can enter a container.
- Add new attribute `childrenTypes` in the `ComponentDefinition` class.

## Changed

Expand Down
10 changes: 10 additions & 0 deletions demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion demo/src/assets/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const networkDefinition = new ComponentDefinition({
type: 'network',
icon: 'network',
model: 'DefaultContainer',
parentTypes: [],
parentTypes: ['network'],
childrenTypes: ['server', 'network'],
definedAttributes: [nameAttributeDefinition],
isContainer: true,
});
Expand Down
55 changes: 31 additions & 24 deletions src/draw/DefaultDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,11 @@ class DefaultDrawer {
setComponentAction(components) {
this.__drag();
this.__dropToRoot(components);
this.__dropToContainer(components);
components.forEach((component) => {
if (component.definition.isContainer) {
this.__dropToContainer(components, component);
}
});
}

/**
Expand Down Expand Up @@ -200,14 +204,18 @@ class DefaultDrawer {
/**
* Set action to drop component in a container component.
* @param {Component[]} components - Array containing components.
* @param {Component} component - Component to set drop action.
* @private
*/
__dropToContainer(components) {
__dropToContainer(components, component) {
let invalidDropzones;
let currentComponent;
this.interact('.isContainer').unset();
this.interact('.isContainer').dropzone({
accept: '.component',
const validChildren = component.definition.childrenTypes.length === 0
? '.component'
: component.definition.childrenTypes.map((el) => `.${el}`).join(', ');
this.interact(`#${component.id}`).unset();
this.interact(`#${component.id}`).dropzone({
accept: validChildren,
overlap: 'pointer',
ondropactivate: (event) => {
currentComponent = this.d3.select(`#${event.relatedTarget.id}`);
Expand All @@ -217,33 +225,32 @@ class DefaultDrawer {
}
},
ondrop: (event) => {
if (!invalidDropzones.includes(event.target.id)) {
const dropzone = this.d3.select(`#${event.target.id}`).datum();
if (invalidDropzones.includes(event.target.id)) { return; }
const dropzone = this.d3.select(`#${event.target.id}`).datum();

if ([...currentComponent.node().classList].includes(this.rootId)) {
dropzone.children.push(currentComponent.datum());
if ([...currentComponent.node().classList].includes(this.rootId)) {
dropzone.children.push(currentComponent.datum());

for (let i = 0; i < components.length; i += 1) {
if (components[i].id === currentComponent.datum().id) {
components.splice(i, 1);
break;
}
for (let i = 0; i < components.length; i += 1) {
if (components[i].id === currentComponent.datum().id) {
components.splice(i, 1);
break;
}
}

this.__resetDrawOption(components, currentComponent);
} else if (currentComponent.node().classList[0] !== event.target.id) {
const container = this.d3.select(`#${currentComponent.node().classList[0]}`).datum();
this.__resetDrawOption(components, currentComponent);
} else if (currentComponent.node().classList[0] !== event.target.id) {
const container = this.d3.select(`#${currentComponent.node().classList[0]}`).datum();

dropzone.children.push(currentComponent.datum());
dropzone.children.push(currentComponent.datum());

container.children = container.children
.filter((child) => child.id !== event.relatedTarget.id);
container.children = container.children
.filter((child) => child.id !== event.relatedTarget.id);

container.children.forEach((child) => { child.drawOption = null; });
this.draw(container.children, container.id);
container.children.forEach((child) => { child.drawOption = null; });
this.draw(container.children, container.id);

this.__resetDrawOption(components, currentComponent);
}
this.__resetDrawOption(components, currentComponent);
}
},
});
Expand Down
7 changes: 7 additions & 0 deletions src/models/ComponentDefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class ComponentDefinition {
* @param {String} [props.icon] - The icon's name of this type of component.
* @param {String} [props.model] - Name of SVG template to render this type of component.
* @param {String[]} [props.parentTypes=[]] - The list of types that can be the parent.
* @param {String[]} [props.childrenTypes=[]] - The list of types that can be children.
* @param {ComponentAttributeDefinition[]} [props.definedAttributes=[]] - Defined attributes for
* this type.
* @param {Boolean} [props.isContainer=false] - Boolean means if this type can be a parent.
Expand All @@ -27,6 +28,7 @@ class ComponentDefinition {
icon,
model,
parentTypes,
childrenTypes,
definedAttributes,
isContainer,
} = props;
Expand All @@ -50,6 +52,11 @@ class ComponentDefinition {
* @type {String[]}
*/
this.parentTypes = parentTypes || [];
/**
* The list of types that can be a child.
* @type {String[]}
*/
this.childrenTypes = childrenTypes || [];
/**
* Defined attributes for this type.
* @type {ComponentAttributeDefinition[]}
Expand Down
37 changes: 34 additions & 3 deletions tests/unit/draw/DefaultDrawer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,23 @@ describe('Test Class: DefaultDrawer()', () => {
drawer.__drag = jest.fn();
drawer.__dropToRoot = jest.fn();
drawer.__dropToContainer = jest.fn();
const components = [
{
definition: {
childrenTypes: [],
isContainer: false,
},
},
{
definition: {
childrenTypes: ['type0', 'type1'],
isContainer: true,
},
},
];

it('Should call inner function', () => {
drawer.setComponentAction({});
drawer.setComponentAction(components);
expect(drawer.__drag).toBeCalled();
expect(drawer.__dropToRoot).toBeCalled();
expect(drawer.__dropToContainer).toBeCalled();
Expand Down Expand Up @@ -248,9 +262,26 @@ describe('Test Class: DefaultDrawer()', () => {
unset: jest.fn(),
dropzone: jest.fn(),
});
it('Should call all interact methods', () => {
drawer.__dropToContainer();
const component0 = {
definition: {
childrenTypes: [],
isContainer: false,
},
};
const component1 = {
definition: {
childrenTypes: ['type0', 'type1'],
isContainer: true,
},
};
it('Should not call interact methods', () => {
drawer.__dropToContainer([], component0);
expect(drawer.interact).toBeCalledTimes(2);
});

it('Should call all interact methods', () => {
drawer.__dropToContainer([], component1);
expect(drawer.interact).toBeCalledTimes(4);
expect(drawer.interact().unset).toBeCalled();
expect(drawer.interact().dropzone).toBeCalled();
});
Expand Down

0 comments on commit 2fdc68e

Please sign in to comment.