mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-05 23:29:37 +02:00
feat(arch): dynamic node sizing
This commit is contained in:
@@ -638,7 +638,7 @@ export interface RequirementDiagramConfig extends BaseDiagramConfig {
|
|||||||
*/
|
*/
|
||||||
export interface ArchitectureDiagramConfig extends BaseDiagramConfig {
|
export interface ArchitectureDiagramConfig extends BaseDiagramConfig {
|
||||||
padding?: number;
|
padding?: number;
|
||||||
maxNodeWidth?: number;
|
iconSize?: number;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* The object containing configurations specific for mindmap diagrams
|
* The object containing configurations specific for mindmap diagrams
|
||||||
|
@@ -6,6 +6,7 @@ import type {
|
|||||||
ArchitectureDirection,
|
ArchitectureDirection,
|
||||||
ArchitectureLine,
|
ArchitectureLine,
|
||||||
} from './architectureTypes.js';
|
} from './architectureTypes.js';
|
||||||
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
import { isArchitectureDirection } from './architectureTypes.js';
|
import { isArchitectureDirection } from './architectureTypes.js';
|
||||||
import {
|
import {
|
||||||
setAccTitle,
|
setAccTitle,
|
||||||
@@ -36,10 +37,6 @@ let lines = DEFAULT_ARCHITECTURE_DB.lines;
|
|||||||
let elements: Record<string, D3Element> = {};
|
let elements: Record<string, D3Element> = {};
|
||||||
let cnt = DEFAULT_ARCHITECTURE_DB.cnt;
|
let cnt = DEFAULT_ARCHITECTURE_DB.cnt;
|
||||||
|
|
||||||
const config: Required<ArchitectureDiagramConfig> = structuredClone(DEFAULT_ARCHITECTURE_CONFIG);
|
|
||||||
|
|
||||||
const getConfig = (): Required<ArchitectureDiagramConfig> => structuredClone(config);
|
|
||||||
|
|
||||||
const clear = (): void => {
|
const clear = (): void => {
|
||||||
services = structuredClone(DEFAULT_ARCHITECTURE_DB.services);
|
services = structuredClone(DEFAULT_ARCHITECTURE_DB.services);
|
||||||
groups = structuredClone(DEFAULT_ARCHITECTURE_DB.groups);
|
groups = structuredClone(DEFAULT_ARCHITECTURE_DB.groups);
|
||||||
@@ -109,7 +106,6 @@ const setElementForId = (id: string, element: D3Element) => {
|
|||||||
const getElementById = (id: string) => elements[id];
|
const getElementById = (id: string) => elements[id];
|
||||||
|
|
||||||
export const db: ArchitectureDB = {
|
export const db: ArchitectureDB = {
|
||||||
getConfig,
|
|
||||||
clear,
|
clear,
|
||||||
setDiagramTitle,
|
setDiagramTitle,
|
||||||
getDiagramTitle,
|
getDiagramTitle,
|
||||||
@@ -127,3 +123,14 @@ export const db: ArchitectureDB = {
|
|||||||
setElementForId,
|
setElementForId,
|
||||||
getElementById,
|
getElementById,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getConfigField<T extends keyof ArchitectureDiagramConfig>(field: T): Required<ArchitectureDiagramConfig>[T] {
|
||||||
|
const arch = getConfig().architecture;
|
||||||
|
if (arch && arch[field] !== undefined) {
|
||||||
|
const a = arch[field];
|
||||||
|
return arch[field] as Required<ArchitectureDiagramConfig>[T]
|
||||||
|
}
|
||||||
|
return DEFAULT_ARCHITECTURE_CONFIG[field]
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getConfigField }
|
@@ -17,9 +17,9 @@ import {
|
|||||||
} from './architectureTypes.js';
|
} from './architectureTypes.js';
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||||
import defaultConfig from '../../defaultConfig.js';
|
|
||||||
import type { D3Element } from '../../mermaidAPI.js';
|
import type { D3Element } from '../../mermaidAPI.js';
|
||||||
import { drawEdges, drawGroups, drawService } from './svgDraw.js';
|
import { drawEdges, drawGroups, drawService } from './svgDraw.js';
|
||||||
|
import { getConfigField } from './architectureDb.js';
|
||||||
|
|
||||||
cytoscape.use(fcose);
|
cytoscape.use(fcose);
|
||||||
|
|
||||||
@@ -33,9 +33,8 @@ function addServices(services: ArchitectureService[], cy: cytoscape.Core) {
|
|||||||
icon: service.icon,
|
icon: service.icon,
|
||||||
label: service.title,
|
label: service.title,
|
||||||
parent: service.in,
|
parent: service.in,
|
||||||
// TODO: dynamic size
|
width: getConfigField('iconSize'),
|
||||||
width: 80,
|
height: getConfigField('iconSize'),
|
||||||
height: 80,
|
|
||||||
},
|
},
|
||||||
classes: 'node-service',
|
classes: 'node-service',
|
||||||
});
|
});
|
||||||
@@ -286,8 +285,8 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram)
|
|||||||
setupGraphViewbox(
|
setupGraphViewbox(
|
||||||
undefined,
|
undefined,
|
||||||
svg,
|
svg,
|
||||||
conf.architecture?.padding ?? defaultConfig.architecture.padding,
|
getConfigField('padding'),
|
||||||
conf.architecture?.useMaxWidth ?? defaultConfig.architecture.useMaxWidth
|
getConfigField('useMaxWidth')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -8,6 +8,7 @@ import type { MermaidConfig } from '../../config.type.js';
|
|||||||
import type cytoscape from 'cytoscape';
|
import type cytoscape from 'cytoscape';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import { getIcon, isIconNameInUse } from '../../rendering-util/svgRegister.js';
|
import { getIcon, isIconNameInUse } from '../../rendering-util/svgRegister.js';
|
||||||
|
import { getConfigField } from './architectureDb.js';
|
||||||
|
|
||||||
declare module 'cytoscape' {
|
declare module 'cytoscape' {
|
||||||
interface EdgeSingular {
|
interface EdgeSingular {
|
||||||
@@ -76,20 +77,23 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
||||||
|
const iconSize = getConfigField('iconSize')
|
||||||
|
const halfIconSize = iconSize / 2
|
||||||
|
|
||||||
cy.nodes().map((node, id) => {
|
cy.nodes().map((node, id) => {
|
||||||
const data = node.data();
|
const data = node.data();
|
||||||
if (data.type === 'group') {
|
if (data.type === 'group') {
|
||||||
const { h, w, x1, x2, y1, y2 } = node.boundingBox();
|
const { h, w, x1, x2, y1, y2 } = node.boundingBox();
|
||||||
let bkgElem = groupsEl
|
let bkgElem = groupsEl
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('x', x1 + 40)
|
.attr('x', x1 + halfIconSize)
|
||||||
.attr('y', y1 + 40)
|
.attr('y', y1 + halfIconSize)
|
||||||
.attr('width', w)
|
.attr('width', w)
|
||||||
.attr('height', h)
|
.attr('height', h)
|
||||||
.attr('class', 'node-bkg');
|
.attr('class', 'node-bkg');
|
||||||
|
|
||||||
const textElem = groupsEl.append('g');
|
const textElem = groupsEl.append('g');
|
||||||
createText(textElem, data.title, {
|
createText(textElem, data.label, {
|
||||||
useHtmlLabels: false,
|
useHtmlLabels: false,
|
||||||
width: w,
|
width: w,
|
||||||
classes: 'architecture-service-label',
|
classes: 'architecture-service-label',
|
||||||
@@ -100,7 +104,7 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
|||||||
.attr('dominant-baseline', 'start')
|
.attr('dominant-baseline', 'start')
|
||||||
.attr('text-anchor', 'start');
|
.attr('text-anchor', 'start');
|
||||||
|
|
||||||
textElem.attr('transform', 'translate(' + (x1 + 44) + ', ' + (y1 + 42) + ')');
|
textElem.attr('transform', 'translate(' + (x1 + halfIconSize + 4) + ', ' + (y1 + halfIconSize + 2) + ')');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -112,6 +116,7 @@ export const drawService = function (
|
|||||||
conf: MermaidConfig
|
conf: MermaidConfig
|
||||||
): number {
|
): number {
|
||||||
const serviceElem = elem.append('g');
|
const serviceElem = elem.append('g');
|
||||||
|
const iconSize = getConfigField('iconSize')
|
||||||
|
|
||||||
if (service.title) {
|
if (service.title) {
|
||||||
const textElem = serviceElem.append('g');
|
const textElem = serviceElem.append('g');
|
||||||
@@ -129,7 +134,7 @@ export const drawService = function (
|
|||||||
textElem.attr(
|
textElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
// TODO: dynamic size
|
// TODO: dynamic size
|
||||||
'translate(' + 80 / 2 + ', ' + 80 + ')'
|
'translate(' + (iconSize / 2) + ', ' + iconSize + ')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,13 +143,13 @@ export const drawService = function (
|
|||||||
if (!isIconNameInUse(service.icon)) {
|
if (!isIconNameInUse(service.icon)) {
|
||||||
throw new Error(`Invalid SVG Icon name: "${service.icon}"`);
|
throw new Error(`Invalid SVG Icon name: "${service.icon}"`);
|
||||||
}
|
}
|
||||||
bkgElem = getIcon(service.icon)?.(bkgElem);
|
bkgElem = getIcon(service.icon)?.(bkgElem, iconSize);
|
||||||
} else {
|
} else {
|
||||||
bkgElem
|
bkgElem
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('class', 'node-bkg')
|
.attr('class', 'node-bkg')
|
||||||
.attr('id', 'node-' + service.id)
|
.attr('id', 'node-' + service.id)
|
||||||
.attr('d', `M0 ${80 - 0} v${-80 + 2 * 0} q0,-5 5,-5 h${80 - 2 * 0} q5,0 5,5 v${80 - 0} H0 Z`);
|
.attr('d', `M0 ${iconSize - 0} v${-iconSize + 2 * 0} q0,-5 5,-5 h${iconSize - 2 * 0} q5,0 5,5 v${iconSize - 0} H0 Z`);
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceElem.attr('class', 'architecture-service');
|
serviceElem.attr('class', 'architecture-service');
|
||||||
|
@@ -865,14 +865,14 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file)
|
|||||||
required:
|
required:
|
||||||
- useMaxWidth
|
- useMaxWidth
|
||||||
- padding
|
- padding
|
||||||
- maxNodeWidth
|
- iconSize
|
||||||
properties:
|
properties:
|
||||||
padding:
|
padding:
|
||||||
type: number
|
type: number
|
||||||
default: 10
|
default: 10
|
||||||
maxNodeWidth:
|
iconSize:
|
||||||
type: number
|
type: number
|
||||||
default: 200
|
default: 80
|
||||||
|
|
||||||
MindmapDiagramConfig:
|
MindmapDiagramConfig:
|
||||||
title: Mindmap Diagram Config
|
title: Mindmap Diagram Config
|
||||||
|
Reference in New Issue
Block a user