diff --git a/packages/mermaid/src/diagrams/railroad/railroadDB.ts b/packages/mermaid/src/diagrams/railroad/railroadDB.ts index 590a84906c..00b2fe6e2e 100644 --- a/packages/mermaid/src/diagrams/railroad/railroadDB.ts +++ b/packages/mermaid/src/diagrams/railroad/railroadDB.ts @@ -8,13 +8,17 @@ const clear = (): void => { commonClear(); }; -// unite all rules -// split by rules -// show states -// display states +// Styles +// +// unite rules +// split rules +// +// show states / display states // hide states -// shape of non-terminals -// shape of terminals +// +// shapes of non-terminals +// shapes of terminals +// // start // end @@ -27,30 +31,46 @@ const clear = (): void => { // null // type ruleID = string; -let rules: Record = {}; +type Rules = Record; + +let rules: Rules = {}; const getConsole = () => console; -class Chunk { - // start?: State; - // end?: State; - // render: () => void; - type(): string { - return 'Chunk'; +interface Chunk { + traverse(callback: (item: Chunk, nested?: T[]) => T): T; + toString(): string; +} + +class Leaf implements Chunk { + constructor(public label: string) {} + + traverse(callback: (item: Chunk, nested?: T[]) => T): T { + return callback(this); } - // [util.inspect.custom](): string { - // return this.toString(); - // } + toString(): string { + return this.label; + } +} + +class Node implements Chunk { + constructor(public child: Chunk) {} - // should be wrapped? + traverse(callback: (item: Chunk, nested?: T[]) => T): T { + const nested = this.child.traverse(callback); - needsWrapping(): boolean { - return false; + return callback(this, [nested]); } +} + +class Chain implements Chunk { + constructor(public children: Chunk[]) {} + + traverse(callback: (item: Chunk, nested?: T[]) => T): T { + const nested = this.children.map((child) => child.traverse(callback)); - wrap(): string { - return '(' + this.toString() + ')'; + return callback(this, nested); } } @@ -59,87 +79,63 @@ class Chunk { // It is implied that every chunk has `start` and `end` states // But we do not create them, simply keeping transition 'body' with label // -class Epsilon extends Chunk { - toString(): string { - return 'ɛ'; +class Epsilon extends Leaf { + constructor() { + super('ɛ'); } } -class Term extends Chunk { +// remote quote??? +class Term extends Leaf { constructor(public label: string, public quote: string) { - super(); - } - toString(): string { - return this.quote + this.label + this.quote; + super(label); } -} -class NonTerm extends Chunk { - constructor(public label: string) { - super(); - } toString(): string { - return '<' + this.label + '>'; + return this.quote + super.toString() + this.quote; } } -class Chain extends Chunk { - constructor(public chunks: Chunk[]) { - super(); - } - - needsWrapping(): boolean { - return this.chunks.length > 1; +class NonTerm extends Leaf { + toString(): string { + return '<' + super.toString() + '>'; } } -// Chain of chunks splitted by | -// class Choice extends Chain { toString(): string { // const content = '[a' + (this.needsWrapping() ? 'Y' : 'N') + this.chunks.join('|') + 'a]'; - const content = this.chunks.join('|'); + const content = this.children.join('|'); // if (this.needsWrapping()) return '(a' + content + 'a)'; // return content; } } -// Chain of chunks splitted by , (optionally) -// class Sequence extends Chain { toString(): string { // return '[d' + (this.needsWrapping() ? 'Y' : 'N') + this.chunks.join(',') + 'd]'; - const content = this.chunks.join(','); + const content = this.children.join(','); // if (this.needsWrapping()) return '(c' + content + 'c)'; } } -class OneOrMany extends Chunk { - constructor(public chunk: Chunk) { - super(); - } +class OneOrMany extends Node { toString(): string { - return this.chunk + '+'; + return this.child + '+'; } } -class ZeroOrOne extends Chunk { - constructor(public chunk: Chunk) { - super(); - } +class ZeroOrOne extends Node { toString(): string { - return this.chunk + '?'; + return this.child + '?'; } } -class ZeroOrMany extends Chunk { - constructor(public chunk: Chunk) { - super(); - } +class ZeroOrMany extends Node { toString(): string { - return this.chunk + '?'; + return this.child + '?'; } } @@ -178,7 +174,7 @@ const addSequence = (chunks: Chunk[]): Chunk => { chunks = chunks .map((chunk) => { if (chunk instanceof Sequence) { - return chunk.chunks; + return chunk.children; } return chunk; }) @@ -201,7 +197,7 @@ const addChoice = (chunks: Chunk[]): Chunk => { chunks = chunks .map((chunk) => { if (chunk instanceof Choice) { - return chunk.chunks; + return chunk.children; } return chunk; }) @@ -219,6 +215,10 @@ const addEpsilon = (): Chunk => { return new Epsilon(); }; +const getRules = (): Rules => { + return rules; +}; + export interface RailroadDB extends DiagramDB { addChoice: (chunks: Chunk[]) => Chunk; addEpsilon: () => Chunk; @@ -231,6 +231,7 @@ export interface RailroadDB extends DiagramDB { addZeroOrOne: (chunk: Chunk) => Chunk; clear: () => void; getConsole: () => Console; + getRules: () => Rules; } export const db: RailroadDB = { @@ -246,4 +247,5 @@ export const db: RailroadDB = { clear, getConfig: () => configApi.getConfig().railroad, getConsole, + getRules, }; diff --git a/packages/mermaid/src/diagrams/railroad/railroadRenderer.ts b/packages/mermaid/src/diagrams/railroad/railroadRenderer.ts index 40658381f6..22dfb63912 100644 --- a/packages/mermaid/src/diagrams/railroad/railroadRenderer.ts +++ b/packages/mermaid/src/diagrams/railroad/railroadRenderer.ts @@ -2,32 +2,42 @@ import { Diagram } from '../../Diagram.js'; import * as configApi from '../../config.js'; import type { DrawDefinition, HTML, SVG } from '../../diagram-api/types.js'; import { select } from 'd3'; +import { RailroadDB } from './railroadDB.js'; +import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; // import { configureSvgSize } from '../../setupGraphViewbox.js'; // import { Uid } from '../../rendering-util/uid.js'; -import { - // select as d3select, - scaleOrdinal as d3scaleOrdinal, - schemeTableau10 as d3schemeTableau10, -} from 'd3'; - -const fetchSVGElement = (id: string): SVG => { - // Get config - const { securityLevel } = configApi.getConfig(); - - // Handle root and document for when rendering in sandbox mode - let sandboxElement: HTML | undefined; - let document: Document | null | undefined; - if (securityLevel === 'sandbox') { - sandboxElement = select('#i' + id); - document = sandboxElement.nodes()[0].contentDocument; - } +// import { +// // select as d3select, +// scaleOrdinal as d3scaleOrdinal, +// schemeTableau10 as d3schemeTableau10, +// } from 'd3'; - // @ts-ignore - figure out how to assign HTML to document type - const root: HTML = sandboxElement && document ? select(document) : select('body'); - const svg: SVG = root.select('#' + id); - return svg; -}; +// const fetchSVGElement = (id: string): SVG => { +// // Get config +// const { securityLevel } = configApi.getConfig(); + +// // Handle root and document for when rendering in sandbox mode +// let sandboxElement: HTML | undefined; +// let document: Document | null | undefined; +// if (securityLevel === 'sandbox') { +// sandboxElement = select('#i' + id); +// document = sandboxElement.nodes()[0].contentDocument; +// } + +// // @ts-ignore - figure out how to assign HTML to document type +// const root: HTML = sandboxElement && document ? select(document) : select('body'); +// const svg: SVG = root.select('#' + id); +// return svg; +// }; + +class Dimension { + constructor(public width: number, public height: number) {} + + add(other: Dimension): Dimension { + return new Dimension(this.width + other.width, this.height + other.height); + } +} /** * Draws Railroad diagram. @@ -37,14 +47,37 @@ const fetchSVGElement = (id: string): SVG => { * @param _version - Mermaid version from package.json * @param diagObj - A standard diagram containing the db and the text and type etc of the diagram */ -export const draw: DrawDefinition = ( - text: string, - id: string, - _version: string, - diagObj: Diagram -): void => { - const svg: SVG = fetchSVGElement(id); +export const draw: DrawDefinition = (_text, id, _version, diagObj): void => { + const svg: SVG = selectSvgElement(id); + const db = diagObj.db as RailroadDB; + const rules = db.getRules(); + + Object.entries(rules).forEach(([label, chunk], index) => { + console.log(`Key: ${label}, Value:`, chunk); + const g = svg.append('g').attr('transform', `translate(${0},${10 + index * 20})`); + + g.append('text').text(label); + + chunk.traverse((_item, nested) => { + if (nested === undefined) { + nested = []; + } + const nestedDimensions = nested.reduce((acc, curr) => acc.add(curr), new Dimension(0, 0)); + return nestedDimensions; + // item.toString() + }); + + // g + // .append('rect') + // .attr('x', 100) + // .attr('y', 0) + // .attr('width', 300) + // .attr('height', 10) + // .attr('fill', '#999') + }); + + // diagObj.renderer // const defaultRailroadConfig = configApi!.defaultConfig!.railroad!; // Establish svg dimensions and get width and height // @@ -58,32 +91,46 @@ export const draw: DrawDefinition = ( // // Get color scheme for the graph - const colorScheme = d3scaleOrdinal(d3schemeTableau10); - - const transitions: object[] = [ - { y: 0, label: 'AAA' }, - { y: 50, label: 'BBB' }, - { y: 100, label: 'CCC' }, - { y: 150, label: 'DDD' }, - ]; - - svg - .append('g') - .attr('class', 'transition') - .selectAll('.transition') - .data(transitions) - .join('g') - .attr('class', 'node') - .attr('id', (d: any) => d.id) - .attr('transform', function (d: any) { - return 'translate(' + 0 + ',' + d.y + ')'; - }) - .attr('x', () => 0) - .attr('y', (d: any) => d.y) - .append('rect') - .attr('height', () => 20) - .attr('width', () => 50) - .attr('fill', (d: any) => colorScheme(d.label)); + // const colorScheme = d3scaleOrdinal(d3schemeTableau10); + + // const transitions: object[] = [ + // { y: 0, label: 'AAA' }, + // { y: 50, label: 'BBB' }, + // { y: 100, label: 'CCC' }, + // { y: 150, label: 'DDD' }, + // ]; + + // svg + // .append('g') + // .attr('class', 'transition') + // .selectAll('.transition') + // .data(transitions) + // .join('g') + // .attr('class', 'node') + // .attr('id', (d: any) => d.id) + // .attr('transform', function (d: any) { + // return 'translate(' + 0 + ',' + d.y + ')'; + // }) + // .attr('x', () => 0) + // .attr('y', (d: any) => d.y) + // .append('rect') + // .attr('height', () => 20) + // .attr('width', () => 50) + // .attr('fill', '#999'); + + // svg + // .append('g') + // .attr('class', 'node-labels') + // .attr('font-family', 'sans-serif') + // .attr('font-size', 14) + // .selectAll('text') + // .data(transitions) + // .join('text') + // .attr('x', (d: any) => (0)) + // .attr('y', (d: any) => (0)) + // // .attr('dy', `${showValues ? '0' : '0.35'}em`) + // // .attr('text-anchor', (d: any) => (d.x0 < width / 2 ? 'start' : 'end')) + // .text((d: any) => d.label); }; export default { diff --git a/packages/mermaid/src/diagrams/railroad/railroadStyles.js b/packages/mermaid/src/diagrams/railroad/railroadStyles.js new file mode 100644 index 0000000000..99e948e7ed --- /dev/null +++ b/packages/mermaid/src/diagrams/railroad/railroadStyles.js @@ -0,0 +1,4 @@ +const getStyles = (options) => ` +`; + +export default getStyles; diff --git a/packages/mermaid/src/styles.spec.ts b/packages/mermaid/src/styles.spec.ts index 420ee9757b..941e23a950 100644 --- a/packages/mermaid/src/styles.spec.ts +++ b/packages/mermaid/src/styles.spec.ts @@ -28,6 +28,7 @@ import state from './diagrams/state/styles.js'; import journey from './diagrams/user-journey/styles.js'; import timeline from './diagrams/timeline/styles.js'; import mindmap from './diagrams/mindmap/styles.js'; +import railroad from './diagrams/railroad/railroadStyles.js'; import themes from './themes/index.js'; async function checkValidStylisCSSStyleSheet(stylisString: string) { @@ -96,6 +97,7 @@ describe('styles', () => { sequence, state, timeline, + railroad, })) { test(`should return a valid style for diagram ${diagramId} and theme ${themeId}`, async () => { const { default: getStyles, addStylesForDiagram } = await import('./styles.js'); diff --git a/packages/mermaid/src/themes/theme-base.js b/packages/mermaid/src/themes/theme-base.js index ce1700d0b0..1446613e67 100644 --- a/packages/mermaid/src/themes/theme-base.js +++ b/packages/mermaid/src/themes/theme-base.js @@ -311,6 +311,14 @@ class Theme { this.commitLabelBackground = this.commitLabelBackground || this.secondaryColor; this.commitLabelFontSize = this.commitLabelFontSize || '10px'; + /* railroad */ + this.railroad = { + backgroundColor: this.railroad?.backgroundColor || this.background, + titleColor: this.railroad?.titleColor || this.primaryTextColor, + nonTerminalColor: this.railroad?.terminalColor || this.primaryColor, + terminalColor: this.railroad?.terminalColor || this.secondaryColor, + }; + /* -------------------------------------------------- */ /* EntityRelationship diagrams */ diff --git a/packages/mermaid/src/themes/theme-dark.js b/packages/mermaid/src/themes/theme-dark.js index fd083e5132..e5ec4dc0b3 100644 --- a/packages/mermaid/src/themes/theme-dark.js +++ b/packages/mermaid/src/themes/theme-dark.js @@ -299,6 +299,14 @@ class Theme { this.commitLabelBackground = this.commitLabelBackground || this.secondaryColor; this.commitLabelFontSize = this.commitLabelFontSize || '10px'; + /* railroad */ + this.railroad = { + backgroundColor: this.railroad?.backgroundColor || this.background, + titleColor: this.railroad?.titleColor || this.primaryTextColor, + nonTerminalColor: this.railroad?.terminalColor || this.primaryColor, + terminalColor: this.railroad?.terminalColor || this.secondaryColor, + }; + /* -------------------------------------------------- */ /* EntityRelationship diagrams */ diff --git a/packages/mermaid/src/themes/theme-default.js b/packages/mermaid/src/themes/theme-default.js index 3cd6bca4f7..487b4edfbf 100644 --- a/packages/mermaid/src/themes/theme-default.js +++ b/packages/mermaid/src/themes/theme-default.js @@ -334,6 +334,14 @@ class Theme { this.commitLabelBackground = this.commitLabelBackground || this.secondaryColor; this.commitLabelFontSize = this.commitLabelFontSize || '10px'; + // railroad + this.railroad = { + backgroundColor: this.railroad?.backgroundColor || this.background, + titleColor: this.railroad?.titleColor || this.primaryTextColor, + nonTerminalColor: this.railroad?.terminalColor || this.primaryColor, + terminalColor: this.railroad?.terminalColor || this.secondaryColor, + }; + /* -------------------------------------------------- */ /* EntityRelationship diagrams */ diff --git a/packages/mermaid/src/themes/theme-forest.js b/packages/mermaid/src/themes/theme-forest.js index 65797b00c4..8621999b02 100644 --- a/packages/mermaid/src/themes/theme-forest.js +++ b/packages/mermaid/src/themes/theme-forest.js @@ -302,6 +302,14 @@ class Theme { this.commitLabelBackground = this.commitLabelBackground || this.secondaryColor; this.commitLabelFontSize = this.commitLabelFontSize || '10px'; + /* railroad */ + this.railroad = { + backgroundColor: this.railroad?.backgroundColor || this.background, + titleColor: this.railroad?.titleColor || this.primaryTextColor, + nonTerminalColor: this.railroad?.terminalColor || this.primaryColor, + terminalColor: this.railroad?.terminalColor || this.secondaryColor, + }; + /* -------------------------------------------------- */ /* EntityRelationship diagrams */ diff --git a/packages/mermaid/src/themes/theme-neutral.js b/packages/mermaid/src/themes/theme-neutral.js index 963ce031d1..616c8ad2f7 100644 --- a/packages/mermaid/src/themes/theme-neutral.js +++ b/packages/mermaid/src/themes/theme-neutral.js @@ -317,6 +317,14 @@ class Theme { this.commitLabelBackground = this.commitLabelBackground || this.secondaryColor; this.commitLabelFontSize = this.commitLabelFontSize || '10px'; + /* railroad */ + this.railroad = { + backgroundColor: this.railroad?.backgroundColor || this.background, + titleColor: this.railroad?.titleColor || this.primaryTextColor, + nonTerminalColor: this.railroad?.terminalColor || this.primaryColor, + terminalColor: this.railroad?.terminalColor || this.secondaryColor, + }; + /* -------------------------------------------------- */ /* EntityRelationship diagrams */