mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-14 17:54:13 +01:00
feat(arch): extended parser to control how edges pass through groups
This commit is contained in:
@@ -1,23 +1,24 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<title>Architecture Mermaid Quick Test Page</title>
|
|
||||||
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
|
|
||||||
<style>
|
|
||||||
div.mermaid {
|
|
||||||
/* font-family: 'trebuchet ms', verdana, arial; */
|
|
||||||
font-family: 'Courier New', Courier, monospace !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
<head>
|
||||||
<h1>Architecture diagram demo</h1>
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<title>Architecture Mermaid Quick Test Page</title>
|
||||||
|
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
|
||||||
|
<style>
|
||||||
|
div.mermaid {
|
||||||
|
/* font-family: 'trebuchet ms', verdana, arial; */
|
||||||
|
font-family: 'Courier New', Courier, monospace !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
<h2>Simple diagram with groups</h2>
|
<body>
|
||||||
<pre class="mermaid">
|
<h1>Architecture diagram demo</h1>
|
||||||
|
|
||||||
|
<h2>Simple diagram with groups</h2>
|
||||||
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
group api(cloud)[API]
|
group api(cloud)[API]
|
||||||
|
|
||||||
@@ -32,10 +33,10 @@
|
|||||||
disk2 T--B db
|
disk2 T--B db
|
||||||
server T--B gateway
|
server T--B gateway
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>Groups within groups</h2>
|
<h2>Groups within groups</h2>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
group api[API]
|
group api[API]
|
||||||
group public[Public API] in api
|
group public[Public API] in api
|
||||||
@@ -56,17 +57,17 @@
|
|||||||
|
|
||||||
serv1 L--R gateway
|
serv1 L--R gateway
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>Default icon (?) from uknown icon name</h2>
|
<h2>Default icon (?) from uknown icon name</h2>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
service unknown(iconnamedoesntexist)[Uknown Icon]
|
service unknown(iconnamedoesntexist)[Uknown Icon]
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>Split Direction</h2>
|
<h2>Split Direction</h2>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
service db(database)[Database]
|
service db(database)[Database]
|
||||||
service s3(disk)[Storage]
|
service s3(disk)[Storage]
|
||||||
@@ -79,10 +80,10 @@
|
|||||||
serv2 L--B s3
|
serv2 L--B s3
|
||||||
serv1 T--B disk
|
serv1 T--B disk
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>Arrow Tests</h2>
|
<h2>Arrow Tests</h2>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
service servC(server)[Server 1]
|
service servC(server)[Server 1]
|
||||||
service servL(server)[Server 2]
|
service servL(server)[Server 2]
|
||||||
@@ -100,7 +101,7 @@
|
|||||||
servR (T--R) servT
|
servR (T--R) servT
|
||||||
servR (B--R) servB
|
servR (B--R) servB
|
||||||
</pre>
|
</pre>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
service servC(server)[Server 1]
|
service servC(server)[Server 1]
|
||||||
service servL(server)[Server 2]
|
service servL(server)[Server 2]
|
||||||
@@ -118,10 +119,32 @@
|
|||||||
servT (R--T) servR
|
servT (R--T) servR
|
||||||
servB (R--B) servR
|
servB (R--B) servR
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<h2>Edge Label Test</h2>
|
<h2>Group Edges</h2>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
|
architecture
|
||||||
|
group left_group(cloud)[Left]
|
||||||
|
group right_group(cloud)[Right]
|
||||||
|
group top_group(cloud)[Top]
|
||||||
|
group bottom_group(cloud)[Bottom]
|
||||||
|
group center_group(cloud)[Center]
|
||||||
|
|
||||||
|
service left_disk(disk)[Disk] in left_group
|
||||||
|
service right_disk(disk)[Disk] in right_group
|
||||||
|
service top_disk(disk)[Disk] in top_group
|
||||||
|
service bottom_disk(disk)[Disk] in bottom_group
|
||||||
|
service center_disk(disk)[Disk] in center_group
|
||||||
|
|
||||||
|
left_disk{group} (R--L) center_disk{group}
|
||||||
|
right_disk{group} (L--R) center_disk{group}
|
||||||
|
top_disk{group} (B--T) center_disk{group}
|
||||||
|
bottom_disk{group} (T--B) center_disk{group}
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h2>Edge Label Test</h2>
|
||||||
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
service servC(server)[Server 1]
|
service servC(server)[Server 1]
|
||||||
service servL(server)[Server 2]
|
service servL(server)[Server 2]
|
||||||
@@ -139,7 +162,7 @@
|
|||||||
servR T-[Label]-R servT
|
servR T-[Label]-R servT
|
||||||
servR B-[Label]-R servB
|
servR B-[Label]-R servB
|
||||||
</pre>
|
</pre>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
architecture
|
architecture
|
||||||
service servC(server)[Server 1]
|
service servC(server)[Server 1]
|
||||||
service servL(server)[Server 2]
|
service servL(server)[Server 2]
|
||||||
@@ -158,74 +181,75 @@
|
|||||||
servR B-[Label that is Long]-R servB
|
servR B-[Label that is Long]-R servB
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import mermaid from './mermaid.esm.mjs';
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
|
||||||
const ALLOWED_TAGS = [
|
const ALLOWED_TAGS = [
|
||||||
'a',
|
'a',
|
||||||
'b',
|
'b',
|
||||||
'blockquote',
|
'blockquote',
|
||||||
'br',
|
'br',
|
||||||
'dd',
|
'dd',
|
||||||
'div',
|
'div',
|
||||||
'dl',
|
'dl',
|
||||||
'dt',
|
'dt',
|
||||||
'em',
|
'em',
|
||||||
'foreignObject',
|
'foreignObject',
|
||||||
'h1',
|
'h1',
|
||||||
'h2',
|
'h2',
|
||||||
'h3',
|
'h3',
|
||||||
'h4',
|
'h4',
|
||||||
'h5',
|
'h5',
|
||||||
'h6',
|
'h6',
|
||||||
'h7',
|
'h7',
|
||||||
'h8',
|
'h8',
|
||||||
'hr',
|
'hr',
|
||||||
'i',
|
'i',
|
||||||
'li',
|
'li',
|
||||||
'ul',
|
'ul',
|
||||||
'ol',
|
'ol',
|
||||||
'p',
|
'p',
|
||||||
'pre',
|
'pre',
|
||||||
'span',
|
'span',
|
||||||
'strike',
|
'strike',
|
||||||
'strong',
|
'strong',
|
||||||
'table',
|
'table',
|
||||||
'tbody',
|
'tbody',
|
||||||
'td',
|
'td',
|
||||||
'tfoot',
|
'tfoot',
|
||||||
'th',
|
'th',
|
||||||
'thead',
|
'thead',
|
||||||
'tr',
|
'tr',
|
||||||
];
|
];
|
||||||
mermaid.parseError = function (err, hash) {
|
mermaid.parseError = function (err, hash) {
|
||||||
// console.error('Mermaid error: ', err);
|
// console.error('Mermaid error: ', err);
|
||||||
};
|
};
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
theme: 'base',
|
theme: 'base',
|
||||||
startOnLoad: true,
|
startOnLoad: true,
|
||||||
logLevel: 0,
|
logLevel: 0,
|
||||||
flowchart: {
|
flowchart: {
|
||||||
useMaxWidth: false,
|
|
||||||
htmlLabels: true,
|
|
||||||
},
|
|
||||||
gantt: {
|
|
||||||
useMaxWidth: false,
|
|
||||||
},
|
|
||||||
architecture: {
|
|
||||||
iconSize: 80,
|
|
||||||
},
|
|
||||||
useMaxWidth: false,
|
useMaxWidth: false,
|
||||||
});
|
htmlLabels: true,
|
||||||
function callback() {
|
},
|
||||||
alert('It worked');
|
gantt: {
|
||||||
}
|
useMaxWidth: false,
|
||||||
mermaid.parseError = function (err, hash) {
|
},
|
||||||
console.error('In parse error:');
|
architecture: {
|
||||||
console.error(err);
|
iconSize: 80,
|
||||||
};
|
},
|
||||||
</script>
|
useMaxWidth: false,
|
||||||
</body>
|
});
|
||||||
</html>
|
function callback() {
|
||||||
|
alert('It worked');
|
||||||
|
}
|
||||||
|
mermaid.parseError = function (err, hash) {
|
||||||
|
console.error('In parse error:');
|
||||||
|
console.error(err);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -128,6 +128,8 @@ const addEdge = function ({
|
|||||||
rhsDir,
|
rhsDir,
|
||||||
lhsInto,
|
lhsInto,
|
||||||
rhsInto,
|
rhsInto,
|
||||||
|
lhsGroup,
|
||||||
|
rhsGroup,
|
||||||
title,
|
title,
|
||||||
}: ArchitectureEdge) {
|
}: ArchitectureEdge) {
|
||||||
if (!isArchitectureDirection(lhsDir)) {
|
if (!isArchitectureDirection(lhsDir)) {
|
||||||
@@ -152,14 +154,29 @@ const addEdge = function ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lhsGroupId = state.records.services[lhsId].in
|
||||||
|
const rhsGroupId = state.records.services[rhsId].in
|
||||||
|
if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
|
||||||
|
throw new Error(
|
||||||
|
`The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
|
||||||
|
throw new Error(
|
||||||
|
`The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const edge = {
|
const edge = {
|
||||||
lhsId,
|
lhsId,
|
||||||
lhsDir,
|
lhsDir,
|
||||||
|
lhsInto,
|
||||||
|
lhsGroup,
|
||||||
rhsId,
|
rhsId,
|
||||||
rhsDir,
|
rhsDir,
|
||||||
title,
|
|
||||||
lhsInto,
|
|
||||||
rhsInto,
|
rhsInto,
|
||||||
|
rhsGroup,
|
||||||
|
title,
|
||||||
};
|
};
|
||||||
|
|
||||||
state.records.edges.push(edge);
|
state.records.edges.push(edge);
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) {
|
|||||||
|
|
||||||
function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
|
function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
|
||||||
edges.forEach((parsedEdge) => {
|
edges.forEach((parsedEdge) => {
|
||||||
const { lhsId, rhsId, lhsInto, rhsInto, lhsDir, rhsDir, title } = parsedEdge;
|
const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } = parsedEdge;
|
||||||
const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir)
|
const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir)
|
||||||
? 'segments'
|
? 'segments'
|
||||||
: 'straight';
|
: 'straight';
|
||||||
@@ -94,6 +94,7 @@ function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
|
|||||||
source: lhsId,
|
source: lhsId,
|
||||||
sourceDir: lhsDir,
|
sourceDir: lhsDir,
|
||||||
sourceArrow: lhsInto,
|
sourceArrow: lhsInto,
|
||||||
|
sourceGroup: lhsGroup,
|
||||||
sourceEndpoint:
|
sourceEndpoint:
|
||||||
lhsDir === 'L'
|
lhsDir === 'L'
|
||||||
? '0 50%'
|
? '0 50%'
|
||||||
@@ -105,6 +106,7 @@ function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
|
|||||||
target: rhsId,
|
target: rhsId,
|
||||||
targetDir: rhsDir,
|
targetDir: rhsDir,
|
||||||
targetArrow: rhsInto,
|
targetArrow: rhsInto,
|
||||||
|
targetGroup: rhsGroup,
|
||||||
targetEndpoint:
|
targetEndpoint:
|
||||||
rhsDir === 'L'
|
rhsDir === 'L'
|
||||||
? '0 50%'
|
? '0 50%'
|
||||||
|
|||||||
@@ -199,11 +199,13 @@ export interface ArchitectureGroup {
|
|||||||
export interface ArchitectureEdge {
|
export interface ArchitectureEdge {
|
||||||
lhsId: string;
|
lhsId: string;
|
||||||
lhsDir: ArchitectureDirection;
|
lhsDir: ArchitectureDirection;
|
||||||
title?: string;
|
lhsInto?: boolean;
|
||||||
|
lhsGroup?: boolean;
|
||||||
rhsId: string;
|
rhsId: string;
|
||||||
rhsDir: ArchitectureDirection;
|
rhsDir: ArchitectureDirection;
|
||||||
lhsInto?: boolean;
|
|
||||||
rhsInto?: boolean;
|
rhsInto?: boolean;
|
||||||
|
rhsGroup?: boolean;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ArchitectureDB extends DiagramDB {
|
export interface ArchitectureDB extends DiagramDB {
|
||||||
@@ -246,9 +248,11 @@ export type EdgeSingularData = {
|
|||||||
source: string;
|
source: string;
|
||||||
sourceDir: ArchitectureDirection;
|
sourceDir: ArchitectureDirection;
|
||||||
sourceArrow?: boolean;
|
sourceArrow?: boolean;
|
||||||
|
sourceGroup?: boolean;
|
||||||
target: string;
|
target: string;
|
||||||
targetDir: ArchitectureDirection;
|
targetDir: ArchitectureDirection;
|
||||||
targetArrow?: boolean;
|
targetArrow?: boolean;
|
||||||
|
targetGroup?: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -274,23 +278,23 @@ export interface EdgeSingular extends cytoscape.EdgeSingular {
|
|||||||
|
|
||||||
export type NodeSingularData =
|
export type NodeSingularData =
|
||||||
| {
|
| {
|
||||||
type: 'service';
|
type: 'service';
|
||||||
id: string;
|
id: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
parent?: string;
|
parent?: string;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'group';
|
type: 'group';
|
||||||
id: string;
|
id: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
parent?: string;
|
parent?: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const nodeData = (node: cytoscape.NodeSingular) => {
|
export const nodeData = (node: cytoscape.NodeSingular) => {
|
||||||
return node.data() as NodeSingularData;
|
return node.data() as NodeSingularData;
|
||||||
|
|||||||
@@ -22,17 +22,37 @@ import { getConfigField } from './architectureDb.js';
|
|||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
||||||
|
const padding = getConfigField('padding');
|
||||||
const iconSize = getConfigField('iconSize');
|
const iconSize = getConfigField('iconSize');
|
||||||
const arrowSize = iconSize / 6;
|
const arrowSize = iconSize / 6;
|
||||||
const halfArrowSize = arrowSize / 2;
|
const halfArrowSize = arrowSize / 2;
|
||||||
|
|
||||||
cy.edges().map((edge, id) => {
|
cy.edges().map((edge, id) => {
|
||||||
const { sourceDir, sourceArrow, targetDir, targetArrow, label } = edgeData(edge);
|
const { sourceDir, sourceArrow, sourceGroup, targetDir, targetArrow, targetGroup, label } = edgeData(edge);
|
||||||
const { x: startX, y: startY } = edge[0].sourceEndpoint();
|
let { x: startX, y: startY } = edge[0].sourceEndpoint();
|
||||||
const { x: midX, y: midY } = edge[0].midpoint();
|
const { x: midX, y: midY } = edge[0].midpoint();
|
||||||
const { x: endX, y: endY } = edge[0].targetEndpoint();
|
let { x: endX, y: endY } = edge[0].targetEndpoint();
|
||||||
|
|
||||||
|
const groupEdgeShift = padding + 4;
|
||||||
|
// +18 comes from the service label height that extends the padding on the bottom side of each group
|
||||||
|
if (sourceGroup) {
|
||||||
|
if (isArchitectureDirectionX(sourceDir)) {
|
||||||
|
sourceDir === 'L' ? startX -= groupEdgeShift : startX += groupEdgeShift;
|
||||||
|
} else {
|
||||||
|
sourceDir === 'T' ? startY -= groupEdgeShift : startY += (groupEdgeShift + 18);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetGroup) {
|
||||||
|
if (isArchitectureDirectionX(targetDir)) {
|
||||||
|
targetDir === 'L' ? endX -= groupEdgeShift : endX += groupEdgeShift;
|
||||||
|
} else {
|
||||||
|
targetDir === 'T' ? endY -= groupEdgeShift : endY += (groupEdgeShift + 18);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (edge[0]._private.rscratch) {
|
if (edge[0]._private.rscratch) {
|
||||||
const bounds = edge[0]._private.rscratch;
|
// const bounds = edge[0]._private.rscratch;
|
||||||
|
|
||||||
const g = edgesEl.insert('g');
|
const g = edgesEl.insert('g');
|
||||||
|
|
||||||
@@ -42,11 +62,11 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
|||||||
|
|
||||||
if (sourceArrow) {
|
if (sourceArrow) {
|
||||||
const xShift = isArchitectureDirectionX(sourceDir)
|
const xShift = isArchitectureDirectionX(sourceDir)
|
||||||
? ArchitectureDirectionArrowShift[sourceDir](bounds.startX, arrowSize)
|
? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize)
|
||||||
: bounds.startX - halfArrowSize;
|
: startX - halfArrowSize;
|
||||||
const yShift = isArchitectureDirectionY(sourceDir)
|
const yShift = isArchitectureDirectionY(sourceDir)
|
||||||
? ArchitectureDirectionArrowShift[sourceDir](bounds.startY, arrowSize)
|
? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize)
|
||||||
: bounds.startY - halfArrowSize;
|
: startY - halfArrowSize;
|
||||||
|
|
||||||
g.insert('polygon')
|
g.insert('polygon')
|
||||||
.attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize))
|
.attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize))
|
||||||
@@ -55,11 +75,11 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
|||||||
}
|
}
|
||||||
if (targetArrow) {
|
if (targetArrow) {
|
||||||
const xShift = isArchitectureDirectionX(targetDir)
|
const xShift = isArchitectureDirectionX(targetDir)
|
||||||
? ArchitectureDirectionArrowShift[targetDir](bounds.endX, arrowSize)
|
? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize)
|
||||||
: bounds.endX - halfArrowSize;
|
: endX - halfArrowSize;
|
||||||
const yShift = isArchitectureDirectionY(targetDir)
|
const yShift = isArchitectureDirectionY(targetDir)
|
||||||
? ArchitectureDirectionArrowShift[targetDir](bounds.endY, arrowSize)
|
? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize)
|
||||||
: bounds.endY - halfArrowSize;
|
: endY - halfArrowSize;
|
||||||
|
|
||||||
g.insert('polygon')
|
g.insert('polygon')
|
||||||
.attr('points', ArchitectureDirectionArrow[targetDir](arrowSize))
|
.attr('points', ArchitectureDirectionArrow[targetDir](arrowSize))
|
||||||
@@ -165,10 +185,10 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
|||||||
bkgElem.attr(
|
bkgElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' +
|
'translate(' +
|
||||||
(shiftedX1 + halfIconSize + 1) +
|
(shiftedX1 + halfIconSize + 1) +
|
||||||
', ' +
|
', ' +
|
||||||
(shiftedY1 + halfIconSize + 1) +
|
(shiftedY1 + halfIconSize + 1) +
|
||||||
')'
|
')'
|
||||||
);
|
);
|
||||||
shiftedX1 += groupIconSize;
|
shiftedX1 += groupIconSize;
|
||||||
// TODO: test with more values
|
// TODO: test with more values
|
||||||
@@ -196,10 +216,10 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
|||||||
textElem.attr(
|
textElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' +
|
'translate(' +
|
||||||
(shiftedX1 + halfIconSize + 4) +
|
(shiftedX1 + halfIconSize + 4) +
|
||||||
', ' +
|
', ' +
|
||||||
(shiftedY1 + halfIconSize + 2) +
|
(shiftedY1 + halfIconSize + 2) +
|
||||||
')'
|
')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Service:
|
|||||||
;
|
;
|
||||||
|
|
||||||
Edge:
|
Edge:
|
||||||
lhsId=ARCH_ID Arrow rhsId=ARCH_ID EOL
|
lhsId=ARCH_ID lhsGroup?=ARROW_GROUP? Arrow rhsId=ARCH_ID rhsGroup?=ARROW_GROUP? EOL
|
||||||
;
|
;
|
||||||
|
|
||||||
terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B';
|
terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B';
|
||||||
@@ -38,4 +38,5 @@ terminal ARCH_ID: /[\w]+/;
|
|||||||
terminal ARCH_TEXT_ICON: /\("[^"]+"\)/;
|
terminal ARCH_TEXT_ICON: /\("[^"]+"\)/;
|
||||||
terminal ARCH_ICON: /\([\w]+\)/;
|
terminal ARCH_ICON: /\([\w]+\)/;
|
||||||
terminal ARCH_TITLE: /\[[\w ]+\]/;
|
terminal ARCH_TITLE: /\[[\w ]+\]/;
|
||||||
|
terminal ARROW_GROUP: /\{group\}/;
|
||||||
terminal ARROW_INTO: /\(|\)/;
|
terminal ARROW_INTO: /\(|\)/;
|
||||||
Reference in New Issue
Block a user