diff --git a/demo/src/assets/resources.js b/demo/src/assets/resources.js index 4aabb66b..257d711b 100644 --- a/demo/src/assets/resources.js +++ b/demo/src/assets/resources.js @@ -56,7 +56,7 @@ export default { {% if hasError %} - + + `, }, diff --git a/src/draw/DefaultDrawer.js b/src/draw/DefaultDrawer.js index 6b467986..cc0dea08 100644 --- a/src/draw/DefaultDrawer.js +++ b/src/draw/DefaultDrawer.js @@ -1,5 +1,5 @@ import * as d3 from 'd3'; -import ElkLayout from './ElkLayout'; +import ElkLayout from './layout/ElkLayout'; import ComponentRenderer from './render/ComponentRenderer'; /** @@ -11,20 +11,41 @@ class DefaultDrawer { * @param {DefaultData} pluginData - Plugin data storage. * @param {object} [resources] - Object that contains resources. * @param {string} [viewPortId] - Id of HTML element where we want to draw. + * @param option */ - constructor(pluginData, resources = null, viewPortId = 'view-port') { + constructor(pluginData, resources = null, viewPortId = 'view-port', option = { + padding: 10, + gap: 50, + }) { /** * Plugin data storage. * @type {DefaultData} */ this.pluginData = pluginData; + /** + * Component renderer. + */ + this.componentRenderer = new ComponentRenderer({ padding: option.padding }); + /** * Plugin layout system. * @type {DefaultLayout} * @default new ElkLayout() */ - this.layout = new ElkLayout(this.pluginData); + this.layout = new ElkLayout( + this.pluginData, + { componentRenderer: this.componentRenderer }, + { + 'elk.padding': `[ + left=${option.padding}, + top=${option.padding}, + right=${option.padding}, + bottom=${option.padding} + ]`, + 'elk.layered.spacing.baseValue': option.gap, + }, + ); /** * Object that contains resources. @@ -51,11 +72,6 @@ class DefaultDrawer { * @type {Selection} */ this.root = null; - - /** - * Component renderer. - */ - this.componentRenderer = new ComponentRenderer(); } /** @@ -80,28 +96,6 @@ class DefaultDrawer { this.componentRenderer.resources = this.resources; } - automaticLayout() { - // TODO: implement automatic layout - // first step: sort components by depth (decrescent) - // like this [[depth: 3, depth: 3], [depth: 2, depth: 2], [depth: 1, depth: 1]] - // second step: automatic layout for the deepest components - // third step: filter the components of the previous depth - 1 - // to keep the container with children - // four step: set the width and height of these container components - - const containerGroups = this.groupNodesByDepth().map((group) => ( - group.filter((d) => d.data.definition.isContainer && !!d.children) - )); - - containerGroups.forEach((group) => { - group.each((d) => { - // TODO: autolayout its children - - this.componentRenderer.setAutomaticlyContainerSize(d.data.id); - }); - }); - } - groupNodesByDepth() { const nodes = d3.selectAll('.component'); const maxDepth = d3.max(nodes.data(), (d) => d.depth); @@ -121,7 +115,6 @@ class DefaultDrawer { draw() { this.__drawingComponents(); this.registerComponentsDrawOption(); - this.automaticLayout(); } /** @@ -215,8 +208,6 @@ class DefaultDrawer { const position = this.getNodePosition(component.id); const size = this.getNodeSize(component.id); - console.log(position, size); - component.drawOption.x = position.x; component.drawOption.y = position.y; component.drawOption.width = size.width; diff --git a/src/draw/DefaultLayout.js b/src/draw/layout/DefaultLayout.js similarity index 100% rename from src/draw/DefaultLayout.js rename to src/draw/layout/DefaultLayout.js diff --git a/src/draw/ElkLayout.js b/src/draw/layout/ElkLayout.js similarity index 94% rename from src/draw/ElkLayout.js rename to src/draw/layout/ElkLayout.js index 5a298b43..9d0a46a3 100644 --- a/src/draw/ElkLayout.js +++ b/src/draw/layout/ElkLayout.js @@ -101,10 +101,11 @@ class ElkLayout extends DefaultLayout { /** * Initializes ELK parameters and inherited fields. * @param {DefaultData} pluginData - A graph to be arranged. + * @param {object} [renderer] - Renderer for the components. (use defaults if unsure) * @param {object} [elkParams] - Parameters for the layout algorithm. (use defaults if unsure) * @see Parameters for ELK: {@link https://eclipse.dev/elk/reference/options.html} */ - constructor(pluginData, elkParams = {}) { + constructor(pluginData, renderer = {}, elkParams = {}) { super(pluginData); /** @@ -114,7 +115,6 @@ class ElkLayout extends DefaultLayout { this.elkParams = { // default parameters 'elk.algorithm': 'elk.layered', - 'spacing.baseValue': '50', separateConnectedComponents: 'true', 'elk.layered.cycleBreaking.strategy': 'INTERACTIVE', 'elk.layered.layering.strategy': 'INTERACTIVE', @@ -126,6 +126,11 @@ class ElkLayout extends DefaultLayout { ...elkParams, }; + + this.renderer = { + componentRenderer: null, + ...renderer, + }; } /** @@ -158,10 +163,7 @@ class ElkLayout extends DefaultLayout { // For each parent, from the deepest nodes up to the root, get a layout for its children. return Promise.all( - this.getParentsByDepth(nodes) - .map( - (node) => this.generateELKLayout(node, nodes, links), - ), + this.getParentsByDepth(nodes).map((node) => this.generateELKLayout(node, nodes, links)), ); } @@ -171,7 +173,9 @@ class ElkLayout extends DefaultLayout { * @private */ writeLayout(layout) { - layout.forEach((elkNode) => this.writeSingleDepthLayout(elkNode)); + layout.forEach((elkNode) => { + this.writeSingleDepthLayout(elkNode); + }); } /** @@ -273,7 +277,15 @@ class ElkLayout extends DefaultLayout { })); // Finally calling ELK. - return ElkLayout.elk.layout(graph); + const layout = await ElkLayout.elk.layout(graph); + + this.writeSingleDepthLayout(layout); + this.renderer.componentRenderer.render(layout.id); + if (layout.id !== 'root') { + this.renderer.componentRenderer.setAutomaticlyContainerSize(layout.id); + } + + return layout; } /** @@ -282,7 +294,6 @@ class ElkLayout extends DefaultLayout { * @private */ writeSingleDepthLayout(elkNode) { - console.log('elkNode: ', elkNode); const nodes = new Map(elkNode.children .map((node) => [node.id, { x: node.x, @@ -297,8 +308,6 @@ class ElkLayout extends DefaultLayout { component.drawOption.x = x; component.drawOption.y = y; }); - - } /** diff --git a/src/draw/render/ComponentRenderer.js b/src/draw/render/ComponentRenderer.js index 686cc041..00b4ddce 100644 --- a/src/draw/render/ComponentRenderer.js +++ b/src/draw/render/ComponentRenderer.js @@ -74,16 +74,18 @@ class ComponentRenderer { /** * Create nodes. * @param {string} contextId - Id of current context. + * @param {number} [depth] - Depth of current context. * @private */ render(contextId = this.drawingContextId) { const context = d3.select(`#${contextId}`); - context.select('.components').selectAll('.component') + context.select('.components').selectAll(`.component[depth="${context.datum().depth + 1}"]`) .data(({ children }) => children) .join('g') .attr('id', ({ data }) => data.id) .attr('class', 'component') + .attr('depth', (data) => data.depth) .html(({ data }) => this.renderModel(data)) .filter(({ data, children }) => data.definition.isContainer && !(!children)) .each(({ data }) => this.render(data.id)); @@ -91,7 +93,6 @@ class ComponentRenderer { setAutomaticlyContainerSize(nodeId) { const node = d3.select(`#${nodeId}`); - const { width, height } = node.select('.components').node().getBoundingClientRect(); node.datum().data.drawOption.innerWidth = width;