mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
feat: Add title support to packetDiagram
This commit is contained in:
parent
659db9f04b
commit
9925b9b455
@ -1,7 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type * as d3 from 'd3';
|
||||
import type { SetRequired } from 'type-fest';
|
||||
import type { Diagram } from '../Diagram.js';
|
||||
import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js';
|
||||
import type * as d3 from 'd3';
|
||||
|
||||
export interface DiagramMetadata {
|
||||
title?: string;
|
||||
@ -32,13 +33,29 @@ export interface DiagramDB {
|
||||
getDiagramTitle?: () => string;
|
||||
setAccTitle?: (title: string) => void;
|
||||
getAccTitle?: () => string;
|
||||
setAccDescription?: (describetion: string) => void;
|
||||
setAccDescription?: (description: string) => void;
|
||||
getAccDescription?: () => string;
|
||||
|
||||
setDisplayMode?: (title: string) => void;
|
||||
bindFunctions?: (element: Element) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* DiagramDB with fields that is required for all new diagrams.
|
||||
*/
|
||||
export type DiagramDBBase<T extends BaseDiagramConfig> = {
|
||||
getConfig: () => Required<T>;
|
||||
} & SetRequired<
|
||||
DiagramDB,
|
||||
| 'clear'
|
||||
| 'getAccTitle'
|
||||
| 'getDiagramTitle'
|
||||
| 'getAccDescription'
|
||||
| 'setAccDescription'
|
||||
| 'setAccTitle'
|
||||
| 'setDiagramTitle'
|
||||
>;
|
||||
|
||||
// This is what is returned from getClasses(...) methods.
|
||||
// It is slightly renamed to ..StyleClassDef instead of just ClassDef because "class" is a greatly ambiguous and overloaded word.
|
||||
// It makes it clear we're working with a style class definition, even though defining the type is currently difficult.
|
||||
|
@ -1,8 +1,17 @@
|
||||
import type { Block, PacketDB, PacketData, Row } from './types.js';
|
||||
import { getConfig as commonGetConfig } from '../../config.js';
|
||||
import type { PacketDiagramConfig } from '../../config.type.js';
|
||||
import DEFAULT_CONFIG from '../../defaultConfig.js';
|
||||
import { getConfig as commonGetConfig } from '../../config.js';
|
||||
import { cleanAndMerge } from '../../utils.js';
|
||||
import {
|
||||
clear as commonClear,
|
||||
getAccDescription,
|
||||
getAccTitle,
|
||||
getDiagramTitle,
|
||||
setAccDescription,
|
||||
setAccTitle,
|
||||
setDiagramTitle,
|
||||
} from '../common/commonDb.js';
|
||||
import type { PacketDB, PacketData, Row } from './types.js';
|
||||
|
||||
const defaultPacketData: PacketData = {
|
||||
packet: [],
|
||||
@ -10,9 +19,9 @@ const defaultPacketData: PacketData = {
|
||||
|
||||
let data: PacketData = structuredClone(defaultPacketData);
|
||||
|
||||
export const DEFAULT_PACKET_CONFIG: Required<PacketDiagramConfig> = DEFAULT_CONFIG.packet;
|
||||
const DEFAULT_PACKET_CONFIG: Required<PacketDiagramConfig> = DEFAULT_CONFIG.packet;
|
||||
|
||||
export const getConfig = (): Required<PacketDiagramConfig> => {
|
||||
const getConfig = (): Required<PacketDiagramConfig> => {
|
||||
const config = cleanAndMerge({
|
||||
...DEFAULT_PACKET_CONFIG,
|
||||
...commonGetConfig().packet,
|
||||
@ -23,20 +32,28 @@ export const getConfig = (): Required<PacketDiagramConfig> => {
|
||||
return config;
|
||||
};
|
||||
|
||||
export const getPacket = (): Row[] => data.packet;
|
||||
const getPacket = (): Row[] => data.packet;
|
||||
|
||||
export const pushWord = (word: Row) => {
|
||||
const pushWord = (word: Row) => {
|
||||
if (word.length > 0) {
|
||||
data.packet.push(word);
|
||||
}
|
||||
};
|
||||
|
||||
export const clear = () => {
|
||||
const clear = () => {
|
||||
commonClear();
|
||||
data = structuredClone(defaultPacketData);
|
||||
};
|
||||
|
||||
export const db: PacketDB = {
|
||||
pushWord,
|
||||
getPacket,
|
||||
getConfig,
|
||||
clear,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
import { parser } from './parser.js';
|
||||
import { db } from './db.js';
|
||||
import { parser } from './parser.js';
|
||||
import { renderer } from './renderer.js';
|
||||
import { styles } from './styles.js';
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { db } from './db.js';
|
||||
import { parser } from './parser.js';
|
||||
import { clear, getPacket } from './db.js';
|
||||
|
||||
const { clear, getPacket } = db;
|
||||
|
||||
describe('packet diagrams', () => {
|
||||
beforeEach(() => {
|
||||
clear();
|
||||
|
@ -1,17 +1,18 @@
|
||||
import type { Packet } from 'mermaid-parser';
|
||||
import type { ParserDefinition } from '../../diagram-api/types.js';
|
||||
import { parse } from 'mermaid-parser';
|
||||
import type { ParserDefinition } from '../../diagram-api/types.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { populateCommonDb } from '../common/populateCommonDb.js';
|
||||
import { db } from './db.js';
|
||||
import type { Block, Row } from './types.js';
|
||||
import { clear, getConfig, getPacket, pushWord } from './db.js';
|
||||
|
||||
const populate = ({ blocks }: { blocks: Block[] }) => {
|
||||
clear();
|
||||
const populate = (ast: Packet) => {
|
||||
populateCommonDb(ast, db);
|
||||
let lastByte = -1;
|
||||
let word: Row = [];
|
||||
let row = 1;
|
||||
const { bitsPerRow } = getConfig();
|
||||
for (let { start, end, label } of blocks) {
|
||||
const { bitsPerRow } = db.getConfig();
|
||||
for (let { start, end, label } of ast.blocks) {
|
||||
if (end && end < start) {
|
||||
throw new Error(`Packet block ${start} - ${end} is invalid. End must be greater than start.`);
|
||||
}
|
||||
@ -25,11 +26,11 @@ const populate = ({ blocks }: { blocks: Block[] }) => {
|
||||
lastByte = end ?? start;
|
||||
log.debug(`Packet block ${start} - ${lastByte} with label ${label}`);
|
||||
|
||||
while (word.length <= bitsPerRow + 1 && getPacket().length < 10_000) {
|
||||
while (word.length <= bitsPerRow + 1 && db.getPacket().length < 10_000) {
|
||||
const [block, nextBlock] = getNextFittingBlock({ start, end, label }, row, bitsPerRow);
|
||||
word.push(block);
|
||||
if (block.end + 1 === row * bitsPerRow) {
|
||||
pushWord(word);
|
||||
db.pushWord(word);
|
||||
word = [];
|
||||
row++;
|
||||
}
|
||||
@ -39,7 +40,7 @@ const populate = ({ blocks }: { blocks: Block[] }) => {
|
||||
({ start, end, label } = nextBlock);
|
||||
}
|
||||
}
|
||||
pushWord(word);
|
||||
db.pushWord(word);
|
||||
};
|
||||
|
||||
const getNextFittingBlock = (
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import type { Diagram } from '../../Diagram.js';
|
||||
import type { PacketDiagramConfig } from '../../config.type.js';
|
||||
import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import type { PacketDB, Row } from './types.js';
|
||||
import type { PacketDiagramConfig } from '../../config.type.js';
|
||||
import type { Diagram } from '../../Diagram.js';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
@ -11,16 +11,27 @@ const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
const config = db.getConfig();
|
||||
const { rowHeight, paddingY, bitWidth, bitsPerRow } = config;
|
||||
const words = db.getPacket();
|
||||
const svgHeight = (rowHeight + paddingY) * words.length + paddingY;
|
||||
const title = db.getDiagramTitle();
|
||||
const totalRowHeight = rowHeight + paddingY;
|
||||
const svgHeight = totalRowHeight * (words.length + 1) - (title ? 0 : rowHeight);
|
||||
const svgWidth = bitWidth * bitsPerRow + 2;
|
||||
|
||||
const svg: SVG = selectSvgElement(id);
|
||||
|
||||
configureSvgSize(svg, svgHeight, svgWidth, true);
|
||||
svg.attr('height', svgHeight + 'px');
|
||||
|
||||
for (const [row, packet] of words.entries()) {
|
||||
drawWord(svg, packet, row, config);
|
||||
}
|
||||
|
||||
svg
|
||||
.append('text')
|
||||
.text(title)
|
||||
.attr('x', svgWidth / 2)
|
||||
.attr('y', svgHeight - rowHeight)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('class', 'packetTitle');
|
||||
};
|
||||
|
||||
const drawWord = (
|
||||
@ -41,14 +52,14 @@ const drawWord = (
|
||||
.attr('y', wordY)
|
||||
.attr('width', width)
|
||||
.attr('height', rowHeight)
|
||||
.attr('class', 'block');
|
||||
.attr('class', 'packetBlock');
|
||||
|
||||
// Block label
|
||||
group
|
||||
.append('text')
|
||||
.attr('x', blockX + width / 2)
|
||||
.attr('y', wordY + rowHeight / 2)
|
||||
.attr('class', 'label')
|
||||
.attr('class', 'packetLabel')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(block.label);
|
||||
@ -63,7 +74,7 @@ const drawWord = (
|
||||
.append('text')
|
||||
.attr('x', blockX + (isSingleBlock ? width / 2 : 0))
|
||||
.attr('y', bitNumberY)
|
||||
.attr('class', 'byte start')
|
||||
.attr('class', 'packetByte start')
|
||||
.attr('dominant-baseline', 'auto')
|
||||
.attr('text-anchor', isSingleBlock ? 'middle' : 'start')
|
||||
.text(block.start);
|
||||
@ -74,7 +85,7 @@ const drawWord = (
|
||||
.append('text')
|
||||
.attr('x', blockX + width)
|
||||
.attr('y', bitNumberY)
|
||||
.attr('class', 'byte end')
|
||||
.attr('class', 'packetByte end')
|
||||
.attr('dominant-baseline', 'auto')
|
||||
.attr('text-anchor', 'end')
|
||||
.text(block.end);
|
||||
|
@ -5,20 +5,24 @@ import type { PacketStyleOptions } from './types.js';
|
||||
export const styles: DiagramStylesProvider = (options: { packet?: PacketStyleOptions } = {}) => {
|
||||
log.debug({ options });
|
||||
return `
|
||||
.byte {
|
||||
.packetByte {
|
||||
font-size: ${options.packet?.byteFontSize ?? '10px'};
|
||||
}
|
||||
.byte.start {
|
||||
.packetByte.start {
|
||||
fill: ${options.packet?.startByteColor ?? 'black'};
|
||||
}
|
||||
.byte.end {
|
||||
.packetByte.end {
|
||||
fill: ${options.packet?.endByteColor ?? 'black'};
|
||||
}
|
||||
.label {
|
||||
.packetLabel {
|
||||
fill: ${options.packet?.labelColor ?? 'black'};
|
||||
font-size: ${options.packet?.labelFontSize ?? '12px'};
|
||||
}
|
||||
.block {
|
||||
.packetTitle {
|
||||
fill: ${options.packet?.titleColor ?? 'black'};
|
||||
font-size: ${options.packet?.titleFontSize ?? '14px'};
|
||||
}
|
||||
.packetBlock {
|
||||
stroke: ${options.packet?.blockStrokeColor ?? 'black'};
|
||||
stroke-width: ${options.packet?.blockStrokeWidth ?? '1'};
|
||||
fill: ${options.packet?.blockFillColor ?? '#efefef'};
|
||||
|
@ -1,15 +1,14 @@
|
||||
import type { Packet } from 'mermaid-parser';
|
||||
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||
import type { PacketDiagramConfig } from '../../config.type.js';
|
||||
import type { DiagramDBBase } from '../../diagram-api/types.js';
|
||||
|
||||
export type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
|
||||
export type Block = Pick<ArrayElement<Packet['blocks']>, 'start' | 'end' | 'label'>;
|
||||
export type Row = Required<Block>[];
|
||||
|
||||
export interface PacketDB extends DiagramDB {
|
||||
export interface PacketDB extends DiagramDBBase<PacketDiagramConfig> {
|
||||
pushWord: (word: Row) => void;
|
||||
getPacket: () => Row[];
|
||||
getConfig: () => Required<PacketDiagramConfig>;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
export interface PacketStyleOptions {
|
||||
@ -21,6 +20,8 @@ export interface PacketStyleOptions {
|
||||
blockStrokeColor?: string;
|
||||
blockStrokeWidth?: string;
|
||||
blockFillColor?: string;
|
||||
titleColor?: string;
|
||||
titleFontSize?: string;
|
||||
}
|
||||
|
||||
export interface PacketData {
|
||||
|
Loading…
x
Reference in New Issue
Block a user