mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 04:44:08 +01:00 
			
		
		
		
	initial commit
This commit is contained in:
		@@ -41,6 +41,21 @@ const packageOptions = {
 | 
			
		||||
    packageName: 'mermaid-mindmap',
 | 
			
		||||
    file: 'detector.ts',
 | 
			
		||||
  },
 | 
			
		||||
  'mermaid-timeline': {
 | 
			
		||||
    name: 'mermaid-timeline',
 | 
			
		||||
    packageName: 'mermaid-timeline',
 | 
			
		||||
    file: 'diagram-definition.ts',
 | 
			
		||||
  },
 | 
			
		||||
  // 'mermaid-timeline-detector': {
 | 
			
		||||
  //   name: 'mermaid-timeline-detector',
 | 
			
		||||
  //   packageName: 'mermaid-timeline',
 | 
			
		||||
  //   file: 'detector.ts',
 | 
			
		||||
  // },
 | 
			
		||||
  // 'mermaid-example-diagram': {
 | 
			
		||||
  //   name: 'mermaid-example-diagram',
 | 
			
		||||
  //   packageName: 'mermaid-example-diagram',
 | 
			
		||||
  //   file: 'diagram-definition.ts',
 | 
			
		||||
  // },
 | 
			
		||||
  // 'mermaid-example-diagram-detector': {
 | 
			
		||||
  //   name: 'mermaid-example-diagram-detector',
 | 
			
		||||
  //   packageName: 'mermaid-example-diagram',
 | 
			
		||||
