feat: Add title support to packetDiagram

This commit is contained in:
Sidharth Vinod 2023-11-15 00:47:03 +05:30
parent 659db9f04b
commit 9925b9b455
No known key found for this signature in database
GPG Key ID: FB5CCD378D3907CD
8 changed files with 92 additions and 38 deletions

View File

@ -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.

View File

@ -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,
};

View File

@ -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';

View File

@ -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();

View File

@ -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 = (

View File

@ -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);

View File

@ -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'};

View File

@ -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 {