mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-21 16:29:40 +02:00
#3358 Recursive positioning
This commit is contained in:
@@ -69,12 +69,15 @@ block-beta
|
|||||||
id2("2")
|
id2("2")
|
||||||
block
|
block
|
||||||
id3["I am a wide one"]
|
id3["I am a wide one"]
|
||||||
id4
|
block
|
||||||
|
id44("A final one")
|
||||||
|
id45("B final one")
|
||||||
end
|
end
|
||||||
%% id4("A final one")
|
end
|
||||||
|
id4("Another final one")
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid">
|
||||||
block-beta
|
block-beta
|
||||||
|
|
||||||
id3["I am a wide one"]
|
id3["I am a wide one"]
|
||||||
@@ -84,7 +87,9 @@ block-beta
|
|||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart RL
|
flowchart RL
|
||||||
|
subgraph "`one`"
|
||||||
id
|
id
|
||||||
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart RL
|
flowchart RL
|
||||||
|
@@ -27,7 +27,7 @@ const question = async (parent, node) => {
|
|||||||
questionElem.attr('style', node.style);
|
questionElem.attr('style', node.style);
|
||||||
updateNodeBounds(node, questionElem);
|
updateNodeBounds(node, questionElem);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
log.warn('Intersect called');
|
log.warn('Intersect called');
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
@@ -52,7 +52,7 @@ const choice = (parent, node) => {
|
|||||||
const choice = shapeSvg.insert('polygon', ':first-child').attr(
|
const choice = shapeSvg.insert('polygon', ':first-child').attr(
|
||||||
'points',
|
'points',
|
||||||
points
|
points
|
||||||
.map(function(d) {
|
.map(function (d) {
|
||||||
return d.x + ',' + d.y;
|
return d.x + ',' + d.y;
|
||||||
})
|
})
|
||||||
.join(' ')
|
.join(' ')
|
||||||
@@ -62,7 +62,7 @@ const choice = (parent, node) => {
|
|||||||
node.width = 28;
|
node.width = 28;
|
||||||
node.height = 28;
|
node.height = 28;
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.circle(node, 14, point);
|
return intersect.circle(node, 14, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ const hexagon = async (parent, node) => {
|
|||||||
hex.attr('style', node.style);
|
hex.attr('style', node.style);
|
||||||
updateNodeBounds(node, hex);
|
updateNodeBounds(node, hex);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ const rect_left_inv_arrow = async (parent, node) => {
|
|||||||
node.width = w + h;
|
node.width = w + h;
|
||||||
node.height = h;
|
node.height = h;
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ const lean_right = async (parent, node) => {
|
|||||||
el.attr('style', node.style);
|
el.attr('style', node.style);
|
||||||
updateNodeBounds(node, el);
|
updateNodeBounds(node, el);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ const lean_left = async (parent, node) => {
|
|||||||
el.attr('style', node.style);
|
el.attr('style', node.style);
|
||||||
updateNodeBounds(node, el);
|
updateNodeBounds(node, el);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ const trapezoid = async (parent, node) => {
|
|||||||
el.attr('style', node.style);
|
el.attr('style', node.style);
|
||||||
updateNodeBounds(node, el);
|
updateNodeBounds(node, el);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ const inv_trapezoid = async (parent, node) => {
|
|||||||
el.attr('style', node.style);
|
el.attr('style', node.style);
|
||||||
updateNodeBounds(node, el);
|
updateNodeBounds(node, el);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ const rect_right_inv_arrow = async (parent, node) => {
|
|||||||
el.attr('style', node.style);
|
el.attr('style', node.style);
|
||||||
updateNodeBounds(node, el);
|
updateNodeBounds(node, el);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ const cylinder = async (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, el);
|
updateNodeBounds(node, el);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
const pos = intersect.rect(node, point);
|
const pos = intersect.rect(node, point);
|
||||||
const x = pos.x - node.x;
|
const x = pos.x - node.x;
|
||||||
|
|
||||||
@@ -350,7 +350,55 @@ const rect = async (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
||||||
|
|
||||||
|
const composite = async (parent, node) => {
|
||||||
|
console.log('This got called');
|
||||||
|
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
'node ' + node.classes,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// add the rect
|
||||||
|
const rect = shapeSvg.insert('rect', ':first-child');
|
||||||
|
|
||||||
|
// const totalWidth = bbox.width + node.padding * 2;
|
||||||
|
// const totalHeight = bbox.height + node.padding * 2;
|
||||||
|
const totalWidth = node.positioned ? node.width : bbox.width + node.padding;
|
||||||
|
const totalHeight = node.positioned ? node.height : bbox.height + node.padding;
|
||||||
|
const x = node.positioned ? -totalWidth / 2 : -bbox.width / 2 - halfPadding;
|
||||||
|
const y = node.positioned ? -totalHeight / 2 : -bbox.height / 2 - halfPadding;
|
||||||
|
rect
|
||||||
|
.attr('class', 'basic cluster composite label-container')
|
||||||
|
.attr('style', node.style)
|
||||||
|
.attr('rx', node.rx)
|
||||||
|
.attr('ry', node.ry)
|
||||||
|
.attr('x', x)
|
||||||
|
.attr('y', y)
|
||||||
|
.attr('width', totalWidth)
|
||||||
|
.attr('height', totalHeight);
|
||||||
|
|
||||||
|
if (node.props) {
|
||||||
|
const propKeys = new Set(Object.keys(node.props));
|
||||||
|
if (node.props.borders) {
|
||||||
|
applyNodePropertyBorders(rect, node.props.borders, totalWidth, totalHeight);
|
||||||
|
propKeys.delete('borders');
|
||||||
|
}
|
||||||
|
propKeys.forEach((propKey) => {
|
||||||
|
log.warn(`Unknown node property ${propKey}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
return intersect.rect(node, point);
|
return intersect.rect(node, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -383,7 +431,7 @@ const labelRect = async (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.rect(node, point);
|
return intersect.rect(node, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -537,7 +585,7 @@ const rectWithTitle = (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.rect(node, point);
|
return intersect.rect(node, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -563,7 +611,7 @@ const stadium = async (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.rect(node, point);
|
return intersect.rect(node, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -587,7 +635,7 @@ const circle = async (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, circle);
|
updateNodeBounds(node, circle);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
log.info('Circle intersect', node, bbox.width / 2 + halfPadding, point);
|
log.info('Circle intersect', node, bbox.width / 2 + halfPadding, point);
|
||||||
return intersect.circle(node, bbox.width / 2 + halfPadding, point);
|
return intersect.circle(node, bbox.width / 2 + halfPadding, point);
|
||||||
};
|
};
|
||||||
@@ -625,7 +673,7 @@ const doublecircle = async (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, outerCircle);
|
updateNodeBounds(node, outerCircle);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
log.info('DoubleCircle intersect', node, bbox.width / 2 + halfPadding + gap, point);
|
log.info('DoubleCircle intersect', node, bbox.width / 2 + halfPadding + gap, point);
|
||||||
return intersect.circle(node, bbox.width / 2 + halfPadding + gap, point);
|
return intersect.circle(node, bbox.width / 2 + halfPadding + gap, point);
|
||||||
};
|
};
|
||||||
@@ -655,7 +703,7 @@ const subroutine = async (parent, node) => {
|
|||||||
el.attr('style', node.style);
|
el.attr('style', node.style);
|
||||||
updateNodeBounds(node, el);
|
updateNodeBounds(node, el);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.polygon(node, points, point);
|
return intersect.polygon(node, points, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -674,7 +722,7 @@ const start = (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, circle);
|
updateNodeBounds(node, circle);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.circle(node, 7, point);
|
return intersect.circle(node, 7, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -706,7 +754,7 @@ const forkJoin = (parent, node, dir) => {
|
|||||||
updateNodeBounds(node, shape);
|
updateNodeBounds(node, shape);
|
||||||
node.height = node.height + node.padding / 2;
|
node.height = node.height + node.padding / 2;
|
||||||
node.width = node.width + node.padding / 2;
|
node.width = node.width + node.padding / 2;
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.rect(node, point);
|
return intersect.rect(node, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -727,7 +775,7 @@ const end = (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, circle);
|
updateNodeBounds(node, circle);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.circle(node, 7, point);
|
return intersect.circle(node, 7, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -950,7 +998,7 @@ const class_box = (parent, node) => {
|
|||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
node.intersect = function(point) {
|
node.intersect = function (point) {
|
||||||
return intersect.rect(node, point);
|
return intersect.rect(node, point);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -959,6 +1007,7 @@ const class_box = (parent, node) => {
|
|||||||
|
|
||||||
const shapes = {
|
const shapes = {
|
||||||
rhombus: question,
|
rhombus: question,
|
||||||
|
composite,
|
||||||
question,
|
question,
|
||||||
rect,
|
rect,
|
||||||
labelRect,
|
labelRect,
|
||||||
|
@@ -27,7 +27,7 @@ const populateBlockDatabase = (blockList: Block[], parent: Block): void => {
|
|||||||
} else {
|
} else {
|
||||||
if (!block.label) {
|
if (!block.label) {
|
||||||
if (block.type === 'composite') {
|
if (block.type === 'composite') {
|
||||||
block.label = 'x';
|
block.label = '';
|
||||||
} else {
|
} else {
|
||||||
block.label = block.id;
|
block.label = block.id;
|
||||||
}
|
}
|
||||||
|
@@ -2,15 +2,17 @@ import { BlockDB } from './blockDB.js';
|
|||||||
import type { Block } from './blockTypes.js';
|
import type { Block } from './blockTypes.js';
|
||||||
|
|
||||||
function calcBlockSizes(block: Block, db: BlockDB) {
|
function calcBlockSizes(block: Block, db: BlockDB) {
|
||||||
let totalWidth = 0;
|
const totalWidth = 0;
|
||||||
let totalHeight = 0;
|
const totalHeight = 0;
|
||||||
|
let maxWidth = 0;
|
||||||
|
let maxHeight = 0;
|
||||||
|
const padding = 20;
|
||||||
|
|
||||||
if (block.children) {
|
if (block.children) {
|
||||||
for (const child of block.children) {
|
for (const child of block.children) {
|
||||||
calcBlockSizes(child, db);
|
calcBlockSizes(child, db);
|
||||||
}
|
}
|
||||||
// find max width of children
|
// find max width of children
|
||||||
let maxWidth = 0;
|
|
||||||
let maxHeight = 0;
|
|
||||||
for (const child of block.children) {
|
for (const child of block.children) {
|
||||||
const { width, height, x, y } = child.size || { width: 0, height: 0, x: 0, y: 0 };
|
const { width, height, x, y } = child.size || { width: 0, height: 0, x: 0, y: 0 };
|
||||||
if (width > maxWidth) {
|
if (width > maxWidth) {
|
||||||
@@ -30,39 +32,41 @@ function calcBlockSizes(block: Block, db: BlockDB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Position items relative to self
|
// Position items relative to self
|
||||||
let x = 0;
|
let x = -padding / 2;
|
||||||
const y = 0;
|
const y = 0;
|
||||||
const padding = 10;
|
|
||||||
|
let accumulatedPaddingX = 0;
|
||||||
for (const child of block.children) {
|
for (const child of block.children) {
|
||||||
if (child.size) {
|
if (child.size) {
|
||||||
child.size.x = x;
|
child.size.x = x;
|
||||||
child.size.y = y;
|
child.size.y = y;
|
||||||
x += maxWidth + padding;
|
x += maxWidth + padding;
|
||||||
}
|
}
|
||||||
if (x > totalWidth) {
|
accumulatedPaddingX += padding;
|
||||||
totalWidth = x;
|
|
||||||
}
|
|
||||||
if (y > totalHeight) {
|
|
||||||
totalHeight = y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (block.children?.length > 0) {
|
if (block.children?.length > 0) {
|
||||||
block.size = { width: totalWidth, height: totalHeight, x: 0, y: 0 };
|
const numChildren = block.children.length;
|
||||||
|
block.size = {
|
||||||
|
width: numChildren * (maxWidth + padding) + padding,
|
||||||
|
height: totalHeight + 4 * padding,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
console.log('layoutBlock (done)', block);
|
console.log('layoutBlock (done)', block);
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionBlock(parent: Block, block: Block, db: BlockDB) {
|
function positionBlock(parent: Block, block: Block, db: BlockDB) {
|
||||||
console.log('layout position block', parent.id, parent?.size?.x, block.id, block?.size?.x);
|
console.log('layout position block', parent.id, parent?.size?.x, block.id, block?.size?.x);
|
||||||
let x = 0;
|
let parentX = 0;
|
||||||
let y = 0;
|
let y = 0;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
x = parent?.size?.x || 0;
|
parentX = parent?.size?.x || 0;
|
||||||
y = parent?.size?.y || 0;
|
y = parent?.size?.y || 0;
|
||||||
}
|
}
|
||||||
if (block.size) {
|
if (block.size && block.id !== 'root') {
|
||||||
block.size.x = block.size.x + x - block.size.width / 2;
|
block.size.x = parentX + block.size.x + -block.size.width / 2;
|
||||||
block.size.y = block.size.y + y;
|
block.size.y = block.size.y + y;
|
||||||
}
|
}
|
||||||
if (block.children) {
|
if (block.children) {
|
||||||
@@ -104,9 +108,9 @@ export function layout(db: BlockDB) {
|
|||||||
const blocks = db.getBlocks();
|
const blocks = db.getBlocks();
|
||||||
const root = { id: 'root', type: 'composite', children: blocks } as Block;
|
const root = { id: 'root', type: 'composite', children: blocks } as Block;
|
||||||
calcBlockSizes(root, db);
|
calcBlockSizes(root, db);
|
||||||
console.log('layout getBlocks', db.getBlocks());
|
|
||||||
// Position blocks relative to parents
|
// Position blocks relative to parents
|
||||||
positionBlock(root, root, db);
|
positionBlock(root, root, db);
|
||||||
|
console.log('getBlocks', JSON.stringify(db.getBlocks(), null, 2));
|
||||||
|
|
||||||
minX = 0;
|
minX = 0;
|
||||||
minY = 0;
|
minY = 0;
|
||||||
|
@@ -26,12 +26,17 @@ function getNodeFromBlock(block: Block, db: BlockDB, positioned = false) {
|
|||||||
let radious = 0;
|
let radious = 0;
|
||||||
let _shape = '';
|
let _shape = '';
|
||||||
let layoutOptions = {};
|
let layoutOptions = {};
|
||||||
|
console.log('This is the type:', vertex.type);
|
||||||
// Set the shape based parameters
|
// Set the shape based parameters
|
||||||
switch (vertex.type) {
|
switch (vertex.type) {
|
||||||
case 'round':
|
case 'round':
|
||||||
radious = 5;
|
radious = 5;
|
||||||
_shape = 'rect';
|
_shape = 'rect';
|
||||||
break;
|
break;
|
||||||
|
case 'composite':
|
||||||
|
radious = 4;
|
||||||
|
_shape = 'composite';
|
||||||
|
break;
|
||||||
case 'square':
|
case 'square':
|
||||||
_shape = 'rect';
|
_shape = 'rect';
|
||||||
break;
|
break;
|
||||||
|
@@ -42,6 +42,8 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
color: ${options.titleColor};
|
color: ${options.titleColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.label text,span,p {
|
.label text,span,p {
|
||||||
fill: ${options.nodeTextColor || options.textColor};
|
fill: ${options.nodeTextColor || options.textColor};
|
||||||
color: ${options.nodeTextColor || options.textColor};
|
color: ${options.nodeTextColor || options.textColor};
|
||||||
@@ -103,9 +105,11 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
// background-color:
|
// background-color:
|
||||||
}
|
}
|
||||||
|
|
||||||
.cluster rect {
|
.node .cluster {
|
||||||
fill: ${options.clusterBkg};
|
// fill: ${fade(options.mainBkg, 0.5)};
|
||||||
stroke: ${options.clusterBorder};
|
fill: ${fade(options.clusterBkg, 0.5)};
|
||||||
|
stroke: ${fade(options.clusterBorder, 0.2)};
|
||||||
|
box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
|
||||||
stroke-width: 1px;
|
stroke-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
190
vite.config.ts.timestamp-1696335530501-05072b5e79635.mjs
Normal file
190
vite.config.ts.timestamp-1696335530501-05072b5e79635.mjs
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user