@@ -123,6 +138,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
 | 
			
		||||
        'packages/mermaid-mindmap/src/**',
 | 
			
		||||
        'packages/mermaid/src/**',
 | 
			
		||||
        // 'packages/mermaid-example-diagram/src/**',
 | 
			
		||||
        'packages/mermaid-timeline/src/**',
 | 
			
		||||
      ],
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
@@ -150,6 +166,8 @@ if (watch) {
 | 
			
		||||
  if (!mermaidOnly) {
 | 
			
		||||
    build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' }));
 | 
			
		||||
    // build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
 | 
			
		||||
  build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-timeline' }));
 | 
			
		||||
  //build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-timeline-detector' }));
 | 
			
		||||
  }
 | 
			
		||||
} else if (visualize) {
 | 
			
		||||
  await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ async function createServer() {
 | 
			
		||||
  app.use(express.static('./packages/mermaid/dist'));
 | 
			
		||||
  app.use(express.static('./packages/mermaid-example-diagram/dist'));
 | 
			
		||||
  app.use(express.static('./packages/mermaid-mindmap/dist'));
 | 
			
		||||
  app.use(express.static('./packages/mermaid-timeline/dist'));
 | 
			
		||||
  app.use(vite.middlewares);
 | 
			
		||||
  app.use(express.static('demos'));
 | 
			
		||||
  app.use(express.static('cypress/platform'));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										162
									
								
								cypress/platform/ashish2.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								cypress/platform/ashish2.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
<html>
 | 
			
		||||
  <head>
 | 
			
		||||
    <link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
 | 
			
		||||
    <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="stylesheet"
 | 
			
		||||
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
 | 
			
		||||
      rel="stylesheet"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
 | 
			
		||||
      rel="stylesheet"
 | 
			
		||||
    />
 | 
			
		||||
    <style>
 | 
			
		||||
      body {
 | 
			
		||||
        /* background: rgb(221, 208, 208); */
 | 
			
		||||
        /* background:#333; */
 | 
			
		||||
        font-family: 'Arial';
 | 
			
		||||
        /* font-size: 18px !important; */
 | 
			
		||||
      }
 | 
			
		||||
      h1 {
 | 
			
		||||
        color: grey;
 | 
			
		||||
      }
 | 
			
		||||
      .mermaid2 {
 | 
			
		||||
        display: none;
 | 
			
		||||
      }
 | 
			
		||||
      .mermaid svg {
 | 
			
		||||
        /* font-size: 18px !important; */
 | 
			
		||||
        background-color: #eee;
 | 
			
		||||
        background-image: radial-gradient(#fff 1%, transparent 11%),
 | 
			
		||||
          radial-gradient(#fff 1%, transparent 11%);
 | 
			
		||||
        background-size: 20px 20px;
 | 
			
		||||
        background-position: 0 0, 10px 10px;
 | 
			
		||||
        background-repeat: repeat;
 | 
			
		||||
      }
 | 
			
		||||
      .malware {
 | 
			
		||||
        position: fixed;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        height: 150px;
 | 
			
		||||
        background: red;
 | 
			
		||||
        color: black;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        font-family: monospace;
 | 
			
		||||
        font-size: 72px;
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div>Security check</div>
 | 
			
		||||
    <pre id="diagram" class="mermaid">
 | 
			
		||||
flowchart TD
 | 
			
		||||
    A --> B
 | 
			
		||||
    B --> C
 | 
			
		||||
    A --> C
 | 
			
		||||
    </pre>
 | 
			
		||||
    <pre id="diagram" class="mermaid">
 | 
			
		||||
classDiagram
 | 
			
		||||
        direction LR
 | 
			
		||||
        class Student {
 | 
			
		||||
          -idCard : IdCard
 | 
			
		||||
        }
 | 
			
		||||
        class IdCard{
 | 
			
		||||
          -id : int
 | 
			
		||||
          -name : string
 | 
			
		||||
        }
 | 
			
		||||
        class Bike{
 | 
			
		||||
          -id : int
 | 
			
		||||
          -name : string
 | 
			
		||||
        }
 | 
			
		||||
        Student "1" --o "1" IdCard : carries
 | 
			
		||||
        Student "1" --o "1" Bike : rides
 | 
			
		||||
    </pre>
 | 
			
		||||
    <pre id="diagram" class="mermaid">
 | 
			
		||||
 timeline
 | 
			
		||||
        title History of Social Media Platform
 | 
			
		||||
          2002 : LinkedIn
 | 
			
		||||
          2004 : Facebook : Google
 | 
			
		||||
          2005 : Youtube
 | 
			
		||||
          2006 : Twitter
 | 
			
		||||
          2007 : Tumblr
 | 
			
		||||
          2008s : Instagram
 | 
			
		||||
          2010 : Pinterest
 | 
			
		||||
    </pre>
 | 
			
		||||
    <pre id="diagram" class="mermaid2">
 | 
			
		||||
mindmap
 | 
			
		||||
  root
 | 
			
		||||
    child1((Circle))
 | 
			
		||||
        grandchild 1
 | 
			
		||||
        grandchild 2
 | 
			
		||||
    child2(Round rectangle)
 | 
			
		||||
        grandchild 3
 | 
			
		||||
        grandchild 4
 | 
			
		||||
    child3[Square]
 | 
			
		||||
        grandchild 5
 | 
			
		||||
        ::icon(mdi mdi-fire)
 | 
			
		||||
        gc6((grand<br/>child 6))
 | 
			
		||||
        ::icon(mdi mdi-fire)
 | 
			
		||||
          gc7((grand<br/>grand<br/>child 8))
 | 
			
		||||
        </pre>
 | 
			
		||||
    <pre id="diagram" class="mermaid">
 | 
			
		||||
      gantt
 | 
			
		||||
        title Style today marker (vertical line should be 5px wide and half-transparent blue)
 | 
			
		||||
        dateFormat YYYY-MM-DD
 | 
			
		||||
        axisFormat %d
 | 
			
		||||
        todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
 | 
			
		||||
        section Section1
 | 
			
		||||
        Today: 1, -1h
 | 
			
		||||
    </pre>
 | 
			
		||||
 | 
			
		||||
    <!-- <div id="cy"></div> -->
 | 
			
		||||
    <!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
 | 
			
		||||
    <!-- <script src="./mermaid-example-diagram-detector.js"></script>    -->
 | 
			
		||||
    <!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> -->
 | 
			
		||||
    <script type="module">
 | 
			
		||||
      import mindmap from '../../packages/mermaid-mindmap/src/detector';
 | 
			
		||||
      // import example from '../../packages/mermaid-example-diagram/src/detector';
 | 
			
		||||
      import timeline from '../../packages/mermaid-timeline/src/detector';
 | 
			
		||||
      import mermaid from '../../packages/mermaid/src/mermaid';
 | 
			
		||||
      await mermaid.registerExternalDiagrams([mindmap, timeline]);
 | 
			
		||||
      mermaid.parseError = function (err, hash) {
 | 
			
		||||
        // console.error('Mermaid error: ', err);
 | 
			
		||||
      };
 | 
			
		||||
      mermaid.initialize({
 | 
			
		||||
        theme: 'base',
 | 
			
		||||
        startOnLoad: true,
 | 
			
		||||
        logLevel: 0,
 | 
			
		||||
        flowchart: {
 | 
			
		||||
          useMaxWidth: false,
 | 
			
		||||
          htmlLabels: true,
 | 
			
		||||
        },
 | 
			
		||||
        gantt: {
 | 
			
		||||
          useMaxWidth: false,
 | 
			
		||||
        },
 | 
			
		||||
        useMaxWidth: false,
 | 
			
		||||
        lazyLoadedDiagrams: [
 | 
			
		||||
          // './mermaid-mindmap-detector.esm.mjs',
 | 
			
		||||
          // './mermaid-example-diagram-detector.esm.mjs',
 | 
			
		||||
          //'./mermaid-timeline-detector.esm.mjs',
 | 
			
		||||
        ],
 | 
			
		||||
      });
 | 
			
		||||
      function callback() {
 | 
			
		||||
        alert('It worked');
 | 
			
		||||
      }
 | 
			
		||||
      mermaid.parseError = function (err, hash) {
 | 
			
		||||
        console.error('In parse error:');
 | 
			
		||||
        console.error(err);
 | 
			
		||||
      };
 | 
			
		||||
      // mermaid.test1('first_slow', 1200).then((r) => console.info(r));
 | 
			
		||||
      // mermaid.test1('second_fast', 200).then((r) => console.info(r));
 | 
			
		||||
      // mermaid.test1('third_fast', 200).then((r) => console.info(r));
 | 
			
		||||
      // mermaid.test1('forth_slow', 1200).then((r) => console.info(r));
 | 
			
		||||
    </script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -46,13 +46,9 @@
 | 
			
		||||
    <pre class="mermaid" style="width: 100%; height: 20%">
 | 
			
		||||
      %%{init: {'theme': 'base',  'fontFamily': 'courier', 'themeVariables': {  'primaryColor': '#fff000'}}}%%
 | 
			
		||||
      classDiagram-v2
 | 
			
		||||
       class BankAccount{
 | 
			
		||||
        +String owner
 | 
			
		||||
        +BigDecimal balance
 | 
			
		||||
        +deposit(amount) bool
 | 
			
		||||
        +withdrawl(amount) int
 | 
			
		||||
       }
 | 
			
		||||
       cssClass "BankAccount" customCss
 | 
			
		||||
classA <|-- classB : implements
 | 
			
		||||
classC *-- classD : composition
 | 
			
		||||
classE o-- classF : aggregation
 | 
			
		||||
    </pre>
 | 
			
		||||
    <pre class="mermaid2" style="width: 100%; height: 20%">
 | 
			
		||||
        %%{init: {'theme': 'base',  'fontFamily': 'courier', 'themeVariables': {  'primaryColor': '#fff000'}}}%%
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								demos/timeline.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								demos/timeline.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
 | 
			
		||||
    <title>Mermaid Quick Test Page</title>
 | 
			
		||||
    <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
 | 
			
		||||
    <style>
 | 
			
		||||
      div.mermaid {
 | 
			
		||||
        /* font-family: 'trebuchet ms', verdana, arial; */
 | 
			
		||||
        font-family: 'Courier New', Courier, monospace !important;
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
  </head>
 | 
			
		||||
 | 
			
		||||
  <body>
 | 
			
		||||
    <pre class="mermaid">
 | 
			
		||||
        timeline
 | 
			
		||||
        title My day
 | 
			
		||||
        section Go to work
 | 
			
		||||
          1930 : first step : second step
 | 
			
		||||
               : third step
 | 
			
		||||
          1940 : fourth step : fifth step
 | 
			
		||||
				</pre>
 | 
			
		||||
 | 
			
		||||
    <script src="./mermaid.js"></script>
 | 
			
		||||
    <script>
 | 
			
		||||
      mermaid.initialize({
 | 
			
		||||
        theme: 'forest',
 | 
			
		||||
        logLevel: 1,
 | 
			
		||||
        securityLevel: 'loose',
 | 
			
		||||
        flowchart: { curve: 'basis' },
 | 
			
		||||
        gantt: { axisFormat: '%m/%d/%Y' },
 | 
			
		||||
        sequence: { actorMargin: 50 },
 | 
			
		||||
      });
 | 
			
		||||
    </script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -22,6 +22,7 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
 | 
			
		||||
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
 | 
			
		||||
export let getConfig: () => object;
 | 
			
		||||
export let sanitizeText: (str: string) => string;
 | 
			
		||||
export let commonDb: any;
 | 
			
		||||
/**
 | 
			
		||||
 * Placeholder for the real function that will be injected by mermaid.
 | 
			
		||||
 */
 | 
			
		||||
@@ -41,15 +42,17 @@ export let setupGraphViewbox: (
 | 
			
		||||
 * @param _getConfig - getConfig from mermaid/src/diagramAPI.ts
 | 
			
		||||
 * @param _sanitizeText - sanitizeText from mermaid/src/diagramAPI.ts
 | 
			
		||||
 * @param _setupGraphViewbox - setupGraphViewbox from mermaid/src/diagramAPI.ts
 | 
			
		||||
 * @param _commonDb
 | 
			
		||||
 */
 | 
			
		||||
export const injectUtils = (
 | 
			
		||||
  _log: Record<keyof typeof LEVELS, typeof console.log>,
 | 
			
		||||
  _setLogLevel: typeof setLogLevel,
 | 
			
		||||
  _getConfig: typeof getConfig,
 | 
			
		||||
  _sanitizeText: typeof sanitizeText,
 | 
			
		||||
  _setupGraphViewbox: typeof setupGraphViewbox
 | 
			
		||||
  _setupGraphViewbox: typeof setupGraphViewbox,
 | 
			
		||||
  _commonDb: any
 | 
			
		||||
) => {
 | 
			
		||||
  _log.debug('Mermaid utils injected into example-diagram');
 | 
			
		||||
  _log.info('Mermaid utils injected into timeline-diagram');
 | 
			
		||||
  log.trace = _log.trace;
 | 
			
		||||
  log.debug = _log.debug;
 | 
			
		||||
  log.info = _log.info;
 | 
			
		||||
@@ -60,4 +63,6 @@ export const injectUtils = (
 | 
			
		||||
  getConfig = _getConfig;
 | 
			
		||||
  sanitizeText = _sanitizeText;
 | 
			
		||||
  setupGraphViewbox = _setupGraphViewbox;
 | 
			
		||||
  commonDb = _commonDb;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
 | 
			
		||||
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
 | 
			
		||||
export let getConfig: () => object;
 | 
			
		||||
export let sanitizeText: (str: string) => string;
 | 
			
		||||
export let commonDb: () => object;
 | 
			
		||||
// eslint-disable @typescript-eslint/no-explicit-any
 | 
			
		||||
export let setupGraphViewbox: (
 | 
			
		||||
  graph: any,
 | 
			
		||||
@@ -39,7 +40,8 @@ export const injectUtils = (
 | 
			
		||||
  _setLogLevel: any,
 | 
			
		||||
  _getConfig: any,
 | 
			
		||||
  _sanitizeText: any,
 | 
			
		||||
  _setupGraphViewbox: any
 | 
			
		||||
  _setupGraphViewbox: any,
 | 
			
		||||
  _commonDb: any
 | 
			
		||||
) => {
 | 
			
		||||
  _log.info('Mermaid utils injected');
 | 
			
		||||
  log.trace = _log.trace;
 | 
			
		||||
@@ -52,4 +54,5 @@ export const injectUtils = (
 | 
			
		||||
  getConfig = _getConfig;
 | 
			
		||||
  sanitizeText = _sanitizeText;
 | 
			
		||||
  setupGraphViewbox = _setupGraphViewbox;
 | 
			
		||||
  commonDb= _commonDb;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ cytoscape.use(coseBilkent);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {any} svg The svg element to draw the diagram onto
 | 
			
		||||
 * @param {object} mindmap The mindmap data and hierarchy
 | 
			
		||||
 * @param {object} mindmap The maindmap data and hierarchy
 | 
			
		||||
 * @param section
 | 
			
		||||
 * @param {object} conf The configuration object
 | 
			
		||||
 */
 | 
			
		||||
@@ -89,6 +89,7 @@ function addNodes(mindmap, cy, conf, level) {
 | 
			
		||||
/**
 | 
			
		||||
 * @param node
 | 
			
		||||
 * @param conf
 | 
			
		||||
 * @param cy
 | 
			
		||||
 */
 | 
			
		||||
function layoutMindmap(node, conf) {
 | 
			
		||||
  return new Promise((resolve) => {
 | 
			
		||||
@@ -109,7 +110,7 @@ function layoutMindmap(node, conf) {
 | 
			
		||||
    renderEl.remove();
 | 
			
		||||
    addNodes(node, cy, conf, 0);
 | 
			
		||||
 | 
			
		||||
    // Make cytoscape care about the dimensions of the nodes
 | 
			
		||||
    // Make cytoscape care about the dimensisions of the nodes
 | 
			
		||||
    cy.nodes().forEach(function (n) {
 | 
			
		||||
      n.layoutDimensions = () => {
 | 
			
		||||
        const data = n.data();
 | 
			
		||||
@@ -131,7 +132,10 @@ function layoutMindmap(node, conf) {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * @param node
 | 
			
		||||
 * @param cy
 | 
			
		||||
 * @param positionedMindmap
 | 
			
		||||
 * @param conf
 | 
			
		||||
 */
 | 
			
		||||
function positionNodes(cy) {
 | 
			
		||||
  cy.nodes().map((node, id) => {
 | 
			
		||||
@@ -169,7 +173,7 @@ export const draw = async (text, id, version, diagObj) => {
 | 
			
		||||
  log.debug('Renering info diagram\n' + text);
 | 
			
		||||
 | 
			
		||||
  const securityLevel = getConfig().securityLevel;
 | 
			
		||||
  // Handle root and Document for when rendering in sandbox mode
 | 
			
		||||
  // Handle root and Document for when rendering in sanbox mode
 | 
			
		||||
  let sandboxElement;
 | 
			
		||||
  if (securityLevel === 'sandbox') {
 | 
			
		||||
    sandboxElement = select('#i' + id);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										69
									
								
								packages/mermaid-timeline/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								packages/mermaid-timeline/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@mermaid-js/mermaid-timeline",
 | 
			
		||||
  "version": "9.2.0-rc2",
 | 
			
		||||
  "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
 | 
			
		||||
  "main": "dist/mermaid-timeline.core.mjs",
 | 
			
		||||
  "module": "dist/mermaid-timeline.core.mjs",
 | 
			
		||||
  "type": "module",
 | 
			
		||||
  "exports": {
 | 
			
		||||
    ".": {
 | 
			
		||||
      "require": "./dist/mermaid-timeline.min.js",
 | 
			
		||||
      "import": "./dist/mermaid-timeline.core.mjs"
 | 
			
		||||
    },
 | 
			
		||||
    "./*": "./*"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "diagram",
 | 
			
		||||
    "markdown",
 | 
			
		||||
    "timeline",
 | 
			
		||||
    "mermaid"
 | 
			
		||||
  ],
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "clean": "rimraf dist",
 | 
			
		||||
    "build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly",
 | 
			
		||||
    "build:watch": "yarn build:code --watch",
 | 
			
		||||
    "build:esbuild": "concurrently \"yarn build:code\" \"yarn build:types\"",
 | 
			
		||||
    "build": "yarn clean; yarn build:esbuild",
 | 
			
		||||
    "dev": "node .esbuild/serve.cjs",
 | 
			
		||||
    "release": "yarn build",
 | 
			
		||||
    "lint": "eslint --cache --ignore-path .gitignore . && yarn lint:jison && prettier --check .",
 | 
			
		||||
    "lint:fix": "eslint --fix --ignore-path .gitignore . && prettier --write .",
 | 
			
		||||
    "lint:jison": "ts-node-esm src/jison/lint.mts",
 | 
			
		||||
    "todo-prepare": "concurrently \"husky install ../../.husky\" \"yarn build\"",
 | 
			
		||||
    "todo-pre-commit": "lint-staged"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "https://github.com/mermaid-js/mermaid"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "Knut Sveidqvist",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "standard": {
 | 
			
		||||
    "ignore": [
 | 
			
		||||
      "**/parser/*.js",
 | 
			
		||||
      "dist/**/*.js",
 | 
			
		||||
      "cypress/**/*.js"
 | 
			
		||||
    ],
 | 
			
		||||
    "globals": [
 | 
			
		||||
      "page"
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "d3": "^7.0.0",
 | 
			
		||||
    "khroma": "^2.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "concurrently": "^7.4.0",
 | 
			
		||||
    "rimraf": "^3.0.2"
 | 
			
		||||
  },
 | 
			
		||||
  "resolutions": {
 | 
			
		||||
    "d3": "^7.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "files": [
 | 
			
		||||
    "dist"
 | 
			
		||||
  ],
 | 
			
		||||
  "sideEffects": [
 | 
			
		||||
    "**/*.css",
 | 
			
		||||
    "**/*.scss"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								packages/mermaid-timeline/src/detector.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								packages/mermaid-timeline/src/detector.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
import type { ExternalDiagramDefinition } from 'mermaid';
 | 
			
		||||
 | 
			
		||||
const id = 'timeline';
 | 
			
		||||
 | 
			
		||||
const detector = (txt: string) => {
 | 
			
		||||
  return txt.match(/^\s*timeline/) !== null;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const loader = async () => {
 | 
			
		||||
  const { diagram } = await import('./diagram-definition');
 | 
			
		||||
  return { id, diagram };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const plugin: ExternalDiagramDefinition = {
 | 
			
		||||
  id,
 | 
			
		||||
  detector,
 | 
			
		||||
  loader,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default plugin;
 | 
			
		||||
							
								
								
									
										14
									
								
								packages/mermaid-timeline/src/diagram-definition.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								packages/mermaid-timeline/src/diagram-definition.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
// @ts-ignore: TODO Fix ts errors
 | 
			
		||||
import parser from './parser/timeline.jison';
 | 
			
		||||
import * as db from './timelineDb';
 | 
			
		||||
import renderer from './timelineRenderer';
 | 
			
		||||
import styles from './styles';
 | 
			
		||||
import { injectUtils } from './mermaidUtils';
 | 
			
		||||
 | 
			
		||||
export const diagram = {
 | 
			
		||||
  db,
 | 
			
		||||
  renderer,
 | 
			
		||||
  parser,
 | 
			
		||||
  styles,
 | 
			
		||||
  injectUtils,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										70
									
								
								packages/mermaid-timeline/src/mermaidUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								packages/mermaid-timeline/src/mermaidUtils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
const warning = () => null;
 | 
			
		||||
let localCommonDb = {};
 | 
			
		||||
 | 
			
		||||
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
 | 
			
		||||
 | 
			
		||||
export const LEVELS: Record<LogLevel, number> = {
 | 
			
		||||
  trace: 0,
 | 
			
		||||
  debug: 1,
 | 
			
		||||
  info: 2,
 | 
			
		||||
  warn: 3,
 | 
			
		||||
  error: 4,
 | 
			
		||||
  fatal: 5,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const log: Record<keyof typeof LEVELS, typeof console.log> = {
 | 
			
		||||
  trace: warning,
 | 
			
		||||
  debug: warning,
 | 
			
		||||
  info: warning,
 | 
			
		||||
  warn: warning,
 | 
			
		||||
  error: warning,
 | 
			
		||||
  fatal: warning,
 | 
			
		||||
};
 | 
			
		||||
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
 | 
			
		||||
export let getConfig: () => object;
 | 
			
		||||
export let sanitizeText: (str: string) => string;
 | 
			
		||||
export const getCommonDb=() => localCommonDb;
 | 
			
		||||
/**
 | 
			
		||||
 * Placeholder for the real function that will be injected by mermaid.
 | 
			
		||||
 */
 | 
			
		||||
// eslint-disable @typescript-eslint/no-explicit-any
 | 
			
		||||
export let setupGraphViewbox: (
 | 
			
		||||
  graph: any,
 | 
			
		||||
  svgElem: any,
 | 
			
		||||
  padding: any,
 | 
			
		||||
  useMaxWidth: boolean
 | 
			
		||||
) => void;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function called by mermaid that injects utility functions that help the diagram to be a good citizen.
 | 
			
		||||
 * @param _log
 | 
			
		||||
 * @param _setLogLevel
 | 
			
		||||
 * @param _getConfig
 | 
			
		||||
 * @param _sanitizeText
 | 
			
		||||
 * @param _setupGraphViewbox
 | 
			
		||||
 * @param _commonDb
 | 
			
		||||
 */
 | 
			
		||||
export const injectUtils = (
 | 
			
		||||
  _log: Record<keyof typeof LEVELS, typeof console.log>,
 | 
			
		||||
  _setLogLevel: any,
 | 
			
		||||
  _getConfig: any,
 | 
			
		||||
  _sanitizeText: any,
 | 
			
		||||
  _setupGraphViewbox: any,
 | 
			
		||||
  _commonDb: any
 | 
			
		||||
) => {
 | 
			
		||||
  _log.info('Mermaid utils injected into timeline-diagram');
 | 
			
		||||
  log.trace = _log.trace;
 | 
			
		||||
  log.debug = _log.debug;
 | 
			
		||||
  log.info = _log.info;
 | 
			
		||||
  log.warn = _log.warn;
 | 
			
		||||
  log.error = _log.error;
 | 
			
		||||
  log.fatal = _log.fatal;
 | 
			
		||||
  setLogLevel = _setLogLevel;
 | 
			
		||||
  getConfig = _getConfig;
 | 
			
		||||
  sanitizeText = _sanitizeText;
 | 
			
		||||
  setupGraphViewbox = _setupGraphViewbox;
 | 
			
		||||
  localCommonDb = _commonDb;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										106
									
								
								packages/mermaid-timeline/src/parser/timeline.jison
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								packages/mermaid-timeline/src/parser/timeline.jison
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
/** mermaid
 | 
			
		||||
 *  https://mermaidjs.github.io/
 | 
			
		||||
 *  (c) 2015 Knut Sveidqvist
 | 
			
		||||
 *  MIT license.
 | 
			
		||||
 */
 | 
			
		||||
%lex
 | 
			
		||||
%options case-insensitive
 | 
			
		||||
%x acc_title
 | 
			
		||||
%x acc_descr
 | 
			
		||||
%x acc_descr_multiline
 | 
			
		||||
 | 
			
		||||
// Directive states
 | 
			
		||||
%x open_directive type_directive arg_directive
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
 | 
			
		||||
\%\%\{                                                          { this.begin('open_directive'); return 'open_directive'; }
 | 
			
		||||
<open_directive>((?:(?!\}\%\%)[^:.])*)                          { this.begin('type_directive'); return 'type_directive'; }
 | 
			
		||||
<type_directive>":"                                             { this.popState(); this.begin('arg_directive'); return ':'; }
 | 
			
		||||
<type_directive,arg_directive>\}\%\%                            { this.popState(); this.popState(); return 'close_directive'; }
 | 
			
		||||
<arg_directive>((?:(?!\}\%\%).|\n)*)                            return 'arg_directive';
 | 
			
		||||
\%%(?!\{)[^\n]*                                                 /* skip comments */
 | 
			
		||||
[^\}]\%\%[^\n]*                                                 /* skip comments */
 | 
			
		||||
[\n]+                   return 'NEWLINE';
 | 
			
		||||
\s+                     /* skip whitespace */
 | 
			
		||||
\#[^\n]*                /* skip comments */
 | 
			
		||||
 | 
			
		||||
"timeline"               return 'timeline';
 | 
			
		||||
"title"\s[^#\n;]+       return 'title';
 | 
			
		||||
accTitle\s*":"\s*                                               { this.begin("acc_title");return 'acc_title'; }
 | 
			
		||||
<acc_title>(?!\n|;|#)*[^\n]*                                    { this.popState(); return "acc_title_value"; }
 | 
			
		||||
accDescr\s*":"\s*                                               { this.begin("acc_descr");return 'acc_descr'; }
 | 
			
		||||
<acc_descr>(?!\n|;|#)*[^\n]*                                    { this.popState(); return "acc_descr_value"; }
 | 
			
		||||
accDescr\s*"{"\s*                                { this.begin("acc_descr_multiline");}
 | 
			
		||||
<acc_descr_multiline>[\}]                       { this.popState(); }
 | 
			
		||||
<acc_descr_multiline>[^\}]*                     return "acc_descr_multiline_value";
 | 
			
		||||
"section"\s[^#:\n;]+    return 'section';
 | 
			
		||||
 | 
			
		||||
// event starting with "==>" keyword
 | 
			
		||||
":"\s[^#:\n;]+        return 'event';
 | 
			
		||||
[^#:\n;]+               return 'period';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<<EOF>>                 return 'EOF';
 | 
			
		||||
.                       return 'INVALID';
 | 
			
		||||
 | 
			
		||||
/lex
 | 
			
		||||
 | 
			
		||||
%left '^'
 | 
			
		||||
 | 
			
		||||
%start start
 | 
			
		||||
 | 
			
		||||
%% /* language grammar */
 | 
			
		||||
 | 
			
		||||
start
 | 
			
		||||
	: timeline document 'EOF' { return $2; }
 | 
			
		||||
	| directive start
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
document
 | 
			
		||||
	: /* empty */ { $$ = [] }
 | 
			
		||||
	| document line {$1.push($2);$$ = $1}
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
line
 | 
			
		||||
	: SPACE statement { $$ = $2 }
 | 
			
		||||
	| statement { $$ = $1 }
 | 
			
		||||
	| NEWLINE { $$=[];}
 | 
			
		||||
	| EOF { $$=[];}
 | 
			
		||||
	;
 | 
			
		||||
 | 
			
		||||
directive
 | 
			
		||||
  : openDirective typeDirective closeDirective 'NEWLINE'
 | 
			
		||||
  | openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
 | 
			
		||||
  ;
 | 
			
		||||
 | 
			
		||||
statement
 | 
			
		||||
  : title {yy.getCommonDb().setDiagramTitle($1.substr(6));$$=$1.substr(6);}
 | 
			
		||||
  | acc_title acc_title_value  { $$=$2.trim();yy.getCommonDb().setAccTitle($$); }
 | 
			
		||||
  | acc_descr acc_descr_value  { $$=$2.trim();yy.getCommonDb().setAccDescription($$); }
 | 
			
		||||
  | acc_descr_multiline_value { $$=$1.trim();yy.getCommonDb().setAccDescription($$); }
 | 
			
		||||
  | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
 | 
			
		||||
  | period {yy.addTask($1,0,'');$$=$1;}
 | 
			
		||||
  | period event {yy.addTask($1,0,$2.substr(2));$$=$1;}
 | 
			
		||||
  | event {yy.addEvent($1.substr(2));$$=$1;}
 | 
			
		||||
  | directive
 | 
			
		||||
  ;
 | 
			
		||||
 | 
			
		||||
openDirective
 | 
			
		||||
  : open_directive { yy.parseDirective('%%{', 'open_directive'); }
 | 
			
		||||
  ;
 | 
			
		||||
 | 
			
		||||
typeDirective
 | 
			
		||||
  : type_directive { yy.parseDirective($1, 'type_directive'); }
 | 
			
		||||
  ;
 | 
			
		||||
 | 
			
		||||
argDirective
 | 
			
		||||
  : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
 | 
			
		||||
  ;
 | 
			
		||||
 | 
			
		||||
closeDirective
 | 
			
		||||
  : close_directive { yy.parseDirective('}%%', 'close_directive', 'timeline'); }
 | 
			
		||||
  ;
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
							
								
								
									
										150
									
								
								packages/mermaid-timeline/src/parser/timeline.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								packages/mermaid-timeline/src/parser/timeline.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
import { parser } from './journey';
 | 
			
		||||
import journeyDb from '../journeyDb';
 | 
			
		||||
 | 
			
		||||
const parserFnConstructor = (str) => {
 | 
			
		||||
  return () => {
 | 
			
		||||
    parser.parse(str);
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('when parsing a journey diagram it', function () {
 | 
			
		||||
  beforeEach(function () {
 | 
			
		||||
    parser.yy = journeyDb;
 | 
			
		||||
    parser.yy.clear();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should handle a title definition', function () {
 | 
			
		||||
    const str = 'journey\ntitle Adding journey diagram functionality to mermaid';
 | 
			
		||||
 | 
			
		||||
    expect(parserFnConstructor(str)).not.toThrow();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should handle an accessibility description (accDescr)', function () {
 | 
			
		||||
    const str =
 | 
			
		||||
      'journey\n' +
 | 
			
		||||
      'accDescr: A user journey for family shopping\n' +
 | 
			
		||||
      'title Adding journey diagram functionality to mermaid\n' +
 | 
			
		||||
      'section Order from website';
 | 
			
		||||
 | 
			
		||||
    expect(parserFnConstructor(str)).not.toThrow();
 | 
			
		||||
  });
 | 
			
		||||
  it('should handle an accessibility multiline description (accDescr)', function () {
 | 
			
		||||
    const str =
 | 
			
		||||
      'journey\n' +
 | 
			
		||||
      `accDescr {
 | 
			
		||||
        A user journey for
 | 
			
		||||
        family shopping
 | 
			
		||||
      }` +
 | 
			
		||||
      'title Adding journey diagram functionality to mermaid\n' +
 | 
			
		||||
      'accTitle: Adding acc journey diagram functionality to mermaid\n' +
 | 
			
		||||
      'section Order from website';
 | 
			
		||||
 | 
			
		||||
    expect(parserFnConstructor(str)).not.toThrow();
 | 
			
		||||
    expect(journeyDb.getAccDescription()).toBe('A user journey for\nfamily shopping');
 | 
			
		||||
    expect(journeyDb.getDiagramTitle()).toBe('Adding journey diagram functionality to mermaid');
 | 
			
		||||
    expect(journeyDb.getAccTitle()).toBe('Adding acc journey diagram functionality to mermaid');
 | 
			
		||||
  });
 | 
			
		||||
  it('should handle an accessibility title (accDescr)', function () {
 | 
			
		||||
    const str = `journey
 | 
			
		||||
    accTitle: The title
 | 
			
		||||
    section Order from website`;
 | 
			
		||||
 | 
			
		||||
    expect(parserFnConstructor(str)).not.toThrow();
 | 
			
		||||
    expect(journeyDb.getAccDescription()).toBe('');
 | 
			
		||||
    expect(journeyDb.getAccTitle()).toBe('The title');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should handle a section definition', function () {
 | 
			
		||||
    const str =
 | 
			
		||||
      'journey\n' +
 | 
			
		||||
      'title Adding journey diagram functionality to mermaid\n' +
 | 
			
		||||
      'section Order from website';
 | 
			
		||||
 | 
			
		||||
    expect(parserFnConstructor(str)).not.toThrow();
 | 
			
		||||
  });
 | 
			
		||||
  it('should handle multiline section titles with different line breaks', function () {
 | 
			
		||||
    const str =
 | 
			
		||||
      'journey\n' +
 | 
			
		||||
      'title Adding gantt diagram functionality to mermaid\n' +
 | 
			
		||||
      'section Line1<br>Line2<br/>Line3</br />Line4<br\t/>Line5';
 | 
			
		||||
 | 
			
		||||
    expect(parserFnConstructor(str)).not.toThrow();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should handle a task definition', function () {
 | 
			
		||||
    const str =
 | 
			
		||||
      'journey\n' +
 | 
			
		||||
      'title Adding journey diagram functionality to mermaid\n' +
 | 
			
		||||
      'section Documentation\n' +
 | 
			
		||||
      'A task: 5: Alice, Bob, Charlie\n' +
 | 
			
		||||
      'B task: 3:Bob, Charlie\n' +
 | 
			
		||||
      'C task: 5\n' +
 | 
			
		||||
      'D task: 5: Charlie, Alice\n' +
 | 
			
		||||
      'E task: 5:\n' +
 | 
			
		||||
      'section Another section\n' +
 | 
			
		||||
      'P task: 5:\n' +
 | 
			
		||||
      'Q task: 5:\n' +
 | 
			
		||||
      'R task: 5:';
 | 
			
		||||
    expect(parserFnConstructor(str)).not.toThrow();
 | 
			
		||||
 | 
			
		||||
    const tasks = parser.yy.getTasks();
 | 
			
		||||
    expect(tasks.length).toEqual(8);
 | 
			
		||||
 | 
			
		||||
    expect(tasks[0]).toEqual({
 | 
			
		||||
      score: 5,
 | 
			
		||||
      people: ['Alice', 'Bob', 'Charlie'],
 | 
			
		||||
      section: 'Documentation',
 | 
			
		||||
      task: 'A task',
 | 
			
		||||
      type: 'Documentation',
 | 
			
		||||
    });
 | 
			
		||||
    expect(tasks[1]).toEqual({
 | 
			
		||||
      score: 3,
 | 
			
		||||
      people: ['Bob', 'Charlie'],
 | 
			
		||||
      section: 'Documentation',
 | 
			
		||||
      type: 'Documentation',
 | 
			
		||||
      task: 'B task',
 | 
			
		||||
    });
 | 
			
		||||
    expect(tasks[2]).toEqual({
 | 
			
		||||
      score: 5,
 | 
			
		||||
      people: [],
 | 
			
		||||
      section: 'Documentation',
 | 
			
		||||
      type: 'Documentation',
 | 
			
		||||
      task: 'C task',
 | 
			
		||||
    });
 | 
			
		||||
    expect(tasks[3]).toEqual({
 | 
			
		||||
      score: 5,
 | 
			
		||||
      people: ['Charlie', 'Alice'],
 | 
			
		||||
      section: 'Documentation',
 | 
			
		||||
      task: 'D task',
 | 
			
		||||
      type: 'Documentation',
 | 
			
		||||
    });
 | 
			
		||||
    expect(tasks[4]).toEqual({
 | 
			
		||||
      score: 5,
 | 
			
		||||
      people: [''],
 | 
			
		||||
      section: 'Documentation',
 | 
			
		||||
      type: 'Documentation',
 | 
			
		||||
      task: 'E task',
 | 
			
		||||
    });
 | 
			
		||||
    expect(tasks[5]).toEqual({
 | 
			
		||||
      score: 5,
 | 
			
		||||
      people: [''],
 | 
			
		||||
      section: 'Another section',
 | 
			
		||||
      type: 'Another section',
 | 
			
		||||
      task: 'P task',
 | 
			
		||||
    });
 | 
			
		||||
    expect(tasks[6]).toEqual({
 | 
			
		||||
      score: 5,
 | 
			
		||||
      people: [''],
 | 
			
		||||
      section: 'Another section',
 | 
			
		||||
      type: 'Another section',
 | 
			
		||||
      task: 'Q task',
 | 
			
		||||
    });
 | 
			
		||||
    expect(tasks[7]).toEqual({
 | 
			
		||||
      score: 5,
 | 
			
		||||
      people: [''],
 | 
			
		||||
      section: 'Another section',
 | 
			
		||||
      type: 'Another section',
 | 
			
		||||
      task: 'R task',
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										78
									
								
								packages/mermaid-timeline/src/styles.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								packages/mermaid-timeline/src/styles.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
import { darken, lighten, isDark } from 'khroma';
 | 
			
		||||
 | 
			
		||||
const genSections = (options) => {
 | 
			
		||||
  let sections = '';
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) {
 | 
			
		||||
    options['lineColor' + i] = options['lineColor' + i] || options['cScaleInv' + i];
 | 
			
		||||
    if (isDark(options['lineColor' + i])) {
 | 
			
		||||
      options['lineColor' + i] = lighten(options['lineColor' + i], 20);
 | 
			
		||||
    } else {
 | 
			
		||||
      options['lineColor' + i] = darken(options['lineColor' + i], 20);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) {
 | 
			
		||||
    const sw = '' + (17 - 3 * i);
 | 
			
		||||
    sections += `
 | 
			
		||||
    .section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${
 | 
			
		||||
      i - 1
 | 
			
		||||
    } path  {
 | 
			
		||||
      fill: ${options['cScale' + i]};
 | 
			
		||||
    }
 | 
			
		||||
    .section-${i - 1} text {
 | 
			
		||||
     fill: ${options['cScaleLabel' + i]};
 | 
			
		||||
    }
 | 
			
		||||
    .node-icon-${i - 1} {
 | 
			
		||||
      font-size: 40px;
 | 
			
		||||
      color: ${options['cScaleLabel' + i]};
 | 
			
		||||
    }
 | 
			
		||||
    .section-edge-${i - 1}{
 | 
			
		||||
      stroke: ${options['cScale' + i]};
 | 
			
		||||
    }
 | 
			
		||||
    .edge-depth-${i - 1}{
 | 
			
		||||
      stroke-width: ${sw};
 | 
			
		||||
    }
 | 
			
		||||
    .section-${i - 1} line {
 | 
			
		||||
      stroke: ${options['cScaleInv' + i]} ;
 | 
			
		||||
      stroke-width: 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .disabled, .disabled circle, .disabled text {
 | 
			
		||||
      fill: lightgray;
 | 
			
		||||
    }
 | 
			
		||||
    .disabled text {
 | 
			
		||||
      fill: #efefef;
 | 
			
		||||
    }
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
  return sections;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getStyles = (options) =>
 | 
			
		||||
  `
 | 
			
		||||
  .edge {
 | 
			
		||||
    stroke-width: 3;
 | 
			
		||||
  }
 | 
			
		||||
  ${genSections(options)}
 | 
			
		||||
  .section-root rect, .section-root path, .section-root circle  {
 | 
			
		||||
    fill: ${options.git0};
 | 
			
		||||
  }
 | 
			
		||||
  .section-root text {
 | 
			
		||||
    fill: ${options.gitBranchLabel0};
 | 
			
		||||
  }
 | 
			
		||||
  .icon-container {
 | 
			
		||||
    height:100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
  }
 | 
			
		||||
  .edge {
 | 
			
		||||
    fill: none;
 | 
			
		||||
  }
 | 
			
		||||
  .eventWrapper  {
 | 
			
		||||
   filter: brightness(120%);
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
`;
 | 
			
		||||
export default getStyles;
 | 
			
		||||
							
								
								
									
										604
									
								
								packages/mermaid-timeline/src/svgDraw.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										604
									
								
								packages/mermaid-timeline/src/svgDraw.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,604 @@
 | 
			
		||||
import { arc as d3arc , select} from 'd3';
 | 
			
		||||
const MAX_SECTIONS = 12;
 | 
			
		||||
 | 
			
		||||
export const drawRect = function (elem, rectData) {
 | 
			
		||||
  const rectElem = elem.append('rect');
 | 
			
		||||
  rectElem.attr('x', rectData.x);
 | 
			
		||||
  rectElem.attr('y', rectData.y);
 | 
			
		||||
  rectElem.attr('fill', rectData.fill);
 | 
			
		||||
  rectElem.attr('stroke', rectData.stroke);
 | 
			
		||||
  rectElem.attr('width', rectData.width);
 | 
			
		||||
  rectElem.attr('height', rectData.height);
 | 
			
		||||
  rectElem.attr('rx', rectData.rx);
 | 
			
		||||
  rectElem.attr('ry', rectData.ry);
 | 
			
		||||
 | 
			
		||||
  if (typeof rectData.class !== 'undefined') {
 | 
			
		||||
    rectElem.attr('class', rectData.class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return rectElem;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const drawFace = function (element, faceData) {
 | 
			
		||||
  const radius = 15;
 | 
			
		||||
  const circleElement = element
 | 
			
		||||
    .append('circle')
 | 
			
		||||
    .attr('cx', faceData.cx)
 | 
			
		||||
    .attr('cy', faceData.cy)
 | 
			
		||||
    .attr('class', 'face')
 | 
			
		||||
    .attr('r', radius)
 | 
			
		||||
    .attr('stroke-width', 2)
 | 
			
		||||
    .attr('overflow', 'visible');
 | 
			
		||||
 | 
			
		||||
  const face = element.append('g');
 | 
			
		||||
 | 
			
		||||
  //left eye
 | 
			
		||||
  face
 | 
			
		||||
    .append('circle')
 | 
			
		||||
    .attr('cx', faceData.cx - radius / 3)
 | 
			
		||||
    .attr('cy', faceData.cy - radius / 3)
 | 
			
		||||
    .attr('r', 1.5)
 | 
			
		||||
    .attr('stroke-width', 2)
 | 
			
		||||
    .attr('fill', '#666')
 | 
			
		||||
    .attr('stroke', '#666');
 | 
			
		||||
 | 
			
		||||
  //right eye
 | 
			
		||||
  face
 | 
			
		||||
    .append('circle')
 | 
			
		||||
    .attr('cx', faceData.cx + radius / 3)
 | 
			
		||||
    .attr('cy', faceData.cy - radius / 3)
 | 
			
		||||
    .attr('r', 1.5)
 | 
			
		||||
    .attr('stroke-width', 2)
 | 
			
		||||
    .attr('fill', '#666')
 | 
			
		||||
    .attr('stroke', '#666');
 | 
			
		||||
 | 
			
		||||
  /** @param {any} face */
 | 
			
		||||
  function smile(face) {
 | 
			
		||||
    const arc = d3arc()
 | 
			
		||||
      .startAngle(Math.PI / 2)
 | 
			
		||||
      .endAngle(3 * (Math.PI / 2))
 | 
			
		||||
      .innerRadius(radius / 2)
 | 
			
		||||
      .outerRadius(radius / 2.2);
 | 
			
		||||
    //mouth
 | 
			
		||||
    face
 | 
			
		||||
      .append('path')
 | 
			
		||||
      .attr('class', 'mouth')
 | 
			
		||||
      .attr('d', arc)
 | 
			
		||||
      .attr('transform', 'translate(' + faceData.cx + ',' + (faceData.cy + 2) + ')');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @param {any} face */
 | 
			
		||||
  function sad(face) {
 | 
			
		||||
    const arc = d3arc()
 | 
			
		||||
      .startAngle((3 * Math.PI) / 2)
 | 
			
		||||
      .endAngle(5 * (Math.PI / 2))
 | 
			
		||||
      .innerRadius(radius / 2)
 | 
			
		||||
      .outerRadius(radius / 2.2);
 | 
			
		||||
    //mouth
 | 
			
		||||
    face
 | 
			
		||||
      .append('path')
 | 
			
		||||
      .attr('class', 'mouth')
 | 
			
		||||
      .attr('d', arc)
 | 
			
		||||
      .attr('transform', 'translate(' + faceData.cx + ',' + (faceData.cy + 7) + ')');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @param {any} face */
 | 
			
		||||
  function ambivalent(face) {
 | 
			
		||||
    face
 | 
			
		||||
      .append('line')
 | 
			
		||||
      .attr('class', 'mouth')
 | 
			
		||||
      .attr('stroke', 2)
 | 
			
		||||
      .attr('x1', faceData.cx - 5)
 | 
			
		||||
      .attr('y1', faceData.cy + 7)
 | 
			
		||||
      .attr('x2', faceData.cx + 5)
 | 
			
		||||
      .attr('y2', faceData.cy + 7)
 | 
			
		||||
      .attr('class', 'mouth')
 | 
			
		||||
      .attr('stroke-width', '1px')
 | 
			
		||||
      .attr('stroke', '#666');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (faceData.score > 3) {
 | 
			
		||||
    smile(face);
 | 
			
		||||
  } else if (faceData.score < 3) {
 | 
			
		||||
    sad(face);
 | 
			
		||||
  } else {
 | 
			
		||||
    ambivalent(face);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return circleElement;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const drawCircle = function (element, circleData) {
 | 
			
		||||
  const circleElement = element.append('circle');
 | 
			
		||||
  circleElement.attr('cx', circleData.cx);
 | 
			
		||||
  circleElement.attr('cy', circleData.cy);
 | 
			
		||||
  circleElement.attr('class', 'actor-' + circleData.pos);
 | 
			
		||||
  circleElement.attr('fill', circleData.fill);
 | 
			
		||||
  circleElement.attr('stroke', circleData.stroke);
 | 
			
		||||
  circleElement.attr('r', circleData.r);
 | 
			
		||||
 | 
			
		||||
  if (typeof circleElement.class !== 'undefined') {
 | 
			
		||||
    circleElement.attr('class', circleElement.class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (typeof circleData.title !== 'undefined') {
 | 
			
		||||
    circleElement.append('title').text(circleData.title);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return circleElement;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const drawText = function (elem, textData) {
 | 
			
		||||
  // Remove and ignore br:s
 | 
			
		||||
  const nText = textData.text.replace(/<br\s*\/?>/gi, ' ');
 | 
			
		||||
 | 
			
		||||
  const textElem = elem.append('text');
 | 
			
		||||
  textElem.attr('x', textData.x);
 | 
			
		||||
  textElem.attr('y', textData.y);
 | 
			
		||||
  textElem.attr('class', 'legend');
 | 
			
		||||
 | 
			
		||||
  textElem.style('text-anchor', textData.anchor);
 | 
			
		||||
 | 
			
		||||
  if (typeof textData.class !== 'undefined') {
 | 
			
		||||
    textElem.attr('class', textData.class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const span = textElem.append('tspan');
 | 
			
		||||
  span.attr('x', textData.x + textData.textMargin * 2);
 | 
			
		||||
  span.text(nText);
 | 
			
		||||
 | 
			
		||||
  return textElem;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const drawLabel = function (elem, txtObject) {
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {any} x
 | 
			
		||||
   * @param {any} y
 | 
			
		||||
   * @param {any} width
 | 
			
		||||
   * @param {any} height
 | 
			
		||||
   * @param {any} cut
 | 
			
		||||
   */
 | 
			
		||||
  function genPoints(x, y, width, height, cut) {
 | 
			
		||||
    return (
 | 
			
		||||
      x +
 | 
			
		||||
      ',' +
 | 
			
		||||
      y +
 | 
			
		||||
      ' ' +
 | 
			
		||||
      (x + width) +
 | 
			
		||||
      ',' +
 | 
			
		||||
      y +
 | 
			
		||||
      ' ' +
 | 
			
		||||
      (x + width) +
 | 
			
		||||
      ',' +
 | 
			
		||||
      (y + height - cut) +
 | 
			
		||||
      ' ' +
 | 
			
		||||
      (x + width - cut * 1.2) +
 | 
			
		||||
      ',' +
 | 
			
		||||
      (y + height) +
 | 
			
		||||
      ' ' +
 | 
			
		||||
      x +
 | 
			
		||||
      ',' +
 | 
			
		||||
      (y + height)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  const polygon = elem.append('polygon');
 | 
			
		||||
  polygon.attr('points', genPoints(txtObject.x, txtObject.y, 50, 20, 7));
 | 
			
		||||
  polygon.attr('class', 'labelBox');
 | 
			
		||||
 | 
			
		||||
  txtObject.y = txtObject.y + txtObject.labelMargin;
 | 
			
		||||
  txtObject.x = txtObject.x + 0.5 * txtObject.labelMargin;
 | 
			
		||||
  drawText(elem, txtObject);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const drawSection = function (elem, section, conf) {
 | 
			
		||||
  const g = elem.append('g');
 | 
			
		||||
 | 
			
		||||
  const rect = getNoteRect();
 | 
			
		||||
  rect.x = section.x;
 | 
			
		||||
  rect.y = section.y;
 | 
			
		||||
  rect.fill = section.fill;
 | 
			
		||||
  rect.width = conf.width;
 | 
			
		||||
  rect.height = conf.height;
 | 
			
		||||
  rect.class = 'journey-section section-type-' + section.num;
 | 
			
		||||
  rect.rx = 3;
 | 
			
		||||
  rect.ry = 3;
 | 
			
		||||
  drawRect(g, rect);
 | 
			
		||||
 | 
			
		||||
  _drawTextCandidateFunc(conf)(
 | 
			
		||||
    section.text,
 | 
			
		||||
    g,
 | 
			
		||||
    rect.x,
 | 
			
		||||
    rect.y,
 | 
			
		||||
    rect.width,
 | 
			
		||||
    rect.height,
 | 
			
		||||
    { class: 'journey-section section-type-' + section.num },
 | 
			
		||||
    conf,
 | 
			
		||||
    section.colour
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let taskCount = -1;
 | 
			
		||||
/**
 | 
			
		||||
 * Draws an actor in the diagram with the attached line
 | 
			
		||||
 *
 | 
			
		||||
 * @param {any} elem The HTML element
 | 
			
		||||
 * @param {any} task The task to render
 | 
			
		||||
 * @param {any} conf The global configuration
 | 
			
		||||
 */
 | 
			
		||||
export const drawTask = function (elem, task, conf) {
 | 
			
		||||
  const center = task.x + conf.width / 2;
 | 
			
		||||
  const g = elem.append('g');
 | 
			
		||||
  taskCount++;
 | 
			
		||||
  const maxHeight = 300 + 5 * 30;
 | 
			
		||||
  g.append('line')
 | 
			
		||||
    .attr('id', 'task' + taskCount)
 | 
			
		||||
    .attr('x1', center)
 | 
			
		||||
    .attr('y1', task.y)
 | 
			
		||||
    .attr('x2', center)
 | 
			
		||||
    .attr('y2', maxHeight)
 | 
			
		||||
    .attr('class', 'task-line')
 | 
			
		||||
    .attr('stroke-width', '1px')
 | 
			
		||||
    .attr('stroke-dasharray', '4 2')
 | 
			
		||||
    .attr('stroke', '#666');
 | 
			
		||||
 | 
			
		||||
  drawFace(g, {
 | 
			
		||||
    cx: center,
 | 
			
		||||
    cy: 300 + (5 - task.score) * 30,
 | 
			
		||||
    score: task.score,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const rect = getNoteRect();
 | 
			
		||||
  rect.x = task.x;
 | 
			
		||||
  rect.y = task.y;
 | 
			
		||||
  rect.fill = task.fill;
 | 
			
		||||
  rect.width = conf.width;
 | 
			
		||||
  rect.height = conf.height;
 | 
			
		||||
  rect.class = 'task task-type-' + task.num;
 | 
			
		||||
  rect.rx = 3;
 | 
			
		||||
  rect.ry = 3;
 | 
			
		||||
  drawRect(g, rect);
 | 
			
		||||
 | 
			
		||||
  let xPos = task.x + 14;
 | 
			
		||||
  // task.people.forEach((person) => {
 | 
			
		||||
  //   const colour = task.actors[person].color;
 | 
			
		||||
 | 
			
		||||
  //   const circle = {
 | 
			
		||||
  //     cx: xPos,
 | 
			
		||||
  //     cy: task.y,
 | 
			
		||||
  //     r: 7,
 | 
			
		||||
  //     fill: colour,
 | 
			
		||||
  //     stroke: '#000',
 | 
			
		||||
  //     title: person,
 | 
			
		||||
  //     pos: task.actors[person].position,
 | 
			
		||||
  //   };
 | 
			
		||||
 | 
			
		||||
  //   drawCircle(g, circle);
 | 
			
		||||
  //   xPos += 10;
 | 
			
		||||
  // });
 | 
			
		||||
 | 
			
		||||
  _drawTextCandidateFunc(conf)(
 | 
			
		||||
    task.task,
 | 
			
		||||
    g,
 | 
			
		||||
    rect.x,
 | 
			
		||||
    rect.y,
 | 
			
		||||
    rect.width,
 | 
			
		||||
    rect.height,
 | 
			
		||||
    { class: 'task' },
 | 
			
		||||
    conf,
 | 
			
		||||
    task.colour
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Draws a background rectangle
 | 
			
		||||
 *
 | 
			
		||||
 * @param {any} elem The html element
 | 
			
		||||
 * @param {any} bounds The bounds of the drawing
 | 
			
		||||
 */
 | 
			
		||||
export const drawBackgroundRect = function (elem, bounds) {
 | 
			
		||||
  const rectElem = drawRect(elem, {
 | 
			
		||||
    x: bounds.startx,
 | 
			
		||||
    y: bounds.starty,
 | 
			
		||||
    width: bounds.stopx - bounds.startx,
 | 
			
		||||
    height: bounds.stopy - bounds.starty,
 | 
			
		||||
    fill: bounds.fill,
 | 
			
		||||
    class: 'rect',
 | 
			
		||||
  });
 | 
			
		||||
  rectElem.lower();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getTextObj = function () {
 | 
			
		||||
  return {
 | 
			
		||||
    x: 0,
 | 
			
		||||
    y: 0,
 | 
			
		||||
    fill: undefined,
 | 
			
		||||
    'text-anchor': 'start',
 | 
			
		||||
    width: 100,
 | 
			
		||||
    height: 100,
 | 
			
		||||
    textMargin: 0,
 | 
			
		||||
    rx: 0,
 | 
			
		||||
    ry: 0,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getNoteRect = function () {
 | 
			
		||||
  return {
 | 
			
		||||
    x: 0,
 | 
			
		||||
    y: 0,
 | 
			
		||||
    width: 100,
 | 
			
		||||
    anchor: 'start',
 | 
			
		||||
    height: 100,
 | 
			
		||||
    rx: 0,
 | 
			
		||||
    ry: 0,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const _drawTextCandidateFunc = (function () {
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {any} content
 | 
			
		||||
   * @param {any} g
 | 
			
		||||
   * @param {any} x
 | 
			
		||||
   * @param {any} y
 | 
			
		||||
   * @param {any} width
 | 
			
		||||
   * @param {any} height
 | 
			
		||||
   * @param {any} textAttrs
 | 
			
		||||
   * @param {any} colour
 | 
			
		||||
   */
 | 
			
		||||
  function byText(content, g, x, y, width, height, textAttrs, colour) {
 | 
			
		||||
    const text = g
 | 
			
		||||
      .append('text')
 | 
			
		||||
      .attr('x', x + width / 2)
 | 
			
		||||
      .attr('y', y + height / 2 + 5)
 | 
			
		||||
      .style('font-color', colour)
 | 
			
		||||
      .style('text-anchor', 'middle')
 | 
			
		||||
      .text(content);
 | 
			
		||||
    _setTextAttrs(text, textAttrs);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {any} content
 | 
			
		||||
   * @param {any} g
 | 
			
		||||
   * @param {any} x
 | 
			
		||||
   * @param {any} y
 | 
			
		||||
   * @param {any} width
 | 
			
		||||
   * @param {any} height
 | 
			
		||||
   * @param {any} textAttrs
 | 
			
		||||
   * @param {any} conf
 | 
			
		||||
   * @param {any} colour
 | 
			
		||||
   */
 | 
			
		||||
  function byTspan(content, g, x, y, width, height, textAttrs, conf, colour) {
 | 
			
		||||
    const { taskFontSize, taskFontFamily } = conf;
 | 
			
		||||
 | 
			
		||||
    const lines = content.split(/<br\s*\/?>/gi);
 | 
			
		||||
    for (let i = 0; i < lines.length; i++) {
 | 
			
		||||
      const dy = i * taskFontSize - (taskFontSize * (lines.length - 1)) / 2;
 | 
			
		||||
      const text = g
 | 
			
		||||
        .append('text')
 | 
			
		||||
        .attr('x', x + width / 2)
 | 
			
		||||
        .attr('y', y)
 | 
			
		||||
        .attr('fill', colour)
 | 
			
		||||
        .style('text-anchor', 'middle')
 | 
			
		||||
        .style('font-size', taskFontSize)
 | 
			
		||||
        .style('font-family', taskFontFamily);
 | 
			
		||||
      text
 | 
			
		||||
        .append('tspan')
 | 
			
		||||
        .attr('x', x + width / 2)
 | 
			
		||||
        .attr('dy', dy)
 | 
			
		||||
        .text(lines[i]);
 | 
			
		||||
 | 
			
		||||
      text
 | 
			
		||||
        .attr('y', y + height / 2.0)
 | 
			
		||||
        .attr('dominant-baseline', 'central')
 | 
			
		||||
        .attr('alignment-baseline', 'central');
 | 
			
		||||
 | 
			
		||||
      _setTextAttrs(text, textAttrs);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {any} content
 | 
			
		||||
   * @param {any} g
 | 
			
		||||
   * @param {any} x
 | 
			
		||||
   * @param {any} y
 | 
			
		||||
   * @param {any} width
 | 
			
		||||
   * @param {any} height
 | 
			
		||||
   * @param {any} textAttrs
 | 
			
		||||
   * @param {any} conf
 | 
			
		||||
   */
 | 
			
		||||
  function byFo(content, g, x, y, width, height, textAttrs, conf) {
 | 
			
		||||
    const body = g.append('switch');
 | 
			
		||||
    const f = body
 | 
			
		||||
      .append('foreignObject')
 | 
			
		||||
      .attr('x', x)
 | 
			
		||||
      .attr('y', y)
 | 
			
		||||
      .attr('width', width)
 | 
			
		||||
      .attr('height', height)
 | 
			
		||||
      .attr('position', 'fixed');
 | 
			
		||||
 | 
			
		||||
    const text = f
 | 
			
		||||
      .append('xhtml:div')
 | 
			
		||||
      .style('display', 'table')
 | 
			
		||||
      .style('height', '100%')
 | 
			
		||||
      .style('width', '100%');
 | 
			
		||||
 | 
			
		||||
    text
 | 
			
		||||
      .append('div')
 | 
			
		||||
      .attr('class', 'label')
 | 
			
		||||
      .style('display', 'table-cell')
 | 
			
		||||
      .style('text-align', 'center')
 | 
			
		||||
      .style('vertical-align', 'middle')
 | 
			
		||||
      .text(content);
 | 
			
		||||
 | 
			
		||||
    byTspan(content, body, x, y, width, height, textAttrs, conf);
 | 
			
		||||
    _setTextAttrs(text, textAttrs);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {any} toText
 | 
			
		||||
   * @param {any} fromTextAttrsDict
 | 
			
		||||
   */
 | 
			
		||||
  function _setTextAttrs(toText, fromTextAttrsDict) {
 | 
			
		||||
    for (const key in fromTextAttrsDict) {
 | 
			
		||||
      if (key in fromTextAttrsDict) {
 | 
			
		||||
        // noinspection JSUnfilteredForInLoop
 | 
			
		||||
        toText.attr(key, fromTextAttrsDict[key]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return function (conf) {
 | 
			
		||||
    return conf.textPlacement === 'fo' ? byFo : conf.textPlacement === 'old' ? byText : byTspan;
 | 
			
		||||
  };
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
const initGraphics = function (graphics) {
 | 
			
		||||
  graphics
 | 
			
		||||
    .append('defs')
 | 
			
		||||
    .append('marker')
 | 
			
		||||
    .attr('id', 'arrowhead')
 | 
			
		||||
    .attr('refX', 5)
 | 
			
		||||
    .attr('refY', 2)
 | 
			
		||||
    .attr('markerWidth', 6)
 | 
			
		||||
    .attr('markerHeight', 4)
 | 
			
		||||
    .attr('orient', 'auto')
 | 
			
		||||
    .append('path')
 | 
			
		||||
    .attr('d', 'M 0,0 V 4 L6,2 Z'); // this is actual shape for arrowhead
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {string} text The text to be wrapped
 | 
			
		||||
 * @param {number} width The max width of the text
 | 
			
		||||
 */
 | 
			
		||||
function wrap(text, width) {
 | 
			
		||||
  text.each(function () {
 | 
			
		||||
    var text = select(this),
 | 
			
		||||
      words = text
 | 
			
		||||
        .text()
 | 
			
		||||
        .split(/(\s+|<br>)/)
 | 
			
		||||
        .reverse(),
 | 
			
		||||
      word,
 | 
			
		||||
      line = [],
 | 
			
		||||
      lineHeight = 1.1, // ems
 | 
			
		||||
      y = text.attr('y'),
 | 
			
		||||
      dy = parseFloat(text.attr('dy')),
 | 
			
		||||
      tspan = text
 | 
			
		||||
        .text(null)
 | 
			
		||||
        .append('tspan')
 | 
			
		||||
        .attr('x', 0)
 | 
			
		||||
        .attr('y', y)
 | 
			
		||||
        .attr('dy', dy + 'em');
 | 
			
		||||
    for (let j = 0; j < words.length; j++) {
 | 
			
		||||
      word = words[words.length - 1 - j];
 | 
			
		||||
      line.push(word);
 | 
			
		||||
      tspan.text(line.join(' ').trim());
 | 
			
		||||
      if (tspan.node().getComputedTextLength() > width || word === '<br>') {
 | 
			
		||||
        line.pop();
 | 
			
		||||
        tspan.text(line.join(' ').trim());
 | 
			
		||||
        if (word === '<br>') {
 | 
			
		||||
          line = [''];
 | 
			
		||||
        } else {
 | 
			
		||||
          line = [word];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tspan = text
 | 
			
		||||
          .append('tspan')
 | 
			
		||||
          .attr('x', 0)
 | 
			
		||||
          .attr('y', y)
 | 
			
		||||
          .attr('dy', lineHeight + 'em')
 | 
			
		||||
          .text(word);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const drawNode = function (elem, node, fullSection, conf) {
 | 
			
		||||
  const section = (fullSection % MAX_SECTIONS) - 1;
 | 
			
		||||
  const nodeElem = elem.append('g');
 | 
			
		||||
  node.section = section;
 | 
			
		||||
  nodeElem.attr(
 | 
			
		||||
    'class',
 | 
			
		||||
    (node.class ? node.class + ' ' : '') +
 | 
			
		||||
      'timeline-node ' +
 | 
			
		||||
      (section < 0 ? 'section-root' : 'section-' + section)
 | 
			
		||||
  );
 | 
			
		||||
  const bkgElem = nodeElem.append('g');
 | 
			
		||||
 | 
			
		||||
  // Create the wrapped text element
 | 
			
		||||
  const textElem = nodeElem.append('g');
 | 
			
		||||
 | 
			
		||||
  const txt = textElem
 | 
			
		||||
    .append('text')
 | 
			
		||||
    .text(node.descr)
 | 
			
		||||
    .attr('dy', '1em')
 | 
			
		||||
    .attr('alignment-baseline', 'middle')
 | 
			
		||||
    .attr('dominant-baseline', 'middle')
 | 
			
		||||
    .attr('text-anchor', 'middle')
 | 
			
		||||
    .call(wrap, node.width);
 | 
			
		||||
  const bbox = txt.node().getBBox();
 | 
			
		||||
  const fontSize = conf.fontSize && conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
 | 
			
		||||
  node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
 | 
			
		||||
  node.height = Math.max(node.height, node.maxHeight);
 | 
			
		||||
  node.width = node.width + 2 * node.padding;
 | 
			
		||||
 | 
			
		||||
  textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')');
 | 
			
		||||
 | 
			
		||||
  // Create the background element
 | 
			
		||||
  defaultBkg(bkgElem, node, section, conf);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  return node;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 export const getVirtualNodeHeight = function (elem,node,conf) {
 | 
			
		||||
 const textElem = elem.append('g');
 | 
			
		||||
    const txt = textElem
 | 
			
		||||
    .append('text')
 | 
			
		||||
    .text(node.descr)
 | 
			
		||||
    .attr('dy', '1em')
 | 
			
		||||
    .attr('alignment-baseline', 'middle')
 | 
			
		||||
    .attr('dominant-baseline', 'middle')
 | 
			
		||||
    .attr('text-anchor', 'middle')
 | 
			
		||||
    .call(wrap, node.width);
 | 
			
		||||
  const bbox = txt.node().getBBox();
 | 
			
		||||
  const fontSize = conf.fontSize && conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
 | 
			
		||||
  textElem.remove();
 | 
			
		||||
  return bbox.height + fontSize * 1.1 * 0.5 + node.padding;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const defaultBkg = function (elem, node, section) {
 | 
			
		||||
  const rd = 5;
 | 
			
		||||
  elem
 | 
			
		||||
    .append('path')
 | 
			
		||||
    .attr('id', 'node-' + node.id)
 | 
			
		||||
    .attr('class', 'node-bkg node-' + node.type)
 | 
			
		||||
    .attr(
 | 
			
		||||
      'd',
 | 
			
		||||
      `M0 ${node.height - rd} v${-node.height + 2 * rd} q0,-5 5,-5 h${
 | 
			
		||||
        node.width - 2 * rd
 | 
			
		||||
      } q5,0 5,5 v${node.height - rd} H0 Z`
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  elem
 | 
			
		||||
    .append('line')
 | 
			
		||||
    .attr('class', 'node-line-' + section)
 | 
			
		||||
    .attr('x1', 0)
 | 
			
		||||
    .attr('y1', node.height)
 | 
			
		||||
    .attr('x2', node.width)
 | 
			
		||||
    .attr('y2', node.height);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  drawRect,
 | 
			
		||||
  drawCircle,
 | 
			
		||||
  drawSection,
 | 
			
		||||
  drawText,
 | 
			
		||||
  drawLabel,
 | 
			
		||||
  drawTask,
 | 
			
		||||
  drawBackgroundRect,
 | 
			
		||||
  getTextObj,
 | 
			
		||||
  getNoteRect,
 | 
			
		||||
  initGraphics,
 | 
			
		||||
  drawNode,
 | 
			
		||||
  getVirtualNodeHeight,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										100
									
								
								packages/mermaid-timeline/src/timelineDb.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								packages/mermaid-timeline/src/timelineDb.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
import { getCommonDb as _getCommonDb,log  } from './mermaidUtils';
 | 
			
		||||
 | 
			
		||||
let currentSection = '';
 | 
			
		||||
let currentTaskId = 0;
 | 
			
		||||
 | 
			
		||||
const sections = [];
 | 
			
		||||
const tasks = [];
 | 
			
		||||
const rawTasks = [];
 | 
			
		||||
 | 
			
		||||
export const getCommonDb = _getCommonDb;
 | 
			
		||||
 | 
			
		||||
export const clear = function () {
 | 
			
		||||
  sections.length = 0;
 | 
			
		||||
  tasks.length = 0;
 | 
			
		||||
  currentSection = '';
 | 
			
		||||
  rawTasks.length = 0;
 | 
			
		||||
  _getCommonDb().clear();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addSection = function (txt) {
 | 
			
		||||
  currentSection = txt;
 | 
			
		||||
  sections.push(txt);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getSections = function () {
 | 
			
		||||
  return sections;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getTasks = function () {
 | 
			
		||||
  let allItemsProcessed = compileTasks();
 | 
			
		||||
  const maxDepth = 100;
 | 
			
		||||
  let iterationCount = 0;
 | 
			
		||||
  while (!allItemsProcessed && iterationCount < maxDepth) {
 | 
			
		||||
    allItemsProcessed = compileTasks();
 | 
			
		||||
    iterationCount++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tasks.push(...rawTasks);
 | 
			
		||||
 | 
			
		||||
  return tasks;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addTask = function (period, length, event) {
 | 
			
		||||
 | 
			
		||||
  const rawTask = {
 | 
			
		||||
    id: currentTaskId++,
 | 
			
		||||
    section: currentSection,
 | 
			
		||||
    type: currentSection,
 | 
			
		||||
    task: period,
 | 
			
		||||
    score : length?length:0,
 | 
			
		||||
    //if event is defined, then add it the events array
 | 
			
		||||
    events: event?[event]:[],
 | 
			
		||||
  };
 | 
			
		||||
  rawTasks.push(rawTask);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addEvent = function (event) {
 | 
			
		||||
 // fetch current task with currnetTaskId
 | 
			
		||||
  const currentTask = rawTasks.find((task) => task.id === currentTaskId - 1);
 | 
			
		||||
  //add event to the events array
 | 
			
		||||
  currentTask.events.push(event);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const addTaskOrg = function (descr) {
 | 
			
		||||
  const newTask = {
 | 
			
		||||
    section: currentSection,
 | 
			
		||||
    type: currentSection,
 | 
			
		||||
    description: descr,
 | 
			
		||||
    task: descr,
 | 
			
		||||
    classes: [],
 | 
			
		||||
  };
 | 
			
		||||
  tasks.push(newTask);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const compileTasks = function () {
 | 
			
		||||
  const compileTask = function (pos) {
 | 
			
		||||
    return rawTasks[pos].processed;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let allProcessed = true;
 | 
			
		||||
  for (let i = 0; i < rawTasks.length; i++) {
 | 
			
		||||
    compileTask(i);
 | 
			
		||||
 | 
			
		||||
    allProcessed = allProcessed && rawTasks[i].processed;
 | 
			
		||||
  }
 | 
			
		||||
  return allProcessed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  clear,
 | 
			
		||||
  getCommonDb,
 | 
			
		||||
  addSection,
 | 
			
		||||
  getSections,
 | 
			
		||||
  getTasks,
 | 
			
		||||
  addTask,
 | 
			
		||||
  addTaskOrg,
 | 
			
		||||
  addEvent,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										91
									
								
								packages/mermaid-timeline/src/timelineDb.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								packages/mermaid-timeline/src/timelineDb.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
import journeyDb from './journeyDb';
 | 
			
		||||
 | 
			
		||||
describe('when using the journeyDb', function () {
 | 
			
		||||
  beforeEach(function () {
 | 
			
		||||
    journeyDb.clear();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('when calling the clear function', function () {
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
      journeyDb.addSection('weekends skip test');
 | 
			
		||||
      journeyDb.addTask('test1', '4: id1, id3');
 | 
			
		||||
      journeyDb.addTask('test2', '2: id2');
 | 
			
		||||
      journeyDb.clear();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it.each`
 | 
			
		||||
      fn               | expected
 | 
			
		||||
      ${'getTasks'}    | ${[]}
 | 
			
		||||
      ${'getAccTitle'} | ${''}
 | 
			
		||||
      ${'getSections'} | ${[]}
 | 
			
		||||
      ${'getActors'}   | ${[]}
 | 
			
		||||
    `('should clear $fn', ({ fn, expected }) => {
 | 
			
		||||
      expect(journeyDb[fn]()).toEqual(expected);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('when calling the clear function', function () {
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
      journeyDb.addSection('weekends skip test');
 | 
			
		||||
      journeyDb.addTask('test1', '3: id1, id3');
 | 
			
		||||
      journeyDb.addTask('test2', '1: id2');
 | 
			
		||||
      journeyDb.clear();
 | 
			
		||||
    });
 | 
			
		||||
    it.each`
 | 
			
		||||
      fn                     | expected
 | 
			
		||||
      ${'getTasks'}          | ${[]}
 | 
			
		||||
      ${'getAccTitle'}       | ${''}
 | 
			
		||||
      ${'getAccDescription'} | ${''}
 | 
			
		||||
      ${'getSections'}       | ${[]}
 | 
			
		||||
    `('should clear $fn', ({ fn, expected }) => {
 | 
			
		||||
      expect(journeyDb[fn]()).toEqual(expected);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('tasks and actors should be added', function () {
 | 
			
		||||
    journeyDb.setAccTitle('Shopping');
 | 
			
		||||
    journeyDb.setAccDescription('A user journey for family shopping');
 | 
			
		||||
    journeyDb.addSection('Journey to the shops');
 | 
			
		||||
    journeyDb.addTask('Get car keys', ':5:Dad');
 | 
			
		||||
    journeyDb.addTask('Go to car', ':3:Dad, Mum, Child#1, Child#2');
 | 
			
		||||
    journeyDb.addTask('Drive to supermarket', ':4:Dad');
 | 
			
		||||
    journeyDb.addSection('Do shopping');
 | 
			
		||||
    journeyDb.addTask('Go shopping', ':5:Mum');
 | 
			
		||||
 | 
			
		||||
    expect(journeyDb.getAccTitle()).toEqual('Shopping');
 | 
			
		||||
    expect(journeyDb.getAccDescription()).toEqual('A user journey for family shopping');
 | 
			
		||||
    expect(journeyDb.getTasks()).toEqual([
 | 
			
		||||
      {
 | 
			
		||||
        score: 5,
 | 
			
		||||
        people: ['Dad'],
 | 
			
		||||
        section: 'Journey to the shops',
 | 
			
		||||
        task: 'Get car keys',
 | 
			
		||||
        type: 'Journey to the shops',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        score: 3,
 | 
			
		||||
        people: ['Dad', 'Mum', 'Child#1', 'Child#2'],
 | 
			
		||||
        section: 'Journey to the shops',
 | 
			
		||||
        task: 'Go to car',
 | 
			
		||||
        type: 'Journey to the shops',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        score: 4,
 | 
			
		||||
        people: ['Dad'],
 | 
			
		||||
        section: 'Journey to the shops',
 | 
			
		||||
        task: 'Drive to supermarket',
 | 
			
		||||
        type: 'Journey to the shops',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        score: 5,
 | 
			
		||||
        people: ['Mum'],
 | 
			
		||||
        section: 'Do shopping',
 | 
			
		||||
        task: 'Go shopping',
 | 
			
		||||
        type: 'Do shopping',
 | 
			
		||||
      },
 | 
			
		||||
    ]);
 | 
			
		||||
    expect(journeyDb.getActors()).toEqual(['Child#1', 'Child#2', 'Dad', 'Mum']);
 | 
			
		||||
 | 
			
		||||
    expect(journeyDb.getSections()).toEqual(['Journey to the shops', 'Do shopping']);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										415
									
								
								packages/mermaid-timeline/src/timelineRenderer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										415
									
								
								packages/mermaid-timeline/src/timelineRenderer.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,415 @@
 | 
			
		||||
// @ts-nocheck TODO: fix file
 | 
			
		||||
import { select } from 'd3';
 | 
			
		||||
import svgDraw from './svgDraw';
 | 
			
		||||
import { configureSvgSize } from '../../setupGraphViewbox';
 | 
			
		||||
import addSVGAccessibilityFields from '../../accessibility';
 | 
			
		||||
 | 
			
		||||
import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
 | 
			
		||||
 | 
			
		||||
export const setConf = function (cnf) {
 | 
			
		||||
  const keys = Object.keys(cnf);
 | 
			
		||||
 | 
			
		||||
  keys.forEach(function (key) {
 | 
			
		||||
    conf[key] = cnf[key];
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const draw = function (text, id, version, diagObj) {
 | 
			
		||||
  //1. Fetch the configuration
 | 
			
		||||
  const conf = getConfig();
 | 
			
		||||
  const LEFT_MARGIN = conf.leftMargin?conf.leftMargin:50;
 | 
			
		||||
 | 
			
		||||
  //2. Clear the diagram db before parsing
 | 
			
		||||
  diagObj.db.clear();
 | 
			
		||||
 | 
			
		||||
  //3. Parse the diagram text
 | 
			
		||||
  diagObj.parser.parse(text + '\n');
 | 
			
		||||
 | 
			
		||||
  log.info('timeline', diagObj.db);
 | 
			
		||||
 | 
			
		||||
  const securityLevel = conf.securityLevel;
 | 
			
		||||
  // Handle root and Document for when rendering in sandbox mode
 | 
			
		||||
  let sandboxElement;
 | 
			
		||||
  if (securityLevel === 'sandbox') {
 | 
			
		||||
    sandboxElement = select('#i' + id);
 | 
			
		||||
  }
 | 
			
		||||
  const root =
 | 
			
		||||
    securityLevel === 'sandbox'
 | 
			
		||||
      ? select(sandboxElement.nodes()[0].contentDocument.body)
 | 
			
		||||
      : select('body');
 | 
			
		||||
 | 
			
		||||
  // Init bounds
 | 
			
		||||
  bounds.init();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  const svg = root.select('#' + id);
 | 
			
		||||
 | 
			
		||||
  svg.append('g');
 | 
			
		||||
 | 
			
		||||
  //4. Fetch the diagram data
 | 
			
		||||
  const tasks = diagObj.db.getTasks();
 | 
			
		||||
  const title = diagObj.db.getCommonDb().getDiagramTitle();
 | 
			
		||||
 | 
			
		||||
  //log tasks
 | 
			
		||||
  log.info(tasks);
 | 
			
		||||
 | 
			
		||||
  //5. Initialize the diagram
 | 
			
		||||
  svgDraw.initGraphics(svg);
 | 
			
		||||
 | 
			
		||||
  //bounds.insert(0, 0, LEFT_MARGIN, 0);
 | 
			
		||||
  // fetch Sections
 | 
			
		||||
  const sections = diagObj.db.getSections();
 | 
			
		||||
  // log sections
 | 
			
		||||
  log.info(sections);
 | 
			
		||||
 | 
			
		||||
  let maxSectionHeight = 0;
 | 
			
		||||
  let maxTaskHeight = 0;
 | 
			
		||||
  let sectionBeginX = 0;
 | 
			
		||||
  let sectionBeginY = 0;
 | 
			
		||||
  let masterX = 50 + LEFT_MARGIN;
 | 
			
		||||
  sectionBeginX = masterX;
 | 
			
		||||
  let masterY = 50;
 | 
			
		||||
  sectionBeginY=50;
 | 
			
		||||
  //draw sections
 | 
			
		||||
  let sectionNumber = 0;
 | 
			
		||||
 | 
			
		||||
  //Calculate the max height of the sections
 | 
			
		||||
  sections.forEach(function (section) {
 | 
			
		||||
    const sectionNode = {
 | 
			
		||||
      number: sectionNumber,
 | 
			
		||||
      descr: section,
 | 
			
		||||
      section: sectionNumber,
 | 
			
		||||
      width: 150,
 | 
			
		||||
      padding: 20,
 | 
			
		||||
      maxHeight: maxSectionHeight,
 | 
			
		||||
    };
 | 
			
		||||
    const sectionHeight = svgDraw.getVirtualNodeHeight(svg, sectionNode, conf);
 | 
			
		||||
    log.info('sectionHeight before draw', sectionHeight);
 | 
			
		||||
    maxSectionHeight = Math.max(maxSectionHeight, sectionHeight +20);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
//tasks length and maxEventCount
 | 
			
		||||
let maxEventCount = 0;
 | 
			
		||||
  log.info('tasks.length', tasks.length);
 | 
			
		||||
   //calculate max task height
 | 
			
		||||
  // for loop till tasks.length
 | 
			
		||||
  for (let i = 0; i < tasks.length; i++) {
 | 
			
		||||
    const task = tasks[i];
 | 
			
		||||
 | 
			
		||||
    const taskNode = {
 | 
			
		||||
      number: i,
 | 
			
		||||
      descr: task,
 | 
			
		||||
      section: task.section,
 | 
			
		||||
      width: 150,
 | 
			
		||||
      padding: 20,
 | 
			
		||||
      maxHeight: maxTaskHeight,
 | 
			
		||||
    };
 | 
			
		||||
    const taskHeight = svgDraw.getVirtualNodeHeight(svg, taskNode, conf);
 | 
			
		||||
    log.info('taskHeight before draw', taskHeight);
 | 
			
		||||
    maxTaskHeight = Math.max(maxTaskHeight, taskHeight + 20);
 | 
			
		||||
 | 
			
		||||
    //calculate maxEventCount
 | 
			
		||||
    maxEventCount = Math.max(maxEventCount, task.events.length);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  log.info('maxSectionHeight before draw', maxSectionHeight);
 | 
			
		||||
  log.info('maxTaskHeight before draw', maxTaskHeight);
 | 
			
		||||
 | 
			
		||||
  if (sections && sections.length > 0) {
 | 
			
		||||
    sections.forEach((section) => {
 | 
			
		||||
 | 
			
		||||
      const sectionNode = {
 | 
			
		||||
        number: sectionNumber,
 | 
			
		||||
        descr: section,
 | 
			
		||||
        section: sectionNumber,
 | 
			
		||||
        width: 150,
 | 
			
		||||
        padding: 20,
 | 
			
		||||
        maxHeight: maxSectionHeight,
 | 
			
		||||
      };
 | 
			
		||||
      //log section node
 | 
			
		||||
      log.info('sectionNode', sectionNode);
 | 
			
		||||
      const sectionNodeWrapper = svg.append('g');
 | 
			
		||||
      const node = svgDraw.drawNode(sectionNodeWrapper, sectionNode, sectionNumber, conf);
 | 
			
		||||
      // add node to section list
 | 
			
		||||
      //sectionList.push(node);
 | 
			
		||||
      //const nodeHeight = node.height + 20;
 | 
			
		||||
      //Post process the node
 | 
			
		||||
      //append g
 | 
			
		||||
 | 
			
		||||
      sectionNodeWrapper.attr(
 | 
			
		||||
        'transform',
 | 
			
		||||
        `translate(${masterX}, ${sectionBeginY})`
 | 
			
		||||
      );
 | 
			
		||||
      //maxSectionHeight = Math.max(maxSectionHeight, nodeHeight);
 | 
			
		||||
      masterY += maxSectionHeight + 50;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      //draw tasks for this section
 | 
			
		||||
      //filter task where tasks.section == section
 | 
			
		||||
      const tasksForSection = tasks.filter((task) => task.section === section);
 | 
			
		||||
      if (tasksForSection.length > 0) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        drawTasks(svg, tasksForSection, sectionNumber, masterX, masterY, maxTaskHeight, conf, maxEventCount,false);
 | 
			
		||||
      }
 | 
			
		||||
      // todo replace with total width of section and its tasks
 | 
			
		||||
      masterX += 200 * Math.max(tasksForSection.length, 1);
 | 
			
		||||
 | 
			
		||||
      masterY = sectionBeginY;
 | 
			
		||||
      sectionNumber++;
 | 
			
		||||
    });
 | 
			
		||||
  } else {
 | 
			
		||||
    //draw tasks
 | 
			
		||||
    drawTasks(svg, tasks, sectionNumber, masterX, masterY, maxTaskHeight, conf, maxEventCount,true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // draw tasks
 | 
			
		||||
  //drawTasks(svg, tasks, 0);
 | 
			
		||||
 | 
			
		||||
  const box = bounds.getBounds();
 | 
			
		||||
  if (title) {
 | 
			
		||||
    svg
 | 
			
		||||
      .append('text')
 | 
			
		||||
      .text(title)
 | 
			
		||||
      .attr('x', LEFT_MARGIN)
 | 
			
		||||
      .attr('font-size', '4ex')
 | 
			
		||||
      .attr('font-weight', 'bold')
 | 
			
		||||
      .attr('y', 25);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const height = box.stopy - box.starty + 2 * conf.diagramMarginY;
 | 
			
		||||
  const width = LEFT_MARGIN + box.stopx + 2 * conf.diagramMarginX;
 | 
			
		||||
 | 
			
		||||
// Setup the view box and size of the svg element
 | 
			
		||||
  setupGraphViewbox(undefined, svg, conf.timeline.padding, conf.timeline.useMaxWidth);
 | 
			
		||||
 | 
			
		||||
  //5. Draw the diagram
 | 
			
		||||
 const maxTaskLength = 500;
 | 
			
		||||
 | 
			
		||||
 // Draw activity line
 | 
			
		||||
  svg
 | 
			
		||||
    .append('line')
 | 
			
		||||
    .attr('x1', LEFT_MARGIN)
 | 
			
		||||
    .attr('y1', maxSectionHeight + maxTaskHeight +150) // One section head + one task + margins
 | 
			
		||||
    .attr('x2', tasks && tasks.length? (tasks.length*200)+ 400 :  400) // Subtract stroke width so arrow point is retained
 | 
			
		||||
    .attr('y2', maxSectionHeight + maxTaskHeight +150)
 | 
			
		||||
    .attr('stroke-width', 4)
 | 
			
		||||
    .attr('stroke', 'black')
 | 
			
		||||
    .attr('marker-end', 'url(#arrowhead)');
 | 
			
		||||
 | 
			
		||||
  const extraVertForTitle = title ? 70 : 0;
 | 
			
		||||
  svg.attr('viewBox', `${box.startx} -25 ${width} ${height + extraVertForTitle}`);
 | 
			
		||||
  svg.attr('preserveAspectRatio', 'xMinYMin meet');
 | 
			
		||||
  svg.attr('height', height + extraVertForTitle + 25);
 | 
			
		||||
 | 
			
		||||
  // addSVGAccessibilityFields(diagObj.db, diagram, id);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const bounds = {
 | 
			
		||||
  data: {
 | 
			
		||||
    startx: undefined,
 | 
			
		||||
    stopx: undefined,
 | 
			
		||||
    starty: undefined,
 | 
			
		||||
    stopy: undefined,
 | 
			
		||||
  },
 | 
			
		||||
  verticalPos: 0,
 | 
			
		||||
 | 
			
		||||
  sequenceItems: [],
 | 
			
		||||
  init: function () {
 | 
			
		||||
    this.sequenceItems = [];
 | 
			
		||||
    this.data = {
 | 
			
		||||
      startx: undefined,
 | 
			
		||||
      stopx: undefined,
 | 
			
		||||
      starty: undefined,
 | 
			
		||||
      stopy: undefined,
 | 
			
		||||
    };
 | 
			
		||||
    this.verticalPos = 0;
 | 
			
		||||
  },
 | 
			
		||||
  updateVal: function (obj, key, val, fun) {
 | 
			
		||||
    if (typeof obj[key] === 'undefined') {
 | 
			
		||||
      obj[key] = val;
 | 
			
		||||
    } else {
 | 
			
		||||
      obj[key] = fun(val, obj[key]);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  updateBounds: function (startx, starty, stopx, stopy) {
 | 
			
		||||
    const conf = getConfig().timeline;
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-this-alias
 | 
			
		||||
    const _self = this;
 | 
			
		||||
    let cnt = 0;
 | 
			
		||||
    /** @param {any} type */
 | 
			
		||||
    function updateFn(type) {
 | 
			
		||||
      return function updateItemBounds(item) {
 | 
			
		||||
        cnt++;
 | 
			
		||||
        // The loop sequenceItems is a stack so the biggest margins in the beginning of the sequenceItems
 | 
			
		||||
        const n = _self.sequenceItems.length - cnt + 1;
 | 
			
		||||
        _self.updateVal(item, 'starty', starty - n * conf.boxMargin, Math.min);
 | 
			
		||||
        _self.updateVal(item, 'stopy', stopy + n * conf.boxMargin, Math.max);
 | 
			
		||||
 | 
			
		||||
        _self.updateVal(bounds.data, 'startx', startx - n * conf.boxMargin, Math.min);
 | 
			
		||||
        _self.updateVal(bounds.data, 'stopx', stopx + n * conf.boxMargin, Math.max);
 | 
			
		||||
 | 
			
		||||
        if (!(type === 'activation')) {
 | 
			
		||||
          _self.updateVal(item, 'startx', startx - n * conf.boxMargin, Math.min);
 | 
			
		||||
          _self.updateVal(item, 'stopx', stopx + n * conf.boxMargin, Math.max);
 | 
			
		||||
 | 
			
		||||
          _self.updateVal(bounds.data, 'starty', starty - n * conf.boxMargin, Math.min);
 | 
			
		||||
          _self.updateVal(bounds.data, 'stopy', stopy + n * conf.boxMargin, Math.max);
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.sequenceItems.forEach(updateFn());
 | 
			
		||||
  },
 | 
			
		||||
  insert: function (startx, starty, stopx, stopy) {
 | 
			
		||||
    const _startx = Math.min(startx, stopx);
 | 
			
		||||
    const _stopx = Math.max(startx, stopx);
 | 
			
		||||
    const _starty = Math.min(starty, stopy);
 | 
			
		||||
    const _stopy = Math.max(starty, stopy);
 | 
			
		||||
 | 
			
		||||
    this.updateVal(bounds.data, 'startx', _startx, Math.min);
 | 
			
		||||
    this.updateVal(bounds.data, 'starty', _starty, Math.min);
 | 
			
		||||
    this.updateVal(bounds.data, 'stopx', _stopx, Math.max);
 | 
			
		||||
    this.updateVal(bounds.data, 'stopy', _stopy, Math.max);
 | 
			
		||||
 | 
			
		||||
    this.updateBounds(_startx, _starty, _stopx, _stopy);
 | 
			
		||||
  },
 | 
			
		||||
  bumpVerticalPos: function (bump) {
 | 
			
		||||
    this.verticalPos = this.verticalPos + bump;
 | 
			
		||||
    this.data.stopy = this.verticalPos;
 | 
			
		||||
  },
 | 
			
		||||
  getVerticalPos: function () {
 | 
			
		||||
    return this.verticalPos;
 | 
			
		||||
  },
 | 
			
		||||
  getBounds: function () {
 | 
			
		||||
    return this.data;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const drawTasks = function (diagram, tasks, sectionColor, masterX, masterY, maxTaskHeight,conf,maxEventCount, isWithoutSections) {
 | 
			
		||||
 | 
			
		||||
  const taskBeginY = masterY;
 | 
			
		||||
 | 
			
		||||
  const taskBeginX = masterX;
 | 
			
		||||
 | 
			
		||||
  // Draw the tasks
 | 
			
		||||
  for (let i = 0; i < tasks.length; i++) {
 | 
			
		||||
 | 
			
		||||
    const task = tasks[i];
 | 
			
		||||
    // create node from task
 | 
			
		||||
    const taskNode = {
 | 
			
		||||
      descr: task.task,
 | 
			
		||||
      section: sectionColor,
 | 
			
		||||
      number : sectionColor,
 | 
			
		||||
      width: 150,
 | 
			
		||||
      padding: 20,
 | 
			
		||||
      maxHeight: maxTaskHeight,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //log task node
 | 
			
		||||
    log.info('taskNode', taskNode);
 | 
			
		||||
    // create task wrapper
 | 
			
		||||
    const taskWrapper = diagram.append('g').attr('class', 'taskWrapper');
 | 
			
		||||
    const node = svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
 | 
			
		||||
    const taskHeight = node.height;
 | 
			
		||||
    //log task height
 | 
			
		||||
    log.info('taskHeight after draw', taskHeight);
 | 
			
		||||
    taskWrapper.attr(
 | 
			
		||||
      'transform',
 | 
			
		||||
      `translate(${masterX}, ${masterY})`
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // update max task height
 | 
			
		||||
    maxTaskHeight = Math.max(maxTaskHeight, taskHeight);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
     // if task has events, draw them
 | 
			
		||||
    if (task.events) {
 | 
			
		||||
    // draw a line between the task and the events
 | 
			
		||||
      const lineWrapper = diagram.append('g').attr('class', 'lineWrapper');
 | 
			
		||||
 | 
			
		||||
      let linelength = maxTaskHeight;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //add margin to task
 | 
			
		||||
    masterY += 100;
 | 
			
		||||
     linelength = linelength+ drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
 | 
			
		||||
      masterY -= 100;
 | 
			
		||||
 | 
			
		||||
      lineWrapper
 | 
			
		||||
    .append('line')
 | 
			
		||||
    .attr('x1', masterX + 190/2)
 | 
			
		||||
    .attr('y1', masterY + maxTaskHeight) // One section head + one task + margins
 | 
			
		||||
    .attr('x2', masterX + 190/2) // Subtract stroke width so arrow point is retained
 | 
			
		||||
    .attr('y2', masterY  + linelength + maxEventCount * 100)
 | 
			
		||||
    .attr('stroke-width', 2)
 | 
			
		||||
    .attr('stroke', 'black')
 | 
			
		||||
    .attr('marker-end', 'url(#arrowhead)')
 | 
			
		||||
      .attr('stroke-dasharray', "5,5");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    masterX = masterX + 200;
 | 
			
		||||
    if (isWithoutSections) {
 | 
			
		||||
       sectionColor++;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// reset Y coordinate for next section
 | 
			
		||||
    masterY= masterY -10; ;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const drawEvents = function (diagram, events, sectionColor, masterX, masterY, conf) {
 | 
			
		||||
 | 
			
		||||
  let maxEventHeight = 0;
 | 
			
		||||
  const eventBeginY = masterY;
 | 
			
		||||
  masterY = masterY + 100
 | 
			
		||||
  // Draw the events
 | 
			
		||||
  for (let i = 0; i < events.length; i++) {
 | 
			
		||||
    const event = events[i];
 | 
			
		||||
    // create node from event
 | 
			
		||||
    const eventNode = {
 | 
			
		||||
      descr: event,
 | 
			
		||||
      section: sectionColor,
 | 
			
		||||
      number : sectionColor,
 | 
			
		||||
      width: 150,
 | 
			
		||||
      padding: 20,
 | 
			
		||||
      maxHeight: 50,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //log task node
 | 
			
		||||
    log.info('eventNode', eventNode);
 | 
			
		||||
    // create event wrapper
 | 
			
		||||
    const eventWrapper = diagram.append('g').attr('class', 'eventWrapper');
 | 
			
		||||
    const node = svgDraw.drawNode(eventWrapper, eventNode, sectionColor, conf)
 | 
			
		||||
    const eventHeight = node.height;
 | 
			
		||||
    maxEventHeight= maxEventHeight + eventHeight;
 | 
			
		||||
    eventWrapper.attr(
 | 
			
		||||
      'transform',
 | 
			
		||||
      `translate(${masterX}, ${masterY})`
 | 
			
		||||
    );
 | 
			
		||||
    masterY = masterY + 10 + eventHeight;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  // set masterY back to eventBeginY
 | 
			
		||||
  masterY = eventBeginY;
 | 
			
		||||
  return maxEventHeight;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  setConf,
 | 
			
		||||
  draw,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/mermaid-timeline/src/types/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/mermaid-timeline/src/types/index.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
export {};
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface Window {
 | 
			
		||||
    mermaid: any; // 👈️ turn off type checking
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/mermaid-timeline/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/mermaid-timeline/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
  "extends": "../../tsconfig.json",
 | 
			
		||||
  "module": "esnext",
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "rootDir": "./src",
 | 
			
		||||
    "outDir": "./dist"
 | 
			
		||||
  },
 | 
			
		||||
  "include": ["./src/**/*.ts"],
 | 
			
		||||
  "typeRoots": ["./src/types"]
 | 
			
		||||
}
 | 
			
		||||
@@ -26,6 +26,7 @@ export interface MermaidConfig {
 | 
			
		||||
  sequence?: SequenceDiagramConfig;
 | 
			
		||||
  gantt?: GanttDiagramConfig;
 | 
			
		||||
  journey?: JourneyDiagramConfig;
 | 
			
		||||
  timeline?: TimelineDiagramConfig;
 | 
			
		||||
  class?: ClassDiagramConfig;
 | 
			
		||||
  state?: StateDiagramConfig;
 | 
			
		||||
  er?: ErDiagramConfig;
 | 
			
		||||
@@ -292,6 +293,29 @@ export interface JourneyDiagramConfig extends BaseDiagramConfig {
 | 
			
		||||
  sectionColours?: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TimelineDiagramConfig extends BaseDiagramConfig {
 | 
			
		||||
  diagramMarginX?: number;
 | 
			
		||||
  diagramMarginY?: number;
 | 
			
		||||
  leftMargin?: number;
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height?: number;
 | 
			
		||||
  boxMargin?: number;
 | 
			
		||||
  boxTextMargin?: number;
 | 
			
		||||
  noteMargin?: number;
 | 
			
		||||
  messageMargin?: number;
 | 
			
		||||
  messageAlign?: string;
 | 
			
		||||
  bottomMarginAdj?: number;
 | 
			
		||||
  rightAngles?: boolean;
 | 
			
		||||
  taskFontSize?: string | number;
 | 
			
		||||
  taskFontFamily?: string;
 | 
			
		||||
  taskMargin?: number;
 | 
			
		||||
  activationWidth?: number;
 | 
			
		||||
  textPlacement?: string;
 | 
			
		||||
  actorColours?: string[];
 | 
			
		||||
  sectionFills?: string[];
 | 
			
		||||
  sectionColours?: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface GanttDiagramConfig extends BaseDiagramConfig {
 | 
			
		||||
  titleTopMargin?: number;
 | 
			
		||||
  barHeight?: number;
 | 
			
		||||
 
 | 
			
		||||
@@ -861,6 +861,155 @@ const config: Partial<MermaidConfig> = {
 | 
			
		||||
    sectionFills: ['#191970', '#8B008B', '#4B0082', '#2F4F4F', '#800000', '#8B4513', '#00008B'],
 | 
			
		||||
    sectionColours: ['#fff'],
 | 
			
		||||
  },
 | 
			
		||||
  /** The object containing configurations specific for timeline diagrams */
 | 
			
		||||
  timeline: {
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter      | Description                                          | Type    | Required | Values             |
 | 
			
		||||
     * | -------------- | ---------------------------------------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | diagramMarginX | Margin to the right and left of the sequence diagram | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 50
 | 
			
		||||
     */
 | 
			
		||||
    diagramMarginX: 50,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter      | Description                                        | Type    | Required | Values             |
 | 
			
		||||
     * | -------------- | -------------------------------------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | diagramMarginY | Margin to the over and under the sequence diagram. | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 10
 | 
			
		||||
     */
 | 
			
		||||
    diagramMarginY: 10,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter   | Description           | Type    | Required | Values             |
 | 
			
		||||
     * | ----------- | --------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | actorMargin | Margin between actors | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 50
 | 
			
		||||
     */
 | 
			
		||||
    leftMargin: 150,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter | Description          | Type    | Required | Values             |
 | 
			
		||||
     * | --------- | -------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | width     | Width of actor boxes | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 150
 | 
			
		||||
     */
 | 
			
		||||
    width: 150,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter | Description           | Type    | Required | Values             |
 | 
			
		||||
     * | --------- | --------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | height    | Height of actor boxes | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 65
 | 
			
		||||
     */
 | 
			
		||||
    height: 50,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter | Description              | Type    | Required | Values             |
 | 
			
		||||
     * | --------- | ------------------------ | ------- | -------- | ------------------ |
 | 
			
		||||
     * | boxMargin | Margin around loop boxes | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 10
 | 
			
		||||
     */
 | 
			
		||||
    boxMargin: 10,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter     | Description                                  | Type    | Required | Values             |
 | 
			
		||||
     * | ------------- | -------------------------------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | boxTextMargin | Margin around the text in loop/alt/opt boxes | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 5
 | 
			
		||||
     */
 | 
			
		||||
    boxTextMargin: 5,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter  | Description         | Type    | Required | Values             |
 | 
			
		||||
     * | ---------- | ------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | noteMargin | Margin around notes | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 10
 | 
			
		||||
     */
 | 
			
		||||
    noteMargin: 10,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter     | Description             | Type    | Required | Values             |
 | 
			
		||||
     * | ------------- | ----------------------- | ------- | -------- | ------------------ |
 | 
			
		||||
     * | messageMargin | Space between messages. | Integer | Required | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:**
 | 
			
		||||
     *
 | 
			
		||||
     * Space between messages.
 | 
			
		||||
     *
 | 
			
		||||
     * Default value: 35
 | 
			
		||||
     */
 | 
			
		||||
    messageMargin: 35,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter    | Description                 | Type | Required | Values                    |
 | 
			
		||||
     * | ------------ | --------------------------- | ---- | -------- | ------------------------- |
 | 
			
		||||
     * | messageAlign | Multiline message alignment | 3    | 4        | 'left', 'center', 'right' |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:** Default value: 'center'
 | 
			
		||||
     */
 | 
			
		||||
    messageAlign: 'center',
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter       | Description                                | Type    | Required | Values             |
 | 
			
		||||
     * | --------------- | ------------------------------------------ | ------- | -------- | ------------------ |
 | 
			
		||||
     * | bottomMarginAdj | Prolongs the edge of the diagram downwards | Integer | 4        | Any Positive Value |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:**
 | 
			
		||||
     *
 | 
			
		||||
     * Depending on css styling this might need adjustment.
 | 
			
		||||
     *
 | 
			
		||||
     * Default value: 1
 | 
			
		||||
     */
 | 
			
		||||
    bottomMarginAdj: 1,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter   | Description | Type    | Required | Values      |
 | 
			
		||||
     * | ----------- | ----------- | ------- | -------- | ----------- |
 | 
			
		||||
     * | useMaxWidth | See notes   | boolean | 4        | true, false |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:**
 | 
			
		||||
     *
 | 
			
		||||
     * When this flag is set the height and width is set to 100% and is then scaling with the
 | 
			
		||||
     * available space if not the absolute space required is used.
 | 
			
		||||
     *
 | 
			
		||||
     * Default value: true
 | 
			
		||||
     */
 | 
			
		||||
    useMaxWidth: true,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * | Parameter   | Description                       | Type | Required | Values      |
 | 
			
		||||
     * | ----------- | --------------------------------- | ---- | -------- | ----------- |
 | 
			
		||||
     * | rightAngles | Curved Arrows become Right Angles | 3    | 4        | true, false |
 | 
			
		||||
     *
 | 
			
		||||
     * **Notes:**
 | 
			
		||||
     *
 | 
			
		||||
     * This will display arrows that start and begin at the same node as right angles, rather than a
 | 
			
		||||
     * curves
 | 
			
		||||
     *
 | 
			
		||||
     * Default value: false
 | 
			
		||||
     */
 | 
			
		||||
    rightAngles: false,
 | 
			
		||||
    taskFontSize: 14,
 | 
			
		||||
    taskFontFamily: '"Open Sans", sans-serif',
 | 
			
		||||
    taskMargin: 50,
 | 
			
		||||
    // width of activation box
 | 
			
		||||
    activationWidth: 10,
 | 
			
		||||
 | 
			
		||||
    // text placement as: tspan | fo | old only text as before
 | 
			
		||||
    textPlacement: 'fo',
 | 
			
		||||
    actorColours: ['#8FBC8F', '#7CFC00', '#00FFFF', '#20B2AA', '#B0E0E6', '#FFFFE0'],
 | 
			
		||||
 | 
			
		||||
    sectionFills: ['#191970', '#8B008B', '#4B0082', '#2F4F4F', '#800000', '#8B4513', '#00008B'],
 | 
			
		||||
    sectionColours: ['#fff'],
 | 
			
		||||
  },
 | 
			
		||||
  class: {
 | 
			
		||||
    /**
 | 
			
		||||
     * ### titleTopMargin
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ import { sanitizeText as _sanitizeText } from '../diagrams/common/common';
 | 
			
		||||
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox';
 | 
			
		||||
import { addStylesForDiagram } from '../styles';
 | 
			
		||||
import { DiagramDefinition, DiagramDetector } from './types';
 | 
			
		||||
import  * as  _commonDb  from '../commonDb';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Packaging and exposing resources for external diagrams so that they can import
 | 
			
		||||
@@ -16,6 +18,7 @@ export const setLogLevel = _setLogLevel;
 | 
			
		||||
export const getConfig = _getConfig;
 | 
			
		||||
export const sanitizeText = (text: string) => _sanitizeText(text, getConfig());
 | 
			
		||||
export const setupGraphViewbox = _setupGraphViewbox;
 | 
			
		||||
export const getCommonDb = () => { return _commonDb };
 | 
			
		||||
 | 
			
		||||
const diagrams: Record<string, DiagramDefinition> = {};
 | 
			
		||||
export interface Detectors {
 | 
			
		||||
@@ -46,7 +49,7 @@ export const registerDiagram = (
 | 
			
		||||
  addStylesForDiagram(id, diagram.styles);
 | 
			
		||||
 | 
			
		||||
  if (diagram.injectUtils) {
 | 
			
		||||
    diagram.injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox);
 | 
			
		||||
    diagram.injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox,getCommonDb());
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ export interface InjectUtils {
 | 
			
		||||
  _getConfig: any;
 | 
			
		||||
  _sanitizeText: any;
 | 
			
		||||
  _setupGraphViewbox: any;
 | 
			
		||||
  _commonDb: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -27,7 +28,8 @@ export interface DiagramDefinition {
 | 
			
		||||
    _setLogLevel: InjectUtils['_setLogLevel'],
 | 
			
		||||
    _getConfig: InjectUtils['_getConfig'],
 | 
			
		||||
    _sanitizeText: InjectUtils['_sanitizeText'],
 | 
			
		||||
    _setupGraphViewbox: InjectUtils['_setupGraphViewbox']
 | 
			
		||||
    _setupGraphViewbox: InjectUtils['_setupGraphViewbox'],
 | 
			
		||||
    _commonDb: InjectUtils['_commonDb']
 | 
			
		||||
  ) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -349,6 +349,22 @@ importers:
 | 
			
		||||
        specifier: ^3.0.2
 | 
			
		||||
        version: 3.0.2
 | 
			
		||||
 | 
			
		||||
  packages/mermaid-timeline:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      d3:
 | 
			
		||||
        specifier: ^7.0.0
 | 
			
		||||
        version: 7.6.1
 | 
			
		||||
      khroma:
 | 
			
		||||
        specifier: ^2.0.0
 | 
			
		||||
        version: 2.0.0
 | 
			
		||||
    devDependencies:
 | 
			
		||||
      concurrently:
 | 
			
		||||
        specifier: ^7.4.0
 | 
			
		||||
        version: 7.5.0
 | 
			
		||||
      rimraf:
 | 
			
		||||
        specifier: ^3.0.2
 | 
			
		||||
        version: 3.0.2
 | 
			
		||||
 | 
			
		||||
  tests/webpack:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@mermaid-js/mermaid-mindmap':
 | 
			
		||||
@@ -3634,7 +3650,7 @@ packages:
 | 
			
		||||
  /axios/0.21.4_debug@4.3.2:
 | 
			
		||||
    resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      follow-redirects: 1.15.2_debug@4.3.2
 | 
			
		||||
      follow-redirects: 1.15.2
 | 
			
		||||
    transitivePeerDependencies:
 | 
			
		||||
      - debug
 | 
			
		||||
    dev: true
 | 
			
		||||
@@ -6309,7 +6325,7 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA==}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /follow-redirects/1.15.2_debug@4.3.2:
 | 
			
		||||
  /follow-redirects/1.15.2:
 | 
			
		||||
    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
 | 
			
		||||
    engines: {node: '>=4.0'}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
@@ -6317,8 +6333,6 @@ packages:
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      debug:
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      debug: 4.3.2
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /foreground-child/2.0.0:
 | 
			
		||||
@@ -6911,7 +6925,7 @@ packages:
 | 
			
		||||
    engines: {node: '>=8.0.0'}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      eventemitter3: 4.0.7
 | 
			
		||||
      follow-redirects: 1.15.2_debug@4.3.2
 | 
			
		||||
      follow-redirects: 1.15.2
 | 
			
		||||
      requires-port: 1.0.0
 | 
			
		||||
    transitivePeerDependencies:
 | 
			
		||||
      - debug
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user