mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
style(arch): prettier formatting & code cleanup
This commit is contained in:
parent
92d819ede5
commit
2709c2d2e1
@ -10,7 +10,11 @@ import type {
|
||||
ArchitectureSpatialMap,
|
||||
} from './architectureTypes.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { getArchitectureDirectionPair, isArchitectureDirection, shiftPositionByArchitectureDirectionPair } from './architectureTypes.js';
|
||||
import {
|
||||
getArchitectureDirectionPair,
|
||||
isArchitectureDirection,
|
||||
shiftPositionByArchitectureDirectionPair,
|
||||
} from './architectureTypes.js';
|
||||
import {
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
@ -54,14 +58,16 @@ const clear = (): void => {
|
||||
const addService = function (id: string, opts: Omit<ArchitectureService, 'id' | 'edges'> = {}) {
|
||||
const { icon, in: inside, title } = opts;
|
||||
if (registeredIds[id] !== undefined) {
|
||||
throw new Error(`The service id [${id}] is already in use by another ${registeredIds[id]}`)
|
||||
throw new Error(`The service id [${id}] is already in use by another ${registeredIds[id]}`);
|
||||
}
|
||||
if (inside !== undefined) {
|
||||
if (id === inside) {
|
||||
throw new Error(`The service [${id}] cannot be placed within itself`)
|
||||
throw new Error(`The service [${id}] cannot be placed within itself`);
|
||||
}
|
||||
if (registeredIds[inside] === undefined) {
|
||||
throw new Error(`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`)
|
||||
throw new Error(
|
||||
`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`
|
||||
);
|
||||
}
|
||||
if (registeredIds[inside] === 'service') {
|
||||
throw new Error(`The service [${id}]'s parent is not a group`);
|
||||
@ -84,14 +90,16 @@ const getServices = (): ArchitectureService[] => Object.values(services);
|
||||
const addGroup = function (id: string, opts: Omit<ArchitectureGroup, 'id'> = {}) {
|
||||
const { icon, in: inside, title } = opts;
|
||||
if (registeredIds[id] !== undefined) {
|
||||
throw new Error(`The group id [${id}] is already in use by another ${registeredIds[id]}`)
|
||||
throw new Error(`The group id [${id}] is already in use by another ${registeredIds[id]}`);
|
||||
}
|
||||
if (inside !== undefined) {
|
||||
if (id === inside) {
|
||||
throw new Error(`The group [${id}] cannot be placed within itself`)
|
||||
throw new Error(`The group [${id}] cannot be placed within itself`);
|
||||
}
|
||||
if (registeredIds[inside] === undefined) {
|
||||
throw new Error(`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`)
|
||||
throw new Error(
|
||||
`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`
|
||||
);
|
||||
}
|
||||
if (registeredIds[inside] === 'service') {
|
||||
throw new Error(`The group [${id}]'s parent is not a group`);
|
||||
@ -108,77 +116,85 @@ const addGroup = function (id: string, opts: Omit<ArchitectureGroup, 'id'> = {})
|
||||
});
|
||||
};
|
||||
const getGroups = (): ArchitectureGroup[] => {
|
||||
return groups
|
||||
return groups;
|
||||
};
|
||||
|
||||
|
||||
const getDataStructures = () => {
|
||||
console.log('===== createSpatialMap =====')
|
||||
console.log('===== createSpatialMap =====');
|
||||
if (datastructures === undefined) {
|
||||
// Create an adjacency list of the diagram to perform BFS on
|
||||
// Outer reduce applied on all services
|
||||
// Inner reduce applied on the edges for a service
|
||||
const adjList = Object.entries(services).reduce<{[id: string]: ArchitectureDirectionPairMap}>((prev, [id, service]) => {
|
||||
prev[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prev, edge) => {
|
||||
if (edge.lhs_id === id) { // source is LHS
|
||||
const pair = getArchitectureDirectionPair(edge.lhs_dir, edge.rhs_dir);
|
||||
if (pair) {
|
||||
prev[pair] = edge.rhs_id
|
||||
const adjList = Object.entries(services).reduce<{ [id: string]: ArchitectureDirectionPairMap }>(
|
||||
(prev, [id, service]) => {
|
||||
prev[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prev, edge) => {
|
||||
if (edge.lhs_id === id) {
|
||||
// source is LHS
|
||||
const pair = getArchitectureDirectionPair(edge.lhs_dir, edge.rhs_dir);
|
||||
if (pair) {
|
||||
prev[pair] = edge.rhs_id;
|
||||
}
|
||||
} else {
|
||||
// source is RHS
|
||||
const pair = getArchitectureDirectionPair(edge.rhs_dir, edge.lhs_dir);
|
||||
if (pair) {
|
||||
prev[pair] = edge.lhs_id;
|
||||
}
|
||||
}
|
||||
} else { // source is RHS
|
||||
const pair = getArchitectureDirectionPair(edge.rhs_dir, edge.lhs_dir);
|
||||
if (pair) {
|
||||
prev[pair] = edge.lhs_id
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
}, {});
|
||||
return prev;
|
||||
}, {})
|
||||
return prev
|
||||
}, {});
|
||||
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
// Configuration for the initial pass of BFS
|
||||
const [firstId, _] = Object.entries(adjList)[0];
|
||||
const visited = {[firstId]: 1};
|
||||
const notVisited = Object.keys(adjList).reduce((prev, id) => (
|
||||
id === firstId ? prev : {...prev, [id]: 1}
|
||||
), {} as Record<string, number>);
|
||||
|
||||
const visited = { [firstId]: 1 };
|
||||
const notVisited = Object.keys(adjList).reduce(
|
||||
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
|
||||
{} as Record<string, number>
|
||||
);
|
||||
|
||||
// Perform BFS on adjacency list
|
||||
const BFS = (startingId: string): ArchitectureSpatialMap => {
|
||||
const spatialMap = {[startingId]: [0,0]};
|
||||
const spatialMap = { [startingId]: [0, 0] };
|
||||
const queue = [startingId];
|
||||
while(queue.length > 0) {
|
||||
while (queue.length > 0) {
|
||||
const id = queue.shift();
|
||||
if (id) {
|
||||
visited[id] = 1
|
||||
delete notVisited[id]
|
||||
visited[id] = 1;
|
||||
delete notVisited[id];
|
||||
const adj = adjList[id];
|
||||
const [posX, posY] = spatialMap[id];
|
||||
Object.entries(adj).forEach(([dir, rhsId]) => {
|
||||
if (!visited[rhsId]) {
|
||||
console.log(`${id} -- ${rhsId}`);
|
||||
spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair([posX, posY], dir as ArchitectureDirectionPair)
|
||||
spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair(
|
||||
[posX, posY],
|
||||
dir as ArchitectureDirectionPair
|
||||
);
|
||||
queue.push(rhsId);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
return spatialMap;
|
||||
}
|
||||
};
|
||||
const spatialMaps = [BFS(firstId)];
|
||||
|
||||
|
||||
// If our diagram is disconnected, keep adding additional spatial maps until all disconnected graphs have been found
|
||||
while (Object.keys(notVisited).length > 0) {
|
||||
spatialMaps.push(BFS(Object.keys(notVisited)[0]))
|
||||
spatialMaps.push(BFS(Object.keys(notVisited)[0]));
|
||||
}
|
||||
datastructures = {
|
||||
adjList,
|
||||
spatialMaps
|
||||
}
|
||||
console.log(datastructures)
|
||||
spatialMaps,
|
||||
};
|
||||
console.log(datastructures);
|
||||
}
|
||||
return datastructures;
|
||||
}
|
||||
};
|
||||
|
||||
const addLine = function (
|
||||
lhs_id: string,
|
||||
@ -217,12 +233,12 @@ const addLine = function (
|
||||
title,
|
||||
lhs_into,
|
||||
rhs_into,
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
lines.push(edge);
|
||||
|
||||
services[lhs_id].edges.push(lines[lines.length - 1])
|
||||
services[rhs_id].edges.push(lines[lines.length - 1])
|
||||
|
||||
services[lhs_id].edges.push(lines[lines.length - 1]);
|
||||
services[rhs_id].edges.push(lines[lines.length - 1]);
|
||||
};
|
||||
const getLines = (): ArchitectureLine[] => lines;
|
||||
|
||||
@ -251,13 +267,15 @@ export const db: ArchitectureDB = {
|
||||
getDataStructures,
|
||||
};
|
||||
|
||||
function getConfigField<T extends keyof ArchitectureDiagramConfig>(field: T): Required<ArchitectureDiagramConfig>[T] {
|
||||
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 arch[field] as Required<ArchitectureDiagramConfig>[T];
|
||||
}
|
||||
return DEFAULT_ARCHITECTURE_CONFIG[field]
|
||||
return DEFAULT_ARCHITECTURE_CONFIG[field];
|
||||
}
|
||||
|
||||
export { getConfigField }
|
||||
export { getConfigField };
|
||||
|
@ -73,7 +73,7 @@ function positionServices(db: ArchitectureDB, cy: cytoscape.Core) {
|
||||
if (data.type === 'group') return;
|
||||
data.x = node.position().x;
|
||||
data.y = node.position().y;
|
||||
console.log(`Position service (${data.id}): (${data.x}, ${data.y})`)
|
||||
console.log(`Position service (${data.id}): (${data.x}, ${data.y})`);
|
||||
|
||||
const nodeElem = db.getElementById(data.id);
|
||||
nodeElem.attr('transform', 'translate(' + (data.x || 0) + ',' + (data.y || 0) + ')');
|
||||
@ -99,7 +99,7 @@ function layoutArchitecture(
|
||||
services: ArchitectureService[],
|
||||
groups: ArchitectureGroup[],
|
||||
lines: ArchitectureLine[],
|
||||
{adjList, spatialMaps}: ArchitectureDataStructures
|
||||
{ spatialMaps }: ArchitectureDataStructures
|
||||
): Promise<cytoscape.Core> {
|
||||
return new Promise((resolve) => {
|
||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||
@ -155,82 +155,91 @@ function layoutArchitecture(
|
||||
|
||||
// Use the spatial map to create alignment arrays for fcose
|
||||
const [horizontalAlignments, verticalAlignments] = (() => {
|
||||
const alignments = spatialMaps.map(spatialMap => {
|
||||
const _horizontalAlignments: Record<number, string[]> = {}
|
||||
const _verticalAlignments: Record<number, string[]> = {}
|
||||
const alignments = spatialMaps.map((spatialMap) => {
|
||||
const _horizontalAlignments: Record<number, string[]> = {};
|
||||
const _verticalAlignments: Record<number, string[]> = {};
|
||||
// Group service ids in an object with their x and y coordinate as the key
|
||||
Object.entries(spatialMap).forEach(([id, [x, y]]) => {
|
||||
if (!_horizontalAlignments[y]) _horizontalAlignments[y] = [];
|
||||
if (!_verticalAlignments[x]) _verticalAlignments[x] = [];
|
||||
_horizontalAlignments[y].push(id);
|
||||
_verticalAlignments[x].push(id);
|
||||
})
|
||||
});
|
||||
// Merge the values of each object into a list if the inner list has at least 2 elements
|
||||
return {
|
||||
horiz: Object.values(_horizontalAlignments).filter(arr => arr.length > 1),
|
||||
vert: Object.values(_verticalAlignments).filter(arr => arr.length > 1)
|
||||
}
|
||||
})
|
||||
horiz: Object.values(_horizontalAlignments).filter((arr) => arr.length > 1),
|
||||
vert: Object.values(_verticalAlignments).filter((arr) => arr.length > 1),
|
||||
};
|
||||
});
|
||||
|
||||
// Merge the alginment lists for each spatial map into one 2d array per axis
|
||||
return alignments.reduce(([prevHoriz, prevVert], {horiz, vert}) => {
|
||||
return [[...prevHoriz, ...horiz], [...prevVert, ...vert]]
|
||||
}, [[] as string[][], [] as string[][]])
|
||||
|
||||
return alignments.reduce(
|
||||
([prevHoriz, prevVert], { horiz, vert }) => {
|
||||
return [
|
||||
[...prevHoriz, ...horiz],
|
||||
[...prevVert, ...vert],
|
||||
];
|
||||
},
|
||||
[[] as string[][], [] as string[][]]
|
||||
);
|
||||
})();
|
||||
|
||||
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
|
||||
const relativeConstraints = (() => {
|
||||
const _relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = []
|
||||
const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`
|
||||
const strToPos = (pos: string) => pos.split(',').map(p => parseInt(p));
|
||||
const _relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = [];
|
||||
const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`;
|
||||
const strToPos = (pos: string) => pos.split(',').map((p) => parseInt(p));
|
||||
|
||||
spatialMaps.forEach(spatialMap => {
|
||||
const invSpatialMap = Object.fromEntries(Object.entries(spatialMap).map(([id, pos]) => [posToStr(pos), id]))
|
||||
console.log('===== invSpatialMap =====')
|
||||
spatialMaps.forEach((spatialMap) => {
|
||||
const invSpatialMap = Object.fromEntries(
|
||||
Object.entries(spatialMap).map(([id, pos]) => [posToStr(pos), id])
|
||||
);
|
||||
console.log('===== invSpatialMap =====');
|
||||
console.log(invSpatialMap);
|
||||
|
||||
|
||||
// perform BFS
|
||||
const queue = [posToStr([0,0])];
|
||||
const queue = [posToStr([0, 0])];
|
||||
const visited: Record<string, number> = {};
|
||||
const directions: Record<ArchitectureDirection, number[]> = {
|
||||
"L": [-1, 0],
|
||||
"R": [1, 0],
|
||||
"T": [0, 1],
|
||||
"B": [0, -1]
|
||||
}
|
||||
L: [-1, 0],
|
||||
R: [1, 0],
|
||||
T: [0, 1],
|
||||
B: [0, -1],
|
||||
};
|
||||
while (queue.length > 0) {
|
||||
const curr = queue.shift();
|
||||
if (curr) {
|
||||
visited[curr] = 1
|
||||
visited[curr] = 1;
|
||||
const currId = invSpatialMap[curr];
|
||||
if (currId) {
|
||||
const currPos = strToPos(curr);
|
||||
Object.entries(directions).forEach(([dir, shift]) => {
|
||||
const newPos = posToStr([(currPos[0]+shift[0]), (currPos[1]+shift[1])]);
|
||||
const newPos = posToStr([currPos[0] + shift[0], currPos[1] + shift[1]]);
|
||||
const newId = invSpatialMap[newPos];
|
||||
// If there is an adjacent service to the current one and it has not yet been visited
|
||||
if (newId && !visited[newPos]) {
|
||||
if (newId && !visited[newPos]) {
|
||||
queue.push(newPos);
|
||||
// @ts-ignore cannot determine if left/right or top/bottom are paired together
|
||||
_relativeConstraints.push({
|
||||
[ArchitectureDirectionName[dir as ArchitectureDirection]]: newId,
|
||||
[ArchitectureDirectionName[getOppositeArchitectureDirection(dir as ArchitectureDirection)]]: currId,
|
||||
gap: 100
|
||||
})
|
||||
[ArchitectureDirectionName[
|
||||
getOppositeArchitectureDirection(dir as ArchitectureDirection)
|
||||
]]: currId,
|
||||
gap: 100,
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
return _relativeConstraints;
|
||||
})();
|
||||
console.log(`Horizontal Alignments:`)
|
||||
console.log(`Horizontal Alignments:`);
|
||||
console.log(horizontalAlignments);
|
||||
console.log(`Vertical Alignments:`)
|
||||
console.log(`Vertical Alignments:`);
|
||||
console.log(verticalAlignments);
|
||||
console.log(`Relative Alignments:`)
|
||||
console.log(`Relative Alignments:`);
|
||||
console.log(relativeConstraints);
|
||||
|
||||
cy.layout({
|
||||
@ -242,25 +251,28 @@ function layoutArchitecture(
|
||||
// Adjust the edge parameters if it passes through the border of a group
|
||||
// Hacky fix for: https://github.com/iVis-at-Bilkent/cytoscape.js-fcose/issues/67
|
||||
idealEdgeLength(edge) {
|
||||
const [nodeA, nodeB] = edge.connectedNodes()
|
||||
const {parent: parentA} = nodeA.data();
|
||||
const {parent: parentB} = nodeB.data();
|
||||
const elasticity = parentA === parentB ? 1.25*getConfigField('iconSize') : 0.5*getConfigField('iconSize');
|
||||
const [nodeA, nodeB] = edge.connectedNodes();
|
||||
const { parent: parentA } = nodeA.data();
|
||||
const { parent: parentB } = nodeB.data();
|
||||
const elasticity =
|
||||
parentA === parentB
|
||||
? 1.25 * getConfigField('iconSize')
|
||||
: 0.5 * getConfigField('iconSize');
|
||||
return elasticity;
|
||||
},
|
||||
edgeElasticity(edge) {
|
||||
const [nodeA, nodeB] = edge.connectedNodes()
|
||||
const [nodeA, nodeB] = edge.connectedNodes();
|
||||
console.log(nodeA.data());
|
||||
const {parent: parentA} = nodeA.data();
|
||||
const {parent: parentB} = nodeB.data();
|
||||
const { parent: parentA } = nodeA.data();
|
||||
const { parent: parentB } = nodeB.data();
|
||||
const elasticity = parentA === parentB ? 0.45 : 0.001;
|
||||
return elasticity
|
||||
},
|
||||
return elasticity;
|
||||
},
|
||||
alignmentConstraint: {
|
||||
horizontal: horizontalAlignments,
|
||||
vertical: verticalAlignments,
|
||||
},
|
||||
relativePlacementConstraint: relativeConstraints
|
||||
relativePlacementConstraint: relativeConstraints,
|
||||
} as FcoseLayoutOptions).run();
|
||||
cy.ready((e) => {
|
||||
log.info('Ready', e);
|
||||
@ -295,20 +307,15 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram)
|
||||
drawServices(db, servicesElem, services, conf);
|
||||
|
||||
const cy = await layoutArchitecture(services, groups, lines, ds);
|
||||
console.log(cy.nodes().map(node => ({a: node.data()})));
|
||||
console.log(cy.nodes().map((node) => ({ a: node.data() })));
|
||||
|
||||
drawEdges(edgesElem, cy);
|
||||
drawGroups(groupElem, cy);
|
||||
positionServices(db, cy);
|
||||
|
||||
setupGraphViewbox(
|
||||
undefined,
|
||||
svg,
|
||||
getConfigField('padding'),
|
||||
getConfigField('useMaxWidth')
|
||||
);
|
||||
setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth'));
|
||||
|
||||
console.log('==============================================================')
|
||||
console.log('==============================================================');
|
||||
};
|
||||
|
||||
export const renderer = { draw };
|
||||
|
@ -3,22 +3,24 @@ import type { ArchitectureDiagramConfig } from '../../config.type.js';
|
||||
import type { D3Element } from '../../mermaidAPI.js';
|
||||
|
||||
export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B';
|
||||
export type ArchitectureDirectionX = Extract<ArchitectureDirection, 'L' | 'R'>
|
||||
export type ArchitectureDirectionY = Extract<ArchitectureDirection, 'T' | 'B'>
|
||||
export type ArchitectureDirectionX = Extract<ArchitectureDirection, 'L' | 'R'>;
|
||||
export type ArchitectureDirectionY = Extract<ArchitectureDirection, 'T' | 'B'>;
|
||||
export const ArchitectureDirectionName = {
|
||||
'L': 'left',
|
||||
'R': 'right',
|
||||
'T': 'top',
|
||||
'B': 'bottom',
|
||||
L: 'left',
|
||||
R: 'right',
|
||||
T: 'top',
|
||||
B: 'bottom',
|
||||
} as const;
|
||||
|
||||
export const getOppositeArchitectureDirection = function(x: ArchitectureDirection): ArchitectureDirection {
|
||||
export const getOppositeArchitectureDirection = function (
|
||||
x: ArchitectureDirection
|
||||
): ArchitectureDirection {
|
||||
if (isArchitectureDirectionX(x)) {
|
||||
return x === 'L' ? 'R' : 'L'
|
||||
return x === 'L' ? 'R' : 'L';
|
||||
} else {
|
||||
return x === 'T' ? 'B' : 'T'
|
||||
return x === 'T' ? 'B' : 'T';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const isArchitectureDirection = function (x: unknown): x is ArchitectureDirection {
|
||||
const temp = x as ArchitectureDirection;
|
||||
@ -41,7 +43,7 @@ export const isArchitectureDirectionY = function (
|
||||
|
||||
export const isArchitectureDirectionXY = function (
|
||||
a: ArchitectureDirection,
|
||||
b: ArchitectureDirection,
|
||||
b: ArchitectureDirection
|
||||
) {
|
||||
const aX_bY = isArchitectureDirectionX(a) && isArchitectureDirectionY(b);
|
||||
const aY_bX = isArchitectureDirectionY(a) && isArchitectureDirectionX(b);
|
||||
@ -51,48 +53,57 @@ export const isArchitectureDirectionXY = function (
|
||||
/**
|
||||
* Contains LL, RR, TT, BB which are impossible conections
|
||||
*/
|
||||
export type InvalidArchitectureDirectionPair = `${ArchitectureDirection}${ArchitectureDirection}`
|
||||
export type ArchitectureDirectionPair = Exclude<InvalidArchitectureDirectionPair, 'LL' | 'RR' | 'TT' | 'BB'>
|
||||
export const isValidArchitectureDirectionPair = function(x: InvalidArchitectureDirectionPair): x is ArchitectureDirectionPair {
|
||||
return x !== 'LL' && x !== 'RR' && x !== 'TT' && x !== 'BB'
|
||||
}
|
||||
export type InvalidArchitectureDirectionPair = `${ArchitectureDirection}${ArchitectureDirection}`;
|
||||
export type ArchitectureDirectionPair = Exclude<
|
||||
InvalidArchitectureDirectionPair,
|
||||
'LL' | 'RR' | 'TT' | 'BB'
|
||||
>;
|
||||
export const isValidArchitectureDirectionPair = function (
|
||||
x: InvalidArchitectureDirectionPair
|
||||
): x is ArchitectureDirectionPair {
|
||||
return x !== 'LL' && x !== 'RR' && x !== 'TT' && x !== 'BB';
|
||||
};
|
||||
export type ArchitectureDirectionPairMap = {
|
||||
[key in ArchitectureDirectionPair]?: string
|
||||
}
|
||||
[key in ArchitectureDirectionPair]?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a pair of the directions of each side of an edge. This function should be used instead of manually creating it to ensure that the source is always the first character.
|
||||
*
|
||||
* Note: Undefined is returned when sourceDir and targetDir are the same. In theory this should never happen since the diagram parser throws an error if a user defines it as such.
|
||||
* @param sourceDir
|
||||
* @param targetDir
|
||||
* @returns
|
||||
* Creates a pair of the directions of each side of an edge. This function should be used instead of manually creating it to ensure that the source is always the first character.
|
||||
*
|
||||
* Note: Undefined is returned when sourceDir and targetDir are the same. In theory this should never happen since the diagram parser throws an error if a user defines it as such.
|
||||
* @param sourceDir
|
||||
* @param targetDir
|
||||
* @returns
|
||||
*/
|
||||
export const getArchitectureDirectionPair = function (sourceDir: ArchitectureDirection, targetDir: ArchitectureDirection): ArchitectureDirectionPair | undefined {
|
||||
export const getArchitectureDirectionPair = function (
|
||||
sourceDir: ArchitectureDirection,
|
||||
targetDir: ArchitectureDirection
|
||||
): ArchitectureDirectionPair | undefined {
|
||||
const pair: `${ArchitectureDirection}${ArchitectureDirection}` = `${sourceDir}${targetDir}`;
|
||||
return isValidArchitectureDirectionPair(pair) ? pair : undefined
|
||||
}
|
||||
return isValidArchitectureDirectionPair(pair) ? pair : undefined;
|
||||
};
|
||||
|
||||
export const shiftPositionByArchitectureDirectionPair = function ([x, y]: number[], pair: ArchitectureDirectionPair): number[] {
|
||||
export const shiftPositionByArchitectureDirectionPair = function (
|
||||
[x, y]: number[],
|
||||
pair: ArchitectureDirectionPair
|
||||
): number[] {
|
||||
const lhs = pair[0] as ArchitectureDirection;
|
||||
const rhs = pair[1] as ArchitectureDirection;
|
||||
console.log(`${pair}: (${x},${y})`);
|
||||
if (isArchitectureDirectionX(lhs)) {
|
||||
if (isArchitectureDirectionY(rhs)) {
|
||||
return [x + (lhs === 'L' ? -1 : 1), y + (rhs === 'T' ? 1 : -1)]
|
||||
return [x + (lhs === 'L' ? -1 : 1), y + (rhs === 'T' ? 1 : -1)];
|
||||
} else {
|
||||
return [x + (lhs === 'L' ? -1 : 1), y]
|
||||
return [x + (lhs === 'L' ? -1 : 1), y];
|
||||
}
|
||||
} else {
|
||||
if (isArchitectureDirectionX(rhs)) {
|
||||
return [x + (rhs === 'L' ? 1 : -1), y + (lhs === 'T' ? 1 : -1)]
|
||||
return [x + (rhs === 'L' ? 1 : -1), y + (lhs === 'T' ? 1 : -1)];
|
||||
} else {
|
||||
return [x, y + (lhs === 'T' ? 1 : -1)]
|
||||
return [x, y + (lhs === 'T' ? 1 : -1)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
export interface ArchitectureStyleOptions {
|
||||
fontFamily: string;
|
||||
@ -144,12 +155,12 @@ export interface ArchitectureDB extends DiagramDB {
|
||||
getDataStructures: () => ArchitectureDataStructures;
|
||||
}
|
||||
|
||||
export type ArchitectureAdjacencyList = {[id: string]: ArchitectureDirectionPairMap}
|
||||
export type ArchitectureSpatialMap = Record<string, number[]>
|
||||
export type ArchitectureAdjacencyList = { [id: string]: ArchitectureDirectionPairMap };
|
||||
export type ArchitectureSpatialMap = Record<string, number[]>;
|
||||
export type ArchitectureDataStructures = {
|
||||
adjList: ArchitectureAdjacencyList;
|
||||
spatialMaps: ArchitectureSpatialMap[];
|
||||
}
|
||||
};
|
||||
|
||||
export interface ArchitectureFields {
|
||||
services: Record<string, ArchitectureService>;
|
||||
|
@ -8,10 +8,9 @@ import type {
|
||||
import type { MermaidConfig } from '../../config.type.js';
|
||||
import type cytoscape from 'cytoscape';
|
||||
import { log } from '../../logger.js';
|
||||
import { getIcon, isIconNameInUse } from '../../rendering-util/svgRegister.js';
|
||||
import { getIcon } from '../../rendering-util/svgRegister.js';
|
||||
import { getConfigField } from './architectureDb.js';
|
||||
|
||||
|
||||
declare module 'cytoscape' {
|
||||
type _EdgeSingularData = {
|
||||
id: string;
|
||||
@ -20,7 +19,7 @@ declare module 'cytoscape' {
|
||||
target: string;
|
||||
targetDir: ArchitectureDirection;
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
interface EdgeSingular {
|
||||
_private: {
|
||||
bodyBounds: unknown;
|
||||
@ -33,9 +32,8 @@ declare module 'cytoscape' {
|
||||
endY: number;
|
||||
};
|
||||
};
|
||||
// data: (() => _EdgeSingularData) | (<T extends keyof _EdgeSingularData>(key: T) => _EdgeSingularData[T])
|
||||
data(): _EdgeSingularData
|
||||
data<T extends keyof _EdgeSingularData>(key: T): _EdgeSingularData[T]
|
||||
data(): _EdgeSingularData;
|
||||
data<T extends keyof _EdgeSingularData>(key: T): _EdgeSingularData[T];
|
||||
}
|
||||
interface NodeSingular {
|
||||
_private: {
|
||||
@ -90,14 +88,14 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
||||
};
|
||||
|
||||
export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
||||
const iconSize = getConfigField('iconSize')
|
||||
const halfIconSize = iconSize / 2
|
||||
const iconSize = getConfigField('iconSize');
|
||||
const halfIconSize = iconSize / 2;
|
||||
|
||||
cy.nodes().map((node, id) => {
|
||||
const data = node.data();
|
||||
if (data.type === 'group') {
|
||||
const { h, w, x1, x2, y1, y2 } = node.boundingBox();
|
||||
console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`)
|
||||
console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`);
|
||||
let bkgElem = groupsEl
|
||||
.append('rect')
|
||||
.attr('x', x1 + halfIconSize)
|
||||
@ -118,7 +116,10 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
||||
.attr('dominant-baseline', 'start')
|
||||
.attr('text-anchor', 'start');
|
||||
|
||||
textElem.attr('transform', 'translate(' + (x1 + halfIconSize + 4) + ', ' + (y1 + halfIconSize + 2) + ')');
|
||||
textElem.attr(
|
||||
'transform',
|
||||
'translate(' + (x1 + halfIconSize + 4) + ', ' + (y1 + halfIconSize + 2) + ')'
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -130,13 +131,13 @@ export const drawService = function (
|
||||
conf: MermaidConfig
|
||||
): number {
|
||||
const serviceElem = elem.append('g');
|
||||
const iconSize = getConfigField('iconSize')
|
||||
const iconSize = getConfigField('iconSize');
|
||||
|
||||
if (service.title) {
|
||||
const textElem = serviceElem.append('g');
|
||||
createText(textElem, service.title, {
|
||||
useHtmlLabels: false,
|
||||
width: iconSize * 1.5,
|
||||
width: iconSize * 1.5,
|
||||
classes: 'architecture-service-label',
|
||||
});
|
||||
textElem
|
||||
@ -145,10 +146,7 @@ export const drawService = function (
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle');
|
||||
|
||||
textElem.attr(
|
||||
'transform',
|
||||
'translate(' + (iconSize / 2) + ', ' + iconSize + ')'
|
||||
);
|
||||
textElem.attr('transform', 'translate(' + iconSize / 2 + ', ' + iconSize + ')');
|
||||
}
|
||||
|
||||
let bkgElem = serviceElem.append('g');
|
||||
@ -163,7 +161,7 @@ export const drawService = function (
|
||||
.append('path')
|
||||
.attr('class', 'node-bkg')
|
||||
.attr('id', 'node-' + service.id)
|
||||
.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`);
|
||||
.attr('d', `M0 ${iconSize} v${-iconSize} q0,-5 5,-5 h${iconSize} q5,0 5,5 v${iconSize} H0 Z`);
|
||||
}
|
||||
|
||||
serviceElem.attr('class', 'architecture-service');
|
||||
@ -171,7 +169,7 @@ export const drawService = function (
|
||||
const { width, height } = serviceElem._groups[0][0].getBBox();
|
||||
service.width = width;
|
||||
service.height = height;
|
||||
console.log(`Draw service (${service.id})`)
|
||||
console.log(`Draw service (${service.id})`);
|
||||
db.setElementForId(service.id, serviceElem);
|
||||
return 0;
|
||||
};
|
||||
|
@ -504,7 +504,7 @@ function initialize(options: MermaidConfig = {}) {
|
||||
// Set default options
|
||||
configApi.saveConfigFromInitialize(options);
|
||||
|
||||
registerIcons(defaultIconLibrary)
|
||||
registerIcons(defaultIconLibrary);
|
||||
if (options?.iconLibraries) {
|
||||
options.iconLibraries.forEach((library) => {
|
||||
registerIcons(library);
|
||||
|
@ -2,9 +2,10 @@
|
||||
* @author Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from "../svgRegister.js";
|
||||
import { createIcon } from '../svgRegister.js';
|
||||
|
||||
export default createIcon(`<g>
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<path id="b" data-name="4" d="m20,57.86c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<path id="c" data-name="3" d="m20,45.95c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
@ -12,5 +13,6 @@ export default createIcon(`<g>
|
||||
<ellipse id="e" data-name="1" cx="40" cy="22.14" rx="20" ry="7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="20" y1="57.86" x2="20" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="60" y1="57.86" x2="60" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
</g>`, 80)
|
||||
|
||||
</g>`,
|
||||
80
|
||||
);
|
||||
|
@ -2,9 +2,10 @@
|
||||
* @author Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from "../svgRegister.js";
|
||||
import { createIcon } from '../svgRegister.js';
|
||||
|
||||
export default createIcon(`<g>
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<rect x="20" y="15" width="40" height="50" rx="1" ry="1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="24" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
@ -14,5 +15,6 @@ export default createIcon(`<g>
|
||||
<ellipse cx="40" cy="33.75" rx="14" ry="14.58" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<ellipse cx="40" cy="33.75" rx="4" ry="4.17" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<path d="m37.51,42.52l-4.83,13.22c-.26.71-1.1,1.02-1.76.64l-4.18-2.42c-.66-.38-.81-1.26-.33-1.84l9.01-10.8c.88-1.05,2.56-.08,2.09,1.2Z" style="fill: #fff; stroke-width: 0px;"/>
|
||||
</g>`, 80)
|
||||
|
||||
</g>`,
|
||||
80
|
||||
);
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { IconLibrary } from "../svgRegister.js";
|
||||
import database from "./database.js";
|
||||
import server from "./server.js";
|
||||
import disk from "./disk.js";
|
||||
import internet from "./internet.js";
|
||||
import unknown from "./unknown.js";
|
||||
import { IconLibrary } from '../svgRegister.js';
|
||||
import database from './database.js';
|
||||
import server from './server.js';
|
||||
import disk from './disk.js';
|
||||
import internet from './internet.js';
|
||||
import unknown from './unknown.js';
|
||||
|
||||
const defaultIconLibrary: IconLibrary = {
|
||||
database: database,
|
||||
server: server,
|
||||
disk: disk,
|
||||
internet: internet,
|
||||
unknown: unknown,
|
||||
}
|
||||
database: database,
|
||||
server: server,
|
||||
disk: disk,
|
||||
internet: internet,
|
||||
unknown: unknown,
|
||||
};
|
||||
|
||||
export default defaultIconLibrary
|
||||
export default defaultIconLibrary;
|
||||
|
@ -2,9 +2,10 @@
|
||||
* @author Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from "../svgRegister.js";
|
||||
import { createIcon } from '../svgRegister.js';
|
||||
|
||||
export default createIcon(`<g>
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<circle cx="40" cy="40" r="22.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="40" y1="17.5" x2="40" y2="62.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
@ -13,5 +14,6 @@ export default createIcon(`<g>
|
||||
<path d="m40.01,17.51c15.28,11.1,15.28,33.88,0,44.98" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="19.75" y1="30.1" x2="60.25" y2="30.1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="19.75" y1="49.9" x2="60.25" y2="49.9" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
</g>`, 80)
|
||||
|
||||
</g>`,
|
||||
80
|
||||
);
|
||||
|
@ -2,9 +2,10 @@
|
||||
* @author Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from "../svgRegister.js";
|
||||
import { createIcon } from '../svgRegister.js';
|
||||
|
||||
export default createIcon(`<g>
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<rect x="17.5" y="17.5" width="45" height="45" rx="2" ry="2" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
<line x1="17.5" y1="32.5" x2="62.5" y2="32.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
|
||||
@ -36,5 +37,6 @@ export default createIcon(`<g>
|
||||
<circle cx="27.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
<circle cx="22.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
|
||||
</g>
|
||||
</g>`, 80)
|
||||
|
||||
</g>`,
|
||||
80
|
||||
);
|
||||
|
@ -2,9 +2,12 @@
|
||||
* @author Nicolas Newman
|
||||
* @see https://github.com/NicolasNewman/IconLibrary
|
||||
*/
|
||||
import { createIcon } from "../svgRegister.js";
|
||||
import { createIcon } from '../svgRegister.js';
|
||||
|
||||
export default createIcon(`<g>
|
||||
export default createIcon(
|
||||
`<g>
|
||||
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||
<text transform="translate(21.16 64.67)" style="fill: #fff; font-family: ArialMT, Arial; font-size: 67.75px;"><tspan x="0" y="0">?</tspan></text>
|
||||
</g>`, 80)
|
||||
</g>`,
|
||||
80
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user